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