1 /*
  2     Copyright (C) 2006 Ralf Huesing <ralf@stormbind.net>
  3 
  4     This program is free software; you can redistribute it and/or modify
  5     it under the terms of the GNU General Public License as published by
  6     the Free Software Foundation; either version 2 of the License, or
  7     (at your option) any later version.
  8 
  9     This program is distributed in the hope that it will be useful,
 10     but WITHOUT ANY WARRANTY; without even the implied warranty of
 11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 12     GNU General Public License for more details.
 13 
 14     You should have received a copy of the GNU General Public License
 15     along with this program; if not, write to the Free Software
 16     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 17 */
 18 #include <stdio.h>
 19 #include <string.h>
 20 #include <errno.h>
 21 #include <locale.h>
 22 #include <ctype.h>
 23 #include <getopt.h>
 24 #include <time.h>
 25 #include <assert.h>
 26 
 27 static void mkmap_lower (unsigned char map[256]);
 28 static void mkmap_upper (unsigned char map[256]);
 29 static void mkmap_alnum (unsigned char map[256]);
 30 static void mkmap_digit(unsigned char map[256]);
 31 static void mkmap_xdigit(unsigned char map[256]);
 32 static void mkmap_http_host(unsigned char map[256]);
 33 static void mkmap_http_uri(unsigned char map[256]);
 34 static void mkmap_http_uri_decoded(unsigned char map[256]);
 35 static void mkmap_http_key(unsigned char map[256]);
 36 static void mkmap_http_val(unsigned char map[256]);
 37 static void mkmap_http_range(unsigned char map[256]);
 38 
 39 static void dump_map (FILE *fh, const char *name, unsigned char map[256]);
 40 
 41 static void version ();
 42 static void banner (FILE *fh, const char *locale, const char *cmdline);
 43 static void usage (const char *program);
 44 
 45 #define DECLARE_MAP(_name,_macro,_descr)        \
 46         { #_name, "MAP_WANT_" _macro, _descr, mkmap_ ##_name, 1 }
 47 static struct {
 48         const char      *name;
 49         const char      *macro;
 50         const char      *description;
 51         void            (*sym)(unsigned char [256]);
 52         unsigned char   use;
 53 } mapsym[] = {
 54         /* description is without newlines or formatting stuff.
 55          * the output is formatted in dump_map_description(). */
 56         
 57         DECLARE_MAP(lower, "LOWER",
 58                         "lowercased characters"),
 59         
 60         DECLARE_MAP(upper, "UPPER",
 61                         "uppercased characters"),
 62         
 63         DECLARE_MAP(alnum, "ALNUM",
 64                         "alpha numeric characters"),
 65         
 66         DECLARE_MAP(digit, "DIGIT",
 67                         "decimal digits with there value(0..9); "
 68                         "non digits are 255" ),
 69         
 70         DECLARE_MAP(xdigit, "XDIGIT",
 71                         "hexadecimal digits [0-9a-fA-F] with there "
 72                         "value (0-15); non xdigits are 255."
 73                         ),
 74         
 75         DECLARE_MAP(http_host, "HTTP_HOST",
 76                         "lowercased characters that are valid for a "
 77                         "HTTP hostname (as defined in RFC 2396); "
 78                         "non valid characters are 0."
 79                         ),
 80         
 81         DECLARE_MAP(http_uri, "HTTP_URI",
 82                         "characters that are valid for a HTTP uri "
 83                         "(as defined in RFC 2396), the uri may hex-encoded; "
 84                         "non valid characters are 0."
 85                         ),
 86         
 87         DECLARE_MAP(http_uri_decoded, "HTTP_URI_DECODED",
 88                         "characters that are valid for a HTTP uri "
 89                         "AFTER hex decoding."
 90                         ),
 91         
 92         DECLARE_MAP(http_key, "HTTP_KEY",
 93                         "characters that are valid for a HTTP header key. "
 94                         "non valid characters are 0."
 95                         ),
 96         
 97         DECLARE_MAP(http_val, "HTTP_VAL",
 98                         "characters that are valid for a HTTP header value. "
 99                         "non valid characters are 0."
100                         ),
101         
102         DECLARE_MAP(http_range, "HTTP_RANGE",
103                         "characters that are valid for byte ranges: "
104                         "space( ), tab(\\t), comma(,), sign(-) and "
105                         "digits(0-9)"),
106         {NULL}
107 };
108 #undef DECLARE_MAP
109 
110 
111 static int mapsym_enable (char *maps, int enabled)
112 {
113         char            *token;
114         unsigned        found=0;
115 
116         for (   token = strtok (maps, ",\t ");
117                 token;
118                 token = strtok (NULL, ",\t "))
119         {
120                 unsigned i;
121                 
122                 if (0 == strcmp(token,"all")) {
123                         for (i=0; mapsym[i].name ; ++i, ++found)
124                                 mapsym[i].use=enabled;
125                         continue;
126                 }
127                 
128                 for (i=0; ; ++i) {
129                         if (NULL==mapsym[i].name) {
130                                 fprintf (stderr, "map ,,%s'' not found\n",
131                                                 token);
132                                 return -1;
133                         }
134                         
135                         if (0 == strcmp(mapsym[i].name,token)) {
136                                 mapsym[i].use=enabled;
137                                 ++found;
138                                 break;
139                         }
140                 }
141         }
142 
143         return (found ? found : -1);
144 }
145 
146 int main (int argc, char *argv[])
147 {
148         char            *locale="";
149         char            *str_error;
150         unsigned        i;
151         enum {
152                 OPT_enable_locale = 500,
153                 OPT_enable_maps,
154                 OPT_disable_maps,
155                 OPT_version,
156                 OPT_help,
157         };
158         struct option   longopt[] = {
159                 { "enable-locale",      2, NULL, OPT_enable_locale },
160                 { "disable-maps",       1, NULL, OPT_disable_maps },
161                 { "enable-maps",        1, NULL, OPT_enable_maps },
162                 { "version",            0, NULL, OPT_version },
163                 { "help",               0, NULL, OPT_help },
164                 { NULL }
165         };
166         char            *cmdline;
167         size_t          cmdline_len = 0;
168 
169         /* copy the command line into `cmdline'. */
170 
171         /* BUG: cmdline is never free'd
172          *      strcat() loop is not optimized. */
173         for (i=0; i<argc; ++i)
174                 cmdline_len += strlen(argv[i]) + 1;
175 
176         cmdline = malloc(cmdline_len+1);
177         assert(cmdline);
178         
179         cmdline[0]=0;
180         for (i=0; i<argc; ++i) {
181                 strcat (cmdline, argv[i]);
182                 strcat (cmdline, " ");
183         }
184         
185         /* parse arguments */
186         for (;;) {
187                 int opt;
188                 
189                 opt = getopt_long (argc, argv, "", longopt, NULL);
190 
191                 switch (opt) {
192                 case OPT_enable_locale:
193                         if (optarg)
194                                 locale = optarg;
195                         else
196                                 locale = "";
197                         
198                         str_error = setlocale (LC_ALL, locale);
199                         
200                         if (NULL==str_error) {
201                                 /* %m is not in c99 :-| */
202                                 fprintf (stderr, "setlocale(%s): %s\n",
203                                                 locale, strerror(errno));
204                                 return -1;
205                         }
206                         locale = str_error;
207                         continue;
208                 case OPT_enable_maps:
209                 case OPT_disable_maps:
210                         if (mapsym_enable(optarg, (opt == OPT_enable_maps))
211                                         < 1)
212                         {
213                                 usage (argv[0]);
214                                 return -1;
215                         }
216                         continue;
217                 case OPT_version:
218                         version ();
219                         return 0;
220                 case OPT_help:
221                         usage (argv[0]);
222                         return 0;
223                 case -1:
224                         break;
225                 default:
226                         usage (argv[0]);
227                         return -1;
228                 }
229                 break;
230         }
231         
232         banner (stdout, locale, cmdline);
233         
234         for (i=0; mapsym[i].name; ++i) {
235                 if (mapsym[i].use) {
236                         unsigned char   buf[256];
237                         mapsym[i].sym(buf);
238                         dump_map(stdout, mapsym[i].macro, buf);
239                 }       
240         }
241 
242         return 0;
243 }
244 
245 static void mkmap_lower (unsigned char map[256])
246 {
247         unsigned short i;
248 
249         for (i=0; i<256; ++i)
250                 map[i] = tolower(i);
251 }
252 
253 static void mkmap_upper (unsigned char map[256])
254 {
255         unsigned short i;
256 
257         for (i=0; i<256; ++i)
258                 map[i] = toupper(i);
259 }
260 
261 static void mkmap_alnum (unsigned char map[256])
262 {
263         unsigned short i;
264 
265         for (i=0; i<256; ++i) {
266                 map[i] = (isalnum(i) ? i : 0);
267         }
268 }
269 
270 
271 static void mkmap_digit (unsigned char map[256])
272 {
273         unsigned short i;
274 
275         for (i=0; i<256; ++i) {
276                 if (isdigit(i)) {
277                         map[i] = i - '0';
278                 } else {
279                         map[i] = 0xff;
280                 }
281         }
282 }
283 
284 static void mkmap_xdigit (unsigned char map[256])
285 {
286         unsigned short i;
287 
288         for (i=0; i<256; ++i) {
289                 map[i] = 0xff;
290                         
291                 if (isxdigit(i)) {
292                         if (isdigit(i))
293                                 map[i] = i - '0';
294                         else
295                         if (islower(i))
296                                 map[i] = i - 'a' + 10;
297                         else
298                         if (isupper(i))
299                                 map[i] = i - 'A' + 10;
300                 }
301         }
302 }
303 
304 static void mkmap_http_host (unsigned char map[256])
305 {
306         unsigned short i;
307 
308         for (i=0; i<256; ++i) {
309                 if (isalnum(i)) {
310                         map[i] = tolower(i);
311                 } else {
312                         switch (i) {
313                         case '-':
314                         case '.':
315                         case '_':
316                         case ':':
317                                 map[i] = i;
318                                 break;
319                         default:
320                                 map[i] = 0x00;
321                         }
322                 }
323         }
324 }
325 
326 static void mkmap_http_key (unsigned char map[256])
327 {
328         unsigned short i;
329 
330         for (i=0; i<256; ++i) {
331                 if (isalnum(i)) {
332                         /* map[i] = tolower(i); */
333                         map[i] = i;
334                 } else {
335                         switch (i) {
336                         case '-':
337                                 map[i] = i;
338                                 break;
339                         default:
340                                 map[i] = 0x00;
341                         }
342                 }
343         }
344 }
345 
346 static void mkmap_http_val (unsigned char map[256])
347 {
348         unsigned short i;
349         
350 
351         for (i=0; i<256; ++i) {
352                 map[i] = 0;
353                 
354                 if (i >= 0x20 && i <= 0x7E) {
355                         map[i] = i;
356                 }
357         }
358 
359         map['\t'] = '\t';
360 }
361 
362 static void mkmap_http_uri_decoded (unsigned char map[256])
363 {
364         mkmap_http_uri(map);
365         map[' '] = ' ';
366 }
367 
368 static void mkmap_http_range (unsigned char map[256])
369 {
370         mkmap_digit(map);
371         map[' '] = ' ';
372         map['\t'] = '\t';
373         map[','] = ',';
374         map['-'] = '-';
375 }
376 
377 
378 static void mkmap_http_uri (unsigned char map[256])
379 {
380         unsigned short i;
381 
382         /* from lighttpd-1.3.13 */
383 
384         for (i=0; i<256; ++i) {
385                 /* alphanum */
386                 if (isalnum(i)) {
387                         map[i] = i;
388                 } else {
389                         /* RFC 2396 - Appendix A */
390                         switch (i) {
391                                 /* reserved */
392                         case ';':
393                         case '/':
394                         case '?': 
395                         case ':':
396                         case '@':
397                         case '&':
398                         case '=':
399                         case '+':
400                         case '$':
401                         case ',':
402                                 
403                                 /* mark */
404                         case '-':
405                         case '_':
406                         case '.':
407                         case '!':
408                         case '~':
409                         case '*':
410                         case '\'':
411                         case '(':
412                         case ')':
413                                 
414                                 /* escaped */
415                         case '%':
416                                 
417                                 /* fragment, should not be out in the wild $*/
418                         case '#': 
419                                 
420                                 /* non RFC */
421                         case '[': 
422                         case ']':
423                         case '|':       
424                                 map[i] = i;
425                                 break;
426                         default:
427                                 map[i] = 0x00;
428                         }
429                 }
430         }
431 }
432 
433 static void dump_map_description (FILE *fh,
434                 const char *prefix, const char *description)
435 {
436         size_t prefix_len = strlen(prefix);
437         size_t description_len = strlen(description);
438         size_t line_len = 79;
439 
440 
441         while (description_len) {
442                 size_t length;
443                 
444                 if (prefix_len + description_len < line_len) {
445                         fprintf (fh, "%.*s%.*s\n",
446                                         prefix_len, prefix,
447                                         description_len, description);
448                         break;
449                 }
450 
451                 /* 
452                  * split description if its larger then a single line
453                  * into multiple lines.
454                  * 
455                  */
456                 length = 0;
457                 for (;;) {
458                         size_t next;
459                         
460                         next = strcspn(&description[length], ";:,. \t");
461                         
462                         if (length + next + 1 + prefix_len >= line_len)
463                                 break;
464                         
465                         length += next+1;
466                 }
467 
468                 fprintf (fh, "%.*s%.*s\n",
469                                 prefix_len, prefix,
470                                 length, description);
471                 
472                 description += length;
473                 description_len -= length;
474         }
475 }
476 
477 static void banner (FILE *fh, const char *locale, const char *cmdline)
478 {
479         time_t          t = time(NULL);
480         unsigned        i;
481         
482         fprintf (fh,
483 "/*\n"
484 " * Copyright (C) 2006 Ralf Huesing <ralf@stormbind.net>\n"
485 " *\n"
486 " * This program is free software; you can redistribute it and/or modify\n"
487 " * it under the terms of the GNU General Public License as published by\n"
488 " * the Free Software Foundation; either version 2 of the License, or\n"
489 " * (at your option) any later version.\n"
490 " *\n"
491 " * This program is distributed in the hope that it will be useful,\n"
492 " * but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
493 " * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
494 " * GNU General Public License for more details.\n"
495 " *\n"
496 " * You should have received a copy of the GNU General Public License\n"
497 " * along with this program; if not, write to the Free Software\n"
498 " * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n"
499 " *\n"
500 " */\n"
501 "\n"
502 "/*\n"
503 " * This file is automatically generated by the mkcharmap utility.\n"
504 " * Dont edit this file, it will be overwritten.\n"
505 " * \n"
506 " * Generated at: %s"
507 " * Using locale: %s\n"
508 " * Command line: %s\n"
509 " * \n"
510 " */\n\n", ctime(&t), locale, cmdline );
511 
512         fprintf (fh,"/* \n * Available maps are:\n *\n");
513         for (i=0; mapsym[i].name; ++i) {
514                 if (mapsym[i].use) {
515                         fprintf (fh, " *   %s\n", mapsym[i].macro);
516                         dump_map_description (fh, " *     ",
517                                         mapsym[i].description);
518                         fprintf (fh, " *\n");
519                 }
520         }
521         
522         fprintf (fh," */\n");
523         
524         fprintf (fh,    "\n"
525                         "#ifndef MAP_LOOKUP\n"
526                         "# define MAP_LOOKUP(_map,_ch)\t"
527                         "( (_map)[(unsigned char)(_ch)] )\n"
528                         "#endif\n"
529                         "\n"
530                         );
531 }
532 static void dump_map (FILE *fh, const char *name, unsigned char map[256])
533 {
534         unsigned short i;
535 
536         fprintf (fh,
537 "#ifdef %s\n"
538 "# ifdef %s_INITIALIZER_ONLY\n"
539 "#  undef %s_INITIALIZER_ONLY\n"
540 "# else\n"
541 "\tstatic const unsigned char %s[] = \n"
542 "# endif\n"
543 "{\n\t", name, name, name, name);
544                 
545         for (i=0; i<256; ++i) {
546                 fprintf(fh,"0x%02x, ", map[i]);
547                 if (9 == (i%10))
548                         fprintf(fh,"\n\t");
549         }
550         
551         fprintf(fh,"\n};\n");
552         fprintf(fh,"# undef %s\n", name);
553         fprintf(fh,"#endif\n\n");
554 }
555 
556 static void usage (const char *program)
557 {
558         unsigned i;
559         fprintf (stderr,
560 "Usage: %s [option(s)]\n"
561 "Generates various character lookup maps to include in C-programs.\n"
562 "\n"
563 "Options are:\n"
564 "  --enable-locale[=locale]  enables the specified locale or uses\n"
565 "                            the environment. see setlocale(3)\n"
566 "  --enable-maps={maps,..}   enables the specified map(s).\n"
567 "  --disable-maps={maps,..}  disables the specified map(s).\n"
568 "  --help                    display this help and exit\n"
569 "  --version                 output version information and exit\n"
570 "\n"
571 "The following maps are available:"
572         , program);
573 
574         fprintf (stderr, "\n  all\n");
575         dump_map_description (stderr, "    ",
576                         "Specifies all available maps.");
577         
578         for (i=0; mapsym[i].name; ++i) {
579                 fprintf (stderr, "\n  %s\n", mapsym[i].name);
580                 dump_map_description (stderr, "    ",
581                                 mapsym[i].description);
582         }
583 }
584 
585 static void version ()
586 {
587         printf (
588 "mkcharmap 0.1\n"
589 "Copyright (C) 2006 Ralf Huesing <ralf@stormbind.net>\n"
590 "This is free software; see the source for copying conditions.  There is NO\n"
591 "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
592 );
593 }


syntax highlighted by Code2HTML, v. 0.9.1