ncurses 6.1 - patch 20191102
[ncurses.git] / ncurses / tinfo / make_hash.c
1 /****************************************************************************
2  * Copyright (c) 1998-2018,2019 Free Software Foundation, Inc.              *
3  *                                                                          *
4  * Permission is hereby granted, free of charge, to any person obtaining a  *
5  * copy of this software and associated documentation files (the            *
6  * "Software"), to deal in the Software without restriction, including      *
7  * without limitation the rights to use, copy, modify, merge, publish,      *
8  * distribute, distribute with modifications, sublicense, and/or sell       *
9  * copies of the Software, and to permit persons to whom the Software is    *
10  * furnished to do so, subject to the following conditions:                 *
11  *                                                                          *
12  * The above copyright notice and this permission notice shall be included  *
13  * in all copies or substantial portions of the Software.                   *
14  *                                                                          *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
16  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
17  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
18  * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
19  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
20  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
21  * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
22  *                                                                          *
23  * Except as contained in this notice, the name(s) of the above copyright   *
24  * holders shall not be used in advertising or otherwise to promote the     *
25  * sale, use or other dealings in this Software without prior written       *
26  * authorization.                                                           *
27  ****************************************************************************/
28
29 /****************************************************************************
30  *  Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995               *
31  *     and: Eric S. Raymond <esr@snark.thyrsus.com>                         *
32  *     and: Thomas E. Dickey                        1996-on                 *
33  ****************************************************************************/
34
35 /*
36  *      make_hash.c --- build-time program for constructing comp_captab.c
37  */
38
39 #include <build.priv.h>
40
41 #include <tic.h>
42 #include <hashsize.h>
43
44 #include <ctype.h>
45
46 MODULE_ID("$Id: make_hash.c,v 1.29 2019/10/19 21:02:19 tom Exp $")
47
48 /*
49  *      _nc_make_hash_table()
50  *
51  *      Takes the entries in table[] and hashes them into hash_table[]
52  *      by name.  There are CAPTABSIZE entries in the predefined table[]
53  *      and HASHTABSIZE slots in hash_table[].
54  *
55  */
56
57 #undef MODULE_ID
58 #define MODULE_ID(id)           /*nothing */
59 #include <tinfo/doalloc.c>
60
61 #define L_PAREN "("
62 #define R_PAREN ")"
63 #define L_BRACE "{"
64 #define R_BRACE "}"
65
66 static const char *typenames[] =
67 {"BOOLEAN", "NUMBER", "STRING"};
68
69 static void
70 failed(const char *s)
71 {
72     perror(s);
73     exit(EXIT_FAILURE);
74 }
75
76 static char *
77 strmalloc(char *s)
78 {
79     size_t need = strlen(s) + 1;
80     char *result = malloc(need);
81     if (result == 0)
82         failed("strmalloc");
83     _nc_STRCPY(result, s, need);
84     return result;
85 }
86
87 /*
88  *      int hash_function(string)
89  *
90  *      Computes the hashing function on the given string.
91  *
92  *      The current hash function is the sum of each consectutive pair
93  *      of characters, taken as two-byte integers, mod HASHTABSIZE.
94  *
95  */
96
97 static int
98 hash_function(const char *string)
99 {
100     long sum = 0;
101
102     while (*string) {
103         sum += (long) (*string + (*(string + 1) << 8));
104         string++;
105     }
106
107     return (int) (sum % HASHTABSIZE);
108 }
109
110 static void
111 _nc_make_hash_table(struct user_table_entry *table,
112                     HashValue * hash_table,
113                     unsigned tablesize)
114 {
115     unsigned i;
116     int hashvalue;
117     int collisions = 0;
118
119     for (i = 0; i < HASHTABSIZE; i++) {
120         hash_table[i] = -1;
121     }
122     for (i = 0; i < tablesize; i++) {
123         hashvalue = hash_function(table[i].ute_name);
124
125         if (hash_table[hashvalue] >= 0)
126             collisions++;
127
128         if (hash_table[hashvalue] != 0)
129             table[i].ute_link = hash_table[hashvalue];
130         hash_table[hashvalue] = (HashValue) i;
131     }
132
133     printf("/* %d collisions out of %d entries */\n", collisions, tablesize);
134 }
135
136 /*
137  * This filter reads from standard input a list of tab-delimited columns,
138  * (e.g., from Caps.filtered) computes the hash-value of a specified column and
139  * writes the hashed tables to standard output.
140  *
141  * By compiling the hash table at build time, we're able to make the entire
142  * set of terminfo and termcap tables readonly (and also provide some runtime
143  * performance enhancement).
144  */
145
146 #define MAX_COLUMNS BUFSIZ      /* this _has_ to be worst-case */
147
148 static int
149 count_columns(char **list)
150 {
151     int result = 0;
152     if (list != 0) {
153         while (*list++) {
154             ++result;
155         }
156     }
157     return result;
158 }
159
160 static char **
161 parse_columns(char *buffer)
162 {
163     static char **list;
164
165     int col = 0;
166
167     if (buffer == 0) {
168         free(list);
169         list = 0;
170         return 0;
171     }
172
173     if (*buffer != '#') {
174         if (list == 0) {
175             list = typeCalloc(char *, (MAX_COLUMNS + 1));
176             if (list == 0)
177                 return (0);
178         }
179         while (*buffer != '\0') {
180             char *s;
181             for (s = buffer; (*s != '\0') && !isspace(UChar(*s)); s++)
182                 /*EMPTY */ ;
183             if (s != buffer) {
184                 char mark = *s;
185                 *s = '\0';
186                 if ((s - buffer) > 1
187                     && (*buffer == '"')
188                     && (s[-1] == '"')) {        /* strip the quotes */
189                     assert(s > buffer + 1);
190                     s[-1] = '\0';
191                     buffer++;
192                 }
193                 list[col] = buffer;
194                 col++;
195                 if (mark == '\0')
196                     break;
197                 while (*++s && isspace(UChar(*s)))
198                     /*EMPTY */ ;
199                 buffer = s;
200             } else
201                 break;
202         }
203     }
204     return col ? list : 0;
205 }
206
207 #define SetType(n,t) \
208         if (is_user) \
209             name_table[n].ute_type |= (int)(1 << (t)); \
210         else \
211             name_table[n].ute_type = (t)
212
213 #define GetType(n) \
214         (is_user \
215          ? get_type(name_table[n].ute_type) \
216          : typenames[name_table[n].ute_type])
217
218 static char *
219 get_type(int type_mask)
220 {
221     static char result[80];
222     unsigned n;
223     _nc_STRCPY(result, L_PAREN, sizeof(result));
224     for (n = 0; n < 3; ++n) {
225         if ((1 << n) & type_mask) {
226             size_t want = 5 + strlen(typenames[n]);
227             if (want > sizeof(result)) {
228                 fprintf(stderr, "Buffer is not large enough for %s + %s\n",
229                         result, typenames[n]);
230                 exit(EXIT_FAILURE);
231             }
232             if (result[1])
233                 _nc_STRCAT(result, "|", sizeof(result));
234             _nc_STRCAT(result, "1<<", sizeof(result));
235             _nc_STRCAT(result, typenames[n], sizeof(result));
236         }
237     }
238     _nc_STRCAT(result, R_PAREN, sizeof(result));
239     return result;
240 }
241
242 int
243 main(int argc, char **argv)
244 {
245     unsigned tablesize = CAPTABSIZE;
246     struct user_table_entry *name_table = typeCalloc(struct
247                                                      user_table_entry, tablesize);
248     HashValue *hash_table = typeCalloc(HashValue, HASHTABSIZE);
249     const char *root_name = "";
250     int column = 0;
251     int bigstring = 0;
252     unsigned n;
253     unsigned nn;
254     unsigned tableused = 0;
255     bool is_user;
256     const char *table_name;
257     char buffer[BUFSIZ];
258
259     short BoolCount = 0;
260     short NumCount = 0;
261     short StrCount = 0;
262
263     /* The first argument is the column-number (starting with 0).
264      * The second is the root name of the tables to generate.
265      */
266     if (argc <= 3
267         || (column = atoi(argv[1])) <= 0
268         || (column >= MAX_COLUMNS)
269         || *(root_name = argv[2]) == 0
270         || (bigstring = atoi(argv[3])) < 0
271         || name_table == 0
272         || hash_table == 0) {
273         fprintf(stderr, "usage: make_hash column root_name bigstring\n");
274         exit(EXIT_FAILURE);
275     }
276     is_user = (*root_name == 'u');
277     table_name = (is_user ? "user" : "name");
278
279     /*
280      * Read the table into our arrays.
281      */
282     for (n = 0; (n < tablesize) && fgets(buffer, BUFSIZ, stdin);) {
283         char **list;
284         char *nlp = strchr(buffer, '\n');
285         if (nlp)
286             *nlp = '\0';
287         else
288             buffer[sizeof(buffer) - 2] = '\0';
289         list = parse_columns(buffer);
290         if (list == 0)          /* blank or comment */
291             continue;
292         if (is_user) {
293             if (strcmp(list[0], "userdef"))
294                 continue;
295         } else if (!strcmp(list[0], "userdef")) {
296             continue;
297         }
298         if (column < 0 || column > count_columns(list)) {
299             fprintf(stderr, "expected %d columns, have %d:\n%s\n",
300                     column,
301                     count_columns(list),
302                     buffer);
303             exit(EXIT_FAILURE);
304         }
305         nn = tableused;
306         if (is_user) {
307             unsigned j;
308             for (j = 0; j < tableused; ++j) {
309                 if (!strcmp(list[column], name_table[j].ute_name)) {
310                     nn = j;
311                     break;
312                 }
313             }
314         }
315         if (nn == tableused) {
316             name_table[nn].ute_link = -1;       /* end-of-hash */
317             name_table[nn].ute_name = strmalloc(list[column]);
318             ++tableused;
319         }
320
321         if (!strcmp(list[2], "bool")) {
322             SetType(nn, BOOLEAN);
323             name_table[nn].ute_index = BoolCount++;
324         } else if (!strcmp(list[2], "num")) {
325             SetType(nn, NUMBER);
326             name_table[nn].ute_index = NumCount++;
327         } else if (!strcmp(list[2], "str")) {
328             SetType(nn, STRING);
329             name_table[nn].ute_index = StrCount++;
330             if (is_user) {
331                 if (*list[3] != '-') {
332                     unsigned j;
333                     name_table[nn].ute_argc = (unsigned) strlen(list[3]);
334                     for (j = 0; j < name_table[nn].ute_argc; ++j) {
335                         if (list[3][j] == 's') {
336                             name_table[nn].ute_args |= (1U << j);
337                         }
338                     }
339                 }
340             }
341         } else {
342             fprintf(stderr, "Unknown type: %s\n", list[2]);
343             exit(EXIT_FAILURE);
344         }
345         n++;
346     }
347     if (tablesize > tableused)
348         tablesize = tableused;
349     _nc_make_hash_table(name_table, hash_table, tablesize);
350
351     /*
352      * Write the compiled tables to standard output
353      */
354     if (bigstring) {
355         int len = 0;
356         int nxt;
357
358         printf("static const char %s_names_text[] = \\\n", root_name);
359         for (n = 0; n < tablesize; n++) {
360             nxt = (int) strlen(name_table[n].ute_name) + 5;
361             if (nxt + len > 72) {
362                 printf("\\\n");
363                 len = 0;
364             }
365             printf("\"%s\\0\" ", name_table[n].ute_name);
366             len += nxt;
367         }
368         printf(";\n\n");
369
370         len = 0;
371         printf("static %s_table_data const %s_names_data[] =\n",
372                table_name,
373                root_name);
374         printf("%s\n", L_BRACE);
375         for (n = 0; n < tablesize; n++) {
376             printf("\t%s %15d,\t%10s,", L_BRACE, len, GetType(n));
377             if (is_user)
378                 printf("\t%d,%d,",
379                        name_table[n].ute_argc,
380                        name_table[n].ute_args);
381             printf("\t%3d, %3d %s%c\n",
382                    name_table[n].ute_index,
383                    name_table[n].ute_link,
384                    R_BRACE,
385                    n < tablesize - 1 ? ',' : ' ');
386             len += (int) strlen(name_table[n].ute_name) + 1;
387         }
388         printf("%s;\n\n", R_BRACE);
389         printf("static struct %s_table_entry *_nc_%s_table = 0;\n\n",
390                table_name,
391                root_name);
392     } else {
393
394         printf("static struct %s_table_entry const _nc_%s_table[] =\n",
395                table_name,
396                root_name);
397         printf("%s\n", L_BRACE);
398         for (n = 0; n < tablesize; n++) {
399             _nc_SPRINTF(buffer, _nc_SLIMIT(sizeof(buffer)) "\"%s\"",
400                         name_table[n].ute_name);
401             printf("\t%s %15s,\t%10s,", L_BRACE, buffer, GetType(n));
402             if (is_user)
403                 printf("\t%d,%d,",
404                        name_table[n].ute_argc,
405                        name_table[n].ute_args);
406             printf("\t%3d, %3d %s%c\n",
407                    name_table[n].ute_index,
408                    name_table[n].ute_link,
409                    R_BRACE,
410                    n < tablesize - 1 ? ',' : ' ');
411         }
412         printf("%s;\n\n", R_BRACE);
413     }
414
415     printf("static const HashValue _nc_%s_hash_table[%d] =\n",
416            root_name,
417            HASHTABSIZE + 1);
418     printf("%s\n", L_BRACE);
419     for (n = 0; n < HASHTABSIZE; n++) {
420         printf("\t%3d,\n", hash_table[n]);
421     }
422     printf("\t0\t/* base-of-table */\n");
423     printf("%s;\n\n", R_BRACE);
424
425     if (!is_user) {
426         printf("#if (BOOLCOUNT!=%d)||(NUMCOUNT!=%d)||(STRCOUNT!=%d)\n",
427                BoolCount, NumCount, StrCount);
428         printf("#error\t--> term.h and comp_captab.c disagree about the <--\n");
429         printf("#error\t--> numbers of booleans, numbers and/or strings <--\n");
430         printf("#endif\n\n");
431     }
432
433     free(hash_table);
434     for (n = 0; (n < tablesize); ++n) {
435         free((void *) name_table[n].ute_name);
436     }
437     free(name_table);
438     parse_columns(0);
439
440     return EXIT_SUCCESS;
441 }