Lines of
xvfs-create-c.c
from check-in 2d9592486f
that are changed by the sequence of edits moving toward
check-in 36d0805e0e:
1: #include <sys/stat.h>
2: #include <stdlib.h>
3: #include <string.h>
4: #include <dirent.h>
5: #include <stdio.h>
6: #include <ctype.h>
7: #include <fcntl.h>
8:
9: struct options {
10: char *name;
11: char *directory;
12: };
13:
14: struct xvfs_state {
15: char **children;
16: unsigned long child_count;
17: unsigned long child_len;
18: int bucket_count;
19: int max_index;
20: };
21:
22: enum xvfs_minirivet_mode {
23: XVFS_MINIRIVET_MODE_COPY,
24: XVFS_MINIRIVET_MODE_TCL,
25: XVFS_MINIRIVET_MODE_TCL_PRINT
26: };
27:
28: /*
29: * adler32() function from zlib 1.1.4 and under the same license
30: */
31: static unsigned long adler32(unsigned long adler, const unsigned char *buf, unsigned int len) {
32: const int len_max = 5552;
33: const unsigned long base = 65521;
34: unsigned long s1 = adler & 0xffff;
35: unsigned long s2 = (adler >> 16) & 0xffff;
36: int k, i;
37:
38: if (buf == NULL) {
39: return(1UL);
40: }
41:
42: while (len > 0) {
43: k = len < len_max ? len : len_max;
44: len -= k;
45:
46: while (k >= 16) {
47: for (i = 0; i < 16; i++) {
48: s2 += buf[i];
49: }
50: s1 = buf[15];
51:
52: buf += 16;
53: k -= 16;
54: }
55:
56: if (k != 0) {
57: do {
58: s1 += *buf++;
59: s2 += s1;
60: } while (--k);
61: }
62:
63: s1 %= base;
64: s2 %= base;
65: }
66:
67: return((s2 << 16) | s1);
68: }
69:
70: /*
71: * Handle XVFS Rivet template file substitution
72: */
73: static void parse_xvfs_minirivet_file(FILE *outfp, const char * const external_file_name, const char * const internal_file_name) {
74: FILE *fp;
75: unsigned long file_size;
76: unsigned char buf[10];
77: size_t item_count;
78: int idx;
79:
80: fp = fopen(external_file_name, "rb");
81: if (!fp) {
82: return;
83: }
84:
85: fprintf(outfp, "\t{\n");
86: fprintf(outfp, "\t\t.name = \"%s\",\n", internal_file_name);
87: fprintf(outfp, "\t\t.type = XVFS_FILE_TYPE_REG,\n");
88: fprintf(outfp, "\t\t.data.fileContents = (const unsigned char *) \"");
89:
90: file_size = 0;
91: while (1) {
92: item_count = fread(&buf, 1, sizeof(buf), fp);
93: if (item_count <= 0) {
94: break;
95: }
96:
97: if (file_size != 0) {
98: fprintf(outfp, "\n\t\t\t\"");
99: }
100:
101: for (idx = 0; idx < item_count; idx++) {
102: fprintf(outfp, "\\x%02x", (int) buf[idx]);
103: }
104: fprintf(outfp, "\"");
105:
106: file_size += item_count;
107: }
108:
109: fclose(fp);
110:
111: fprintf(outfp, ",\n");
112: fprintf(outfp, "\t\t.size = %lu\n", file_size);
113: fprintf(outfp, "\t},\n");
114: }
115:
116: static void parse_xvfs_minirivet_directory(FILE *outfp, struct xvfs_state *xvfs_state, const char * const directory, const char * const prefix) {
117: const unsigned int max_path_len = 8192, max_children = 65536;
118: unsigned long child_idx, child_count;
119: DIR *dp;
120: struct dirent *file_info;
121: struct stat file_stat;
122: char *full_path_buf;
123: char *rel_path_buf;
124: char **children;
125: int stat_ret;
126: int snprintf_ret;
127:
128: dp = opendir(directory);
129: if (!dp) {
130: return;
131: }
132:
133: full_path_buf = malloc(max_path_len);
134: rel_path_buf = malloc(max_path_len);
135: children = malloc(sizeof(*children) * max_children);
136:
137: child_idx = 0;
138: while (1) {
139: file_info = readdir(dp);
140: if (!file_info) {
141: break;
142: }
143:
144: if (strcmp(file_info->d_name, ".") == 0) {
145: continue;
146: }
147:
148: if (strcmp(file_info->d_name, "..") == 0) {
149: continue;
150: }
151:
152: snprintf_ret = snprintf(full_path_buf, max_path_len, "%s/%s", directory, file_info->d_name);
153: if (snprintf_ret >= max_path_len) {
154: continue;
155: }
156:
157: snprintf_ret = snprintf(rel_path_buf, max_path_len, "%s%s%s",
158: prefix,
159: strcmp(prefix, "") == 0 ? "" : "/",
160: file_info->d_name
161: );
162: if (snprintf_ret >= max_path_len) {
163: continue;
164: }
165:
166: stat_ret = stat(full_path_buf, &file_stat);
167: if (stat_ret != 0) {
168: continue;
169: }
170:
171: children[child_idx] = strdup(file_info->d_name);
172: child_idx++;
173:
174: if (S_ISDIR(file_stat.st_mode)) {
175: parse_xvfs_minirivet_directory(outfp, xvfs_state, full_path_buf, rel_path_buf);
176: } else {
177: parse_xvfs_minirivet_file(outfp, full_path_buf, rel_path_buf);
178:
179: xvfs_state->children[xvfs_state->child_count] = strdup(rel_path_buf);
180: xvfs_state->child_count++;
181: }
182: }
183: free(full_path_buf);
184: free(rel_path_buf);
185:
186: child_count = child_idx;
187:
188: fprintf(outfp, "\t{\n");
189: fprintf(outfp, "\t\t.name = \"%s\",\n", prefix);
190: fprintf(outfp, "\t\t.type = XVFS_FILE_TYPE_DIR,\n");
2d9592486f 2019-12-02 191: fprintf(outfp, "\t\t.size = %lu,\n", child_count);
192: fprintf(outfp, "\t\t.data.dirChildren = (const char *[]) {");
193: for (child_idx = 0; child_idx < child_count; child_idx++) {
194: if (child_idx != 0) {
195: fprintf(outfp, ", ");
196: }
197:
198: fprintf(outfp, "\"%s\"", children[child_idx]);
199:
200: free(children[child_idx]);
201: }
2d9592486f 2019-12-02 202: fprintf(outfp, "}\n");
203:
204: free(children);
205:
206: fprintf(outfp, "\t},\n");
207:
208: xvfs_state->children[xvfs_state->child_count] = strdup(prefix);
209: xvfs_state->child_count++;
210:
211: closedir(dp);
212:
213: return;
214: }
215:
216: static void parse_xvfs_minirivet_hashtable_header(FILE *outfp, struct xvfs_state *xvfs_state) {
217: const int max_bucket_count = 30;
218: int bucket_count;
219: int idx1, idx2;
220: int check_hash;
221: int first_entry;
222:
223: if (xvfs_state->child_count > max_bucket_count) {
224: bucket_count = max_bucket_count;
225: } else {
226: bucket_count = xvfs_state->child_count;
227: }
228: xvfs_state->bucket_count = bucket_count;
229: xvfs_state->max_index = xvfs_state->child_count;
230:
231: fprintf(outfp, "\tlong pathIndex_idx;\n");
232: fprintf(outfp, "\tint pathIndex_hash;\n");
233:
234: /*
235: * XXX:TODO: Make this not O(n^2)
236: */
237: for (idx1 = 0; idx1 < bucket_count; idx1++) {
238: fprintf(outfp, "\tstatic const long pathIndex_hashTable_%i[] = {\n", idx1);
239: fprintf(outfp, "\t\t");
240: first_entry = 1;
241:
242: for (idx2 = 0; idx2 < xvfs_state->child_count; idx2++) {
243: check_hash = adler32(0, (unsigned char *) xvfs_state->children[idx2], strlen(xvfs_state->children[idx2])) % bucket_count;
244: if (check_hash != idx1) {
245: continue;
246: }
247:
248: if (!first_entry) {
249: fprintf(outfp, ", ");
250: }
251: first_entry = 0;
252:
253: if (check_hash == idx1) {
254: fprintf(outfp, "%i", idx2);
255: }
256: }
257:
258: if (!first_entry) {
259: fprintf(outfp, ", ");
260: }
261: fprintf(outfp, "XVFS_NAME_LOOKUP_ERROR");
262:
263: fprintf(outfp, "\n");
264:
265: fprintf(outfp, "\t};\n");
266: }
267:
268: for (idx2 = 0; idx2 < xvfs_state->child_count; idx2++) {
269: free(xvfs_state->children[idx2]);
270: }
271: free(xvfs_state->children);
272:
273: fprintf(outfp, "\tstatic const long * const pathIndex_hashTable[%i] = {\n", bucket_count);
274: for (idx1 = 0; idx1 < bucket_count; idx1++) {
275: fprintf(outfp, "\t\tpathIndex_hashTable_%i,\n", idx1);
276: }
277: fprintf(outfp, "\t};\n");
278: return;
279: }
280:
281: static void parse_xvfs_minirivet_hashtable_body(FILE *outfp, struct xvfs_state *xvfs_state) {
282: fprintf(outfp, "\tpathIndex_hash = Tcl_ZlibAdler32(0, (unsigned char *) path, pathLen) %% %i;\n", xvfs_state->bucket_count);
283: fprintf(outfp, "\tfor (pathIndex_idx = 0; pathIndex_idx < %i; pathIndex_idx++) {\n", xvfs_state->max_index);
284: fprintf(outfp, "\t\tpathIndex = pathIndex_hashTable[pathIndex_hash][pathIndex_idx];\n");
285: fprintf(outfp, "\t\tif (pathIndex == XVFS_NAME_LOOKUP_ERROR) {\n");
286: fprintf(outfp, "\t\t\tbreak;\n");
287: fprintf(outfp, "\t\t}\n");
288: fprintf(outfp, "\n");
289: fprintf(outfp, "\t\tif (strcmp(path, xvfs_example_data[pathIndex].name) == 0) {\n");
290: fprintf(outfp, "\t\t\treturn(pathIndex);\n");
291: fprintf(outfp, "\t\t}\n");
292: fprintf(outfp, "\t}\n");
293: return;
294: }
295:
296: static void parse_xvfs_minirivet_handle_tcl_print(FILE *outfp, const struct options * const options, struct xvfs_state *xvfs_state, char *command) {
297: char *buffer_p, *buffer_e;
298:
299: buffer_p = command;
300: while (*buffer_p && isspace(*buffer_p)) {
301: buffer_p++;
302: }
303:
304: buffer_e = buffer_p + strlen(buffer_p) - 1;
305: while (buffer_e >= buffer_p && isspace(*buffer_e)) {
306: *buffer_e = '\0';
307: buffer_e--;
308: }
309:
310: if (strcmp(buffer_p, "$::xvfs::fsName") == 0) {
311: fprintf(outfp, "%s", options->name);
312: } else if (strcmp(buffer_p, "$::xvfs::fileInfoStruct") == 0) {
313: fprintf(outfp, "static const struct xvfs_file_data xvfs_");
314: fprintf(outfp, "%s", options->name);
315: fprintf(outfp, "_data[] = {\n");
316: parse_xvfs_minirivet_directory(outfp, xvfs_state, options->directory, "");
317: fprintf(outfp, "};\n");
318: } else if (strcmp(buffer_p, "[zlib adler32 $::xvfs::fsName 0]") == 0) {
319: fprintf(outfp, "%lu", adler32(0, (unsigned char *) options->name, strlen(options->name)));
320: } else if (strcmp(buffer_p, "$hashTableHeader") == 0) {
321: parse_xvfs_minirivet_hashtable_header(outfp, xvfs_state);
322: } else if (strcmp(buffer_p, "[dict get $hashTable body]") == 0) {
323: parse_xvfs_minirivet_hashtable_body(outfp, xvfs_state);
324: } else {
325: fprintf(outfp, "@INVALID@%s@INVALID@", buffer_p);
326: }
327:
328: return;
329: }
330:
331: static int parse_xvfs_minirivet(FILE *outfp, const char * const template, const struct options * const options) {
332: struct xvfs_state xvfs_state;
333: int ch, ch_buf;
334: int template_idx = 0;
335: char tcl_buffer[8192], *tcl_buffer_p;
336: enum xvfs_minirivet_mode mode;
337:
338: xvfs_state.child_count = 0;
339: xvfs_state.child_len = 65536;
340: xvfs_state.children = malloc(sizeof(*xvfs_state.children) * xvfs_state.child_len);
341:
342: #define parse_xvfs_minirivet_getbyte(var) var = template[template_idx]; template_idx++; if (var == 0) { break; }
343:
344: mode = XVFS_MINIRIVET_MODE_COPY;
345: tcl_buffer_p = NULL;
346: while (1) {
347: parse_xvfs_minirivet_getbyte(ch);
348:
349: switch (mode) {
350: case XVFS_MINIRIVET_MODE_COPY:
351: if (ch == '<') {
352: parse_xvfs_minirivet_getbyte(ch_buf);
353: if (ch_buf != '?') {
354: fputc('<', outfp);
355: ch = ch_buf;
356:
357: break;
358: }
359:
360: tcl_buffer_p = tcl_buffer;
361: parse_xvfs_minirivet_getbyte(ch_buf);
362: if (ch_buf == '=') {
363: mode = XVFS_MINIRIVET_MODE_TCL_PRINT;
364: } else {
365: mode = XVFS_MINIRIVET_MODE_TCL;
366: *tcl_buffer_p = ch_buf;
367: tcl_buffer_p++;
368: }
369: *tcl_buffer_p = '\0';
370: continue;
371: }
372: break;
373: case XVFS_MINIRIVET_MODE_TCL:
374: case XVFS_MINIRIVET_MODE_TCL_PRINT:
375: if (ch == '?') {
376: parse_xvfs_minirivet_getbyte(ch_buf);
377: if (ch_buf != '>') {
378: *tcl_buffer_p = ch;
379: tcl_buffer_p++;
380:
381: ch = ch_buf;
382:
383: break;
384: }
385:
386: *tcl_buffer_p = '\0';
387:
388: if (mode == XVFS_MINIRIVET_MODE_TCL_PRINT) {
389: parse_xvfs_minirivet_handle_tcl_print(outfp, options, &xvfs_state, tcl_buffer);
390: }
391:
392: mode = XVFS_MINIRIVET_MODE_COPY;
393: continue;
394: }
395: break;
396: }
397:
398: switch (mode) {
399: case XVFS_MINIRIVET_MODE_COPY:
400: fputc(ch, outfp);
401: break;
402: case XVFS_MINIRIVET_MODE_TCL:
403: case XVFS_MINIRIVET_MODE_TCL_PRINT:
404: *tcl_buffer_p = ch;
405: tcl_buffer_p++;
406: break;
407: }
408: }
409:
410: #undef parse_xvfs_minirivet_getbyte
411:
412: return(1);
413: }
414:
415: static int xvfs_create(FILE *outfp, const struct options * const options) {
416: const int template_len = 65536;
417: const char * const template_file = "lib/xvfs/xvfs.c.rvt";
418: FILE *fp;
419: char *template, *template_p;
420: int template_remain;
421: size_t fread_ret;
422: int retval;
423:
424: fp = fopen(template_file, "r");
425: if (!fp) {
426: return(0);
427: }
428:
429: template = malloc(template_len);
430: template_remain = template_len;
431: if (!template) {
432: fclose(fp);
433:
434: return(0);
435: }
436:
437: template_p = template;
438: while (1) {
439: fread_ret = fread(template_p, 1, template_remain, fp);
440: if (fread_ret <= 0) {
441: break;
442: }
443:
444: template_p += fread_ret;
445: template_remain -= fread_ret;
446: }
447: if (template_remain > 0) {
448: *template_p = '\0';
449: }
450:
451: fclose(fp);
452:
453: retval = parse_xvfs_minirivet(outfp, template, options);
454:
455: free(template);
456:
457: return(retval);
458: }
459:
460: /*
461: * Parse command line options
462: */
463: static int parse_options(int argc, char **argv, struct options *options) {
464: char *arg;
465: char **option;
466: int idx;
467: int retval;
468:
469: for (idx = 0; idx < argc; idx++) {
470: arg = argv[idx];
471:
472: if (strcmp(arg, "--directory") == 0) {
473: option = &options->directory;
474: } else if (strcmp(arg, "--name") == 0) {
475: option = &options->name;
476: } else {
477: fprintf(stderr, "Invalid argument %s\n", arg);
478:
479: return(0);
480: }
481:
482: idx++;
483: arg = argv[idx];
484: *option = arg;
485: }
486:
487: retval = 1;
488: if (!options->directory) {
489: fprintf(stderr, "error: --directory must be specified\n");
490: retval = 0;
491: }
492:
493: if (!options->name) {
494: fprintf(stderr, "error: --name must be specified\n");
495: retval = 0;
496: }
497:
498: return(retval);
499: }
500:
501: int main(int argc, char **argv) {
502: struct options options = {0};
503: int parse_options_ret, xvfs_create_ret;
504:
505: argc--;
506: argv++;
507:
508: parse_options_ret = parse_options(argc, argv, &options);
509: if (!parse_options_ret) {
510: return(1);
511: }
512:
513: xvfs_create_ret = xvfs_create(stdout, &options);
514: if (!xvfs_create_ret) {
515: return(1);
516: }
517:
518: return(0);
519: }