f44517fd99747497bc925b2fa82c6c17283c81ca
[platform/upstream/libatasmart.git] / strpool.c
1 #include <stdlib.h>
2 #include <stdio.h>
3 #include <string.h>
4 #include <sys/types.h>
5 #include <assert.h>
6 #include <errno.h>
7
8 typedef struct item {
9     char *cnt;
10     size_t cntl;
11     char *text;
12     size_t size;
13     unsigned idx;
14     struct item *suffix_of;
15     struct item *next;
16 } item;
17
18 static void free_items(struct item *first) {
19
20     while (first) {
21         struct item *n = first->next;
22
23         free(first->cnt);
24         free(first->text);
25         free(first);
26
27         first = n;
28     }
29 }
30
31 static void find_suffixes(struct item *first) {
32     struct item *i, *j;
33
34     for (i = first; i; i = i->next) {
35         int right = 0;
36
37         for (j = first; j; j = j->next) {
38
39             if (i == j) {
40                 right = 1;
41                 continue;
42             }
43
44             if (i->size > j->size)
45                 continue;
46
47             if (i->size == j->size && !right)
48                 continue;
49
50             if (memcmp(i->text, j->text+j->size-i->size, i->size) != 0)
51                 continue;
52
53             i->suffix_of = j;
54             break;
55         }
56     }
57 }
58
59 static void fill_idx(struct item *first) {
60     struct item *i;
61     unsigned k = 0;
62
63     for (i = first; i; i = i->next) {
64         if (i->suffix_of)
65             continue;
66
67         i->idx = k;
68         k += i->size+1;
69     }
70
71     for (i = first; i; i = i->next) {
72         struct item *p;
73
74         if (!i->suffix_of)
75             continue;
76
77         for (p = i->suffix_of; p->suffix_of; p = p->suffix_of)
78             ;
79
80         assert(i->size <= p->size);
81         assert(memcmp(i->text, p->text + p->size - i->size, i->size) == 0);
82
83         i->idx = p->idx + p->size - i->size;
84     }
85 }
86
87 static void dump_string(FILE *out, struct item *i) {
88     const char *t;
89
90     fputs("\n\t\"", out);
91
92     for (t = i->text; t < i->text+i->size; t++) {
93         switch (*t) {
94             case '\\':
95                 fputs("\\\\", out);
96                 break;
97             case '\"':
98                 fputs("\\\"", out);
99                 break;
100             case '\'':
101                 fputs("\\'", out);
102                 break;
103             case '\n':
104                 fputs("\\n\"\n\t\"", out);
105                 break;
106             case '\r':
107                 fputs("\\r", out);
108                 break;
109             case '\b':
110                 fputs("\\b", out);
111                 break;
112             case '\t':
113                 fputs("\\t", out);
114                 break;
115             case '\f':
116                 fputs("\\f", out);
117                 break;
118             case '\a':
119                 fputs("\\f", out);
120                 break;
121             case '\v':
122                 fputs("\\v", out);
123                 break;
124             default:
125                 if (*t >= 32 && *t < 127)
126                     putc(*t, out);
127                 else
128                     fprintf(out, "\\x%02x", *t);
129                 break;
130         }
131     }
132
133     fputs("\\0\"", out);
134 }
135
136 static void dump_text(FILE *out, struct item *first) {
137     struct item *i;
138
139     for (i = first; i; i = i->next) {
140
141         if (i->cnt)
142             fwrite(i->cnt, 1, i->cntl, out);
143
144         fprintf(out, "((const char*) %u)", i->idx);
145     }
146 }
147
148 static void dump_pool(FILE *out, struct item *first) {
149     struct item *i;
150     int saved_rel=-1, saved_bytes=0, saved_strings=0;
151
152     for (i = first; i; i = i->next) {
153         saved_rel++;
154
155         if (i->suffix_of) {
156             saved_strings ++;
157             saved_bytes += i->size;
158         }
159     }
160
161     fprintf(out, "/* Saved %i relocations, saved %i strings (%i b) due to suffix compression. */\n", saved_rel, saved_strings, saved_bytes);
162
163     fputs("static const char _strpool_[] =", out);
164
165     for (i = first; i; i = i->next) {
166
167         if (i->suffix_of)
168             fputs("\n\t/*** Suppressed due to suffix: ", out);
169
170         dump_string(out, i);
171
172         if (i->suffix_of)
173             fputs(" ***/", out);
174
175     }
176
177     fputs(";\n", out);
178 }
179
180 static char *append(char *r, size_t *rl, char **c, size_t n) {
181
182     r = realloc(r, *rl + n);
183
184     if (!r)
185         abort();
186
187     memcpy(r + *rl, *c, n);
188
189     *rl += n;
190     *c += n;
191
192     return r;
193 }
194
195 static int parse_hex_digit(char c) {
196
197     if (c >= '0' && c <= '9')
198         return c - '0' + 0x0;
199
200     if (c >= 'a' && c <= 'f')
201         return c - 'a' + 0xA;
202
203     if (c >= 'A' && c <= 'F')
204         return c - 'A' + 0xA;
205
206     return -1;
207 }
208
209 static int parse_hex(const char *t, char *r) {
210     int a, b = 0;
211     int k = 1;
212
213     if ((a = parse_hex_digit(t[0])) < 0)
214         return -1;
215
216     if (t[1]) {
217         if ((b = parse_hex_digit(t[1])) < 0)
218             b = 0;
219         else
220             k = 2;
221     }
222
223     *r = (a << 4) | b;
224     return k;
225 }
226
227 static int parse_oct_digit(char c) {
228
229     if (c >= '0' && c <= '7')
230         return c - '0';
231
232     return -1;
233 }
234
235 static int parse_oct(const char *t, char *r) {
236     int a, b = 0, c = 0, m;
237     int k = 1;
238
239     if ((a = parse_oct_digit(t[0])) < 0)
240         return -1;
241
242     if (t[1]) {
243
244         if ((b = parse_oct_digit(t[1])) < 0)
245             b = 0;
246         else {
247             k = 2;
248
249             if (t[2]) {
250
251                 if ((c = parse_oct_digit(t[2])) < 0)
252                     c = 0;
253                 else
254                     k = 3;
255             }
256         }
257     }
258
259     m = (a << 6) | (b << 3) | c;
260
261     if (m > 0xFF)
262         return -1;
263
264     *r = (char) m;
265
266     return k;
267 }
268
269 static int parse(FILE *in, const char *fname, struct item **rfirst, char **remain, size_t *remain_size) {
270
271     int enabled = 0;
272     enum {
273         STATE_TEXT,
274         STATE_COMMENT_C,
275         STATE_COMMENT_CPP,
276         STATE_STRING,
277         STATE_CHAR,
278     } state = STATE_TEXT;
279
280     char *r = NULL;
281     size_t rl = 0;
282     char *cnt = NULL;
283     size_t cntl = 0;
284     struct item *first = NULL, *last = NULL;
285     unsigned nline = 0;
286     unsigned pool_started_line = 0;
287     *rfirst = NULL;
288
289     if (!fname)
290         fname = "<stdin>";
291
292     for (;;) {
293         char t[1024], *c;
294         int done = 0;
295
296         if (!(fgets(t, sizeof(t), in))) {
297
298             if (feof(in))
299                 break;
300
301             fprintf(stderr, "Failed to read: %s\n", strerror(errno));
302             goto fail;
303         }
304
305         nline++;
306
307         c = t;
308
309         do {
310
311 /*             fprintf(stderr, "enabled %i, state %i, cnt %i, remaining string is: %s", enabled, state, !!cnt, c); */
312
313             switch (state) {
314
315                 case STATE_TEXT:
316
317                     if (!strncmp(c, "/*", 2)) {
318                         state = STATE_COMMENT_C;
319                         r = append(r, &rl, &c, 2);
320                     } else if (!strncmp(c, "//", 2)) {
321                         state = STATE_COMMENT_CPP;
322                         r = append(r, &rl, &c, 2);
323                     } else if (*c == '"') {
324                         state = STATE_STRING;
325
326                         if (enabled) {
327                             cnt = r;
328                             cntl = rl;
329
330                             r = NULL;
331                             rl = 0;
332
333                             c ++;
334                         } else
335                             r = append(r, &rl, &c, 1);
336                     } else if (*c == '\'') {
337                         state = STATE_CHAR;
338                         r = append(r, &rl, &c, 1);
339                     } else if (*c == 0)
340                         done = 1;
341                     else
342                         r = append(r, &rl, &c, 1);
343
344                     break;
345
346                 case STATE_COMMENT_C:
347
348                     if (!strncmp(c, "*/", 2)) {
349                         state = STATE_TEXT;
350                         r = append(r, &rl, &c, 2);
351                     } else if (!strncmp(c, "%STRINGPOOLSTART%", 17)) {
352                         enabled = 1;
353                         pool_started_line = nline;
354                         r = append(r, &rl, &c, 17);
355                     } else if (!strncmp(c, "%STRINGPOOLSTOP%", 16)) {
356                         enabled = 0;
357                         r = append(r, &rl, &c, 16);
358                     } else if (*c == 0)
359                         done = 1;
360                     else
361                         r = append(r, &rl, &c, 1);
362
363                     break;
364
365                 case STATE_COMMENT_CPP:
366
367                     if (*c == '\n' || *c == '\r') {
368                         state = STATE_TEXT;
369                         r = append(r, &rl, &c, 1);
370                     } else if (!strncmp(c, "%STRINGPOOLSTART%", 17)) {
371                         enabled = 1;
372                         pool_started_line = nline;
373                         r = append(r, &rl, &c, 17);
374                     } else if (!strncmp(c, "%STRINGPOOLSTOP%", 16)) {
375                         enabled = 0;
376                         r = append(r, &rl, &c, 16);
377                     } else if (*c == 0) {
378                         state = STATE_TEXT;
379                         done = 1;
380                     } else
381                         r = append(r, &rl, &c, 1);
382
383                     break;
384
385                 case STATE_STRING:
386                 case STATE_CHAR:
387
388                     if ((*c == '\'' && state == STATE_CHAR) || (*c == '"' && state == STATE_STRING)) {
389
390                         if (state == STATE_STRING && enabled) {
391                             struct item *i;
392                             i = malloc(sizeof(struct item));
393
394                             if (!i)
395                                 abort();
396
397                             i->cnt = cnt;
398                             i->cntl = cntl;
399
400                             cnt = NULL;
401                             cntl = 0;
402
403                             i->text = r;
404                             i->size = rl;
405
406                             r = NULL;
407                             rl = 0;
408
409                             i->next = NULL;
410
411                             if (last)
412                                 last->next = i;
413                             else
414                                 first = i;
415
416                             last = i;
417
418                             c++;
419
420                         } else
421                             r = append(r, &rl, &c, 1);
422
423                         state = STATE_TEXT;
424
425                     } else if (*c == '\\') {
426
427                         char d;
428                         char l = 2;
429
430                         switch (c[1]) {
431
432                             case '\\':
433                             case '"':
434                             case '\'':
435                             case '?':
436                                 d = c[1];
437                                 break;
438                             case 'n':
439                                 d = '\n';
440                                 break;
441                             case 'r':
442                                 d = '\r';
443                                 break;
444                             case 'b':
445                                 d = '\b';
446                                 break;
447                             case 't':
448                                 d = '\t';
449                                 break;
450                             case 'f':
451                                 d = '\f';
452                                 break;
453                             case 'a':
454                                 d = '\a';
455                                 break;
456                             case 'v':
457                                 d = '\v';
458                                 break;
459                             case 'x': {
460                                 int k;
461                                 if ((k = parse_hex(c+2, &d)) < 0) {
462                                     fprintf(stderr, "%s:%u: Parse failure: invalid hexadecimal escape sequence.\n", fname, nline);
463                                     goto fail;
464                                 }
465                                 l = 2 + k;
466                                 break;
467                             }
468                             case '0':
469                             case '1':
470                             case '2':
471                             case '3':
472                             case '4':
473                             case '5':
474                             case '6':
475                             case '7': {
476                                 int k;
477                                 if ((k = parse_oct(c+1, &d)) < 0) {
478                                     fprintf(stderr, "%s:%u: Parse failure: invalid octal escape sequence.\n", fname, nline);
479                                     goto fail;
480                                 }
481                                 l = 1 + k;
482                                 break;
483                             }
484                             default:
485                                 fprintf(stderr, "%s:%u: Parse failure: invalid escape sequence.\n", fname, nline);
486                                 goto fail;
487                         }
488
489                         if (state == STATE_STRING && enabled) {
490                             char *x = &d;
491                             r = append(r, &rl, &x, 1);
492                             c += l;
493                         } else
494                             r = append(r, &rl, &c, l);
495                     } else if (*c == 0) {
496                         fprintf(stderr, "%s:%u: Parse failure: multiline strings suck.\n", fname, nline);
497                         goto fail;
498                     } else
499                         r = append(r, &rl, &c, 1);
500
501                     break;
502             }
503         } while (!done);
504     }
505
506     if (enabled) {
507         fprintf(stderr, "%s:%u: Parse failure: missing %%STRINGPOOLSTOP%%\n", fname, pool_started_line);
508         goto fail;
509     }
510
511     if (state != STATE_TEXT) {
512         fprintf(stderr, "%s:%u: Parse failure: unexpected EOF.\n", fname, nline);
513         goto fail;
514     }
515
516     assert(!cnt);
517
518     *rfirst = first;
519
520     *remain = r;
521     *remain_size = rl;
522
523     return 0;
524
525 fail:
526
527     free(cnt);
528     free(r);
529     free_items(first);
530
531     return -1;
532 }
533
534 static int process(FILE *in, FILE *out, const char*ifname) {
535
536     struct item *first = NULL;
537     char *remain = NULL;
538     size_t remain_size = 0;
539
540     if (parse(in, ifname, &first, &remain, &remain_size) < 0)
541         return -1;
542
543     if (!first)
544         fwrite(remain, 1, remain_size, out);
545     else {
546         find_suffixes(first);
547         fill_idx(first);
548
549         dump_pool(out, first);
550
551         fprintf(out,
552                 "#ifndef STRPOOL\n"
553                 "#define STRPOOL\n"
554                 "#endif\n"
555                 "#ifndef _P\n"
556                 "#define _P(x) (_strpool_ + ((x) - (const char*) 0))\n"
557                 "#endif\n\n");
558
559
560         if (ifname)
561             fprintf(out, "#line 1 \"%s\"\n", ifname);
562
563         dump_text(out, first);
564         fwrite(remain, 1, remain_size, out);
565
566         free_items(first);
567     }
568
569     free(remain);
570
571     return 0;
572 }
573
574 int main(int argc, char *argv[]) {
575     int ret;
576     FILE *in = NULL, *out = NULL;
577
578     if (argc > 1) {
579         if (!(in = fopen(argv[1], "r"))) {
580             fprintf(stderr, "Failed to open '%s': %s\n", argv[1], strerror(errno));
581             return 1;
582         }
583     } else
584         in = stdin;
585
586     if (argc > 2) {
587
588         if (!(out = fopen(argv[2], "w"))) {
589             fprintf(stderr, "Failed to open '%s': %s\n", argv[2], strerror(errno));
590             return 1;
591         }
592     } else
593         out = stdout;
594
595     if (process(in, out, argc > 1 ? argv[1] : NULL) < 0)
596         goto finish;
597
598     ret = 0;
599
600 finish:
601
602     if (in != stdin)
603         fclose(in);
604
605     if (out != stdout)
606         fclose(out);
607
608     return ret;
609 }