int ap_cache_control(request_rec *r, cache_control_t *cc,
        const char *cc_header, const char *pragma_header, apr_table_t *headers)
{
    char *last;
    apr_status_t rv;

    if (cc->parsed) {
        return cc->cache_control || cc->pragma;
    }

    cc->parsed = 1;
    cc->max_age_value = -1;
    cc->max_stale_value = -1;
    cc->min_fresh_value = -1;
    cc->s_maxage_value = -1;

    if (pragma_header) {
        char *header = apr_pstrdup(r->pool, pragma_header), *token;
        for (rv = cache_strqtok(header, &token, NULL, &last);
             rv == APR_SUCCESS;
             rv = cache_strqtok(NULL, &token, NULL, &last)) {
            if (!ap_cstr_casecmp(token, "no-cache")) {
                cc->no_cache = 1;
            }
        }
        cc->pragma = 1;
    }

    if (cc_header) {
        char *header = apr_pstrdup(r->pool, cc_header), *token, *arg;
        for (rv = cache_strqtok(header, &token, &arg, &last);
             rv == APR_SUCCESS;
             rv = cache_strqtok(NULL, &token, &arg, &last)) {
            char *endp;
            apr_off_t offt;

            switch (token[0]) {
            case 'n':
            case 'N':
                if (!ap_cstr_casecmp(token, "no-cache")) {
                    if (!arg) {
                        cc->no_cache = 1;
                    }
                    else {
                        cc->no_cache_header = 1;
                    }
                }
                else if (!ap_cstr_casecmp(token, "no-store")) {
                    cc->no_store = 1;
                }
                else if (!ap_cstr_casecmp(token, "no-transform")) {
                    cc->no_transform = 1;
                }
                break;

            case 'm':
            case 'M':
                if (arg && !ap_cstr_casecmp(token, "max-age")) {
                    if (!apr_strtoff(&offt, arg, &endp, 10)
                            && endp > arg && !*endp) {
                        cc->max_age = 1;
                        cc->max_age_value = offt;
                    }
                }
                else if (!ap_cstr_casecmp(token, "must-revalidate")) {
                    cc->must_revalidate = 1;
                }
                else if (!ap_cstr_casecmp(token, "max-stale")) {
                    if (!arg) {
                        cc->max_stale = 1;
                        cc->max_stale_value = -1;
                    }
                    else if (!apr_strtoff(&offt, arg, &endp, 10)
                             && endp > arg && !*endp) {
                        cc->max_stale = 1;
                        cc->max_stale_value = offt;
                    }
                }
                else if (arg && !ap_cstr_casecmp(token, "min-fresh")) {
                    if (!apr_strtoff(&offt, arg, &endp, 10)
                            && endp > arg && !*endp) {
                        cc->min_fresh = 1;
                        cc->min_fresh_value = offt;
                    }
                }
                break;

            case 'o':
            case 'O':
                if (!ap_cstr_casecmp(token, "only-if-cached")) {
                    cc->only_if_cached = 1;
                }
                break;

            case 'p':
            case 'P':
                if (!ap_cstr_casecmp(token, "public")) {
                    cc->public = 1;
                }
                else if (!ap_cstr_casecmp(token, "private")) {
                    if (!arg) {
                        cc->private = 1;
                    }
                    else {
                        cc->private_header = 1;
                    }
                }
                else if (!ap_cstr_casecmp(token, "proxy-revalidate")) {
                    cc->proxy_revalidate = 1;
                }
                break;

            case 's':
            case 'S':
                if (arg && !ap_cstr_casecmp(token, "s-maxage")) {
                    if (!apr_strtoff(&offt, arg, &endp, 10)
                            && endp > arg && !*endp) {
                        cc->s_maxage = 1;
                        cc->s_maxage_value = offt;
                    }
                }
                break;
            }
        }
        cc->cache_control = 1;
    }

    return (cc_header != NULL || pragma_header != NULL);
}