No empty .Rs/.Re
[netbsd-mini2440.git] / gnu / dist / texinfo / info / info-utils.c
blob74719463c3e943cf40a8f2f4f7f5432bf6a24d29
1 /* $NetBSD$ */
3 /* info-utils.c -- miscellanous.
4 Id: info-utils.c,v 1.4 2004/04/11 17:56:45 karl Exp
6 Copyright (C) 1993, 1998, 2003, 2004 Free Software Foundation, Inc.
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.
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.
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.
22 Originally written by Brian Fox (bfox@ai.mit.edu). */
24 #include "info.h"
25 #include "info-utils.h"
26 #if defined (HANDLE_MAN_PAGES)
27 # include "man.h"
28 #endif /* HANDLE_MAN_PAGES */
30 /* When non-zero, various display and input functions handle ISO Latin
31 character sets correctly. */
32 int ISO_Latin_p = 1;
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;
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;
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;
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);
52 /* How to get a reference (either menu or cross). */
53 static REFERENCE **info_references_internal (char *label,
54 SEARCH_BINDING *binding);
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)
64 register int i = 0;
66 /* Default the answer. */
67 save_filename ((char *)NULL);
68 save_nodename ((char *)NULL);
70 /* Special case of nothing passed. Return nothing. */
71 if (!string || !*string)
72 return;
74 string += skip_whitespace (string);
76 /* Check for (FILENAME)NODENAME. */
77 if (*string == '(')
79 i = 0;
80 /* Advance past the opening paren. */
81 string++;
83 /* Find the closing paren. */
84 while (string[i] && string[i] != ')')
85 i++;
87 /* Remember parsed filename. */
88 saven_filename (string, i);
90 /* Point directly at the nodename. */
91 string += i;
93 if (*string)
94 string++;
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)
103 free (info_parsed_nodename);
104 info_parsed_nodename = (char *)NULL;
107 /* Parse ``(line ...)'' part of menus, if any. */
109 char *rest = string + i;
111 /* Advance only if it's not already at end of string. */
112 if (*rest)
113 rest++;
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')
122 rest++;
123 while (whitespace(*rest))
124 rest++;
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)
131 rest += strlen ("(line ");
132 info_parsed_line_number = strtol (rest, NULL, 0);
134 else
135 info_parsed_line_number = 0;
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)
146 register int i;
147 char *nodeline;
149 /* Default answer to failure. */
150 save_nodename ((char *)NULL);
151 save_filename ((char *)NULL);
153 /* Find the label in the first line of this node. */
154 nodeline = node->contents;
155 i = string_in_line (label, nodeline);
157 if (i == -1)
158 return;
160 nodeline += i;
161 nodeline += skip_whitespace (nodeline);
162 info_parse_node (nodeline, DONT_SKIP_NEWLINES);
165 /* **************************************************************** */
166 /* */
167 /* Finding and Building Menus */
168 /* */
169 /* **************************************************************** */
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 **
174 info_menu_of_node (NODE *node)
176 long position;
177 SEARCH_BINDING tmp_search;
178 REFERENCE **menu = (REFERENCE **)NULL;
180 tmp_search.buffer = node->contents;
181 tmp_search.start = 0;
182 tmp_search.end = node->nodelen;
183 tmp_search.flags = S_FoldCase;
185 /* Find the start of the menu. */
186 position = search_forward (INFO_MENU_LABEL, &tmp_search);
188 if (position == -1)
189 return ((REFERENCE **) NULL);
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);
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 **
204 info_xrefs_of_node (NODE *node)
206 SEARCH_BINDING tmp_search;
208 #if defined (HANDLE_MAN_PAGES)
209 if (node->flags & N_IsManPage)
210 return (xrefs_of_manpage (node));
211 #endif
213 tmp_search.buffer = node->contents;
214 tmp_search.start = 0;
215 tmp_search.end = node->nodelen;
216 tmp_search.flags = S_FoldCase;
218 return (info_xrefs (&tmp_search));
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 **
225 info_menu_items (SEARCH_BINDING *binding)
227 return (info_references_internal (INFO_MENU_ENTRY_LABEL, binding));
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 **
234 info_xrefs (SEARCH_BINDING *binding)
236 return (info_references_internal (INFO_XREF_LABEL, binding));
239 /* Glean cross references or menu items from BINDING. Return an array
240 of REFERENCE * that represents the items found. */
241 static REFERENCE **
242 info_references_internal (char *label, SEARCH_BINDING *binding)
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;
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;
255 searching_for_menu_items = (strcasecmp (label, INFO_MENU_ENTRY_LABEL) == 0);
257 while ((position = search_forward (label, &tmp_search)) != -1)
259 int offset, start;
260 char *refdef;
261 REFERENCE *entry;
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);
269 /* When searching for menu items, if no colon, there is no
270 menu item on this line. */
271 if (offset == -1)
273 if (searching_for_menu_items)
274 continue;
275 else
277 int temp;
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;
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);
296 refdef += offset;
297 entry->start = start;
298 entry->end = refdef - binding->buffer;
300 /* If this reference entry continues with another ':' then the
301 nodename is the same as the label. */
302 if (*refdef == ':')
304 entry->nodename = xstrdup (entry->label);
306 else
308 /* This entry continues with a specific nodename. Parse the
309 nodename from the specification. */
311 refdef += skip_whitespace_and_newlines (refdef);
313 if (searching_for_menu_items)
314 info_parse_node (refdef, DONT_SKIP_NEWLINES);
315 else
316 info_parse_node (refdef, SKIP_NEWLINES);
318 if (info_parsed_filename)
319 entry->filename = xstrdup (info_parsed_filename);
321 if (info_parsed_nodename)
322 entry->nodename = xstrdup (info_parsed_nodename);
324 entry->line_number = info_parsed_line_number;
327 add_pointer_to_array
328 (entry, refs_index, refs, refs_slots, 50, REFERENCE *);
330 return (refs);
333 /* Get the entry associated with LABEL in REFERENCES. Return a pointer
334 to the ENTRY if found, or NULL. */
335 REFERENCE *
336 info_get_labeled_reference (char *label, REFERENCE **references)
338 register int i;
339 REFERENCE *entry;
341 for (i = 0; references && (entry = references[i]); i++)
343 if (strcmp (label, entry->label) == 0)
344 return (entry);
346 return ((REFERENCE *)NULL);
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 **
353 info_concatenate_references (REFERENCE **ref1, REFERENCE **ref2)
355 register int i, j;
356 REFERENCE **result;
357 int size;
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);
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;
371 result = (REFERENCE **)xmalloc ((1 + size) * sizeof (REFERENCE *));
373 /* Copy the contents over. */
374 for (i = 0; ref1[i]; i++)
375 result[i] = ref1[i];
377 j = i;
378 for (i = 0; ref2[i]; i++)
379 result[j++] = ref2[i];
381 result[j] = (REFERENCE *)NULL;
382 free (ref1);
383 free (ref2);
384 return (result);
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 *
393 info_copy_reference (REFERENCE *src)
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;
402 return dest;
407 /* Free the data associated with REFERENCES. */
408 void
409 info_free_references (REFERENCE **references)
411 register int i;
412 REFERENCE *entry;
414 if (references)
416 for (i = 0; references && (entry = references[i]); i++)
418 maybe_free (entry->label);
419 maybe_free (entry->filename);
420 maybe_free (entry->nodename);
422 free (entry);
425 free (references);
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
433 canonicalize_whitespace (char *string)
435 register int i, j;
436 int len, whitespace_found, whitespace_loc = 0;
437 char *temp;
439 if (!string)
440 return;
442 len = strlen (string);
443 temp = (char *)xmalloc (1 + len);
445 /* Search for sequences of whitespace or newlines. Replace all such
446 sequences in the string with just a single space. */
448 whitespace_found = 0;
449 for (i = 0, j = 0; string[i]; i++)
451 if (whitespace_or_newline (string[i]))
453 whitespace_found++;
454 whitespace_loc = i;
455 continue;
457 else
459 if (whitespace_found && whitespace_loc)
461 whitespace_found = 0;
463 /* Suppress whitespace at start of string. */
464 if (j)
465 temp[j++] = ' ';
468 temp[j++] = string[i];
472 /* Kill trailing whitespace. */
473 if (j && whitespace (temp[j - 1]))
474 j--;
476 temp[j] = '\0';
477 strcpy (string, temp);
478 free (temp);
481 /* String representation of a char returned by printed_representation (). */
482 static char the_rep[10];
484 /* Return a pointer to a string which is the printed representation
485 of CHARACTER if it were printed at HPOS. */
486 char *
487 printed_representation (unsigned char character, int hpos)
489 register int i = 0;
490 int printable_limit = ISO_Latin_p ? 255 : 127;
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)
497 switch (character)
499 case '\r':
500 case '\n':
501 the_rep[i++] = character;
502 break;
504 case '\t':
506 int tw;
508 tw = ((hpos + 8) & 0xf8) - hpos;
509 while (i < tw)
510 the_rep[i++] = ' ';
512 break;
514 default:
515 the_rep[i++] = '^';
516 the_rep[i++] = (character | 0x40);
519 /* Show META-x as 0370. */
520 else if (character > printable_limit)
522 sprintf (the_rep + i, "\\%0o", character);
523 i = strlen (the_rep);
525 else if (character == DEL)
527 the_rep[i++] = '^';
528 the_rep[i++] = '?';
530 else
531 the_rep[i++] = character;
533 the_rep[i] = 0;
535 return the_rep;
539 /* **************************************************************** */
540 /* */
541 /* Functions Static To This File */
542 /* */
543 /* **************************************************************** */
545 /* Amount of space allocated to INFO_PARSED_FILENAME via xmalloc (). */
546 static int parsed_filename_size = 0;
548 /* Amount of space allocated to INFO_PARSED_NODENAME via xmalloc (). */
549 static int parsed_nodename_size = 0;
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);
555 /* Remember FILENAME in PARSED_FILENAME. An empty FILENAME is translated
556 to a NULL pointer in PARSED_FILENAME. */
557 static void
558 save_filename (char *filename)
560 save_string (filename, &info_parsed_filename, &parsed_filename_size);
563 /* Just like save_filename (), but you pass the length of the string. */
564 static void
565 saven_filename (char *filename, int len)
567 saven_string (filename, len,
568 &info_parsed_filename, &parsed_filename_size);
571 /* Remember NODENAME in PARSED_NODENAME. An empty NODENAME is translated
572 to a NULL pointer in PARSED_NODENAME. */
573 static void
574 save_nodename (char *nodename)
576 save_string (nodename, &info_parsed_nodename, &parsed_nodename_size);
579 /* Just like save_nodename (), but you pass the length of the string. */
580 static void
581 saven_nodename (char *nodename, int len)
583 saven_string (nodename, len,
584 &info_parsed_nodename, &parsed_nodename_size);
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
591 save_string (char *string, char **string_p, int *string_size_p)
593 if (!string || !*string)
595 if (*string_p)
596 free (*string_p);
598 *string_p = (char *)NULL;
599 *string_size_p = 0;
601 else
603 if (strlen (string) >= (unsigned int) *string_size_p)
604 *string_p = (char *)xrealloc
605 (*string_p, (*string_size_p = 1 + strlen (string)));
607 strcpy (*string_p, string);
611 /* Just like save_string (), but you also pass the length of STRING. */
612 static void
613 saven_string (char *string, int len, char **string_p, int *string_size_p)
615 if (!string)
617 if (*string_p)
618 free (*string_p);
620 *string_p = (char *)NULL;
621 *string_size_p = 0;
623 else
625 if (len >= *string_size_p)
626 *string_p = (char *)xrealloc (*string_p, (*string_size_p = 1 + len));
628 strncpy (*string_p, string, len);
629 (*string_p)[len] = '\0';
633 /* Return a pointer to the part of PATHNAME that simply defines the file. */
634 char *
635 filename_non_directory (char *pathname)
637 register char *filename = pathname + strlen (pathname);
639 if (HAVE_DRIVE (pathname))
640 pathname += 2;
642 while (filename > pathname && !IS_SLASH (filename[-1]))
643 filename--;
645 return (filename);
648 /* Return non-zero if NODE is one especially created by Info. */
650 internal_info_node_p (NODE *node)
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 */
664 /* Make NODE appear to be one especially created by Info. */
665 void
666 name_internal_node (NODE *node, char *name)
668 if (!node)
669 return;
671 node->filename = "";
672 node->parent = (char *)NULL;
673 node->nodename = name;
674 node->flags |= N_IsInternal;
677 /* Return the window displaying NAME, the name of an internally created
678 Info window. */
679 WINDOW *
680 get_internal_info_window (char *name)
682 WINDOW *win;
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;
689 return (win);
692 /* Return a window displaying the node NODE. */
693 WINDOW *
694 get_window_of_node (NODE *node)
696 WINDOW *win = (WINDOW *)NULL;
698 for (win = windows; win; win = win->next)
699 if (win->node == node)
700 break;
702 return (win);