↑ 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 hig