Back to index

tetex-bin  3.0
info-utils.c
Go to the documentation of this file.
00001 /* info-utils.c -- miscellanous.
00002    $Id: info-utils.c,v 1.4 2004/04/11 17:56:45 karl Exp $
00003 
00004    Copyright (C) 1993, 1998, 2003, 2004 Free Software Foundation, Inc.
00005 
00006    This program is free software; you can redistribute it and/or modify
00007    it under the terms of the GNU General Public License as published by
00008    the Free Software Foundation; either version 2, or (at your option)
00009    any later version.
00010 
00011    This program is distributed in the hope that it will be useful,
00012    but WITHOUT ANY WARRANTY; without even the implied warranty of
00013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014    GNU General Public License for more details.
00015 
00016    You should have received a copy of the GNU General Public License
00017    along with this program; if not, write to the Free Software
00018    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00019 
00020    Originally written by Brian Fox (bfox@ai.mit.edu). */
00021 
00022 #include "info.h"
00023 #include "info-utils.h"
00024 #if defined (HANDLE_MAN_PAGES)
00025 #  include "man.h"
00026 #endif /* HANDLE_MAN_PAGES */
00027 
00028 /* When non-zero, various display and input functions handle ISO Latin
00029    character sets correctly. */
00030 int ISO_Latin_p = 1;
00031 
00032 /* Variable which holds the most recent filename parsed as a result of
00033    calling info_parse_xxx (). */
00034 char *info_parsed_filename = (char *)NULL;
00035 
00036 /* Variable which holds the most recent nodename parsed as a result of
00037    calling info_parse_xxx (). */
00038 char *info_parsed_nodename = (char *)NULL;
00039 
00040 /* Variable which holds the most recent line number parsed as a result of
00041    calling info_parse_xxx (). */
00042 int info_parsed_line_number = 0;
00043 
00044 /* Functions to remember a filename or nodename for later return. */
00045 static void save_filename (char *filename);
00046 static void saven_filename (char *filename, int len);
00047 static void save_nodename (char *nodename);
00048 static void saven_nodename (char *nodename, int len);
00049 
00050 /* How to get a reference (either menu or cross). */
00051 static REFERENCE **info_references_internal (char *label,
00052     SEARCH_BINDING *binding);
00053 
00054 /* Parse the filename and nodename out of STRING.  If STRING doesn't
00055    contain a filename (i.e., it is NOT (FILENAME)NODENAME) then set
00056    INFO_PARSED_FILENAME to NULL.  If second argument NEWLINES_OKAY is
00057    non-zero, it says to allow the nodename specification to cross a
00058    newline boundary (i.e., only `,', `.', or `TAB' can end the spec). */
00059 void
00060 info_parse_node (char *string, int newlines_okay)
00061 {
00062   register int i = 0;
00063 
00064   /* Default the answer. */
00065   save_filename ((char *)NULL);
00066   save_nodename ((char *)NULL);
00067 
00068   /* Special case of nothing passed.  Return nothing. */
00069   if (!string || !*string)
00070     return;
00071 
00072   string += skip_whitespace (string);
00073 
00074   /* Check for (FILENAME)NODENAME. */
00075   if (*string == '(')
00076     {
00077       i = 0;
00078       /* Advance past the opening paren. */
00079       string++;
00080 
00081       /* Find the closing paren. */
00082       while (string[i] && string[i] != ')')
00083         i++;
00084 
00085       /* Remember parsed filename. */
00086       saven_filename (string, i);
00087 
00088       /* Point directly at the nodename. */
00089       string += i;
00090 
00091       if (*string)
00092         string++;
00093     }
00094 
00095   /* Parse out nodename. */
00096   i = skip_node_characters (string, newlines_okay);
00097   saven_nodename (string, i);
00098   canonicalize_whitespace (info_parsed_nodename);
00099   if (info_parsed_nodename && !*info_parsed_nodename)
00100     {
00101       free (info_parsed_nodename);
00102       info_parsed_nodename = (char *)NULL;
00103     }
00104 
00105   /* Parse ``(line ...)'' part of menus, if any.  */
00106   {
00107     char *rest = string + i;
00108 
00109     /* Advance only if it's not already at end of string.  */
00110     if (*rest)
00111       rest++;
00112 
00113     /* Skip any whitespace first, and then a newline in case the item
00114        was so long to contain the ``(line ...)'' string in the same
00115        physical line.  */
00116     while (whitespace(*rest))
00117       rest++;
00118     if (*rest == '\n')
00119       {
00120         rest++;
00121         while (whitespace(*rest))
00122           rest++;
00123       }
00124 
00125     /* Are we looking at an opening parenthesis?  That can only mean
00126        we have a winner. :)  */
00127     if (strncmp (rest, "(line ", strlen ("(line ")) == 0)
00128       {
00129         rest += strlen ("(line ");
00130         info_parsed_line_number = strtol (rest, NULL, 0);
00131       }
00132     else
00133       info_parsed_line_number = 0;
00134   }
00135 }
00136 
00137 /* Return the node addressed by LABEL in NODE (usually one of "Prev:",
00138    "Next:", "Up:", "File:", or "Node:".  After a call to this function,
00139    the global INFO_PARSED_NODENAME and INFO_PARSED_FILENAME contain
00140    the information. */
00141 void
00142 info_parse_label (char *label, NODE *node)
00143 {
00144   register int i;
00145   char *nodeline;
00146 
00147   /* Default answer to failure. */
00148   save_nodename ((char *)NULL);
00149   save_filename ((char *)NULL);
00150 
00151   /* Find the label in the first line of this node. */
00152   nodeline = node->contents;
00153   i = string_in_line (label, nodeline);
00154 
00155   if (i == -1)
00156     return;
00157 
00158   nodeline += i;
00159   nodeline += skip_whitespace (nodeline);
00160   info_parse_node (nodeline, DONT_SKIP_NEWLINES);
00161 }
00162 
00163 /* **************************************************************** */
00164 /*                                                                  */
00165 /*                  Finding and Building Menus                      */
00166 /*                                                                  */
00167 /* **************************************************************** */
00168 
00169 /* Return a NULL terminated array of REFERENCE * which represents the menu
00170    found in NODE.  If there is no menu in NODE, just return a NULL pointer. */
00171 REFERENCE **
00172 info_menu_of_node (NODE *node)
00173 {
00174   long position;
00175   SEARCH_BINDING tmp_search;
00176   REFERENCE **menu = (REFERENCE **)NULL;
00177 
00178   tmp_search.buffer = node->contents;
00179   tmp_search.start = 0;
00180   tmp_search.end = node->nodelen;
00181   tmp_search.flags = S_FoldCase;
00182 
00183   /* Find the start of the menu. */
00184   position = search_forward (INFO_MENU_LABEL, &tmp_search);
00185 
00186   if (position == -1)
00187     return ((REFERENCE **) NULL);
00188 
00189   /* We have the start of the menu now.  Glean menu items from the rest
00190      of the node. */
00191   tmp_search.start = position + strlen (INFO_MENU_LABEL);
00192   tmp_search.start += skip_line (tmp_search.buffer + tmp_search.start);
00193   tmp_search.start--;
00194   menu = info_menu_items (&tmp_search);
00195   return (menu);
00196 }
00197 
00198 /* Return a NULL terminated array of REFERENCE * which represents the cross
00199    refrences found in NODE.  If there are no cross references in NODE, just
00200    return a NULL pointer. */
00201 REFERENCE **
00202 info_xrefs_of_node (NODE *node)
00203 {
00204   SEARCH_BINDING tmp_search;
00205 
00206 #if defined (HANDLE_MAN_PAGES)
00207   if (node->flags & N_IsManPage)
00208     return (xrefs_of_manpage (node));
00209 #endif
00210 
00211   tmp_search.buffer = node->contents;
00212   tmp_search.start = 0;
00213   tmp_search.end = node->nodelen;
00214   tmp_search.flags = S_FoldCase;
00215 
00216   return (info_xrefs (&tmp_search));
00217 }
00218 
00219 /* Glean menu entries from BINDING->buffer + BINDING->start until we
00220    have looked at the entire contents of BINDING.  Return an array
00221    of REFERENCE * that represents each menu item in this range. */
00222 REFERENCE **
00223 info_menu_items (SEARCH_BINDING *binding)
00224 {
00225   return (info_references_internal (INFO_MENU_ENTRY_LABEL, binding));
00226 }
00227 
00228 /* Glean cross references from BINDING->buffer + BINDING->start until
00229    BINDING->end.  Return an array of REFERENCE * that represents each
00230    cross reference in this range. */
00231 REFERENCE **
00232 info_xrefs (SEARCH_BINDING *binding)
00233 {
00234   return (info_references_internal (INFO_XREF_LABEL, binding));
00235 }
00236 
00237 /* Glean cross references or menu items from BINDING.  Return an array
00238    of REFERENCE * that represents the items found. */
00239 static REFERENCE **
00240 info_references_internal (char *label, SEARCH_BINDING *binding)
00241 {
00242   SEARCH_BINDING tmp_search;
00243   REFERENCE **refs = (REFERENCE **)NULL;
00244   int refs_index = 0, refs_slots = 0;
00245   int searching_for_menu_items = 0;
00246   long position;
00247 
00248   tmp_search.buffer = binding->buffer;
00249   tmp_search.start = binding->start;
00250   tmp_search.end = binding->end;
00251   tmp_search.flags = S_FoldCase | S_SkipDest;
00252 
00253   searching_for_menu_items = (strcasecmp (label, INFO_MENU_ENTRY_LABEL) == 0);
00254 
00255   while ((position = search_forward (label, &tmp_search)) != -1)
00256     {
00257       int offset, start;
00258       char *refdef;
00259       REFERENCE *entry;
00260 
00261       tmp_search.start = position;
00262       tmp_search.start += skip_whitespace (tmp_search.buffer + tmp_search.start);
00263       start = tmp_search.start - binding->start;
00264       refdef = tmp_search.buffer + tmp_search.start;
00265       offset = string_in_line (":", refdef);
00266 
00267       /* When searching for menu items, if no colon, there is no
00268          menu item on this line. */
00269       if (offset == -1)
00270         {
00271           if (searching_for_menu_items)
00272             continue;
00273           else
00274             {
00275               int temp;
00276 
00277               temp = skip_line (refdef);
00278               offset = string_in_line (":", refdef + temp);
00279               if (offset == -1)
00280                 continue;       /* Give up? */
00281               else
00282                 offset += temp;
00283             }
00284         }
00285 
00286       entry = (REFERENCE *)xmalloc (sizeof (REFERENCE));
00287       entry->filename = (char *)NULL;
00288       entry->nodename = (char *)NULL;
00289       entry->label = (char *)xmalloc (offset);
00290       strncpy (entry->label, refdef, offset - 1);
00291       entry->label[offset - 1] = '\0';
00292       canonicalize_whitespace (entry->label);
00293 
00294       refdef += offset;
00295       entry->start = start;
00296       entry->end = refdef - binding->buffer;
00297 
00298       /* If this reference entry continues with another ':' then the
00299          nodename is the same as the label. */
00300       if (*refdef == ':')
00301         {
00302           entry->nodename = xstrdup (entry->label);
00303         }
00304       else
00305         {
00306           /* This entry continues with a specific nodename.  Parse the
00307              nodename from the specification. */
00308 
00309           refdef += skip_whitespace_and_newlines (refdef);
00310 
00311           if (searching_for_menu_items)
00312             info_parse_node (refdef, DONT_SKIP_NEWLINES);
00313           else
00314             info_parse_node (refdef, SKIP_NEWLINES);
00315 
00316           if (info_parsed_filename)
00317             entry->filename = xstrdup (info_parsed_filename);
00318 
00319           if (info_parsed_nodename)
00320             entry->nodename = xstrdup (info_parsed_nodename);
00321 
00322           entry->line_number = info_parsed_line_number;
00323         }
00324 
00325       add_pointer_to_array
00326         (entry, refs_index, refs, refs_slots, 50, REFERENCE *);
00327     }
00328   return (refs);
00329 }
00330 
00331 /* Get the entry associated with LABEL in REFERENCES.  Return a pointer
00332    to the ENTRY if found, or NULL. */
00333 REFERENCE *
00334 info_get_labeled_reference (char *label, REFERENCE **references)
00335 {
00336   register int i;
00337   REFERENCE *entry;
00338 
00339   for (i = 0; references && (entry = references[i]); i++)
00340     {
00341       if (strcmp (label, entry->label) == 0)
00342         return (entry);
00343     }
00344   return ((REFERENCE *)NULL);
00345 }
00346 
00347 /* A utility function for concatenating REFERENCE **.  Returns a new
00348    REFERENCE ** which is the concatenation of REF1 and REF2.  The REF1
00349    and REF2 arrays are freed, but their contents are not. */
00350 REFERENCE **
00351 info_concatenate_references (REFERENCE **ref1, REFERENCE **ref2)
00352 {
00353   register int i, j;
00354   REFERENCE **result;
00355   int size;
00356 
00357   /* With one argument passed as NULL, simply return the other arg. */
00358   if (!ref1)
00359     return (ref2);
00360   else if (!ref2)
00361     return (ref1);
00362 
00363   /* Get the total size of the slots that we will need. */
00364   for (i = 0; ref1[i]; i++);
00365   size = i;
00366   for (i = 0; ref2[i]; i++);
00367   size += i;
00368 
00369   result = (REFERENCE **)xmalloc ((1 + size) * sizeof (REFERENCE *));
00370 
00371   /* Copy the contents over. */
00372   for (i = 0; ref1[i]; i++)
00373     result[i] = ref1[i];
00374 
00375   j = i;
00376   for (i = 0; ref2[i]; i++)
00377     result[j++] = ref2[i];
00378 
00379   result[j] = (REFERENCE *)NULL;
00380   free (ref1);
00381   free (ref2);
00382   return (result);
00383 }
00384 
00385 
00386 
00387 /* Copy a reference structure.  Since we tend to free everything at
00388    every opportunity, we don't share any points, but copy everything into
00389    new memory.  */
00390 REFERENCE *
00391 info_copy_reference (REFERENCE *src)
00392 {
00393   REFERENCE *dest = xmalloc (sizeof (REFERENCE));
00394   dest->label = src->label ? xstrdup (src->label) : NULL;
00395   dest->filename = src->filename ? xstrdup (src->filename) : NULL;
00396   dest->nodename = src->nodename ? xstrdup (src->nodename) : NULL;
00397   dest->start = src->start;
00398   dest->end = src->end;
00399   
00400   return dest;
00401 }
00402 
00403 
00404 
00405 /* Free the data associated with REFERENCES. */
00406 void
00407 info_free_references (REFERENCE **references)
00408 {
00409   register int i;
00410   REFERENCE *entry;
00411 
00412   if (references)
00413     {
00414       for (i = 0; references && (entry = references[i]); i++)
00415         {
00416           maybe_free (entry->label);
00417           maybe_free (entry->filename);
00418           maybe_free (entry->nodename);
00419 
00420           free (entry);
00421         }
00422 
00423       free (references);
00424     }
00425 }
00426 
00427 /* Search for sequences of whitespace or newlines in STRING, replacing
00428    all such sequences with just a single space.  Remove whitespace from
00429    start and end of string. */
00430 void
00431 canonicalize_whitespace (char *string)
00432 {
00433   register int i, j;
00434   int len, whitespace_found, whitespace_loc = 0;
00435   char *temp;
00436 
00437   if (!string)
00438     return;
00439 
00440   len = strlen (string);
00441   temp = (char *)xmalloc (1 + len);
00442 
00443   /* Search for sequences of whitespace or newlines.  Replace all such
00444      sequences in the string with just a single space. */
00445 
00446   whitespace_found = 0;
00447   for (i = 0, j = 0; string[i]; i++)
00448     {
00449       if (whitespace_or_newline (string[i]))
00450         {
00451           whitespace_found++;
00452           whitespace_loc = i;
00453           continue;
00454         }
00455       else
00456         {
00457           if (whitespace_found && whitespace_loc)
00458             {
00459               whitespace_found = 0;
00460 
00461               /* Suppress whitespace at start of string. */
00462               if (j)
00463                 temp[j++] = ' ';
00464             }
00465 
00466           temp[j++] = string[i];
00467         }
00468     }
00469 
00470   /* Kill trailing whitespace. */
00471   if (j && whitespace (temp[j - 1]))
00472     j--;
00473 
00474   temp[j] = '\0';
00475   strcpy (string, temp);
00476   free (temp);
00477 }
00478 
00479 /* String representation of a char returned by printed_representation (). */
00480 static char the_rep[10];
00481 
00482 /* Return a pointer to a string which is the printed representation
00483    of CHARACTER if it were printed at HPOS. */
00484 char *
00485 printed_representation (unsigned char character, int hpos)
00486 {
00487   register int i = 0;
00488   int printable_limit = ISO_Latin_p ? 255 : 127;
00489 
00490   if (raw_escapes_p && character == '\033')
00491     the_rep[i++] = character;
00492   /* Show CTRL-x as ^X.  */
00493   else if (iscntrl (character) && character < 127)
00494     {
00495       switch (character)
00496         {
00497         case '\r':
00498         case '\n':
00499           the_rep[i++] = character;
00500           break;
00501 
00502         case '\t':
00503           {
00504             int tw;
00505 
00506             tw = ((hpos + 8) & 0xf8) - hpos;
00507             while (i < tw)
00508               the_rep[i++] = ' ';
00509           }
00510           break;
00511 
00512         default:
00513           the_rep[i++] = '^';
00514           the_rep[i++] = (character | 0x40);
00515         }
00516     }
00517   /* Show META-x as 0370.  */
00518   else if (character > printable_limit)
00519     {
00520       sprintf (the_rep + i, "\\%0o", character);
00521       i = strlen (the_rep);
00522     }
00523   else if (character == DEL)
00524     {
00525       the_rep[i++] = '^';
00526       the_rep[i++] = '?';
00527     }
00528   else
00529     the_rep[i++] = character;
00530 
00531   the_rep[i] = 0;
00532 
00533   return the_rep;
00534 }
00535 
00536 
00537 /* **************************************************************** */
00538 /*                                                                  */
00539 /*                  Functions Static To This File                   */
00540 /*                                                                  */
00541 /* **************************************************************** */
00542 
00543 /* Amount of space allocated to INFO_PARSED_FILENAME via xmalloc (). */
00544 static int parsed_filename_size = 0;
00545 
00546 /* Amount of space allocated to INFO_PARSED_NODENAME via xmalloc (). */
00547 static int parsed_nodename_size = 0;
00548 
00549 static void save_string (char *string, char **string_p, int *string_size_p);
00550 static void saven_string (char *string, int len, char **string_p,
00551     int *string_size_p);
00552 
00553 /* Remember FILENAME in PARSED_FILENAME.  An empty FILENAME is translated
00554    to a NULL pointer in PARSED_FILENAME. */
00555 static void
00556 save_filename (char *filename)
00557 {
00558   save_string (filename, &info_parsed_filename, &parsed_filename_size);
00559 }
00560 
00561 /* Just like save_filename (), but you pass the length of the string. */
00562 static void
00563 saven_filename (char *filename, int len)
00564 {
00565   saven_string (filename, len,
00566                 &info_parsed_filename, &parsed_filename_size);
00567 }
00568 
00569 /* Remember NODENAME in PARSED_NODENAME.  An empty NODENAME is translated
00570    to a NULL pointer in PARSED_NODENAME. */
00571 static void
00572 save_nodename (char *nodename)
00573 {
00574   save_string (nodename, &info_parsed_nodename, &parsed_nodename_size);
00575 }
00576 
00577 /* Just like save_nodename (), but you pass the length of the string. */
00578 static void
00579 saven_nodename (char *nodename, int len)
00580 {
00581   saven_string (nodename, len,
00582                 &info_parsed_nodename, &parsed_nodename_size);
00583 }
00584 
00585 /* Remember STRING in STRING_P.  STRING_P should currently have STRING_SIZE_P
00586    bytes allocated to it.  An empty STRING is translated to a NULL pointer
00587    in STRING_P. */
00588 static void
00589 save_string (char *string, char **string_p, int *string_size_p)
00590 {
00591   if (!string || !*string)
00592     {
00593       if (*string_p)
00594         free (*string_p);
00595 
00596       *string_p = (char *)NULL;
00597       *string_size_p = 0;
00598     }
00599   else
00600     {
00601       if (strlen (string) >= (unsigned int) *string_size_p)
00602         *string_p = (char *)xrealloc
00603           (*string_p, (*string_size_p = 1 + strlen (string)));
00604 
00605       strcpy (*string_p, string);
00606     }
00607 }
00608 
00609 /* Just like save_string (), but you also pass the length of STRING. */
00610 static void
00611 saven_string (char *string, int len, char **string_p, int *string_size_p)
00612 {
00613   if (!string)
00614     {
00615       if (*string_p)
00616         free (*string_p);
00617 
00618       *string_p = (char *)NULL;
00619       *string_size_p = 0;
00620     }
00621   else
00622     {
00623       if (len >= *string_size_p)
00624         *string_p = (char *)xrealloc (*string_p, (*string_size_p = 1 + len));
00625 
00626       strncpy (*string_p, string, len);
00627       (*string_p)[len] = '\0';
00628     }
00629 }
00630 
00631 /* Return a pointer to the part of PATHNAME that simply defines the file. */
00632 char *
00633 filename_non_directory (char *pathname)
00634 {
00635   register char *filename = pathname + strlen (pathname);
00636 
00637   if (HAVE_DRIVE (pathname))
00638     pathname += 2;
00639 
00640   while (filename > pathname && !IS_SLASH (filename[-1]))
00641     filename--;
00642 
00643   return (filename);
00644 }
00645 
00646 /* Return non-zero if NODE is one especially created by Info. */
00647 int
00648 internal_info_node_p (NODE *node)
00649 {
00650 #if defined (NEVER)
00651   if (node &&
00652       (node->filename && !*node->filename) &&
00653       !node->parent && node->nodename)
00654     return (1);
00655   else
00656     return (0);
00657 #else
00658   return ((node != (NODE *)NULL) && ((node->flags & N_IsInternal) != 0));
00659 #endif /* !NEVER */
00660 }
00661 
00662 /* Make NODE appear to be one especially created by Info. */
00663 void
00664 name_internal_node (NODE *node, char *name)
00665 {
00666   if (!node)
00667     return;
00668 
00669   node->filename = "";
00670   node->parent = (char *)NULL;
00671   node->nodename = name;
00672   node->flags |= N_IsInternal;
00673 }
00674 
00675 /* Return the window displaying NAME, the name of an internally created
00676    Info window. */
00677 WINDOW *
00678 get_internal_info_window (char *name)
00679 {
00680   WINDOW *win;
00681 
00682   for (win = windows; win; win = win->next)
00683     if (internal_info_node_p (win->node) &&
00684         (strcmp (win->node->nodename, name) == 0))
00685       break;
00686 
00687   return (win);
00688 }
00689 
00690 /* Return a window displaying the node NODE. */
00691 WINDOW *
00692 get_window_of_node (NODE *node)
00693 {
00694   WINDOW *win = (WINDOW *)NULL;
00695 
00696   for (win = windows; win; win = win->next)
00697     if (win->node == node)
00698       break;
00699 
00700   return (win);
00701 }