1 /* info-utils.c -- miscellanous.
2 $Id: info-utils.c,v 1.8 2002/03/08 21:41:44 karl Exp $
4 Copyright (C) 1993, 98 Free Software Foundation, Inc.
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 Written by Brian Fox (bfox@ai.mit.edu). */
23 #include "info-utils.h"
24 #if defined (HANDLE_MAN_PAGES)
26 #endif /* HANDLE_MAN_PAGES */
28 /* When non-zero, various display and input functions handle ISO Latin
29 character sets correctly. */
32 /* Variable which holds the most recent filename parsed as a result of
33 calling info_parse_xxx (). */
34 char *info_parsed_filename = (char *)NULL;
36 /* Variable which holds the most recent nodename parsed as a result of
37 calling info_parse_xxx (). */
38 char *info_parsed_nodename = (char *)NULL;
40 /* Functions to remember a filename or nodename for later return. */
41 static void save_filename (), saven_filename ();
42 static void save_nodename (), saven_nodename ();
44 /* How to get a reference (either menu or cross). */
45 static REFERENCE **info_references_internal ();
47 /* Parse the filename and nodename out of STRING. If STRING doesn't
48 contain a filename (i.e., it is NOT (FILENAME)NODENAME) then set
49 INFO_PARSED_FILENAME to NULL. If second argument NEWLINES_OKAY is
50 non-zero, it says to allow the nodename specification to cross a
51 newline boundary (i.e., only `,', `.', or `TAB' can end the spec). */
53 info_parse_node (string, newlines_okay)
59 /* Default the answer. */
60 save_filename ((char *)NULL);
61 save_nodename ((char *)NULL);
63 /* Special case of nothing passed. Return nothing. */
64 if (!string || !*string)
67 string += skip_whitespace (string);
69 /* Check for (FILENAME)NODENAME. */
73 /* Advance past the opening paren. */
76 /* Find the closing paren. */
77 while (string[i] && string[i] != ')')
80 /* Remember parsed filename. */
81 saven_filename (string, i);
83 /* Point directly at the nodename. */
90 /* Parse out nodename. */
91 i = skip_node_characters (string, newlines_okay);
92 saven_nodename (string, i);
93 canonicalize_whitespace (info_parsed_nodename);
94 if (info_parsed_nodename && !*info_parsed_nodename)
96 free (info_parsed_nodename);
97 info_parsed_nodename = (char *)NULL;
101 /* Return the node addressed by LABEL in NODE (usually one of "Prev:",
102 "Next:", "Up:", "File:", or "Node:". After a call to this function,
103 the global INFO_PARSED_NODENAME and INFO_PARSED_FILENAME contain
106 info_parse_label (label, node)
113 /* Default answer to failure. */
114 save_nodename ((char *)NULL);
115 save_filename ((char *)NULL);
117 /* Find the label in the first line of this node. */
118 nodeline = node->contents;
119 i = string_in_line (label, nodeline);
125 nodeline += skip_whitespace (nodeline);
126 info_parse_node (nodeline, DONT_SKIP_NEWLINES);
129 /* **************************************************************** */
131 /* Finding and Building Menus */
133 /* **************************************************************** */
135 /* Return a NULL terminated array of REFERENCE * which represents the menu
136 found in NODE. If there is no menu in NODE, just return a NULL pointer. */
138 info_menu_of_node (node)
142 SEARCH_BINDING search;
143 REFERENCE **menu = (REFERENCE **)NULL;
145 search.buffer = node->contents;
147 search.end = node->nodelen;
148 search.flags = S_FoldCase;
150 /* Find the start of the menu. */
151 position = search_forward (INFO_MENU_LABEL, &search);
154 return ((REFERENCE **) NULL);
156 /* We have the start of the menu now. Glean menu items from the rest
158 search.start = position + strlen (INFO_MENU_LABEL);
159 search.start += skip_line (search.buffer + search.start);
161 menu = info_menu_items (&search);
165 /* Return a NULL terminated array of REFERENCE * which represents the cross
166 refrences found in NODE. If there are no cross references in NODE, just
167 return a NULL pointer. */
169 info_xrefs_of_node (node)
172 SEARCH_BINDING search;
174 #if defined (HANDLE_MAN_PAGES)
175 if (node->flags & N_IsManPage)
176 return (xrefs_of_manpage (node));
179 search.buffer = node->contents;
181 search.end = node->nodelen;
182 search.flags = S_FoldCase;
184 return (info_xrefs (&search));
187 /* Glean menu entries from BINDING->buffer + BINDING->start until we
188 have looked at the entire contents of BINDING. Return an array
189 of REFERENCE * that represents each menu item in this range. */
191 info_menu_items (binding)
192 SEARCH_BINDING *binding;
194 return (info_references_internal (INFO_MENU_ENTRY_LABEL, binding));
197 /* Glean cross references from BINDING->buffer + BINDING->start until
198 BINDING->end. Return an array of REFERENCE * that represents each
199 cross reference in this range. */
202 SEARCH_BINDING *binding;
204 return (info_references_internal (INFO_XREF_LABEL, binding));
207 /* Glean cross references or menu items from BINDING. Return an array
208 of REFERENCE * that represents the items found. */
210 info_references_internal (label, binding)
212 SEARCH_BINDING *binding;
214 SEARCH_BINDING search;
215 REFERENCE **refs = (REFERENCE **)NULL;
216 int refs_index = 0, refs_slots = 0;
217 int searching_for_menu_items = 0;
220 search.buffer = binding->buffer;
221 search.start = binding->start;
222 search.end = binding->end;
223 search.flags = S_FoldCase | S_SkipDest;
225 searching_for_menu_items = (strcasecmp (label, INFO_MENU_ENTRY_LABEL) == 0);
227 while ((position = search_forward (label, &search)) != -1)
233 search.start = position;
234 search.start += skip_whitespace (search.buffer + search.start);
235 start = search.start - binding->start;
236 refdef = search.buffer + search.start;
237 offset = string_in_line (":", refdef);
239 /* When searching for menu items, if no colon, there is no
240 menu item on this line. */
243 if (searching_for_menu_items)
249 temp = skip_line (refdef);
250 offset = string_in_line (":", refdef + temp);
252 continue; /* Give up? */
258 entry = (REFERENCE *)xmalloc (sizeof (REFERENCE));
259 entry->filename = (char *)NULL;
260 entry->nodename = (char *)NULL;
261 entry->label = (char *)xmalloc (offset);
262 strncpy (entry->label, refdef, offset - 1);
263 entry->label[offset - 1] = '\0';
264 canonicalize_whitespace (entry->label);
267 entry->start = start;
268 entry->end = refdef - binding->buffer;
270 /* If this reference entry continues with another ':' then the
271 nodename is the same as the label. */
274 entry->nodename = xstrdup (entry->label);
278 /* This entry continues with a specific nodename. Parse the
279 nodename from the specification. */
281 refdef += skip_whitespace_and_newlines (refdef);
283 if (searching_for_menu_items)
284 info_parse_node (refdef, DONT_SKIP_NEWLINES);
286 info_parse_node (refdef, SKIP_NEWLINES);
288 if (info_parsed_filename)
289 entry->filename = xstrdup (info_parsed_filename);
291 if (info_parsed_nodename)
292 entry->nodename = xstrdup (info_parsed_nodename);
296 (entry, refs_index, refs, refs_slots, 50, REFERENCE *);
301 /* Get the entry associated with LABEL in REFERENCES. Return a pointer
302 to the ENTRY if found, or NULL. */
304 info_get_labeled_reference (label, references)
306 REFERENCE **references;
311 for (i = 0; references && (entry = references[i]); i++)
313 if (strcmp (label, entry->label) == 0)
316 return ((REFERENCE *)NULL);
319 /* A utility function for concatenating REFERENCE **. Returns a new
320 REFERENCE ** which is the concatenation of REF1 and REF2. The REF1
321 and REF2 arrays are freed, but their contents are not. */
323 info_concatenate_references (ref1, ref2)
324 REFERENCE **ref1, **ref2;
330 /* With one argument passed as NULL, simply return the other arg. */
336 /* Get the total size of the slots that we will need. */
337 for (i = 0; ref1[i]; i++);
339 for (i = 0; ref2[i]; i++);
342 result = (REFERENCE **)xmalloc ((1 + size) * sizeof (REFERENCE *));
344 /* Copy the contents over. */
345 for (i = 0; ref1[i]; i++)
349 for (i = 0; ref2[i]; i++)
350 result[j++] = ref2[i];
352 result[j] = (REFERENCE *)NULL;
358 /* Free the data associated with REFERENCES. */
360 info_free_references (references)
361 REFERENCE **references;
368 for (i = 0; references && (entry = references[i]); i++)
370 maybe_free (entry->label);
371 maybe_free (entry->filename);
372 maybe_free (entry->nodename);
381 /* Search for sequences of whitespace or newlines in STRING, replacing
382 all such sequences with just a single space. Remove whitespace from
383 start and end of string. */
385 canonicalize_whitespace (string)
389 int len, whitespace_found, whitespace_loc;
395 len = strlen (string);
396 temp = (char *)xmalloc (1 + len);
398 /* Search for sequences of whitespace or newlines. Replace all such
399 sequences in the string with just a single space. */
401 whitespace_found = 0;
402 for (i = 0, j = 0; string[i]; i++)
404 if (whitespace_or_newline (string[i]))
412 if (whitespace_found && whitespace_loc)
414 whitespace_found = 0;
416 /* Suppress whitespace at start of string. */
421 temp[j++] = string[i];
425 /* Kill trailing whitespace. */
426 if (j && whitespace (temp[j - 1]))
430 strcpy (string, temp);
434 /* String representation of a char returned by printed_representation (). */
435 static char the_rep[10];
437 /* Return a pointer to a string which is the printed representation
438 of CHARACTER if it were printed at HPOS. */
440 printed_representation (character, hpos)
441 unsigned char character;
445 int printable_limit = ISO_Latin_p ? 255 : 127;
447 if (raw_escapes_p && character == '\033')
448 the_rep[i++] = character;
449 /* Show CTRL-x as ^X. */
450 else if (iscntrl (character) && character < 127)
456 the_rep[i++] = character;
463 tw = ((hpos + 8) & 0xf8) - hpos;
471 the_rep[i++] = (character | 0x40);
474 /* Show META-x as 0370. */
475 else if (character > printable_limit)
477 sprintf (the_rep + i, "\\%0o", character);
478 i = strlen (the_rep);
480 else if (character == DEL)
486 the_rep[i++] = character;
494 /* **************************************************************** */
496 /* Functions Static To This File */
498 /* **************************************************************** */
500 /* Amount of space allocated to INFO_PARSED_FILENAME via xmalloc (). */
501 static int parsed_filename_size = 0;
503 /* Amount of space allocated to INFO_PARSED_NODENAME via xmalloc (). */
504 static int parsed_nodename_size = 0;
506 static void save_string (), saven_string ();
508 /* Remember FILENAME in PARSED_FILENAME. An empty FILENAME is translated
509 to a NULL pointer in PARSED_FILENAME. */
511 save_filename (filename)
514 save_string (filename, &info_parsed_filename, &parsed_filename_size);
517 /* Just like save_filename (), but you pass the length of the string. */
519 saven_filename (filename, len)
523 saven_string (filename, len,
524 &info_parsed_filename, &parsed_filename_size);
527 /* Remember NODENAME in PARSED_NODENAME. An empty NODENAME is translated
528 to a NULL pointer in PARSED_NODENAME. */
530 save_nodename (nodename)
533 save_string (nodename, &info_parsed_nodename, &parsed_nodename_size);
536 /* Just like save_nodename (), but you pass the length of the string. */
538 saven_nodename (nodename, len)
542 saven_string (nodename, len,
543 &info_parsed_nodename, &parsed_nodename_size);
546 /* Remember STRING in STRING_P. STRING_P should currently have STRING_SIZE_P
547 bytes allocated to it. An empty STRING is translated to a NULL pointer
550 save_string (string, string_p, string_size_p)
555 if (!string || !*string)
560 *string_p = (char *)NULL;
565 if (strlen (string) >= *string_size_p)
566 *string_p = (char *)xrealloc
567 (*string_p, (*string_size_p = 1 + strlen (string)));
569 strcpy (*string_p, string);
573 /* Just like save_string (), but you also pass the length of STRING. */
575 saven_string (string, len, string_p, string_size_p)
586 *string_p = (char *)NULL;
591 if (len >= *string_size_p)
592 *string_p = (char *)xrealloc (*string_p, (*string_size_p = 1 + len));
594 strncpy (*string_p, string, len);
595 (*string_p)[len] = '\0';
599 /* Return a pointer to the part of PATHNAME that simply defines the file. */
601 filename_non_directory (pathname)
604 register char *filename = pathname + strlen (pathname);
606 if (HAVE_DRIVE (pathname))
609 while (filename > pathname && !IS_SLASH (filename[-1]))
615 /* Return non-zero if NODE is one especially created by Info. */
617 internal_info_node_p (node)
622 (node->filename && !*node->filename) &&
623 !node->parent && node->nodename)
628 return ((node != (NODE *)NULL) && ((node->flags & N_IsInternal) != 0));
632 /* Make NODE appear to be one especially created by Info. */
634 name_internal_node (node, name)
642 node->parent = (char *)NULL;
643 node->nodename = name;
644 node->flags |= N_IsInternal;
647 /* Return the window displaying NAME, the name of an internally created
650 get_internal_info_window (name)
655 for (win = windows; win; win = win->next)
656 if (internal_info_node_p (win->node) &&
657 (strcmp (win->node->nodename, name) == 0))
663 /* Return a window displaying the node NODE. */
665 get_window_of_node (node)
668 WINDOW *win = (WINDOW *)NULL;
670 for (win = windows; win; win = win->next)
671 if (win->node == node)