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