/* * The Npic library * * Copyright (C) 2003 Edouard Thiel * * This library is free software under the terms of the * GNU Lesser General Public License (LGPL) version 2.1. */ /* * npic-templa.c - version 2.3 - 17/01/2009 * * Template preprocessor : expand Npic @macros in C files. */ #include #include #include #include #include #include static char *separa_if = "\"'`:?#~&^"; #define BUF1_LEN 4096 #define BUF2_LEN 4096 #define STA_LEN 1024 enum { S_NONE, S_LOOP, S_IF, S_IF_SUB , S_IF_INW, S_ERROR }; static char *sta_kw[] = { "keyword", "@ELOOP", "@FI", "end of @[..]x..x", "end of @x..x", "internal error" }; typedef struct { FILE *f1, *f2; char *in1, *out1, *buf1, buf2[BUF2_LEN], loop_dim, loop_pix; int pos2, blank, nega, visib, sta[STA_LEN], sta_nb; } Glo; int parse_block (Glo *g, int a, int b); void ShowUsage () { printf ( "npic-templa - expand Npic @macros in C files.\n" "Usage:\n" " npic-templa -h | -help | --help : print this help\n" " npic-templa in1 out1 : expand in1 to out1\n" "\n" "in1 : .ct file containing Npic @macros\n" "out1 : .c resulting file\n" "\n" "General macros:\n" " @@ : expand to '@'\n" " @# ... \\n : comment, suppressed in output\n" " @G : expand a generation message and date\n" "\n" "Rule: any line expanding to blank is suppressed.\n" "\n" "Loop:\n" " @BLOOP \\n\n" " \n" " @ELOOP\n" "\n" " is a list of words in 2C 2L 2D 2Q .. 6C 6L 6D 6Q\n" " separated by ' ' or ',' or '\\t'\n" " is a multi-line text; it will be duplicated for each word in \n" "\n" "Macros available in a loop:\n" " @H : expand to the current word in loop list (upper case)\n" " @h : the same in lower case\n" " @J : expand to first letter in @H (the dimension).\n" " @K : expand to second letter in @H (pixel type).\n" " @k : the same in lower case\n" "\n" "Conditionnal in loop: x is a char in %s\n" " @IF[]@FI : expand if @H in \n" " @[]xx : expand if @H in \n" " @<23456>xx : expand if @J in <23456>\n" " @xx : expand if @K in \n" "Any conditionnal can be negated by: @!...\n" "\n" "Coordinates for dimension: word ~$~ can be empty; ? in ',;)'\n" " @ox~$~x] : expand to ~r~][...][~y~][~x~]\n" " @ox~$~x? : expand to ~r~, ..., ~y~, ~x~?\n" "\n", separa_if); } void error_line (Glo *g, int pos, const char *format, ...) { int k = 1, i; for (i = 0; i <= pos; i++) if (g->buf1[i] == '\n') k++; fprintf (stderr, "ERROR line %d: ", k); if (format && format[0]) { va_list ap; va_start (ap, format); vfprintf (stderr, format, ap); va_end (ap); } fprintf (stderr, "\n"); exit (1); } int in_str (int c, char *s) { int i; for (i = 0; s[i] != 0; i++) if (c == s[i]) return 1; return 0; } int blank_line (char *line) { int i; for (i = 0; line[i] != 0; i++) if (line[i] != ' ' && line[i] != '\t' && line[i] != '\n') return 0; return 1; } void putc_buf2 (Glo *g, int pos, char c) { if (g->visib == 0) return; if (g->pos2 >= BUF2_LEN-1) error_line (g, pos, "generated line exceeds %d bytes", BUF2_LEN); g->buf2[g->pos2++] = c; } void puts_buf2 (Glo *g, int pos, char *s) { int k = strlen (s); if (g->visib == 0) return; if (g->pos2 + k >= BUF2_LEN-1) error_line (g, pos, "generated line exceeds %d bytes", BUF2_LEN); strcpy (g->buf2+g->pos2, s); g->pos2 += k; } void write_buf2 (Glo *g) { g->buf2[g->pos2] = 0; if (!(g->blank && blank_line (g->buf2))) if (fputs (g->buf2, g->f2) == EOF) { fprintf (stderr, "ERROR writing \"%s\" : %s\n", g->buf2, strerror(errno)); exit (1); } g->pos2 = g->blank = 0; } void sta_push (Glo *g, int pos, int sta) { if (g->sta_nb >= STA_LEN) error_line (g, pos, "too many imbrications"); g->sta[g->sta_nb++] = sta; } void sta_check (Glo *g, int pos, int sta) { if (g->sta_nb <= 0) error_line (g, pos, "unexpected %s", sta_kw[sta]); if (g->sta[g->sta_nb-1] != sta) { int k = g->sta[g->sta_nb-1]; error_line (g, pos, "expect %s before %s", sta_kw[k], sta_kw[sta]); } } void sta_pop (Glo *g, int pos, int sta) { sta_check (g, pos, sta); g->sta_nb--; } int next_eol (Glo *g, int a, int b) { int i; for (i = a; i < b; i++) if (g->buf1[i] == '\n') return i; return b; } int parse_loop (Glo *g, int a, int b) { int j, k, q = -1; char old_pix = g->loop_pix, old_dim = g->loop_dim; sta_push (g, a, S_LOOP); k = next_eol (g, a+5, b); /* Iterate on each arg */ for (j = a+5; j < k; j++) { if ( in_str (g->buf1[j], " \t,") ) continue; if ( in_str (g->buf1[j], "23456") && in_str (g->buf1[j+1], "LCDQ") ) { g->loop_dim = g->buf1[j]; g->loop_pix = g->buf1[j+1]; j++; q = parse_block (g, k+1, b); if (q >= b) error_line (g, q-1, "missing @ELOOP"); } else error_line (g, j, "after @BLOOP, expect {23456}{LCDQ}[ \\t,]"); } if (q == -1) error_line (g, a, "empty loop"); g->loop_pix = old_pix; g->loop_dim = old_dim; sta_pop (g, q-1, S_LOOP); return q+5; /* position after @ELOOP */ } int parse_if (Glo *g, int a, int b) { int j, old_visib = g->visib, found = 0, q = -1; sta_push (g, a, S_IF); if (g->buf1[a+2] != '[') error_line (g, a, "after @IF, missing ["); for (j = a+3; j < b && g->buf1[j] != ']'; j++) if (g->buf1[j] == g->loop_dim && g->buf1[j+1] == g->loop_pix) found = 1; else if (! in_str (g->buf1[j], " \t,23456LCDQ")) error_line (g, j, "after @IF[, unexpected char '%c'", g->buf1[j]); if (g->buf1[j] != ']') error_line (g, j, "after @IF[, missing ]"); if (g->nega) { found = !found; g->nega = 0; } g->visib = (g->visib && found); q = parse_block (g, j+1, b); g->visib = old_visib; if (q >= b) error_line (g, q-1, "missing @FI"); sta_pop (g, q-1, S_IF); return q+2; /* position after @FI */ } int parse_if_subset (Glo *g, int a, int b) { int j, k, old_visib = g->visib, found = 0; char c; sta_push (g, a, S_IF_SUB); for (j = a+1; j < b && g->buf1[j] != ']'; j++) if (g->buf1[j] == g->loop_dim && g->buf1[j+1] == g->loop_pix) found = 1; else if (! in_str (g->buf1[j], " \t,23456LCDQ")) error_line (g, j, "after @[, unexpected char '%c'", g->buf1[j]); if (g->buf1[j] != ']') error_line (g, j, "after @[, missing ]"); if (g->nega) { found = !found; g->nega = 0; } c = g->buf1[j+1]; if (! in_str (c, separa_if)) error_line (g, j+1, "after @[..], char expected in %s", separa_if); for (k = j+2; k < b && g->buf1[k] != c; k++) ; if (g->buf1[k] != c) error_line (g, j+2, "after @[..]%c, char %c expected", c, c); g->visib = (g->visib && found); parse_block (g, j+2, k); g->visib = old_visib; sta_pop (g, k, S_IF_SUB); return k+1; /* position after @[..]x..x */ } int parse_if_inw (Glo *g, int a, int b, char *w) { int j, k, old_visib = g->visib, found = 0; char c; sta_push (g, a, S_IF_INW); for (j = a; j < b && in_str (g->buf1[j], w) ; j++) { if (g->buf1[j] == g->loop_dim || g->buf1[j] == g->loop_pix) found = 1; } if (g->nega) { found = !found; g->nega = 0; } c = g->buf1[j]; if (! in_str (c, separa_if)) error_line (g, j, "after @<%s>, char expected in %s", w, separa_if); for (k = j+1; k < b && g->buf1[k] != c; k++) ; if (g->buf1[k] != c) error_line (g, j+1, "after @<%s>%c, char %c expected", w, c, c); g->visib = (g->visib && found); parse_block (g, j+1, k); g->visib = old_visib; sta_pop (g, k, S_IF_INW); return k+1; /* position after @x..x */ } void put_oword (Glo *g, int a, char *s1, char c1) { char s2[BUF2_LEN]; int i; for (i = 0; s1[i] && i < BUF2_LEN-1; i++) s2[i] = (s1[i] == '$') ? c1 : s1[i]; s2[i] = s1[i]; puts_buf2 (g, a, s2); } int parse_oword (Glo *g, int a, int b) { int i, j, d; char s1[BUF2_LEN], c; /* @o can be outside a loop */ d = (g->loop_dim == ' ') ? 6 : g->loop_dim - '0'; if (a+1 >= b) error_line (g, a+1, "after @o, missing char"); c = g->buf1[a+1]; if (! in_str (c, separa_if)) error_line (g, a+1, "after @o, char expected in %s", separa_if); for (j = a+2, i = 0; j < b && g->buf1[j] != c && i < BUF2_LEN; j++, i++) s1[i] = g->buf1[j]; s1[i] = 0; if (g->buf1[j] != c || j >= b) error_line (g, a+1, "after @o%c, char %c expected", c, c); j++; if (j >= b) error_line (g, j, "after @o%c%s%c, missing char", c, s1, c); switch (g->buf1[j]) { case ']' : if (d >= 6) { put_oword (g, a, s1, 'r'); puts_buf2 (g, a, "]["); } if (d >= 5) { put_oword (g, a, s1, 's'); puts_buf2 (g, a, "]["); } if (d >= 4) { put_oword (g, a, s1, 't'); puts_buf2 (g, a, "]["); } if (d >= 3) { put_oword (g, a, s1, 'z'); puts_buf2 (g, a, "]["); } put_oword (g, a, s1, 'y'); puts_buf2 (g, a, "]["); put_oword (g, a, s1, 'x'); break; case ',' : case ';' : case ')' : if (d >= 6) { put_oword (g, a, s1, 'r'); puts_buf2 (g, a, ", "); } if (d >= 5) { put_oword (g, a, s1, 's'); puts_buf2 (g, a, ", "); } if (d >= 4) { put_oword (g, a, s1, 't'); puts_buf2 (g, a, ", "); } if (d >= 3) { put_oword (g, a, s1, 'z'); puts_buf2 (g, a, ", "); } put_oword (g, a, s1, 'y'); puts_buf2 (g, a, ", "); put_oword (g, a, s1, 'x'); break; default : error_line (g, j, "after @o%c%s%c, expect ],;)", c, s1, c); } return j; /* position of ],;) */ } int parse_block (Glo *g, int a, int b) { int pos; char prev, cur = ' '; for (pos = a; pos < b; pos++) { prev = cur; cur = g->buf1[pos]; if (prev == '@') switch (cur) { case '@' : /* @@ -> @ */ putc_buf2 (g, pos, '@'); cur = ' '; break; case '#' : /* @# comment */ g->blank = 1; pos = next_eol (g, pos, b)-1; break; case 'G' : { /* @G : generation date */ char db[512]; sprintf (db, "DO NOT EDIT !!! Generated " "by npic-templa from \"%s\"", g->in1); puts_buf2 (g, pos, db); } break; case 'J' : /* @J -> {23456} */ if (g->sta_nb == 0) error_line (g, pos, "@J outside loop"); putc_buf2 (g, pos, g->loop_dim); break; case 'K' : /* @K -> {LCDQ} */ if (g->sta_nb == 0) error_line (g, pos, "@K outside loop"); putc_buf2 (g, pos, g->loop_pix); break; case 'k' : /* @k -> {lcdq} */ if (g->sta_nb == 0) error_line (g, pos, "@k outside loop"); putc_buf2 (g, pos, g->loop_pix-'A'+'a'); break; case 'H' : /* @H -> {23456}{LCDQ} */ if (g->sta_nb == 0) error_line (g, pos, "@H outside loop"); putc_buf2 (g, pos, g->loop_dim); putc_buf2 (g, pos, g->loop_pix); break; case 'h' : /* @h -> {23456}{lcdq} */ if (g->sta_nb == 0) error_line (g, pos, "@h outside loop"); putc_buf2 (g, pos, g->loop_dim); putc_buf2 (g, pos, g->loop_pix-'A'+'a'); break; case 'B' : /* @BLOOP */ if (strncmp (g->buf1+pos, "BLOOP", 5) != 0) error_line (g, pos, "after @B, expect LOOP"); g->blank = 1; putc_buf2 (g, pos, '\n'); write_buf2 (g); pos = parse_loop (g, pos, b)-1; g->blank = 1; break; case 'E' : /* @ELOOP */ if (strncmp (g->buf1+pos, "ELOOP", 5) != 0) error_line (g, pos, "after @E, expect LOOP"); g->blank = 1; putc_buf2 (g, pos, '\n'); write_buf2 (g); sta_check (g, pos, S_LOOP); return pos; /* position of 'E' */ case '!' : /* @!IF or other tests */ g->nega = 1; cur = '@'; break; case 'I' : /* @IF[2L,3C,..] ... @FI */ if (strncmp (g->buf1+pos, "IF", 2) != 0) error_line (g, pos, "after @I, expect F"); if (g->sta_nb == 0) error_line (g, pos, "@IF outside loop"); g->blank = 1; pos = parse_if (g, pos, b)-1; break; case 'F' : /* @FI */ if (strncmp (g->buf1+pos, "FI", 2) != 0) error_line (g, pos, "after @F, expect I"); g->blank = 1; sta_check (g, pos, S_IF); return pos; /* position of 'F' */ case '[' : /* @[2L,3C,..]x...x */ if (g->sta_nb == 0) error_line (g, pos, "@[ outside loop"); g->blank = 1; pos = parse_if_subset (g, pos, b)-1; break; case '2' : case '3' : /* @23456x...x */ case '4' : case '5' : case '6' : if (g->sta_nb == 0) error_line (g, pos, "@<23456> outside loop"); g->blank = 1; pos = parse_if_inw (g, pos, b, "23456")-1; break; case 'L' : case 'C' : /* @LCDQx...x */ case 'D' : case 'Q' : if (g->sta_nb == 0) error_line (g, pos, "@ outside loop"); g->blank = 1; pos = parse_if_inw (g, pos, b, "LCDQ")-1; break; case 'o' : /* @ox~$~x? : word ~$~ can be empty, ? in ],;) */ pos = parse_oword (g, pos, b)-1; break; default : error_line (g, pos, "syntax error in \"@%c\"", cur); } else if (cur != '@') { putc_buf2 (g, pos, cur); if (cur == '\n') write_buf2 (g); } } return pos; } int memo_file1 (Glo *g) { int c, pos1 = 0, tot1 = 0; g->buf1 = NULL; while ((c = fgetc (g->f1)) != EOF) { if (pos1 >= tot1-1) { tot1 += BUF1_LEN; g->buf1 = realloc (g->buf1, tot1); if (g->buf1 == NULL) { fprintf (stderr, "ERROR: malloc error.\n"); exit (1); } } g->buf1[pos1++] = c; } g->buf1[pos1] = 0; return pos1; } void parse_file1 (Glo *g) { int k; /* General init */ g->pos2 = g->blank = g->nega = g->sta_nb = 0; g->visib = 1; g->sta_nb = 0; g->loop_dim = ' '; g->loop_pix = ' '; k = memo_file1 (g); parse_block (g, 0, k); free (g->buf1); } void ArgcExit (int argc, int n) { if (argc < n) { fprintf (stderr, "ERROR: %d argument(s) missing, " "type \"npic-templa -h\" to get help.\n", n-argc); exit (1); } } int main (int argc, char *argv[]) { Glo g; char svgc[1024]; ArgcExit (argc, 1+1); if (strcmp (argv[1], "-h") == 0 || strcmp (argv[1], "-help") == 0 || strcmp (argv[1], "--help") == 0) { ShowUsage (); exit (0); } ArgcExit (argc, 2+1); g.in1 = argv[1]; g.out1 = argv[2]; g.f1 = fopen (g.in1, "r"); if (g.f1 == NULL) { fprintf (stderr, "ERROR: can't read file \"%s\": %s\n", g.in1, strerror(errno)); exit (0); } snprintf (svgc, sizeof(svgc), "%s%%", g.out1); if (rename (g.out1, svgc) != 0 && errno != ENOENT) fprintf (stderr, "ERROR: can't rename \"%s\" as \"%s\": %s\n", g.out1, svgc, strerror(errno)); g.f2 = fopen (g.out1, "w"); if (g.f2 == NULL) { fprintf (stderr, "ERROR: can't write file \"%s\": %s\n", g.out1, strerror(errno)); exit (0); } parse_file1 (&g); fclose (g.f1); fclose (g.f2); exit (0); }