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