1 /*
2 * ============================================================================
3 * MK_TBL: Makes a lookup table from a description file
4 *
5 * Author: J. Zbiciak
6 *
7 *
8 * ============================================================================
9 *
10 * Makes a redundantly coded table in "N" bits.
11 *
12 * Takes a description of the form "0xxx1010xxx entry", and expands out all
13 * the 'x's. Processes entries in order, so exceptions to a don't-care can
14 * be handled:
15 *
16 * 00xx, "entry 1"
17 * 0000, "entry 2"
18 *
19 * This causes 0000 to have "entry 2" and 0001, 0010, 0011 to have "entry 1".
20 *
21 * The table description file should contain a single integer on the first
22 * non-comment line which corresponds to the number of bits that the table
23 * encodes. The remaining lines should contain patterns as described above.
24 *
25 * Usage: make_tbl table.tbl table.c
26 * ===========================================================================
27 */
28
29
30 #include "config.h"
31
32 #define MAX_BITS (20)
33 #define MAX_ENTRY (1024)
34 #define MAX_LINE (1024)
35
36 /*
37 * ===========================================================================
38 * RENDER_BITS -- converts value into a string of 1's and 0's
39 *
40 * Notes: This renders into a static buffer, and so it can't be used for
41 * multiple %s fields in a printf(). Sorry.
42 * ===========================================================================
43 */
render_bits(unsigned value,int bits)44 const char * render_bits(unsigned value, int bits)
45 {
46 int i;
47 static char string[32];
48 char * s = string;
49
50 for (i = bits-1; i >= 0; i--)
51 *s++ = ( ((value >> i) & 1) + '0' );
52
53 return string;
54 }
55
56 /*
57 * ===========================================================================
58 * EXPAND_DC -- expands the don't-cares in a description line.
59 *
60 * This function isn't terribly efficient, but then it doesn't need to be.
61 * ===========================================================================
62 */
expand_dc(char * tbl[],char * pattern,char * entry,int bits)63 void expand_dc(char *tbl[], char *pattern, char *entry, int bits)
64 {
65 int len, num_dc;
66 char *s;
67 int dc_tmp[MAX_BITS], dc_pos[MAX_BITS];
68 int j;
69 unsigned pat = 0, msk = 0, idx, i;
70
71
72 /* -------------------------------------------------------------------- */
73 /* Scan the pattern buffer looking for "don't cares", and note their */
74 /* positions. Also, build a bit mask for the bits that aren't DC's. */
75 /* -------------------------------------------------------------------- */
76 for (len = num_dc = 0, s = pattern; *s=='1' || *s=='0' || *s=='x'; s++)
77 {
78 pat <<= 1;
79 pat |= *s == '1';
80
81 msk <<= 1;
82 if (*s == 'x')
83 {
84 dc_tmp[num_dc++] = len;
85 } else
86 {
87 msk |= 1;
88 }
89 len++;
90
91 if (len > bits || num_dc > bits)
92 {
93 fprintf(stderr,"Oversized pattern encountered in expand_dc()\n");
94 exit(1);
95 }
96 }
97
98 /* -------------------------------------------------------------------- */
99 /* At this point, the dc_tmp array contains a list of all the don't- */
100 /* care positions. Re-order the array so that we can quickly fill in */
101 /* the don't-care positions in the next loop. This essentially does */
102 /* an endian swap bitwise on the recorded don't-care positions. */
103 /* -------------------------------------------------------------------- */
104
105 for (j = 0; j < num_dc; j++)
106 {
107 dc_pos[num_dc - j - 1] = len - dc_tmp[j] - 1;
108 }
109
110 /* -------------------------------------------------------------------- */
111 /* Now, fill in the table by expanding all of the don't-cares. */
112 /* -------------------------------------------------------------------- */
113 for (i = 0; i < (1U<<num_dc); i++)
114 {
115 idx = pat & msk;
116
117 for (j = 0; j < num_dc; j++)
118 {
119 idx |= (i << (dc_pos[j] - j)) & (1 << dc_pos[j]);
120 }
121
122 tbl[idx] = entry;
123 }
124 }
125
126 /*
127 * ===========================================================================
128 * GRAB_QUOTED_STRING -- Reads a quoted string out of an input line.
129 *
130 * Reads a quoted string from an input line. Accepts a pointer to the
131 * first character (which must be the opening quote), and returns a pointer
132 * to the last character (which is the closing quote), or NULL on failure.
133 * ===========================================================================
134 */
grab_quoted_string(char * in,char * out,int max_len)135 char * grab_quoted_string
136 (
137 char *in,
138 char *out,
139 int max_len
140 )
141 {
142 char *s, *es;
143 int p;
144
145 s = in;
146 es = out;
147
148 /* -------------------------------------------------------------------- */
149 /* Initial sanity check. Do not proceed if this fails. */
150 /* -------------------------------------------------------------------- */
151 if (!s || !es || *s++ != '"')
152 return NULL;
153
154 /* -------------------------------------------------------------------- */
155 /* Keep processing until we hit the closing quote. If we don't get */
156 /* the closing quote, or if we hit a syntax error in the string, or */
157 /* if the string is too long, we fail and return NULL. */
158 /* -------------------------------------------------------------------- */
159 while (*s && *s != '"')
160 {
161 p = *s++;
162
163 if (p == '\\')
164 {
165 p = *s++;
166
167 switch (p)
168 {
169 case 0 : return NULL; break;
170 case 'a' : p = '\a'; break;
171 case 'b' : p = '\b'; break;
172 case 'n' : p = '\n'; break;
173 case 'r' : p = '\r'; break;
174 case 't' : p = '\t'; break;
175 case 'v' : p = '\v'; break;
176 case 'x' :
177 {
178 int x1, x2;
179
180 if ( !(x1 = *s++) || !(x2 = *s++) ) return NULL;
181 x1 = HEXINT(toupper(x1));
182 x2 = HEXINT(toupper(x2));
183 if ( x1 < 0 || x2 < 0 ) return NULL;
184
185 p = (x1 << 4) | x2;
186
187 break;
188 }
189 case '0' : case '1' : case '2' : case '3' : case '4' :
190 case '5' : case '6' : case '7' :
191 {
192 int o1, o2, o3;
193
194 o1 = p;
195
196 if ( !(o2 = *s++) || !(o3 = *s++) ) return NULL;
197
198 o1 -= '0'; o2 -= '0'; o3 -= '0';
199
200 if ( o2 < 0 || o3 < 0 || o2 > 7 || o3 > 7 ) return NULL;
201
202 p = (o1 << 6) | (o2 << 3) | o3;
203
204 break;
205 }
206 default:
207 break;
208 /* just return whatever character that was escaped */
209 }
210 }
211
212 *es++ = p;
213
214 if (es - out >= max_len)
215 return NULL; /* entry too long */
216 }
217
218 *es = 0;
219
220 if (*s != '"')
221 return NULL;
222
223 return s;
224 }
225
226 /*
227 * ===========================================================================
228 * DECODE_BITS_LINE -- Decodes a line looking for # of bits designation
229 *
230 * Format:
231 *
232 * [space] [decimal number][, "default entry"] [space] [# comment]
233 *
234 * Return:
235 * -- 0 if the line was complete
236 * -- 1 if the line was empty/comments
237 * -- -1 if the line was invalid
238 * ===========================================================================
239 */
240 int
decode_bits_line(char * input,int * bits,char * entry,int max_entry)241 decode_bits_line
242 (
243 char *input,
244 int *bits,
245 char *entry,
246 int max_entry
247 )
248 {
249 char *s;
250 int p,val;
251
252 *bits = 0;
253
254 /* -------------------------------------------------------------------- */
255 /* Skip leading whitespace and find first char of the bit pattern. */
256 /* -------------------------------------------------------------------- */
257 s = input;
258 while (*s && isspace(*s)) s++;
259
260 /* -------------------------------------------------------------------- */
261 /* If we stopped at a # or NUL, just return that this is a comment. */
262 /* -------------------------------------------------------------------- */
263 if (*s == '#' || !*s) return 1;
264
265 /* -------------------------------------------------------------------- */
266 /* See if this is an integer, and if so decode it into our bit-length. */
267 /* If it isn't an integer, report an error. */
268 /* -------------------------------------------------------------------- */
269 if (!isdigit(*s))
270 {
271 return -1;
272 }
273
274 val = 0;
275 while (*s && isdigit(*s))
276 {
277 p = *s++;
278
279 val = val * 10 + p - '0';
280 }
281
282 *bits = val;
283
284 /* -------------------------------------------------------------------- */
285 /* Next, look for any whitespace, skipping it. If we run into end of */
286 /* string, stop here, returning what we have. Otherwise, look for the */
287 /* whitespace-comma-whitespace-quote sequence that preceeds a string. */
288 /* -------------------------------------------------------------------- */
289 while (*s && isspace(*s)) s++;
290 if (!*s || *s == '\n' || *s == '#')
291 {
292 strncpy(entry,"NULL,",max_entry);
293 return 0;
294 }
295 if (*s++ != ',') return -1;
296
297 while (*s && isspace(*s)) s++;
298 if (*s != '"') return -1;
299
300 /* -------------------------------------------------------------------- */
301 /* Now, copy the quoted string. It should end with another quote. */
302 /* Handle C-style escapes for \b, \x##, \###, \n, \t, \", etc.. */
303 /* -------------------------------------------------------------------- */
304 s = grab_quoted_string(s, entry, max_entry);
305
306 if (!s) return -1;
307
308
309 /* -------------------------------------------------------------------- */
310 /* We should be ok -- we'll just silently ignore the rest of the line. */
311 /* Return SUCCESS! */
312 /* -------------------------------------------------------------------- */
313 return 0;
314
315 }
316
317 /*
318 * ===========================================================================
319 * DECODE_DC_LINE -- Decodes a line, returning the pattern and entry
320 *
321 * Format:
322 *
323 * [space] [10x]* [space],[space] "[C-style string]" [space] # [space] [cmt]
324 * .prolog [space] "[C-style string]" [space] # [space] [cmt]
325 * .epilog [space] "[C-style string]" [space] # [space] [cmt]
326 *
327 * Return:
328 * -- 0 if the line was complete
329 * -- 1 if the line was empty/comments
330 * -- 2 if the line was a .prolog directive
331 * -- 3 if the line was a .epilog directive
332 * -- -1 if the line was invalid
333 * ===========================================================================
334 */
335 int
decode_dc_line(char * input,char * pattern,int max_bits,char * entry,int max_entry)336 decode_dc_line
337 (
338 char *input,
339 char *pattern,
340 int max_bits,
341 char *entry,
342 int max_entry
343 )
344 {
345 char *s, *ps;
346 int p;
347 int directive = 0;
348
349 /* -------------------------------------------------------------------- */
350 /* Skip leading whitespace and find first char of the bit pattern. */
351 /* -------------------------------------------------------------------- */
352 s = input;
353 while (*s && isspace(*s)) s++;
354
355
356 /* -------------------------------------------------------------------- */
357 /* If we stopped at a # or NUL, just return that this is a comment. */
358 /* -------------------------------------------------------------------- */
359 if (*s == '#' || !*s) return 1;
360
361 /* -------------------------------------------------------------------- */
362 /* Detect if this was a prolog/epilog directive. */
363 /* -------------------------------------------------------------------- */
364 if (!strncmp(s, ".prolog", 7)) directive = 2; else
365 if (!strncmp(s, ".epilog", 7)) directive = 3;
366
367 if (directive)
368 s += 7;
369
370 /* -------------------------------------------------------------------- */
371 /* Copy pattern into our pattern buffer. Do not allow the pattern */
372 /* length to exceed "bits." Allowed characters are [01A-Za-z._-]. */
373 /* Characters other than 0 and 1 are treated as "don't-cares" and are */
374 /* converted into 'x' in the pattern buffer. */
375 /* -------------------------------------------------------------------- */
376 if (!directive)
377 {
378 ps = pattern;
379 while (*s && (strchr("01._-",*s) || isalpha(*s)))
380 {
381 p = *s++;
382 if (p != '0' && p != '1') p = 'x';
383 *ps++ = p;
384
385 if (ps - pattern > max_bits)
386 {
387 return -1; /* pattern too long */
388 }
389 }
390
391 if (ps == pattern)
392 return -1; /* we didn't find a pattern! */
393
394 *ps++ = 0;
395 }
396
397 /* -------------------------------------------------------------------- */
398 /* Next, look for any whitespace, skipping it. The character after */
399 /* the (optional) whitespace must be a comma if this isn't a */
400 /* directive. After the comma is some more (optional) whitespace. */
401 /* It must be followed by a double quote (which starts the entry.) */
402 /* -------------------------------------------------------------------- */
403 if (!directive)
404 {
405 while (*s && isspace(*s)) s++;
406 if (*s++ != ',') return -1;
407 }
408
409 while (*s && isspace(*s)) s++;
410 if (*s != '"') return -1;
411
412 /* -------------------------------------------------------------------- */
413 /* Now, copy the quoted string. It should end with another quote. */
414 /* Handle C-style escapes for \b, \x##, \###, \n, \t, \", etc.. */
415 /* -------------------------------------------------------------------- */
416 s = grab_quoted_string(s, entry, max_entry);
417
418 if (!s) return -1;
419
420
421 /* -------------------------------------------------------------------- */
422 /* We should be ok -- we'll just silently ignore the rest of the line. */
423 /* Return SUCCESS! */
424 /* -------------------------------------------------------------------- */
425 return directive;
426 }
427
428 /*
429 * ===========================================================================
430 * GETLINE -- Reads a line of input. Handles 'Unix-style' files on
431 * non-Unix platforms (eg. DOS, Mac). Note: this only works
432 * on files, not redirected input.
433 * ===========================================================================
434 */
getline(char * buf,size_t len,FILE * f)435 char *getline(char *buf, size_t len, FILE *f)
436 {
437 long r_len;
438 long keep_len;
439 long seek_back;
440 char *s;
441
442 /* -------------------------------------------------------------------- */
443 /* Fill up the read buffer to the top. We'll go looking for an EOL */
444 /* in a second. (This fread() is the reason why we don't work on */
445 /* pipes/streams.) Abort on error. (Caller can use 'errno' to find */
446 /* out what happened.) */
447 /* -------------------------------------------------------------------- */
448 r_len = fread(buf, 1, len - 1, f); /* Fill the buffer. */
449 if (r_len <= 0) return NULL; /* Abort on error or EOF. */
450 buf[r_len] = '\0'; /* Guarantee NUL termination. */
451
452 /* -------------------------------------------------------------------- */
453 /* Now scan the string looking for 'LF' or 'CR' as our EOL marker. */
454 /* -------------------------------------------------------------------- */
455 s = buf;
456 while (*s)
457 {
458 /* ---------------------------------------------------------------- */
459 /* LF == ASCII code 10. CR == ASCII code 13. */
460 /* ---------------------------------------------------------------- */
461 if (*s == (char)10 || *s == (char)13) break;
462
463 s++;
464 }
465
466 /* -------------------------------------------------------------------- */
467 /* If we hit the NUL terminator, then we didn't see an EOL. Just */
468 /* return the whole darn string! */
469 /* -------------------------------------------------------------------- */
470 if (!*s)
471 return buf;
472
473 /* -------------------------------------------------------------------- */
474 /* Figure out how many characters we're keeping, including 1 */
475 /* character for newline. */
476 /* -------------------------------------------------------------------- */
477 keep_len = s - buf + 1;
478
479 /* -------------------------------------------------------------------- */
480 /* Next, figure out how many characters we need to seek back because */
481 /* we read too far. Note: If we have a CR + LF in the input, we */
482 /* need to consume both the CR and LF, but pass only one character */
483 /* for newline back to the application. Fun, fun. */
484 /* -------------------------------------------------------------------- */
485 seek_back = r_len - keep_len;
486 if (*s == (char)13 && s[1] == (char)10) /* CR + LF */
487 {
488 s[1] = '\0';
489 seek_back--;
490 }
491
492 /* -------------------------------------------------------------------- */
493 /* Now, push the unused characters back on the input. */
494 /* -------------------------------------------------------------------- */
495 fseek(f, -seek_back, SEEK_CUR);
496
497 /* -------------------------------------------------------------------- */
498 /* Lastly, replace the EOL marker with the platform's expected */
499 /* newline designator (good old '\n'), NUL-terminate it, and return */
500 /* it to the caller. */
501 /* -------------------------------------------------------------------- */
502 *s++ = '\n';
503 *s = '\0';
504
505 return buf;
506 }
507
508 /*
509 * ===========================================================================
510 * MAIN -- Uhm... uh... uhm... yeah! Huh-huh.
511 * ===========================================================================
512 */
main(int argc,char * argv[])513 int main(int argc, char *argv[])
514 {
515 int bits, i;
516 FILE *i_file, *o_file;
517 char **tbl;
518 char pattern[MAX_BITS + 2];
519 char entry [MAX_ENTRY + 1];
520 char d_entry[MAX_ENTRY + 1];
521 char buf [MAX_LINE + 1];
522 char *prolog_string = NULL;
523 char *epilog_string = NULL;
524
525 /* -------------------------------------------------------------------- */
526 /* Check to see that we've been given the appropriate number of args. */
527 /* -------------------------------------------------------------------- */
528 if (argc < 3)
529 {
530 fprintf(stderr,"Usage: mk_tbl table.tbl table.c\n");
531 exit(1);
532 }
533
534 /* -------------------------------------------------------------------- */
535 /* Next, try to open our input file for reading. */
536 /* -------------------------------------------------------------------- */
537 i_file = fopen(argv[1], "rb");
538 if ( !i_file )
539 {
540 perror(argv[1]);
541 fprintf(stderr,"Unable to read input file '%s'\n", argv[1]);
542 exit(1);
543 }
544
545 /* -------------------------------------------------------------------- */
546 /* Read the table size in bits from the file. Complain if we can't */
547 /* find it, or if its value is out of range. */
548 /* -------------------------------------------------------------------- */
549 while (getline(buf, sizeof(buf)-1, i_file) &&
550 (i = decode_bits_line(buf, &bits, d_entry, MAX_ENTRY)) )
551 {
552
553 if (i < 0)
554 {
555 fprintf(stderr,"Syntax error in input. "
556 "Table size in bits expected.\n");
557 fprintf(stderr,"--> %s\n",buf);
558 exit(1);
559 }
560
561 }
562
563 if (bits <= 0 || bits > MAX_BITS)
564 {
565 fprintf(stderr,"Bit size %d is out of range\n", bits);
566 exit(1);
567 }
568
569
570 /* -------------------------------------------------------------------- */
571 /* Now that we have the table size, allocate the table and set all of */
572 /* the entries to the default value. */
573 /* -------------------------------------------------------------------- */
574 tbl = malloc(sizeof(char *) << bits);
575 if (!tbl)
576 {
577 fprintf(stderr,"Out of memory allocating look-up table.\n"
578 "Try a smaller table size.\n");
579 exit(1);
580 }
581
582 for (i = 0; i < (1 << bits); i++)
583 tbl[i] = d_entry;
584
585 while (getline(buf, sizeof(buf)-1, i_file))
586 {
587 if (! (i = decode_dc_line(buf, pattern, bits, entry, MAX_ENTRY)) )
588 {
589 expand_dc(tbl, pattern, strdup(entry), bits);
590 }
591
592 if (i == 2)
593 {
594 if (prolog_string)
595 free(prolog_string);
596 prolog_string = strdup(entry);
597 }
598
599 if (i == 3)
600 {
601 if (epilog_string)
602 free(epilog_string);
603 epilog_string = strdup(entry);
604 }
605
606 if (i == -1)
607 {
608 fprintf(stderr,"Syntax error in pattern line.\n");
609 fprintf(stderr,"--> %s\n",buf);
610 exit(1);
611 }
612 }
613
614 /* -------------------------------------------------------------------- */
615 /* Close our input file, because we're done with it. */
616 /* -------------------------------------------------------------------- */
617 fclose(i_file);
618
619 /* -------------------------------------------------------------------- */
620 /* Next try to open our output file, now that we've successfully read */
621 /* in all of the patterns from our input file. */
622 /* -------------------------------------------------------------------- */
623 o_file = fopen(argv[2], "w");
624 if ( !o_file )
625 {
626 perror(argv[2]);
627 fprintf(stderr,"Unable to write output file '%s'\n", argv[2]);
628 exit(1);
629 }
630
631
632 /* -------------------------------------------------------------------- */
633 /* Write all of the entries in our table out to the output file. */
634 /* Note, we rely on the user to provide all the syntactic requirements */
635 /* such as commas. We do provide newlines, though. */
636 /* -------------------------------------------------------------------- */
637 if (prolog_string)
638 fprintf(o_file,"%s\n", prolog_string);
639
640 for (i = 0; i < (1<<bits) ; i++)
641 {
642 fprintf(o_file,"/*%*s*/\t %s\n", bits, render_bits(i,bits), tbl[i]);
643 }
644
645 if (epilog_string)
646 fprintf(o_file,"%s\n", epilog_string);
647
648 /* -------------------------------------------------------------------- */
649 /* Close our output file, because we're done with it too. */
650 /* -------------------------------------------------------------------- */
651 fclose(o_file);
652
653 return 0;
654 }
655
656 /* ======================================================================== */
657 /* This program is free software; you can redistribute it and/or modify */
658 /* it under the terms of the GNU General Public License as published by */
659 /* the Free Software Foundation; either version 2 of the License, or */
660 /* (at your option) any later version. */
661 /* */
662 /* This program is distributed in the hope that it will be useful, */
663 /* but WITHOUT ANY WARRANTY; without even the implied warranty of */
664 /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU */
665 /* General Public License for more details. */
666 /* */
667 /* You should have received a copy of the GNU General Public License along */
668 /* with this program; if not, write to the Free Software Foundation, Inc., */
669 /* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
670 /* ======================================================================== */
671 /* Copyright (c) 1998-1999, Joseph Zbiciak */
672 /* ======================================================================== */
673