Home | History | Annotate | Line # | Download | only in info
      1 /*	$NetBSD: info-utils.c,v 1.1.1.1 2016/01/14 00:11:29 christos Exp $	*/
      2 
      3 /* info-utils.c -- miscellanous.
      4    Id: info-utils.c,v 1.4 2004/04/11 17:56:45 karl Exp
      5 
      6    Copyright (C) 1993, 1998, 2003, 2004 Free Software Foundation, Inc.
      7 
      8    This program is free software; you can redistribute it and/or modify
      9    it under the terms of the GNU General Public License as published by
     10    the Free Software Foundation; either version 2, or (at your option)
     11    any later version.
     12 
     13    This program is distributed in the hope that it will be useful,
     14    but WITHOUT ANY WARRANTY; without even the implied warranty of
     15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     16    GNU General Public License for more details.
     17 
     18    You should have received a copy of the GNU General Public License
     19    along with this program; if not, write to the Free Software
     20    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
     21 
     22    Originally written by Brian Fox (bfox (at) ai.mit.edu). */
     23 
     24 #include "info.h"
     25 #include "info-utils.h"
     26 #if defined (HANDLE_MAN_PAGES)
     27 #  include "man.h"
     28 #endif /* HANDLE_MAN_PAGES */
     29 
     30 /* When non-zero, various display and input functions handle ISO Latin
     31    character sets correctly. */
     32 int ISO_Latin_p = 1;
     33 
     34 /* Variable which holds the most recent filename parsed as a result of
     35    calling info_parse_xxx (). */
     36 char *info_parsed_filename = (char *)NULL;
     37 
     38 /* Variable which holds the most recent nodename parsed as a result of
     39    calling info_parse_xxx (). */
     40 char *info_parsed_nodename = (char *)NULL;
     41 
     42 /* Variable which holds the most recent line number parsed as a result of
     43    calling info_parse_xxx (). */
     44 int info_parsed_line_number = 0;
     45 
     46 /* Functions to remember a filename or nodename for later return. */
     47 static void save_filename (char *filename);
     48 static void saven_filename (char *filename, int len);
     49 static void save_nodename (char *nodename);
     50 static void saven_nodename (char *nodename, int len);
     51 
     52 /* How to get a reference (either menu or cross). */
     53 static REFERENCE **info_references_internal (char *label,
     54     SEARCH_BINDING *binding);
     55 
     56 /* Parse the filename and nodename out of STRING.  If STRING doesn't
     57    contain a filename (i.e., it is NOT (FILENAME)NODENAME) then set
     58    INFO_PARSED_FILENAME to NULL.  If second argument NEWLINES_OKAY is
     59    non-zero, it says to allow the nodename specification to cross a
     60    newline boundary (i.e., only `,', `.', or `TAB' can end the spec). */
     61 void
     62 info_parse_node (char *string, int newlines_okay)
     63 {
     64   register int i = 0;
     65 
     66   /* Default the answer. */
     67   save_filename ((char *)NULL);
     68   save_nodename ((char *)NULL);
     69 
     70   /* Special case of nothing passed.  Return nothing. */
     71   if (!string || !*string)
     72     return;
     73 
     74   string += skip_whitespace (string);
     75 
     76   /* Check for (FILENAME)NODENAME. */
     77   if (*string == '(')
     78     {
     79       i = 0;
     80       /* Advance past the opening paren. */
     81       string++;
     82 
     83       /* Find the closing paren. */
     84       while (string[i] && string[i] != ')')
     85         i++;
     86 
     87       /* Remember parsed filename. */
     88       saven_filename (string, i);
     89 
     90       /* Point directly at the nodename. */
     91       string += i;
     92 
     93       if (*string)
     94         string++;
     95     }
     96 
     97   /* Parse out nodename. */
     98   i = skip_node_characters (string, newlines_okay);
     99   saven_nodename (string, i);
    100   canonicalize_whitespace (info_parsed_nodename);
    101   if (info_parsed_nodename && !*info_parsed_nodename)
    102     {
    103       free (info_parsed_nodename);
    104       info_parsed_nodename = (char *)NULL;
    105     }
    106 
    107   /* Parse ``(line ...)'' part of menus, if any.  */
    108   {
    109     char *rest = string + i;
    110 
    111     /* Advance only if it's not already at end of string.  */
    112     if (*rest)
    113       rest++;
    114 
    115     /* Skip any whitespace first, and then a newline in case the item
    116        was so long to contain the ``(line ...)'' string in the same
    117        physical line.  */
    118     while (whitespace(*rest))
    119       rest++;
    120     if (*rest == '\n')
    121       {
    122         rest++;
    123         while (whitespace(*rest))
    124           rest++;
    125       }
    126 
    127     /* Are we looking at an opening parenthesis?  That can only mean
    128        we have a winner. :)  */
    129     if (strncmp (rest, "(line ", strlen ("(line ")) == 0)
    130       {
    131         rest += strlen ("(line ");
    132         info_parsed_line_number = strtol (rest, NULL, 0);
    133       }
    134     else
    135       info_parsed_line_number = 0;
    136   }
    137 }
    138 
    139 /* Return the node addressed by LABEL in NODE (usually one of "Prev:",
    140    "Next:", "Up:", "File:", or "Node:".  After a call to this function,
    141    the global INFO_PARSED_NODENAME and INFO_PARSED_FILENAME contain
    142    the information. */
    143 void
    144 info_parse_label (char *label, NODE *node)
    145 {
    146   register int i;
    147   char *nodeline;
    148 
    149   /* Default answer to failure. */
    150   save_nodename ((char *)NULL);
    151   save_filename ((char *)NULL);
    152 
    153   /* Find the label in the first line of this node. */
    154   nodeline = node->contents;
    155   i = string_in_line (label, nodeline);
    156 
    157   if (i == -1)
    158     return;
    159 
    160   nodeline += i;
    161   nodeline += skip_whitespace (nodeline);
    162   info_parse_node (nodeline, DONT_SKIP_NEWLINES);
    163 }
    164 
    165 /* **************************************************************** */
    167 /*                                                                  */
    168 /*                  Finding and Building Menus                      */
    169 /*                                                                  */
    170 /* **************************************************************** */
    171 
    172 /* Return a NULL terminated array of REFERENCE * which represents the menu
    173    found in NODE.  If there is no menu in NODE, just return a NULL pointer. */
    174 REFERENCE **
    175 info_menu_of_node (NODE *node)
    176 {
    177   long position;
    178   SEARCH_BINDING tmp_search;
    179   REFERENCE **menu = (REFERENCE **)NULL;
    180 
    181   tmp_search.buffer = node->contents;
    182   tmp_search.start = 0;
    183   tmp_search.end = node->nodelen;
    184   tmp_search.flags = S_FoldCase;
    185 
    186   /* Find the start of the menu. */
    187   position = search_forward (INFO_MENU_LABEL, &tmp_search);
    188 
    189   if (position == -1)
    190     return ((REFERENCE **) NULL);
    191 
    192   /* We have the start of the menu now.  Glean menu items from the rest
    193      of the node. */
    194   tmp_search.start = position + strlen (INFO_MENU_LABEL);
    195   tmp_search.start += skip_line (tmp_search.buffer + tmp_search.start);
    196   tmp_search.start--;
    197   menu = info_menu_items (&tmp_search);
    198   return (menu);
    199 }
    200 
    201 /* Return a NULL terminated array of REFERENCE * which represents the cross
    202    refrences found in NODE.  If there are no cross references in NODE, just
    203    return a NULL pointer. */
    204 REFERENCE **
    205 info_xrefs_of_node (NODE *node)
    206 {
    207   SEARCH_BINDING tmp_search;
    208 
    209 #if defined (HANDLE_MAN_PAGES)
    210   if (node->flags & N_IsManPage)
    211     return (xrefs_of_manpage (node));
    212 #endif
    213 
    214   tmp_search.buffer = node->contents;
    215   tmp_search.start = 0;
    216   tmp_search.end = node->nodelen;
    217   tmp_search.flags = S_FoldCase;
    218 
    219   return (info_xrefs (&tmp_search));
    220 }
    221 
    222 /* Glean menu entries from BINDING->buffer + BINDING->start until we
    223    have looked at the entire contents of BINDING.  Return an array
    224    of REFERENCE * that represents each menu item in this range. */
    225 REFERENCE **
    226 info_menu_items (SEARCH_BINDING *binding)
    227 {
    228   return (info_references_internal (INFO_MENU_ENTRY_LABEL, binding));
    229 }
    230 
    231 /* Glean cross references from BINDING->buffer + BINDING->start until
    232    BINDING->end.  Return an array of REFERENCE * that represents each
    233    cross reference in this range. */
    234 REFERENCE **
    235 info_xrefs (SEARCH_BINDING *binding)
    236 {
    237   return (info_references_internal (INFO_XREF_LABEL, binding));
    238 }
    239 
    240 /* Glean cross references or menu items from BINDING.  Return an array
    241    of REFERENCE * that represents the items found. */
    242 static REFERENCE **
    243 info_references_internal (char *label, SEARCH_BINDING *binding)
    244 {
    245   SEARCH_BINDING tmp_search;
    246   REFERENCE **refs = (REFERENCE **)NULL;
    247   int refs_index = 0, refs_slots = 0;
    248   int searching_for_menu_items = 0;
    249   long position;
    250 
    251   tmp_search.buffer = binding->buffer;
    252   tmp_search.start = binding->start;
    253   tmp_search.end = binding->end;
    254   tmp_search.flags = S_FoldCase | S_SkipDest;
    255 
    256   searching_for_menu_items = (strcasecmp (label, INFO_MENU_ENTRY_LABEL) == 0);
    257 
    258   while ((position = search_forward (label, &tmp_search)) != -1)
    259     {
    260       int offset, start;
    261       char *refdef;
    262       REFERENCE *entry;
    263 
    264       tmp_search.start = position;
    265       tmp_search.start += skip_whitespace (tmp_search.buffer + tmp_search.start);
    266       start = tmp_search.start - binding->start;
    267       refdef = tmp_search.buffer + tmp_search.start;
    268       offset = string_in_line (":", refdef);
    269 
    270       /* When searching for menu items, if no colon, there is no
    271          menu item on this line. */
    272       if (offset == -1)
    273         {
    274           if (searching_for_menu_items)
    275             continue;
    276           else
    277             {
    278               int temp;
    279 
    280               temp = skip_line (refdef);
    281               offset = string_in_line (":", refdef + temp);
    282               if (offset == -1)
    283                 continue;       /* Give up? */
    284               else
    285                 offset += temp;
    286             }
    287         }
    288 
    289       entry = (REFERENCE *)xmalloc (sizeof (REFERENCE));
    290       entry->filename = (char *)NULL;
    291       entry->nodename = (char *)NULL;
    292       entry->label = (char *)xmalloc (offset);
    293       strncpy (entry->label, refdef, offset - 1);
    294       entry->label[offset - 1] = '\0';
    295       canonicalize_whitespace (entry->label);
    296 
    297       refdef += offset;
    298       entry->start = start;
    299       entry->end = refdef - binding->buffer;
    300 
    301       /* If this reference entry continues with another ':' then the
    302          nodename is the same as the label. */
    303       if (*refdef == ':')
    304         {
    305           entry->nodename = xstrdup (entry->label);
    306         }
    307       else
    308         {
    309           /* This entry continues with a specific nodename.  Parse the
    310              nodename from the specification. */
    311 
    312           refdef += skip_whitespace_and_newlines (refdef);
    313 
    314           if (searching_for_menu_items)
    315             info_parse_node (refdef, DONT_SKIP_NEWLINES);
    316           else
    317             info_parse_node (refdef, SKIP_NEWLINES);
    318 
    319           if (info_parsed_filename)
    320             entry->filename = xstrdup (info_parsed_filename);
    321 
    322           if (info_parsed_nodename)
    323             entry->nodename = xstrdup (info_parsed_nodename);
    324 
    325           entry->line_number = info_parsed_line_number;
    326         }
    327 
    328       add_pointer_to_array
    329         (entry, refs_index, refs, refs_slots, 50, REFERENCE *);
    330     }
    331   return (refs);
    332 }
    333 
    334 /* Get the entry associated with LABEL in REFERENCES.  Return a pointer
    335    to the ENTRY if found, or NULL. */
    336 REFERENCE *
    337 info_get_labeled_reference (char *label, REFERENCE **references)
    338 {
    339   register int i;
    340   REFERENCE *entry;
    341 
    342   for (i = 0; references && (entry = references[i]); i++)
    343     {
    344       if (strcmp (label, entry->label) == 0)
    345         return (entry);
    346     }
    347   return ((REFERENCE *)NULL);
    348 }
    349 
    350 /* A utility function for concatenating REFERENCE **.  Returns a new
    351    REFERENCE ** which is the concatenation of REF1 and REF2.  The REF1
    352    and REF2 arrays are freed, but their contents are not. */
    353 REFERENCE **
    354 info_concatenate_references (REFERENCE **ref1, REFERENCE **ref2)
    355 {
    356   register int i, j;
    357   REFERENCE **result;
    358   int size;
    359 
    360   /* With one argument passed as NULL, simply return the other arg. */
    361   if (!ref1)
    362     return (ref2);
    363   else if (!ref2)
    364     return (ref1);
    365 
    366   /* Get the total size of the slots that we will need. */
    367   for (i = 0; ref1[i]; i++);
    368   size = i;
    369   for (i = 0; ref2[i]; i++);
    370   size += i;
    371 
    372   result = (REFERENCE **)xmalloc ((1 + size) * sizeof (REFERENCE *));
    373 
    374   /* Copy the contents over. */
    375   for (i = 0; ref1[i]; i++)
    376     result[i] = ref1[i];
    377 
    378   j = i;
    379   for (i = 0; ref2[i]; i++)
    380     result[j++] = ref2[i];
    381 
    382   result[j] = (REFERENCE *)NULL;
    383   free (ref1);
    384   free (ref2);
    385   return (result);
    386 }
    387 
    388 
    389 
    390 /* Copy a reference structure.  Since we tend to free everything at
    392    every opportunity, we don't share any points, but copy everything into
    393    new memory.  */
    394 REFERENCE *
    395 info_copy_reference (REFERENCE *src)
    396 {
    397   REFERENCE *dest = xmalloc (sizeof (REFERENCE));
    398   dest->label = src->label ? xstrdup (src->label) : NULL;
    399   dest->filename = src->filename ? xstrdup (src->filename) : NULL;
    400   dest->nodename = src->nodename ? xstrdup (src->nodename) : NULL;
    401   dest->start = src->start;
    402   dest->end = src->end;
    403 
    404   return dest;
    405 }
    406 
    407 
    408 
    409 /* Free the data associated with REFERENCES. */
    411 void
    412 info_free_references (REFERENCE **references)
    413 {
    414   register int i;
    415   REFERENCE *entry;
    416 
    417   if (references)
    418     {
    419       for (i = 0; references && (entry = references[i]); i++)
    420         {
    421           maybe_free (entry->label);
    422           maybe_free (entry->filename);
    423           maybe_free (entry->nodename);
    424 
    425           free (entry);
    426         }
    427 
    428       free (references);
    429     }
    430 }
    431 
    432 /* Search for sequences of whitespace or newlines in STRING, replacing
    433    all such sequences with just a single space.  Remove whitespace from
    434    start and end of string. */
    435 void
    436 canonicalize_whitespace (char *string)
    437 {
    438   register int i, j;
    439   int len, whitespace_found, whitespace_loc = 0;
    440   char *temp;
    441 
    442   if (!string)
    443     return;
    444 
    445   len = strlen (string);
    446   temp = (char *)xmalloc (1 + len);
    447 
    448   /* Search for sequences of whitespace or newlines.  Replace all such
    449      sequences in the string with just a single space. */
    450 
    451   whitespace_found = 0;
    452   for (i = 0, j = 0; string[i]; i++)
    453     {
    454       if (whitespace_or_newline (string[i]))
    455         {
    456           whitespace_found++;
    457           whitespace_loc = i;
    458           continue;
    459         }
    460       else
    461         {
    462           if (whitespace_found && whitespace_loc)
    463             {
    464               whitespace_found = 0;
    465 
    466               /* Suppress whitespace at start of string. */
    467               if (j)
    468                 temp[j++] = ' ';
    469             }
    470 
    471           temp[j++] = string[i];
    472         }
    473     }
    474 
    475   /* Kill trailing whitespace. */
    476   if (j && whitespace (temp[j - 1]))
    477     j--;
    478 
    479   temp[j] = '\0';
    480   strcpy (string, temp);
    481   free (temp);
    482 }
    483 
    484 /* String representation of a char returned by printed_representation (). */
    485 static char the_rep[10];
    486 
    487 /* Return a pointer to a string which is the printed representation
    488    of CHARACTER if it were printed at HPOS. */
    489 char *
    490 printed_representation (unsigned char character, int hpos)
    491 {
    492   register int i = 0;
    493   int printable_limit = ISO_Latin_p ? 255 : 127;
    494 
    495   if (raw_escapes_p && character == '\033')
    496     the_rep[i++] = character;
    497   /* Show CTRL-x as ^X.  */
    498   else if (iscntrl (character) && character < 127)
    499     {
    500       switch (character)
    501         {
    502         case '\r':
    503         case '\n':
    504           the_rep[i++] = character;
    505           break;
    506 
    507         case '\t':
    508           {
    509             int tw;
    510 
    511             tw = ((hpos + 8) & 0xf8) - hpos;
    512             while (i < tw)
    513               the_rep[i++] = ' ';
    514           }
    515           break;
    516 
    517         default:
    518           the_rep[i++] = '^';
    519           the_rep[i++] = (character | 0x40);
    520         }
    521     }
    522   /* Show META-x as 0370.  */
    523   else if (character > printable_limit)
    524     {
    525       sprintf (the_rep + i, "\\%0o", character);
    526       i = strlen (the_rep);
    527     }
    528   else if (character == DEL)
    529     {
    530       the_rep[i++] = '^';
    531       the_rep[i++] = '?';
    532     }
    533   else
    534     the_rep[i++] = character;
    535 
    536   the_rep[i] = 0;
    537 
    538   return the_rep;
    539 }
    540 
    541 
    542 /* **************************************************************** */
    544 /*                                                                  */
    545 /*                  Functions Static To This File                   */
    546 /*                                                                  */
    547 /* **************************************************************** */
    548 
    549 /* Amount of space allocated to INFO_PARSED_FILENAME via xmalloc (). */
    550 static int parsed_filename_size = 0;
    551 
    552 /* Amount of space allocated to INFO_PARSED_NODENAME via xmalloc (). */
    553 static int parsed_nodename_size = 0;
    554 
    555 static void save_string (char *string, char **string_p, int *string_size_p);
    556 static void saven_string (char *string, int len, char **string_p,
    557     int *string_size_p);
    558 
    559 /* Remember FILENAME in PARSED_FILENAME.  An empty FILENAME is translated
    560    to a NULL pointer in PARSED_FILENAME. */
    561 static void
    562 save_filename (char *filename)
    563 {
    564   save_string (filename, &info_parsed_filename, &parsed_filename_size);
    565 }
    566 
    567 /* Just like save_filename (), but you pass the length of the string. */
    568 static void
    569 saven_filename (char *filename, int len)
    570 {
    571   saven_string (filename, len,
    572                 &info_parsed_filename, &parsed_filename_size);
    573 }
    574 
    575 /* Remember NODENAME in PARSED_NODENAME.  An empty NODENAME is translated
    576    to a NULL pointer in PARSED_NODENAME. */
    577 static void
    578 save_nodename (char *nodename)
    579 {
    580   save_string (nodename, &info_parsed_nodename, &parsed_nodename_size);
    581 }
    582 
    583 /* Just like save_nodename (), but you pass the length of the string. */
    584 static void
    585 saven_nodename (char *nodename, int len)
    586 {
    587   saven_string (nodename, len,
    588                 &info_parsed_nodename, &parsed_nodename_size);
    589 }
    590 
    591 /* Remember STRING in STRING_P.  STRING_P should currently have STRING_SIZE_P
    592    bytes allocated to it.  An empty STRING is translated to a NULL pointer
    593    in STRING_P. */
    594 static void
    595 save_string (char *string, char **string_p, int *string_size_p)
    596 {
    597   if (!string || !*string)
    598     {
    599       if (*string_p)
    600         free (*string_p);
    601 
    602       *string_p = (char *)NULL;
    603       *string_size_p = 0;
    604     }
    605   else
    606     {
    607       if (strlen (string) >= (unsigned int) *string_size_p)
    608         *string_p = (char *)xrealloc
    609           (*string_p, (*string_size_p = 1 + strlen (string)));
    610 
    611       strcpy (*string_p, string);
    612     }
    613 }
    614 
    615 /* Just like save_string (), but you also pass the length of STRING. */
    616 static void
    617 saven_string (char *string, int len, char **string_p, int *string_size_p)
    618 {
    619   if (!string)
    620     {
    621       if (*string_p)
    622         free (*string_p);
    623 
    624       *string_p = (char *)NULL;
    625       *string_size_p = 0;
    626     }
    627   else
    628     {
    629       if (len >= *string_size_p)
    630         *string_p = (char *)xrealloc (*string_p, (*string_size_p = 1 + len));
    631 
    632       strncpy (*string_p, string, len);
    633       (*string_p)[len] = '\0';
    634     }
    635 }
    636 
    637 /* Return a pointer to the part of PATHNAME that simply defines the file. */
    638 char *
    639 filename_non_directory (char *pathname)
    640 {
    641   register char *filename = pathname + strlen (pathname);
    642 
    643   if (HAVE_DRIVE (pathname))
    644     pathname += 2;
    645 
    646   while (filename > pathname && !IS_SLASH (filename[-1]))
    647     filename--;
    648 
    649   return (filename);
    650 }
    651 
    652 /* Return non-zero if NODE is one especially created by Info. */
    653 int
    654 internal_info_node_p (NODE *node)
    655 {
    656 #if defined (NEVER)
    657   if (node &&
    658       (node->filename && !*node->filename) &&
    659       !node->parent && node->nodename)
    660     return (1);
    661   else
    662     return (0);
    663 #else
    664   return ((node != (NODE *)NULL) && ((node->flags & N_IsInternal) != 0));
    665 #endif /* !NEVER */
    666 }
    667 
    668 /* Make NODE appear to be one especially created by Info. */
    669 void
    670 name_internal_node (NODE *node, char *name)
    671 {
    672   if (!node)
    673     return;
    674 
    675   node->filename = "";
    676   node->parent = (char *)NULL;
    677   node->nodename = name;
    678   node->flags |= N_IsInternal;
    679 }
    680 
    681 /* Return the window displaying NAME, the name of an internally created
    682    Info window. */
    683 WINDOW *
    684 get_internal_info_window (char *name)
    685 {
    686   WINDOW *win;
    687 
    688   for (win = windows; win; win = win->next)
    689     if (internal_info_node_p (win->node) &&
    690         (strcmp (win->node->nodename, name) == 0))
    691       break;
    692 
    693   return (win);
    694 }
    695 
    696 /* Return a window displaying the node NODE. */
    697 WINDOW *
    698 get_window_of_node (NODE *node)
    699 {
    700   WINDOW *win = (WINDOW *)NULL;
    701 
    702   for (win = windows; win; win = win->next)
    703     if (win->node == node)
    704       break;
    705 
    706   return (win);
    707 }
    708