#include "aa.h"
#include "request.h"
-void cgirls_freereq (cgirls_req req) {
- free(req.cgirls_project);
- free(req.cgirls_id);
- if (req.cgirls_subpath) {
+static char const*const cgirls_mtypes[] = {
+ [cgirls_mtype_txt] = ".txt",
+ [cgirls_mtype_htm] = ".htm",
+ [cgirls_mtype_xml] = ".xml",
+ [cgirls_mtype_rdf] = ".rdf",
+};
+constexpr size_t cgirls_n·mtypes =
+ sizeof(cgirls_mtypes) / sizeof(char*);
+
+static char const*const cgirls_vbs[] = {
+ [cgirls_vb_unknown] = "unknown",
+ [cgirls_vb_index] = "index",
+ [cgirls_vb_show] = "show",
+};
+constexpr size_t cgirls_n·vbs =
+ sizeof(cgirls_vbs) / sizeof(char*);
+static cgirls_vb const cgirls_parsable·vbs[] = {
+ cgirls_vb_index,
+ cgirls_vb_show,
+};
+constexpr size_t cgirls_n·parsable·vbs =
+ sizeof(cgirls_parsable·vbs) / sizeof(cgirls_vb);
+
+void cgirls_req·free (cgirls_req req) {
+ free(req.project);
+ free(req.id_THIS_WILL_CHANGE);
+ if (req.subpath) {
size_t i = 0;
- char* c = req.cgirls_subpath[i];
+ char* c = req.subpath[i];
while (c) {
free(c);
- c = req.cgirls_subpath[++i];
+ c = req.subpath[++i];
}
- free(req.cgirls_subpath);
+ free(req.subpath);
+ }
+ free(req.baseid_THIS_WILL_CHANGE);
+ free(req.status.message);
+}
+
+static char* cgirls_gobble·path(char const* ndx[1], char const*const end[1]) {
+ char const* eor = strchr(ndx[0], '/');
+ char* result = nullptr;
+ if (!eor) {
+ eor = end[0];
}
- free(req.cgirls_baseid);
- free(req.cgirls_status.cgirls_message);
+ if (eor > ndx[0]) {
+ result = strndup(ndx[0], eor - ndx[0]);
+ }
+ if (eor < end[0]) {
+ ndx[0] = eor + 1;
+ } else {
+ ndx[0] = end[0];
+ }
+ return result;
}
-cgirls_req cgirls_path2req(char const*const pathinfo) {
+cgirls_req cgirls_path·to·req(char const*const pathinfo) {
assert(pathinfo != nullptr);
// Initialize the result.
cgirls_req req = {
- .cgirls_action = cgirls_vb_index,
- .cgirls_type = cgirls_mediatype_any,
- .cgirls_project = nullptr,
- .cgirls_id = nullptr,
- .cgirls_subpath = nullptr,
- .cgirls_baseid = nullptr,
- .cgirls_status = {
- .cgirls_code = 200,
- .cgirls_message = nullptr,
+ .verb = cgirls_vb_unknown,
+ .mtype = cgirls_mtype_any,
+ .project = nullptr,
+ .id_THIS_WILL_CHANGE = nullptr,
+ .subpath = nullptr,
+ .baseid_THIS_WILL_CHANGE = nullptr,
+ .status = {
+ .code = 200,
+ .message = nullptr,
},
};
- // `sont´ stores the start of the next term; `eopi´ stores the end of
- // the `pathinfo´ string, excluding any extension.
- char const* sont = pathinfo;
- char const*const eopi = strchr(pathinfo, 0);
+ // `ndx´ stores the start of the next term; `end´ stores the end of
+ // the `pathinfo´ string.
+ char const* ndx[1] = { pathinfo };
+ char const*const end[1] = { strchr(pathinfo, 0) };
+ assert(end[0] != nullptr);
// The portion of the pathinfo which precedes the first slash gives
// the project of the request. If there is no first slash, the
// project extends to the end of the string. An empty string is
// equivalent to having no project.
- char const* eopj = strchr(sont, '/');
- if (!eopj) {
- eopj = eopi;
- }
- if (eopj > sont) {
- req.cgirls_project = strndup(sont, eopj - sont);
- }
- if (eopj < eopi) {
- sont = eopj + 1;
- } else {
- sont = eopi;
- }
+ req.project = cgirls_gobble·path(ndx, end);
- // The portion of the pathinfo which follows the first slash but
- // precedes the second gives the verb of the request. If there is no
- // second slash, the verb extends to the end of the string. If the
- // verb is not present, or is the empty string, it is treated as
- // `"index"´, unless the second slash is present, in which case it is
- // treated as `"unknown"´.
- //
- // Verbs may be suffixed with one of a small number of extensions to
- // request a specific type of response.
- //
- // Only a few verbs are recognized (corresponding to the `cgirls_vb´
- // constants). If a verb is present, but unrecognized, it is assigned
- // the special value `cgirls_vb_unknown´, which should generally be
- // interpreted as an error.
- char const* eovb = strchr(sont, '/');
- if (!eovb) {
- eovb = eopi;
- }
- char const*const eove = eovb;
- char* verb = nullptr;
- if (eovb - sont > 4) {
- // If the verb is at least 5 characters, extract the extension if
- // present (it will be the last 4), and then set the end of the
- // verb to the start of the extension.
- char const* exts = eovb - 4;
- do {
- // This “loop” encapsulates extension checking for readability.
- // If an extension matches, `eovb´ is re·assigned to point to the
- // beginning of the extension. Otherwise, the loop exits early
- // and `eovb´ keeps pointing at the end of the string.
- if (strncmp(exts, ".txt", 4) == 0) {
- req.cgirls_type = cgirls_mediatype_txt;
- } else if (strncmp(exts, ".htm", 4) == 0) {
- req.cgirls_type = cgirls_mediatype_htm;
- } else if (strncmp(exts, ".xml", 4) == 0) {
- req.cgirls_type = cgirls_mediatype_xml;
- } else if (strncmp(exts, ".rdf", 4) == 0) {
- req.cgirls_type = cgirls_mediatype_rdf;
- } else {
- break; // do not re·assign `eovb´
+ // The portion of the pathinfo which follows the first slash but
+ // precedes the second gives the action of the request. If there is
+ // no second slash, the action extends to the end of the string. If
+ // the action is not present, or is the empty string, it is treated
+ // as `"index"´, unless the second slash is present, in which case it
+ // is treated as `"unknown"´.
+ //
+ // Actions consist of verbs optionally suffixed with one of a small
+ // number of extensions to request a specific type of response.
+ //
+ // Only a few verbs are recognized (corresponding to the `cgirls_vb´
+ // constants). If a verb is present, but unrecognized, it is assigned
+ // the special value `cgirls_vb_unknown´, which should generally be
+ // interpreted as an error.
+ char* soa = cgirls_gobble·path(ndx, end);
+ if (soa) {
+ char*const eoa = strchr(soa, 0);
+ if (eoa - soa > 4) {
+ // If the verb is at least 5 characters, extract the extension if
+ // present (it will be the last 4). Then set the first character
+ // of the extension to null, effectively trimming the verb.
+ char* ext = eoa - 4;
+ for (cgirls_mtype i = 0; i < cgirls_n·mtypes; ++i) {
+ char const*const ixt = cgirls_mtypes[i];
+ if (ixt && strncmp(ext, ixt, 4) == 0) {
+ req.mtype = i;
+ ext[0] = 0;
+ break;
+ }
+ }
+ }
+ for (size_t i = 0; i < cgirls_n·parsable·vbs; ++i) {
+ cgirls_vb ivb = cgirls_parsable·vbs[i];
+ char const*const svb = cgirls_vbs[ivb];
+ if (svb && strcmp(soa, svb) == 0) {
+ req.verb = ivb;
+ break;
}
- eovb = exts;
- } while (false);
- }
- if (eovb > sont) {
- verb = strndup(sont, eovb - sont);
- }
- if (eove < eopi) {
- sont = eove + 1;
- } else {
- sont = eopi;
- }
- if (verb) {
- if (strcmp(verb, "branches") == 0) {
- req.cgirls_action = cgirls_vb_branches;
- } else if (strcmp(verb, "tags") == 0) {
- req.cgirls_action = cgirls_vb_tags;
- } else if (strcmp(verb, "show") == 0) {
- req.cgirls_action = cgirls_vb_show;
- } else if (strcmp(verb, "raw") == 0) {
- req.cgirls_action = cgirls_vb_raw;
- } else if (strcmp(verb, "blame") == 0) {
- req.cgirls_action = cgirls_vb_blame;
- } else if (strcmp(verb, "log") == 0) {
- req.cgirls_action = cgirls_vb_log;
- } else if (strcmp(verb, "shortlog") == 0) {
- req.cgirls_action = cgirls_vb_shortlog;
- } else if (strcmp(verb, "atom") == 0) {
- req.cgirls_action = cgirls_vb_atom;
- } else if (strcmp(verb, "patch") == 0) {
- req.cgirls_action = cgirls_vb_patch;
- } else if (strcmp(verb, "index") != 0) {
- req.cgirls_action = cgirls_vb_unknown;
}
- free(verb);
- } else if (eovb < eopi) {
- req.cgirls_action = cgirls_vb_unknown;
+ free(soa);
+ } else if (ndx[0] == end[0]) {
+ req.verb = cgirls_vb_index;
}
- // The portion of the pathinfo which follows the second slash but
- // precedes the third identifies the identifiers for the request. If
- // there is no third slash, the identifiers extend to the end of the
- // string. A single identifier may be given, or two identifiers may
- // be given separated by two periods. An empty string is equivalent
- // to no identifier.
- char const* eoid = strchr(sont, '/');
- if (!eoid) {
- eoid = eopi;
- }
- char* idid = nullptr;
- if (eoid > sont) {
- idid = strndup(sont, eoid - sont);
- }
- if (eoid < eopi) {
- sont = eoid + 1;
- } else {
- sont = eopi;
- }
+ // The portion of the pathinfo which follows the second slash but
+ // precedes the third identifies the identifiers for the request. If
+ // there is no third slash, the identifiers extend to the end of the
+ // string. A single identifier may be given, or two identifiers may
+ // be given separated by two periods. An empty string is equivalent
+ // to no identifier.
+ char* idid = cgirls_gobble·path(ndx, end);
if (idid) {
// If the identifier string contains two successive dots, the base
// and target identifiers must be extracted and the original
char const*const eods = dots + 2;
char const*const eoii = strchr(idid, 0);
if (dots > idid) {
- req.cgirls_baseid = strndup(idid, dots - idid);
+ req.baseid_THIS_WILL_CHANGE = strndup(idid, dots - idid);
}
if (eods < eoii) {
- req.cgirls_id = strndup(eods, eoii - eods);
+ req.id_THIS_WILL_CHANGE = strndup(eods, eoii - eods);
}
free(idid);
} else {
- req.cgirls_id = idid;
+ req.id_THIS_WILL_CHANGE = idid;
}
}
// The portion of the pathinfo which follows the third slash is the
// subpath of the request. An empty sting is equivalent to having no
// subpath. Trailing and successive slashes are dropped.
- char const* soct = sont;
- char const* psep = nullptr;
- size_t npth = 0;
- while (eopi > soct) {
+ char const* sep = nullptr;
+ size_t n·s = 0;
+ for (
+ char const* sos = ndx[0];
+ sos < end[0];
+ sos = (end[0] > sep ? sep + 1 : end[0])
+ ) {
// Count the number of segments in the pathinfo so that the correct
// amount of space can be allocated.
- psep = strchr(soct, '/');
- if (!psep) {
- psep = eopi;
+ sep = strchr(sos, '/');
+ if (!sep) {
+ sep = end[0];
}
- if (psep > soct) {
- ++npth;
+ if (sep > sos) {
+ ++n·s;
}
- if (eopi > psep) {
- soct = psep + 1;
- } else {
- soct = eopi;
- }
- }
- req.cgirls_subpath = calloc(npth + 1, sizeof(char*));
- if (!req.cgirls_subpath) {
- return req;
}
- size_t pthi = 0;
- while (eopi > sont) {
- // Add the segments to the newly allocated array.
- psep = strchr(sont, '/');
- if (!psep) {
- psep = eopi;
+ if (n·s > 0) {
+ req.subpath = calloc(n·s + 1, sizeof(char*));
+ if (!req.subpath) {
+ return req;
}
- if (psep > sont) {
- req.cgirls_subpath[pthi++] = strndup(sont, psep - sont);
- }
- if (eopi > psep) {
- sont = psep + 1;
- } else {
- sont = eopi;
+ for (
+ size_t i·s = 0;
+ i·s < n·s;
+ ndx[0] = (end[0] > sep ? sep + 1 : end[0])
+ ) {
+ // Add the segments to the newly allocated array.
+ sep = strchr(ndx[0], '/');
+ if (!sep) {
+ sep = end[0];
+ }
+ if (sep > ndx[0]) {
+ req.subpath[i·s++] = strndup(ndx[0], sep - ndx[0]);
+ }
}
+ req.subpath[n·s] = nullptr;
}
- assert(pthi == npth);
- req.cgirls_subpath[pthi] = nullptr;
// Return the result.
return req;
}
-char* cgirls_req2path(cgirls_req req) {
- char* action = "unknown";
- char* extnsn = "";
- size_t length = 8; // length of `action´ plus 1, to start
+char* cgirls_req·to·path(cgirls_req req) {
+ char const* vb = nullptr;
+ char const* mtype = nullptr;
+ bool has·ids = req.baseid_THIS_WILL_CHANGE || req.id_THIS_WILL_CHANGE;
+ bool has·subpath = req.subpath && req.subpath[0];
+ size_t length = 0;
- // Get the length of the various parts, saving the verb and the
- // extension. This length includes a trailing slash, but in practice
- // this will be replaced by the final null byte.
- switch (req.cgirls_action) {
- case cgirls_vb_index:
- action = "index";
- length = 6;
- break;
- case cgirls_vb_branches:
- action = "branches";
- length = 9;
- break;
- case cgirls_vb_tags:
- action = "tags";
- length = 5;
- break;
- case cgirls_vb_show:
- action = "show";
- length = 5;
- break;
- case cgirls_vb_raw:
- action = "raw";
- length = 4;
- break;
- case cgirls_vb_blame:
- action = "blame";
- length = 6;
- break;
- case cgirls_vb_log:
- action = "log";
- length = 4;
- break;
- case cgirls_vb_shortlog:
- action = "shortlog";
- length = 9;
- break;
- case cgirls_vb_atom:
- action = "atom";
- length = 5;
- break;
- case cgirls_vb_patch:
- action = "patch";
- length = 6;
- break;
- default:
- break;
+ // Get the string corresponding to the verb. Do not assume that the
+ // verb is welbehaved (actually corresponding to an enumeration
+ // constant).
+ if (req.verb < cgirls_n·vbs) {
+ vb = cgirls_vbs[req.verb];
}
- switch (req.cgirls_type) {
- case cgirls_mediatype_txt:
- extnsn = ".txt";
- break;
- case cgirls_mediatype_htm:
- extnsn = ".htm";
- break;
- case cgirls_mediatype_xml:
- extnsn = ".xml";
- break;
- case cgirls_mediatype_rdf:
- extnsn = ".rdf";
- break;
- default:
- break;
+ if (!vb) {
+ vb = cgirls_vbs[cgirls_vb_unknown];
}
- if (req.cgirls_project) {
- length += strlen(req.cgirls_project) + 1;
- if (req.cgirls_type != cgirls_mediatype_any) {
- length += 4;
+
+ // Get the string corresponding to the mediatype, or `nullptr´. Do
+ // not assume that the verb is welbehaved (actually corresponding to
+ // an enumeration constant).
+ if (req.mtype < cgirls_n·mtypes) {
+ mtype = cgirls_mtypes[req.mtype];
+ }
+
+ // Get the length of the various parts. This length includes a
+ // trailing slash, but in practice this will be replaced by the final
+ // null byte.
+ if (req.project) {
+ length += strlen(req.project) + 1;
+ if (req.verb != cgirls_vb_index || mtype || has·ids || has·subpath) {
+ length += strlen(vb) + 1;
}
- if (req.cgirls_baseid || req.cgirls_id) {
- if (req.cgirls_baseid) {
- length += strlen(req.cgirls_baseid) + 2;
+ if (mtype) {
+ length += strlen(mtype);
+ }
+ if (has·ids) {
+ if (req.baseid_THIS_WILL_CHANGE) {
+ length += strlen(req.baseid_THIS_WILL_CHANGE) + 2;
}
- if (req.cgirls_id) {
- length += strlen(req.cgirls_id);
+ if (req.id_THIS_WILL_CHANGE) {
+ length += strlen(req.id_THIS_WILL_CHANGE);
}
length += 1;
- } else if (req.cgirls_subpath && req.cgirls_subpath[0]) {
+ } else if (has·subpath) {
length += 3;
}
- if (req.cgirls_subpath) {
+ if (has·subpath) {
size_t i = 0;
- char* c = req.cgirls_subpath[i];
+ char* c = req.subpath[i];
while (c) {
length += strlen(c) + 1;
- c = req.cgirls_subpath[++i];
+ c = req.subpath[++i];
}
}
} else {
return nullptr;
}
char* cursor = result;
- if (req.cgirls_project) {
- cursor = stpcpy(cursor, req.cgirls_project);
+ if (req.project) {
+ cursor = stpcpy(cursor, req.project);
(cursor++)[0] = '/';
- cursor = stpcpy(cursor, action);
- if (req.cgirls_type != cgirls_mediatype_any) {
- cursor = stpcpy(cursor, extnsn);
+ if (req.verb != cgirls_vb_index || mtype || has·ids || has·subpath) {
+ cursor = stpcpy(cursor, vb);
+ if (mtype) {
+ cursor = stpcpy(cursor, mtype);
+ }
+ (cursor++)[0] = '/';
}
- (cursor++)[0] = '/';
- if (req.cgirls_baseid || req.cgirls_id) {
- if (req.cgirls_baseid) {
- cursor = stpcpy(cursor, req.cgirls_baseid);
- cursor[0] = '.';
- cursor[1] = '.';
- cursor += 2;
+ if (has·ids) {
+ if (req.baseid_THIS_WILL_CHANGE) {
+ cursor = stpcpy(cursor, req.baseid_THIS_WILL_CHANGE);
+ cursor = stpcpy(cursor, "..");
}
- if (req.cgirls_id) {
- cursor = stpcpy(cursor, req.cgirls_id);
+ if (req.id_THIS_WILL_CHANGE) {
+ cursor = stpcpy(cursor, req.id_THIS_WILL_CHANGE);
}
(cursor++)[0] = '/';
- } else if (req.cgirls_subpath && req.cgirls_subpath[0]) {
+ } else if (has·subpath) {
cursor = stpcpy(cursor, "../");
}
- if (req.cgirls_subpath) {
+ if (has·subpath) {
size_t i = 0;
- char* c = req.cgirls_subpath[i];
+ char* c = req.subpath[i];
while (c) {
cursor = stpcpy(cursor, c);
- c = req.cgirls_subpath[++i];
+ c = req.subpath[++i];
(cursor++)[0] = '/';
}
}