1 // SPDX-FileCopyrightText: 2025 Lady <https://www.ladys.computer/about/#lady>
2 // SPDX-License-Identifier: GPL-2.0-only
7 static char const*const cgirls_mtypes
[] = {
8 [cgirls_mtype_txt
] = ".txt",
9 [cgirls_mtype_htm
] = ".htm",
10 [cgirls_mtype_xml
] = ".xml",
11 [cgirls_mtype_rdf
] = ".rdf",
13 constexpr size_t cgirls_n·mtypes
=
14 sizeof(cgirls_mtypes
) / sizeof(char*);
16 static char const*const cgirls_vbs
[] = {
17 [cgirls_vb_unknown
] = "unknown",
18 [cgirls_vb_index
] = "index",
19 [cgirls_vb_show
] = "show",
21 constexpr size_t cgirls_n·vbs
=
22 sizeof(cgirls_vbs
) / sizeof(char*);
23 static cgirls_vb
const cgirls_parsable·vbs
[] = {
27 constexpr size_t cgirls_n·parsable·vbs
=
28 sizeof(cgirls_parsable·vbs
) / sizeof(cgirls_vb
);
30 void cgirls_req·
free (cgirls_req req
) {
32 free(req
.id_THIS_WILL_CHANGE
);
35 char* c
= req
.subpath
[i
];
42 free(req
.baseid_THIS_WILL_CHANGE
);
43 free(req
.status
.message
);
46 static char* cgirls_gobble·
path(char const* ndx
[1], char const*const end
[1]) {
47 char const* eor
= strchr(ndx
[0], '/');
48 char* result
= nullptr;
53 result
= strndup(ndx
[0], eor
- ndx
[0]);
63 cgirls_req cgirls_path·to·
req(char const*const pathinfo
) {
64 assert(pathinfo
!= nullptr);
66 // Initialize the result.
68 .verb
= cgirls_vb_unknown
,
69 .mtype
= cgirls_mtype_any
,
71 .id_THIS_WILL_CHANGE
= nullptr,
73 .baseid_THIS_WILL_CHANGE
= nullptr,
80 // `ndx´ stores the start of the next term; `end´ stores the end of
81 // the `pathinfo´ string.
82 char const* ndx
[1] = { pathinfo
};
83 char const*const end
[1] = { strchr(pathinfo
, 0) };
84 assert(end
[0] != nullptr);
86 // The portion of the pathinfo which precedes the first slash gives
87 // the project of the request. If there is no first slash, the
88 // project extends to the end of the string. An empty string is
89 // equivalent to having no project.
90 req
.project
= cgirls_gobble·
path(ndx
, end
);
92 // The portion of the pathinfo which follows the first slash but
93 // precedes the second gives the action of the request. If there is
94 // no second slash, the action extends to the end of the string. If
95 // the action is not present, or is the empty string, it is treated
96 // as `"index"´, unless the second slash is present, in which case it
97 // is treated as `"unknown"´.
99 // Actions consist of verbs optionally suffixed with one of a small
100 // number of extensions to request a specific type of response.
102 // Only a few verbs are recognized (corresponding to the `cgirls_vb´
103 // constants). If a verb is present, but unrecognized, it is assigned
104 // the special value `cgirls_vb_unknown´, which should generally be
105 // interpreted as an error.
106 char* soa
= cgirls_gobble·
path(ndx
, end
);
108 char*const eoa
= strchr(soa
, 0);
110 // If the verb is at least 5 characters, extract the extension if
111 // present (it will be the last 4). Then set the first character
112 // of the extension to null, effectively trimming the verb.
114 for (cgirls_mtype i
= 0; i
< cgirls_n·mtypes
; ++i
) {
115 char const*const ixt
= cgirls_mtypes
[i
];
116 if (ixt
&& strncmp(ext
, ixt
, 4) == 0) {
123 for (size_t i
= 0; i
< cgirls_n·parsable·vbs
; ++i
) {
124 cgirls_vb ivb
= cgirls_parsable·vbs
[i
];
125 char const*const svb
= cgirls_vbs
[ivb
];
126 if (svb
&& strcmp(soa
, svb
) == 0) {
132 } else if (ndx
[0] == end
[0]) {
133 req
.verb
= cgirls_vb_index
;
136 // The portion of the pathinfo which follows the second slash but
137 // precedes the third identifies the identifiers for the request. If
138 // there is no third slash, the identifiers extend to the end of the
139 // string. A single identifier may be given, or two identifiers may
140 // be given separated by two periods. An empty string is equivalent
142 char* idid
= cgirls_gobble·
path(ndx
, end
);
144 // If the identifier string contains two successive dots, the base
145 // and target identifiers must be extracted and the original
146 // identifier string freed. Otherwise, the identifier string is the
147 // target identifier, and there is no base.
148 char const*const dots
= strstr(idid
, "..");
150 char const*const eods
= dots
+ 2;
151 char const*const eoii
= strchr(idid
, 0);
153 req
.baseid_THIS_WILL_CHANGE
= strndup(idid
, dots
- idid
);
156 req
.id_THIS_WILL_CHANGE
= strndup(eods
, eoii
- eods
);
160 req
.id_THIS_WILL_CHANGE
= idid
;
164 // The portion of the pathinfo which follows the third slash is the
165 // subpath of the request. An empty sting is equivalent to having no
166 // subpath. Trailing and successive slashes are dropped.
167 char const* sep
= nullptr;
170 char const* sos
= ndx
[0];
172 sos
= (end
[0] > sep
? sep
+ 1 : end
[0])
174 // Count the number of segments in the pathinfo so that the correct
175 // amount of space can be allocated.
176 sep
= strchr(sos
, '/');
185 req
.subpath
= calloc(n·s
+ 1, sizeof(char*));
192 ndx
[0] = (end
[0] > sep
? sep
+ 1 : end
[0])
194 // Add the segments to the newly allocated array.
195 sep
= strchr(ndx
[0], '/');
200 req
.subpath
[i·s
++] = strndup(ndx
[0], sep
- ndx
[0]);
203 req
.subpath
[n·s
] = nullptr;
206 // Return the result.
210 char* cgirls_req·to·
path(cgirls_req req
) {
211 char const* vb
= nullptr;
212 char const* mtype
= nullptr;
213 bool has·ids
= req
.baseid_THIS_WILL_CHANGE
|| req
.id_THIS_WILL_CHANGE
;
214 bool has·subpath
= req
.subpath
&& req
.subpath
[0];
217 // Get the string corresponding to the verb. Do not assume that the
218 // verb is welbehaved (actually corresponding to an enumeration
220 if (req
.verb
< cgirls_n·vbs
) {
221 vb
= cgirls_vbs
[req
.verb
];
224 vb
= cgirls_vbs
[cgirls_vb_unknown
];
227 // Get the string corresponding to the mediatype, or `nullptr´. Do
228 // not assume that the verb is welbehaved (actually corresponding to
229 // an enumeration constant).
230 if (req
.mtype
< cgirls_n·mtypes
) {
231 mtype
= cgirls_mtypes
[req
.mtype
];
234 // Get the length of the various parts. This length includes a
235 // trailing slash, but in practice this will be replaced by the final
238 length
+= strlen(req
.project
) + 1;
239 if (req
.verb
!= cgirls_vb_index
|| mtype
|| has·ids
|| has·subpath
) {
240 length
+= strlen(vb
) + 1;
243 length
+= strlen(mtype
);
246 if (req
.baseid_THIS_WILL_CHANGE
) {
247 length
+= strlen(req
.baseid_THIS_WILL_CHANGE
) + 2;
249 if (req
.id_THIS_WILL_CHANGE
) {
250 length
+= strlen(req
.id_THIS_WILL_CHANGE
);
253 } else if (has·subpath
) {
258 char* c
= req
.subpath
[i
];
260 length
+= strlen(c
) + 1;
261 c
= req
.subpath
[++i
];
265 // If there is no project, then the action must be removed, and the
266 // length is just that of the trailing slash.
269 // Create and compose the final path.
270 char* result
= calloc(length
, sizeof(char*));
274 char* cursor
= result
;
276 cursor
= stpcpy(cursor
, req
.project
);
278 if (req
.verb
!= cgirls_vb_index
|| mtype
|| has·ids
|| has·subpath
) {
279 cursor
= stpcpy(cursor
, vb
);
281 cursor
= stpcpy(cursor
, mtype
);
286 if (req
.baseid_THIS_WILL_CHANGE
) {
287 cursor
= stpcpy(cursor
, req
.baseid_THIS_WILL_CHANGE
);
288 cursor
= stpcpy(cursor
, "..");
290 if (req
.id_THIS_WILL_CHANGE
) {
291 cursor
= stpcpy(cursor
, req
.id_THIS_WILL_CHANGE
);
294 } else if (has·subpath
) {
295 cursor
= stpcpy(cursor
, "../");
299 char* c
= req
.subpath
[i
];
301 cursor
= stpcpy(cursor
, c
);
302 c
= req
.subpath
[++i
];
310 // At this point, `cursor´ points one ⹐past⹑ the last element of the
311 // array (this is allowed in C), and the last element is a slash.
312 // Rewind and set it to the null byte, and assert that everything was
315 assert((cursor
+ 1) - result
== length
);