xref: /netbsd-src/external/gpl2/texinfo/dist/info/info-utils.c (revision 29619d2afe564e54d657b83e5a3ae89584f83720)
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@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
info_parse_node(char * string,int newlines_okay)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
info_parse_label(char * label,NODE * node)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 /* **************************************************************** */
166 /*                                                                  */
167 /*                  Finding and Building Menus                      */
168 /*                                                                  */
169 /* **************************************************************** */
170 
171 /* Return a NULL terminated array of REFERENCE * which represents the menu
172    found in NODE.  If there is no menu in NODE, just return a NULL pointer. */
173 REFERENCE **
info_menu_of_node(NODE * node)174 info_menu_of_node (NODE *node)
175 {
176   long position;
177   SEARCH_BINDING tmp_search;
178   REFERENCE **menu = (REFERENCE **)NULL;
179 
180   tmp_search.buffer = node->contents;
181   tmp_search.start = 0;
182   tmp_search.end = node->nodelen;
183   tmp_search.flags = S_FoldCase;
184 
185   /* Find the start of the menu. */
186   position = search_forward (INFO_MENU_LABEL, &tmp_search);
187 
188   if (position == -1)
189     return ((REFERENCE **) NULL);
190 
191   /* We have the start of the menu now.  Glean menu items from the rest
192      of the node. */
193   tmp_search.start = position + strlen (INFO_MENU_LABEL);
194   tmp_search.start += skip_line (tmp_search.buffer + tmp_search.start);
195   tmp_search.start--;
196   menu = info_menu_items (&tmp_search);
197   return (menu);
198 }
199 
200 /* Return a NULL terminated array of REFERENCE * which represents the cross
201    refrences found in NODE.  If there are no cross references in NODE, just
202    return a NULL pointer. */
203 REFERENCE **
info_xrefs_of_node(NODE * node)204 info_xrefs_of_node (NODE *node)
205 {
206   SEARCH_BINDING tmp_search;
207 
208 #if defined (HANDLE_MAN_PAGES)
209   if (node->flags & N_IsManPage)
210     return (xrefs_of_manpage (node));
211 #endif
212 
213   tmp_search.buffer = node->contents;
214   tmp_search.start = 0;
215   tmp_search.end = node->nodelen;
216   tmp_search.flags = S_FoldCase;
217 
218   return (info_xrefs (&tmp_search));
219 }
220 
221 /* Glean menu entries from BINDING->buffer + BINDING->start until we
222    have looked at the entire contents of BINDING.  Return an array
223    of REFERENCE * that represents each menu item in this range. */
224 REFERENCE **
info_menu_items(SEARCH_BINDING * binding)225 info_menu_items (SEARCH_BINDING *binding)
226 {
227   return (info_references_internal (INFO_MENU_ENTRY_LABEL, binding));
228 }
229 
230 /* Glean cross references from BINDING->buffer + BINDING->start until
231    BINDING->end.  Return an array of REFERENCE * that represents each
232    cross reference in this range. */
233 REFERENCE **
info_xrefs(SEARCH_BINDING * binding)234 info_xrefs (SEARCH_BINDING *binding)
235 {
236   return (info_references_internal (INFO_XREF_LABEL, binding));
237 }
238 
239 /* Glean cross references or menu items from BINDING.  Return an array
240    of REFERENCE * that represents the items found. */
241 static REFERENCE **
info_references_internal(char * label,SEARCH_BINDING * binding)242 info_references_internal (char *label, SEARCH_BINDING *binding)
243 {
244   SEARCH_BINDING tmp_search;
245   REFERENCE **refs = (REFERENCE **)NULL;
246   int refs_index = 0, refs_slots = 0;
247   int searching_for_menu_items = 0;
248   long position;
249 
250   tmp_search.buffer = binding->buffer;
251   tmp_search.start = binding->start;
252   tmp_search.end = binding->end;
253   tmp_search.flags = S_FoldCase | S_SkipDest;
254 
255   searching_for_menu_items = (strcasecmp (label, INFO_MENU_ENTRY_LABEL) == 0);
256 
257   while ((position = search_forward (label, &tmp_search)) != -1)
258     {
259       int offset, start;
260       char *refdef;
261       REFERENCE *entry;
262 
263       tmp_search.start = position;
264       tmp_search.start += skip_whitespace (tmp_search.buffer + tmp_search.start);
265       start = tmp_search.start - binding->start;
266       refdef = tmp_search.buffer + tmp_search.start;
267       offset = string_in_line (":", refdef);
268 
269       /* When searching for menu items, if no colon, there is no
270          menu item on this line. */
271       if (offset == -1)
272         {
273           if (searching_for_menu_items)
274             continue;
275           else
276             {
277               int temp;
278 
279               temp = skip_line (refdef);
280               offset = string_in_line (":", refdef + temp);
281               if (offset == -1)
282                 continue;       /* Give up? */
283               else
284                 offset += temp;
285             }
286         }
287 
288       entry = (REFERENCE *)xmalloc (sizeof (REFERENCE));
289       entry->filename = (char *)NULL;
290       entry->nodename = (char *)NULL;
291       entry->label = (char *)xmalloc (offset);
292       strncpy (entry->label, refdef, offset - 1);
293       entry->label[offset - 1] = '\0';
294       canonicalize_whitespace (entry->label);
295 
296       refdef += offset;
297       entry->start = start;
298       entry->end = refdef - binding->buffer;
299 
300       /* If this reference entry continues with another ':' then the
301          nodename is the same as the label. */
302       if (*refdef == ':')
303         {
304           entry->nodename = xstrdup (entry->label);
305         }
306       else
307         {
308           /* This entry continues with a specific nodename.  Parse the
309              nodename from the specification. */
310 
311           refdef += skip_whitespace_and_newlines (refdef);
312 
313           if (searching_for_menu_items)
314             info_parse_node (refdef, DONT_SKIP_NEWLINES);
315           else
316             info_parse_node (refdef, SKIP_NEWLINES);
317 
318           if (info_parsed_filename)
319             entry->filename = xstrdup (info_parsed_filename);
320 
321           if (info_parsed_nodename)
322             entry->nodename = xstrdup (info_parsed_nodename);
323 
324           entry->line_number = info_parsed_line_number;
325         }
326 
327       add_pointer_to_array
328         (entry, refs_index, refs, refs_slots, 50, REFERENCE *);
329     }
330   return (refs);
331 }
332 
333 /* Get the entry associated with LABEL in REFERENCES.  Return a pointer
334    to the ENTRY if found, or NULL. */
335 REFERENCE *
info_get_labeled_reference(char * label,REFERENCE ** references)336 info_get_labeled_reference (char *label, REFERENCE **references)
337 {
338   register int i;
339   REFERENCE *entry;
340 
341   for (i = 0; references && (entry = references[i]); i++)
342     {
343       if (strcmp (label, entry->label) == 0)
344         return (entry);
345     }
346   return ((REFERENCE *)NULL);
347 }
348 
349 /* A utility function for concatenating REFERENCE **.  Returns a new
350    REFERENCE ** which is the concatenation of REF1 and REF2.  The REF1
351    and REF2 arrays are freed, but their contents are not. */
352 REFERENCE **
info_concatenate_references(REFERENCE ** ref1,REFERENCE ** ref2)353 info_concatenate_references (REFERENCE **ref1, REFERENCE **ref2)
354 {
355   register int i, j;
356   REFERENCE **result;
357   int size;
358 
359   /* With one argument passed as NULL, simply return the other arg. */
360   if (!ref1)
361     return (ref2);
362   else if (!ref2)
363     return (ref1);
364 
365   /* Get the total size of the slots that we will need. */
366   for (i = 0; ref1[i]; i++);
367   size = i;
368   for (i = 0; ref2[i]; i++);
369   size += i;
370 
371   result = (REFERENCE **)xmalloc ((1 + size) * sizeof (REFERENCE *));
372 
373   /* Copy the contents over. */
374   for (i = 0; ref1[i]; i++)
375     result[i] = ref1[i];
376 
377   j = i;
378   for (i = 0; ref2[i]; i++)
379     result[j++] = ref2[i];
380 
381   result[j] = (REFERENCE *)NULL;
382   free (ref1);
383   free (ref2);
384   return (result);
385 }
386 
387 
388 
389 /* Copy a reference structure.  Since we tend to free everything at
390    every opportunity, we don't share any points, but copy everything into
391    new memory.  */
392 REFERENCE *
info_copy_reference(REFERENCE * src)393 info_copy_reference (REFERENCE *src)
394 {
395   REFERENCE *dest = xmalloc (sizeof (REFERENCE));
396   dest->label = src->label ? xstrdup (src->label) : NULL;
397   dest->filename = src->filename ? xstrdup (src->filename) : NULL;
398   dest->nodename = src->nodename ? xstrdup (src->nodename) : NULL;
399   dest->start = src->start;
400   dest->end = src->end;
401 
402   return dest;
403 }
404 
405 
406 
407 /* Free the data associated with REFERENCES. */
408 void
info_free_references(REFERENCE ** references)409 info_free_references (REFERENCE **references)
410 {
411   register int i;
412   REFERENCE *entry;
413 
414   if (references)
415     {
416       for (i = 0; references && (entry = references[i]); i++)
417         {
418           maybe_free (entry->label);
419           maybe_free (entry->filename);
420           maybe_free (entry->nodename);
421 
422           free (entry);
423         }
424 
425       free (references);
426     }
427 }
428 
429 /* Search for sequences of whitespace or newlines in STRING, replacing
430    all such sequences with just a single space.  Remove whitespace from
431    start and end of string. */
432 void
canonicalize_whitespace(char * string)433 canonicalize_whitespace (char *string)
434 {
435   register int i, j;
436   int len, whitespace_found, whitespace_loc = 0;
437   char *temp;
438 
439   if (!string)
440     return;
441 
442   len = strlen (string);
443   temp = (char *)xmalloc (1 + len);
444 
445   /* Search for sequences of whitespace or newlines.  Replace all such
446      sequences in the string with just a single space. */
447 
448   whitespace_found = 0;
449   for (i = 0, j = 0; string[i]; i++)
450     {
451       if (whitespace_or_newline (string[i]))
452         {
453           whitespace_found++;
454           whitespace_loc = i;
455           continue;
456         }
457       else
458         {
459           if (whitespace_found && whitespace_loc)
460             {
461               whitespace_found = 0;
462 
463               /* Suppress whitespace at start of string. */
464               if (j)
465                 temp[j++] = ' ';
466             }
467 
468           temp[j++] = string[i];
469         }
470     }
471 
472   /* Kill trailing whitespace. */
473   if (j && whitespace (temp[j - 1]))
474     j--;
475 
476   temp[j] = '\0';
477   strcpy (string, temp);
478   free (temp);
479 }
480 
481 /* String representation of a char returned by printed_representation (). */
482 static char the_rep[10];
483 
484 /* Return a pointer to a string which is the printed representation
485    of CHARACTER if it were printed at HPOS. */
486 char *
printed_representation(unsigned char character,int hpos)487 printed_representation (unsigned char character, int hpos)
488 {
489   register int i = 0;
490   int printable_limit = ISO_Latin_p ? 255 : 127;
491 
492   if (raw_escapes_p && character == '\033')
493     the_rep[i++] = character;
494   /* Show CTRL-x as ^X.  */
495   else if (iscntrl (character) && character < 127)
496     {
497       switch (character)
498         {
499         case '\r':
500         case '\n':
501           the_rep[i++] = character;
502           break;
503 
504         case '\t':
505           {
506             int tw;
507 
508             tw = ((hpos + 8) & 0xf8) - hpos;
509             while (i < tw)
510               the_rep[i++] = ' ';
511           }
512           break;
513 
514         default:
515           the_rep[i++] = '^';
516           the_rep[i++] = (character | 0x40);
517         }
518     }
519   /* Show META-x as 0370.  */
520   else if (character > printable_limit)
521     {
522       sprintf (the_rep + i, "\\%0o", character);
523       i = strlen (the_rep);
524     }
525   else if (character == DEL)
526     {
527       the_rep[i++] = '^';
528       the_rep[i++] = '?';
529     }
530   else
531     the_rep[i++] = character;
532 
533   the_rep[i] = 0;
534 
535   return the_rep;
536 }
537 
538 
539 /* **************************************************************** */
540 /*                                                                  */
541 /*                  Functions Static To This File                   */
542 /*                                                                  */
543 /* **************************************************************** */
544 
545 /* Amount of space allocated to INFO_PARSED_FILENAME via xmalloc (). */
546 static int parsed_filename_size = 0;
547 
548 /* Amount of space allocated to INFO_PARSED_NODENAME via xmalloc (). */
549 static int parsed_nodename_size = 0;
550 
551 static void save_string (char *string, char **string_p, int *string_size_p);
552 static void saven_string (char *string, int len, char **string_p,
553     int *string_size_p);
554 
555 /* Remember FILENAME in PARSED_FILENAME.  An empty FILENAME is translated
556    to a NULL pointer in PARSED_FILENAME. */
557 static void
save_filename(char * filename)558 save_filename (char *filename)
559 {
560   save_string (filename, &info_parsed_filename, &parsed_filename_size);
561 }
562 
563 /* Just like save_filename (), but you pass the length of the string. */
564 static void
saven_filename(char * filename,int len)565 saven_filename (char *filename, int len)
566 {
567   saven_string (filename, len,
568                 &info_parsed_filename, &parsed_filename_size);
569 }
570 
571 /* Remember NODENAME in PARSED_NODENAME.  An empty NODENAME is translated
572    to a NULL pointer in PARSED_NODENAME. */
573 static void
save_nodename(char * nodename)574 save_nodename (char *nodename)
575 {
576   save_string (nodename, &info_parsed_nodename, &parsed_nodename_size);
577 }
578 
579 /* Just like save_nodename (), but you pass the length of the string. */
580 static void
saven_nodename(char * nodename,int len)581 saven_nodename (char *nodename, int len)
582 {
583   saven_string (nodename, len,
584                 &info_parsed_nodename, &parsed_nodename_size);
585 }
586 
587 /* Remember STRING in STRING_P.  STRING_P should currently have STRING_SIZE_P
588    bytes allocated to it.  An empty STRING is translated to a NULL pointer
589    in STRING_P. */
590 static void
save_string(char * string,char ** string_p,int * string_size_p)591 save_string (char *string, char **string_p, int *string_size_p)
592 {
593   if (!string || !*string)
594     {
595       if (*string_p)
596         free (*string_p);
597 
598       *string_p = (char *)NULL;
599       *string_size_p = 0;
600     }
601   else
602     {
603       if (strlen (string) >= (unsigned int) *string_size_p)
604         *string_p = (char *)xrealloc
605           (*string_p, (*string_size_p = 1 + strlen (string)));
606 
607       strcpy (*string_p, string);
608     }
609 }
610 
611 /* Just like save_string (), but you also pass the length of STRING. */
612 static void
saven_string(char * string,int len,char ** string_p,int * string_size_p)613 saven_string (char *string, int len, char **string_p, int *string_size_p)
614 {
615   if (!string)
616     {
617       if (*string_p)
618         free (*string_p);
619 
620       *string_p = (char *)NULL;
621       *string_size_p = 0;
622     }
623   else
624     {
625       if (len >= *string_size_p)
626         *string_p = (char *)xrealloc (*string_p, (*string_size_p = 1 + len));
627 
628       strncpy (*string_p, string, len);
629       (*string_p)[len] = '\0';
630     }
631 }
632 
633 /* Return a pointer to the part of PATHNAME that simply defines the file. */
634 char *
filename_non_directory(char * pathname)635 filename_non_directory (char *pathname)
636 {
637   register char *filename = pathname + strlen (pathname);
638 
639   if (HAVE_DRIVE (pathname))
640     pathname += 2;
641 
642   while (filename > pathname && !IS_SLASH (filename[-1]))
643     filename--;
644 
645   return (filename);
646 }
647 
648 /* Return non-zero if NODE is one especially created by Info. */
649 int
internal_info_node_p(NODE * node)650 internal_info_node_p (NODE *node)
651 {
652 #if defined (NEVER)
653   if (node &&
654       (node->filename && !*node->filename) &&
655       !node->parent && node->nodename)
656     return (1);
657   else
658     return (0);
659 #else
660   return ((node != (NODE *)NULL) && ((node->flags & N_IsInternal) != 0));
661 #endif /* !NEVER */
662 }
663 
664 /* Make NODE appear to be one especially created by Info. */
665 void
name_internal_node(NODE * node,char * name)666 name_internal_node (NODE *node, char *name)
667 {
668   if (!node)
669     return;
670 
671   node->filename = "";
672   node->parent = (char *)NULL;
673   node->nodename = name;
674   node->flags |= N_IsInternal;
675 }
676 
677 /* Return the window displaying NAME, the name of an internally created
678    Info window. */
679 WINDOW *
get_internal_info_window(char * name)680 get_internal_info_window (char *name)
681 {
682   WINDOW *win;
683 
684   for (win = windows; win; win = win->next)
685     if (internal_info_node_p (win->node) &&
686         (strcmp (win->node->nodename, name) == 0))
687       break;
688 
689   return (win);
690 }
691 
692 /* Return a window displaying the node NODE. */
693 WINDOW *
get_window_of_node(NODE * node)694 get_window_of_node (NODE *node)
695 {
696   WINDOW *win = (WINDOW *)NULL;
697 
698   for (win = windows; win; win = win->next)
699     if (win->node == node)
700       break;
701 
702   return (win);
703 }
704