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