Handle Total LBAs Written and Total LBSa Read
[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                                                         i->suffix_of = NULL;
439
440                                                         if (last)
441                                                                 last->next = i;
442                                                         else
443                                                                 first = i;
444
445                                                         last = i;
446
447                                                         c++;
448
449                                                 } else
450                                                         r = append(r, &rl, &c, 1);
451
452                                                 state = STATE_TEXT;
453
454                                         } else if (*c == '\\') {
455
456                                                 char d;
457                                                 char l = 2;
458
459                                                 switch (c[1]) {
460
461                                                         case '\\':
462                                                         case '"':
463                                                         case '\'':
464                                                         case '?':
465                                                                 d = c[1];
466                                                                 break;
467                                                         case 'n':
468                                                                 d = '\n';
469                                                                 break;
470                                                         case 'r':
471                                                                 d = '\r';
472                                                                 break;
473                                                         case 'b':
474                                                                 d = '\b';
475                                                                 break;
476                                                         case 't':
477                                                                 d = '\t';
478                                                                 break;
479                                                         case 'f':
480                                                                 d = '\f';
481                                                                 break;
482                                                         case 'a':
483                                                                 d = '\a';
484                                                                 break;
485                                                         case 'v':
486                                                                 d = '\v';
487                                                                 break;
488                                                         case 'x': {
489                                                                 int k;
490                                                                 if ((k = parse_hex(c+2, &d)) < 0) {
491                                                                         fprintf(stderr, "%s:%u: Parse failure: invalid hexadecimal escape sequence.\n", fname, nline);
492                                                                         goto fail;
493                                                                 }
494                                                                 l = 2 + k;
495                                                                 break;
496                                                         }
497                                                         case '0':
498                                                         case '1':
499                                                         case '2':
500                                                         case '3':
501                                                         case '4':
502                                                         case '5':
503                                                         case '6':
504                                                         case '7': {
505                                                                 int k;
506                                                                 if ((k = parse_oct(c+1, &d)) < 0) {
507                                                                         fprintf(stderr, "%s:%u: Parse failure: invalid octal escape sequence.\n", fname, nline);
508                                                                         goto fail;
509                                                                 }
510                                                                 l = 1 + k;
511                                                                 break;
512                                                         }
513                                                         default:
514                                                                 fprintf(stderr, "%s:%u: Parse failure: invalid escape sequence.\n", fname, nline);
515                                                                 goto fail;
516                                                 }
517
518                                                 if (state == STATE_STRING && enabled) {
519                                                         char *x = &d;
520                                                         r = append(r, &rl, &x, 1);
521                                                         c += l;
522                                                 } else
523                                                         r = append(r, &rl, &c, l);
524                                         } else if (*c == 0) {
525                                                 fprintf(stderr, "%s:%u: Parse failure: multiline strings suck.\n", fname, nline);
526                                                 goto fail;
527                                         } else
528                                                 r = append(r, &rl, &c, 1);
529
530                                         break;
531                         }
532                 } while (!done);
533         }
534
535         if (enabled) {
536                 fprintf(stderr, "%s:%u: Parse failure: missing %%STRINGPOOLSTOP%%\n", fname, pool_started_line);
537                 goto fail;
538         }
539
540         if (state != STATE_TEXT) {
541                 fprintf(stderr, "%s:%u: Parse failure: unexpected EOF.\n", fname, nline);
542                 goto fail;
543         }
544
545         assert(!cnt);
546
547         *rfirst = first;
548
549         *remain = r;
550         *remain_size = rl;
551
552         return 0;
553
554 fail:
555
556         free(cnt);
557         free(r);
558         free_items(first);
559
560         return -1;
561 }
562
563 static int process(FILE *in, FILE *out, const char*ifname) {
564
565         struct item *first = NULL;
566         char *remain = NULL;
567         size_t remain_size = 0;
568
569         if (parse(in, ifname, &first, &remain, &remain_size) < 0)
570                 return -1;
571
572         if (!first)
573                 fwrite(remain, 1, remain_size, out);
574         else {
575                 find_suffixes(first);
576                 fill_idx(first);
577
578                 dump_pool(out, first);
579
580                 fprintf(out,
581                         "#ifndef STRPOOL\n"
582                         "#define STRPOOL\n"
583                         "#endif\n"
584                         "#ifndef _P\n"
585                         "#define _P(x) (_strpool_ + ((x) - (const char*) 1))\n"
586                         "#endif\n\n");
587
588
589                 if (ifname)
590                         fprintf(out, "#line 1 \"%s\"\n", ifname);
591
592                 dump_text(out, first);
593                 fwrite(remain, 1, remain_size, out);
594
595                 free_items(first);
596         }
597
598         free(remain);
599
600         return 0;
601 }
602
603 int main(int argc, char *argv[]) {
604         int ret;
605         FILE *in = NULL, *out = NULL;
606
607         if (argc > 1) {
608                 if (!(in = fopen(argv[1], "r"))) {
609                         fprintf(stderr, "Failed to open '%s': %s\n", argv[1], strerror(errno));
610                         return 1;
611                 }
612         } else
613                 in = stdin;
614
615         if (argc > 2) {
616                 if (!(out = fopen(argv[2], "w"))) {
617                         fprintf(stderr, "Failed to open '%s': %s\n", argv[2], strerror(errno));
618                         return 1;
619                 }
620         } else
621                 out = stdout;
622
623         if (process(in, out, argc > 1 ? argv[1] : NULL) < 0)
624                 goto finish;
625
626         ret = 0;
627
628 finish:
629
630         if (in != stdin)
631                 fclose(in);
632
633         if (out != stdout)
634                 fclose(out);
635
636         return ret;
637 }