ncurses 6.1 - patch 20190727
[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.28 2019/07/27 23:06:54 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 NO_LEAKS
168     if (buffer == 0) {
169         free(list);
170         list = 0;
171         return 0;
172     }
173 #endif
174
175     if (*buffer != '#') {
176         if (list == 0) {
177             list = typeCalloc(char *, (MAX_COLUMNS + 1));
178             if (list == 0)
179                 return (0);
180         }
181         while (*buffer != '\0') {
182             char *s;
183             for (s = buffer; (*s != '\0') && !isspace(UChar(*s)); s++)
184                 /*EMPTY */ ;
185             if (s != buffer) {
186                 char mark = *s;
187                 *s = '\0';
188                 if ((s - buffer) > 1
189                     && (*buffer == '"')
190                     && (s[-1] == '"')) {        /* strip the quotes */
191                     assert(s > buffer + 1);
192                     s[-1] = '\0';
193                     buffer++;
194                 }
195                 list[col] = buffer;
196                 col++;
197                 if (mark == '\0')
198                     break;
199                 while (*++s && isspace(UChar(*s)))
200                     /*EMPTY */ ;
201                 buffer = s;
202             } else
203                 break;
204         }
205     }
206     return col ? list : 0;
207 }
208
209 #define SetType(n,t) \
210         if (is_user) \
211             name_table[n].ute_type |= (int)(1 << (t)); \
212         else \
213             name_table[n].ute_type = (t)
214
215 #define GetType(n) \
216         (is_user \
217          ? get_type(name_table[n].ute_type) \
218          : typenames[name_table[n].ute_type])
219
220 static char *
221 get_type(int type_mask)
222 {
223     static char result[80];
224     unsigned n;
225     _nc_STRCPY(result, L_PAREN, sizeof(result));
226     for (n = 0; n < 3; ++n) {
227         if ((1 << n) & type_mask) {
228             size_t want = 5 + strlen(typenames[n]);
229             if (want > sizeof(result)) {
230                 fprintf(stderr, "Buffer is not large enough for %s + %s\n",
231                         result, typenames[n]);
232                 exit(EXIT_FAILURE);
233             }
234             if (result[1])
235                 _nc_STRCAT(result, "|", sizeof(result));
236             _nc_STRCAT(result, "1<<", sizeof(result));
237             _nc_STRCAT(result, typenames[n], sizeof(result));
238         }
239     }
240     _nc_STRCAT(result, R_PAREN, sizeof(result));
241     return result;
242 }
243
244 int
245 main(int argc, char **argv)
246 {
247     unsigned tablesize = CAPTABSIZE;
248     struct user_table_entry *name_table = typeCalloc(struct
249                                                      user_table_entry, tablesize);
250     HashValue *hash_table = typeCalloc(HashValue, HASHTABSIZE);
251     const char *root_name = "";
252     int column = 0;
253     int bigstring = 0;
254     unsigned n;
255     unsigned nn;
256     unsigned tableused = 0;
257     bool is_user;
258     const char *table_name;
259     char buffer[BUFSIZ];
260
261     short BoolCount = 0;
262     short NumCount = 0;
263     short StrCount = 0;
264
265     /* The first argument is the column-number (starting with 0).
266      * The second is the root name of the tables to generate.
267      */
268     if (argc <= 3
269         || (column = atoi(argv[1])) <= 0
270         || (column >= MAX_COLUMNS)
271         || *(root_name = argv[2]) == 0
272         || (bigstring = atoi(argv[3])) < 0
273         || name_table == 0
274         || hash_table == 0) {
275         fprintf(stderr, "usage: make_hash column root_name bigstring\n");
276         exit(EXIT_FAILURE);
277     }
278     is_user = (*root_name == 'u');
279     table_name = (is_user ? "user" : "name");
280
281     /*
282      * Read the table into our arrays.
283      */
284     for (n = 0; (n < tablesize) && fgets(buffer, BUFSIZ, stdin);) {
285         char **list;
286         char *nlp = strchr(buffer, '\n');
287         if (nlp)
288             *nlp = '\0';
289         else
290             buffer[sizeof(buffer) - 2] = '\0';
291         list = parse_columns(buffer);
292         if (list == 0)          /* blank or comment */
293             continue;
294         if (is_user) {
295             if (strcmp(list[0], "userdef"))
296                 continue;
297         } else if (!strcmp(list[0], "userdef")) {
298             continue;
299         }
300         if (column < 0 || column > count_columns(list)) {
301             fprintf(stderr, "expected %d columns, have %d:\n%s\n",
302                     column,
303                     count_columns(list),
304                     buffer);
305             exit(EXIT_FAILURE);
306         }
307         nn = tableused;
308         if (is_user) {
309             unsigned j;
310             for (j = 0; j < tableused; ++j) {
311                 if (!strcmp(list[column], name_table[j].ute_name)) {
312                     nn = j;
313                     break;
314                 }
315             }
316         }
317         if (nn == tableused) {
318             name_table[nn].ute_link = -1;       /* end-of-hash */
319             name_table[nn].ute_name = strmalloc(list[column]);
320             ++tableused;
321         }
322
323         if (!strcmp(list[2], "bool")) {
324             SetType(nn, BOOLEAN);
325             name_table[nn].ute_index = BoolCount++;
326         } else if (!strcmp(list[2], "num")) {
327             SetType(nn, NUMBER);
328             name_table[nn].ute_index = NumCount++;
329         } else if (!strcmp(list[2], "str")) {
330             SetType(nn, STRING);
331             name_table[nn].ute_index = StrCount++;
332             if (is_user) {
333                 if (*list[3] != '-') {
334                     unsigned j;
335                     name_table[nn].ute_argc = (unsigned) strlen(list[3]);
336                     for (j = 0; j < name_table[nn].ute_argc; ++j) {
337                         if (list[3][j] == 's') {
338                             name_table[nn].ute_args |= (1U << j);
339                         }
340                     }
341                 }
342             }
343         } else {
344             fprintf(stderr, "Unknown type: %s\n", list[2]);
345             exit(EXIT_FAILURE);
346         }
347         n++;
348     }
349     if (tablesize > tableused)
350         tablesize = tableused;
351     _nc_make_hash_table(name_table, hash_table, tablesize);
352
353     /*
354      * Write the compiled tables to standard output
355      */
356     if (bigstring) {
357         int len = 0;
358         int nxt;
359
360         printf("static const char %s_names_text[] = \\\n", root_name);
361         for (n = 0; n < tablesize; n++) {
362             nxt = (int) strlen(name_table[n].ute_name) + 5;
363             if (nxt + len > 72) {
364                 printf("\\\n");
365                 len = 0;
366             }
367             printf("\"%s\\0\" ", name_table[n].ute_name);
368             len += nxt;
369         }
370         printf(";\n\n");
371
372         len = 0;
373         printf("static %s_table_data const %s_names_data[] =\n",
374                table_name,
375                root_name);
376         printf("%s\n", L_BRACE);
377         for (n = 0; n < tablesize; n++) {
378             printf("\t%s %15d,\t%10s,", L_BRACE, len, GetType(n));
379             if (is_user)
380                 printf("\t%d,%d,",
381                        name_table[n].ute_argc,
382                        name_table[n].ute_args);
383             printf("\t%3d, %3d %s%c\n",
384                    name_table[n].ute_index,
385                    name_table[n].ute_link,
386                    R_BRACE,
387                    n < tablesize - 1 ? ',' : ' ');
388             len += (int) strlen(name_table[n].ute_name) + 1;
389         }
390         printf("%s;\n\n", R_BRACE);
391         printf("static struct %s_table_entry *_nc_%s_table = 0;\n\n",
392                table_name,
393                root_name);
394     } else {
395
396         printf("static struct %s_table_entry const _nc_%s_table[] =\n",
397                table_name,
398                root_name);
399         printf("%s\n", L_BRACE);
400         for (n = 0; n < tablesize; n++) {
401             _nc_SPRINTF(buffer, _nc_SLIMIT(sizeof(buffer)) "\"%s\"",
402                         name_table[n].ute_name);
403             printf("\t%s %15s,\t%10s,", L_BRACE, buffer, GetType(n));
404             if (is_user)
405                 printf("\t%d,%d,",
406                        name_table[n].ute_argc,
407                        name_table[n].ute_args);
408             printf("\t%3d, %3d %s%c\n",
409                    name_table[n].ute_index,
410                    name_table[n].ute_link,
411                    R_BRACE,
412                    n < tablesize - 1 ? ',' : ' ');
413         }
414         printf("%s;\n\n", R_BRACE);
415     }
416
417     printf("static const HashValue _nc_%s_hash_table[%d] =\n",
418            root_name,
419            HASHTABSIZE + 1);
420     printf("%s\n", L_BRACE);
421     for (n = 0; n < HASHTABSIZE; n++) {
422         printf("\t%3d,\n", hash_table[n]);
423     }
424     printf("\t0\t/* base-of-table */\n");
425     printf("%s;\n\n", R_BRACE);
426
427     if (!is_user) {
428         printf("#if (BOOLCOUNT!=%d)||(NUMCOUNT!=%d)||(STRCOUNT!=%d)\n",
429                BoolCount, NumCount, StrCount);
430         printf("#error\t--> term.h and comp_captab.c disagree about the <--\n");
431         printf("#error\t--> numbers of booleans, numbers and/or strings <--\n");
432         printf("#endif\n\n");
433     }
434
435     free(hash_table);
436 #if NO_LEAKS
437     for (n = 0; (n < tablesize); ++n) {
438         free((void *) name_table[n].ute_name);
439     }
440     free(name_table);
441     parse_columns(0);
442 #endif
443     return EXIT_SUCCESS;
444 }