xref: /openbsd/gnu/usr.bin/texinfo/info/makedoc.c (revision 09467b48)
1 /* makedoc.c -- make doc.c and funs.h from input files.
2    $Id: makedoc.c,v 1.5 2006/07/17 16:12:36 espie Exp $
3 
4    Copyright (C) 1993, 1997, 1998, 1999, 2001, 2002, 2003, 2004 Free Software
5    Foundation, Inc.
6 
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2, or (at your option)
10    any later version.
11 
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16 
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 
21    Originally written by Brian Fox (bfox@ai.mit.edu). */
22 
23 /* This program grovels the contents of the source files passed as arguments
24    and writes out a file of function pointers and documentation strings, and
25    a header file which describes the contents.  This only does the functions
26    declared with DECLARE_INFO_COMMAND. */
27 
28 #include "info.h"
29 #include "infokey.h"
30 
31 static void fatal_file_error (char *filename);
32 
33 /* Name of the header file which receives the declarations of functions. */
34 static char *funs_filename = "funs.h";
35 
36 /* Name of the documentation to function pointer file. */
37 static char *doc_filename = "doc.c";
38 static char *key_filename = "key.c";
39 
40 static char *doc_header[] = {
41   "/* doc.c -- Generated structure containing function names and doc strings.",
42   "",
43   "   This file was automatically made from various source files with the",
44   "   command `%s'.  DO NOT EDIT THIS FILE, only `%s.c'.",
45   (char *)NULL
46 };
47 
48 static char *doc_header_1[] = {
49   "   An entry in the array FUNCTION_DOC_ARRAY is made for each command",
50   "   found in the above files; each entry consists of a function pointer,",
51 #if defined (NAMED_FUNCTIONS)
52   "   a string which is the user-visible name of the function,",
53 #endif /* NAMED_FUNCTIONS */
54   "   and a string which documents its purpose. */",
55   "",
56   "#include \"info.h\"",
57   "#include \"funs.h\"",
58   "",
59   "FUNCTION_DOC function_doc_array[] = {",
60   "",
61   (char *)NULL
62 };
63 
64 static char *key_header[] = {
65   "/* key.c -- Generated array containing function names.",
66   "",
67   "   This file was automatically made from various source files with the",
68   "   command \"%s\".  DO NOT EDIT THIS FILE, only \"%s.c\".",
69   "",
70   (char *)NULL
71 };
72 
73 static char *key_header_1[] = {
74   "   An entry in the array FUNCTION_KEY_ARRAY is made for each command",
75   "   found in the above files; each entry consists of",
76   "   a string which is the user-visible name of the function.  */",
77   "",
78   "#include \"key.h\"",
79   "#include \"funs.h\"",
80   "",
81   "FUNCTION_KEY function_key_array[] = {",
82   "",
83   (char *)NULL
84 };
85 
86 /* How to remember the locations of the functions found so that Emacs
87    can use the information in a tag table. */
88 typedef struct {
89   char *name;                   /* Name of the tag. */
90   int line;                     /* Line number at which it appears. */
91   long char_offset;             /* Character offset at which it appears. */
92 } EMACS_TAG;
93 
94 typedef struct {
95   char *filename;               /* Name of the file containing entries. */
96   long entrylen;                /* Total number of characters in tag block. */
97   EMACS_TAG **entries;          /* Entries found in FILENAME. */
98   int entries_index;
99   int entries_slots;
100 } EMACS_TAG_BLOCK;
101 
102 EMACS_TAG_BLOCK **emacs_tags = (EMACS_TAG_BLOCK **)NULL;
103 int emacs_tags_index = 0;
104 int emacs_tags_slots = 0;
105 
106 #define DECLARATION_STRING "\nDECLARE_INFO_COMMAND"
107 
108 static void process_one_file (char *filename, FILE *doc_stream,
109     FILE *key_stream, FILE *funs_stream);
110 static void maybe_dump_tags (FILE *stream);
111 static FILE *must_fopen (char *filename, char *mode);
112 static void init_func_key (unsigned int val);
113 static unsigned int next_func_key (void);
114 
115 int
116 main (int argc, char **argv)
117 {
118   register int i;
119   int tags_only = 0;
120   FILE *funs_stream, *doc_stream;
121   FILE *key_stream;
122 
123 #if STRIP_DOT_EXE
124   {
125     char *dot = strrchr (argv[0], '.');
126 
127     if (dot && FILENAME_CMP (dot, ".exe") == 0)
128       *dot = 0;
129   }
130 #endif
131 
132   for (i = 1; i < argc; i++)
133     if (strcmp (argv[i], "-tags") == 0)
134       {
135         tags_only++;
136         break;
137       }
138 
139   if (tags_only)
140     {
141       funs_filename = NULL_DEVICE;
142       doc_filename = NULL_DEVICE;
143       key_filename = NULL_DEVICE;
144     }
145 
146   funs_stream = must_fopen (funs_filename, "w");
147   doc_stream = must_fopen (doc_filename, "w");
148   key_stream = must_fopen (key_filename, "w");
149 
150   fprintf (funs_stream,
151       "/* %s -- Generated declarations for Info commands. */\n\n\
152 #include \"info.h\"\n",
153       funs_filename);
154 
155   for (i = 0; doc_header[i]; i++)
156     {
157       fprintf (doc_stream, doc_header[i], argv[0], argv[0]);
158       fprintf (doc_stream, "\n");
159     }
160 
161   fprintf (doc_stream,
162            _("   Source files groveled to make this file include:\n\n"));
163 
164   for (i = 0; key_header[i]; i++)
165     {
166       fprintf (key_stream, key_header[i], argv[0], argv[0]);
167       fprintf (key_stream, "\n");
168     }
169   fprintf (key_stream,
170            _("   Source files groveled to make this file include:\n\n"));
171 
172   for (i = 1; i < argc; i++)
173     {
174       fprintf (doc_stream, "\t%s\n", argv[i]);
175       fprintf (key_stream, "\t%s\n", argv[i]);
176     }
177 
178   fprintf (doc_stream, "\n");
179   for (i = 0; doc_header_1[i]; i++)
180     fprintf (doc_stream, "%s\n", doc_header_1[i]);
181 
182   fprintf (key_stream, "\n");
183   for (i = 0; key_header_1[i]; i++)
184     fprintf (key_stream, "%s\n", key_header_1[i]);
185 
186   init_func_key(0);
187 
188   for (i = 1; i < argc; i++)
189     {
190       char *curfile;
191       curfile = argv[i];
192 
193       if (*curfile == '-')
194         continue;
195 
196       fprintf (doc_stream, "/* Commands found in \"%s\". */\n", curfile);
197       fprintf (key_stream, "/* Commands found in \"%s\". */\n", curfile);
198       fprintf (funs_stream, "\n/* Functions declared in \"%s\". */\n",
199                curfile);
200 
201       process_one_file (curfile, doc_stream, key_stream, funs_stream);
202     }
203 
204 #if defined (INFOKEY)
205 
206 #if defined (NAMED_FUNCTIONS)
207   fprintf (doc_stream,
208            "   { (VFunction *)NULL, (char *)NULL, (FUNCTION_KEYSEQ *)NULL, (char *)NULL }\n};\n");
209 #else /* !NAMED_FUNCTIONS */
210   fprintf (doc_stream, "   { (VFunction *)NULL, (FUNCTION_KEYSEQ *)NULL, (char *)NULL }\n};\n");
211 #endif /* !NAMED_FUNCTIONS */
212 
213 #else /* !INFOKEY */
214 
215 #if defined (NAMED_FUNCTIONS)
216   fprintf (doc_stream,
217            "   { (VFunction *)NULL, (char *)NULL, (char *)NULL }\n};\n");
218 #else /* !NAMED_FUNCTIONS */
219   fprintf (doc_stream, "   { (VFunction *)NULL, (char *)NULL }\n};\n");
220 #endif /* !NAMED_FUNCTIONS */
221 
222 #endif /* !INFOKEY */
223 
224   fprintf (key_stream, "   { (char *)NULL, 0 }\n};\n");
225   fprintf (funs_stream, "\n#define A_NCOMMANDS %u\n", next_func_key());
226 
227   fclose (funs_stream);
228   fclose (doc_stream);
229   fclose (key_stream);
230 
231   if (tags_only)
232     maybe_dump_tags (stdout);
233   return 0;
234 }
235 
236 /* Dumping out the contents of an Emacs tags table. */
237 static void
238 maybe_dump_tags (FILE *stream)
239 {
240   register int i;
241 
242   /* Emacs needs its TAGS file to be in Unix text format (i.e., only
243      newline at end of every line, no CR), so when we generate a
244      TAGS table, we must switch the output stream to binary mode.
245      (If the table is written to a terminal, this is obviously not needed.) */
246   SET_BINARY (fileno (stream));
247 
248   /* Print out the information for each block. */
249   for (i = 0; i < emacs_tags_index; i++)
250     {
251       register int j;
252       register EMACS_TAG_BLOCK *block;
253       register EMACS_TAG *etag;
254       long block_len;
255 
256       block_len = 0;
257       block = emacs_tags[i];
258 
259       /* Calculate the length of the dumped block first. */
260       for (j = 0; j < block->entries_index; j++)
261         {
262           char digits[30];
263           etag = block->entries[j];
264           block_len += 3 + strlen (etag->name);
265           sprintf (digits, "%d,%ld", etag->line, etag->char_offset);
266           block_len += strlen (digits);
267         }
268 
269       /* Print out the defining line. */
270       fprintf (stream, "\f\n%s,%ld\n", block->filename, block_len);
271 
272       /* Print out the individual tags. */
273       for (j = 0; j < block->entries_index; j++)
274         {
275           etag = block->entries[j];
276 
277           fprintf (stream, "%s,\177%d,%ld\n",
278                    etag->name, etag->line, etag->char_offset);
279         }
280     }
281 }
282 
283 /* Keeping track of names, line numbers and character offsets of functions
284    found in source files. */
285 static EMACS_TAG_BLOCK *
286 make_emacs_tag_block (char *filename)
287 {
288   EMACS_TAG_BLOCK *block;
289 
290   block = (EMACS_TAG_BLOCK *)xmalloc (sizeof (EMACS_TAG_BLOCK));
291   block->filename = xstrdup (filename);
292   block->entrylen = 0;
293   block->entries = (EMACS_TAG **)NULL;
294   block->entries_index = 0;
295   block->entries_slots = 0;
296   return (block);
297 }
298 
299 static void
300 add_tag_to_block (EMACS_TAG_BLOCK *block,
301     char *name, int line, long int char_offset)
302 {
303   EMACS_TAG *tag;
304 
305   tag = (EMACS_TAG *)xmalloc (sizeof (EMACS_TAG));
306   tag->name = name;
307   tag->line = line;
308   tag->char_offset = char_offset;
309   add_pointer_to_array (tag, block->entries_index, block->entries,
310                         block->entries_slots, 50, EMACS_TAG *);
311 }
312 
313 /* Read the file represented by FILENAME into core, and search it for Info
314    function declarations.  Output the declarations in various forms to the
315    DOC_STREAM, KEY_STREAM, and FUNS_STREAM. */
316 static void
317 process_one_file (char *filename, FILE *doc_stream,
318     FILE *key_stream, FILE *funs_stream)
319 {
320   int descriptor, decl_len;
321   char *buffer, *decl_str;
322   struct stat finfo;
323   long offset;
324   long file_size;
325   EMACS_TAG_BLOCK *block;
326 
327   if (stat (filename, &finfo) == -1)
328     fatal_file_error (filename);
329 
330   descriptor = open (filename, O_RDONLY, 0666);
331 
332   if (descriptor == -1)
333     fatal_file_error (filename);
334 
335   file_size = (long) finfo.st_size;
336   buffer = (char *)xmalloc (1 + file_size);
337   /* On some systems, the buffer will actually contain
338      less characters than the full file's size, because
339      the CR characters are removed from line endings.  */
340   file_size = read (descriptor, buffer, file_size);
341   close (descriptor);
342 
343   offset = 0;
344   decl_str = DECLARATION_STRING;
345   decl_len = strlen (decl_str);
346 
347   block = make_emacs_tag_block (filename);
348 
349   while (1)
350     {
351       long point = 0;
352       long line_start = 0;
353       int line_number = 0;
354 
355       char *func, *doc;
356 #if defined (INFOKEY) || defined (NAMED_FUNCTIONS)
357       char *func_name;
358 #endif /* INFOKEY || NAMED_FUNCTIONS */
359 
360       for (; offset < (file_size - decl_len); offset++)
361         {
362           if (buffer[offset] == '\n')
363             {
364               line_number++;
365               line_start = offset + 1;
366             }
367 
368           if (strncmp (buffer + offset, decl_str, decl_len) == 0)
369             {
370               offset += decl_len;
371               point = offset;
372               break;
373             }
374         }
375 
376       if (!point)
377         break;
378 
379       /* Skip forward until we find the open paren. */
380       while (point < file_size)
381         {
382           if (buffer[point] == '\n')
383             {
384               line_number++;
385               line_start = point + 1;
386             }
387           else if (buffer[point] == '(')
388             break;
389 
390           point++;
391         }
392 
393       while (point++ < file_size)
394         {
395           if (!whitespace_or_newline (buffer[point]))
396             break;
397           else if (buffer[point] == '\n')
398             {
399               line_number++;
400               line_start = point + 1;
401             }
402         }
403 
404       if (point >= file_size)
405         break;
406 
407       /* Now looking at name of function.  Get it. */
408       for (offset = point; buffer[offset] != ','; offset++);
409       func = (char *)xmalloc (1 + (offset - point));
410       strncpy (func, buffer + point, offset - point);
411       func[offset - point] = '\0';
412 
413       /* Remember this tag in the current block. */
414       {
415         char *tag_name;
416 
417         tag_name = (char *)xmalloc (1 + (offset - line_start));
418         strncpy (tag_name, buffer + line_start, offset - line_start);
419         tag_name[offset - line_start] = '\0';
420         add_tag_to_block (block, tag_name, line_number, point);
421       }
422 
423 #if defined (INFOKEY) || defined (NAMED_FUNCTIONS)
424       /* Generate the user-visible function name from the function's name. */
425       {
426         register int i;
427         char *name_start;
428 
429         name_start = func;
430 
431         if (strncmp (name_start, "info_", 5) == 0)
432           name_start += 5;
433 
434         func_name = xstrdup (name_start);
435 
436         /* Fix up "ea" commands. */
437         if (strncmp (func_name, "ea_", 3) == 0)
438           {
439             char *temp_func_name;
440 
441             temp_func_name = (char *)xmalloc (10 + strlen (func_name));
442             strcpy (temp_func_name, "echo_area_");
443             strcat (temp_func_name, func_name + 3);
444             free (func_name);
445             func_name = temp_func_name;
446           }
447 
448         for (i = 0; func_name[i]; i++)
449           if (func_name[i] == '_')
450             func_name[i] = '-';
451       }
452 #endif /* INFOKEY || NAMED_FUNCTIONS */
453 
454       /* Find doc string. */
455       point = offset + 1;
456 
457       while (point < file_size)
458         {
459           if (buffer[point] == '\n')
460             {
461               line_number++;
462               line_start = point + 1;
463             }
464 
465           if (buffer[point] == '"')
466             break;
467           else
468             point++;
469         }
470 
471       offset = point + 1;
472 
473       while (offset < file_size)
474         {
475           if (buffer[offset] == '\n')
476             {
477               line_number++;
478               line_start = offset + 1;
479             }
480 
481           if (buffer[offset] == '\\')
482             offset += 2;
483           else if (buffer[offset] == '"')
484             break;
485           else
486             offset++;
487         }
488 
489       offset++;
490       if (offset >= file_size)
491         break;
492 
493       doc = (char *)xmalloc (1 + (offset - point));
494       strncpy (doc, buffer + point, offset - point);
495       doc[offset - point] = '\0';
496 
497 #if defined (INFOKEY)
498 
499 #if defined (NAMED_FUNCTIONS)
500       fprintf (doc_stream,
501           "   { (VFunction *)%s, \"%s\", (FUNCTION_KEYSEQ *)0, %s },\n",
502           func, func_name, doc);
503 #else /* !NAMED_FUNCTIONS */
504       fprintf (doc_stream,
505           "   { (VFunction *) %s, (FUNCTION_KEYSEQ *)0, %s },\n", func, doc);
506 #endif /* !NAMED_FUNCTIONS */
507 
508       fprintf (key_stream, "   { \"%s\", A_%s },\n", func_name, func);
509 
510 #else /* !INFOKEY */
511 
512 #if defined (NAMED_FUNCTIONS)
513       fprintf (doc_stream, "   { %s, \"%s\", %s },\n", func, func_name, doc);
514 #else /* !NAMED_FUNCTIONS */
515       fprintf (doc_stream, "   { %s, %s },\n", func, doc);
516 #endif /* !NAMED_FUNCTIONS */
517 
518 #endif /* !INFOKEY */
519 
520 #if defined (INFOKEY) || defined (NAMED_FUNCTIONS)
521       free (func_name);
522 #endif /* INFOKEY || NAMED_FUNCTIONS */
523 
524 #if defined (INFOKEY)
525       fprintf (funs_stream, "#define A_%s %u\n", func, next_func_key());
526 #endif /* INFOKEY */
527       fprintf (funs_stream,
528           "extern void %s (WINDOW *window, int count, unsigned char key);\n",
529           func);
530       free (func);
531       free (doc);
532     }
533   free (buffer);
534 
535   /* If we created any tags, remember this file on our global list.  Otherwise,
536      free the memory already allocated to it. */
537   if (block->entries)
538     add_pointer_to_array (block, emacs_tags_index, emacs_tags,
539                           emacs_tags_slots, 10, EMACS_TAG_BLOCK *);
540   else
541     {
542       free (block->filename);
543       free (block);
544     }
545 }
546 
547 static void
548 fatal_file_error (char *filename)
549 {
550   fprintf (stderr, _("Couldn't manipulate the file %s.\n"), filename);
551   xexit (2);
552 }
553 
554 static FILE *
555 must_fopen (char *filename, char *mode)
556 {
557   FILE *stream;
558 
559   stream = fopen (filename, mode);
560   if (!stream)
561     fatal_file_error (filename);
562 
563   return (stream);
564 }
565 
566 static unsigned int func_key;
567 
568 static void
569 init_func_key(unsigned int val)
570 {
571 	func_key = val;
572 }
573 
574 static unsigned int
575 next_func_key(void)
576 {
577 	return func_key++;
578 }
579