X-Git-Url: https://git.ladys.computer/CGirls/blobdiff_plain/f6f2fd79a596ecedaadd8b605b7de9d8c662151c..d65576276453389c6d97085203b90108198ed187:/request.c

diff --git a/request.c b/request.c
index ca58f0b..f9fa453 100644
--- a/request.c
+++ b/request.c
@@ -4,159 +4,142 @@
 #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
@@ -167,156 +150,115 @@ cgirls_req cgirls_path2req(char const*const pathinfo) {
 			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 {
@@ -330,34 +272,34 @@ char* cgirls_req2path(cgirls_req req) {
 		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] = '/';
 			}
 		}