3.6 branching and setup.
[dragonfly.git] / contrib / texinfo / info / info-utils.c
blob7236452b1d97fbcbd99e1ff24ec983ac1f16db8b
1 /* info-utils.c -- miscellanous.
2 $Id: info-utils.c,v 1.12 2008/06/11 09:55:42 gray Exp $
4 Copyright (C) 1993, 1998, 2003, 2004, 2007, 2008
5 Free Software Foundation, Inc.
7 This program is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
20 Originally written by Brian Fox (bfox@ai.mit.edu). */
22 #include "info.h"
23 #include "info-utils.h"
24 #if defined (HANDLE_MAN_PAGES)
25 # include "man.h"
26 #endif /* HANDLE_MAN_PAGES */
28 /* When non-zero, various display and input functions handle ISO Latin
29 character sets correctly. */
30 int ISO_Latin_p = 1;
32 /* Variable which holds the most recent filename parsed as a result of
33 calling info_parse_xxx (). */
34 char *info_parsed_filename = NULL;
36 /* Variable which holds the most recent nodename parsed as a result of
37 calling info_parse_xxx (). */
38 char *info_parsed_nodename = NULL;
40 /* Variable which holds the most recent line number parsed as a result of
41 calling info_parse_xxx (). */
42 int info_parsed_line_number = 0;
44 /* Functions to remember a filename or nodename for later return. */
45 static void save_filename (char *filename);
46 static void saven_filename (char *filename, int len);
47 static void save_nodename (char *nodename);
48 static void saven_nodename (char *nodename, int len);
50 /* How to get a reference (either menu or cross). */
51 static REFERENCE **info_references_internal (char *label,
52 SEARCH_BINDING *binding);
54 /* Parse the filename and nodename out of STRING. If STRING doesn't
55 contain a filename (i.e., it is NOT (FILENAME)NODENAME) then set
56 INFO_PARSED_FILENAME to NULL. If second argument NEWLINES_OKAY is
57 non-zero, it says to allow the nodename specification to cross a
58 newline boundary (i.e., only `,', `.', or `TAB' can end the spec). */
59 void
60 info_parse_node (char *string, int newlines_okay)
62 register int i = 0;
64 /* Default the answer. */
65 save_filename (NULL);
66 save_nodename (NULL);
68 /* Special case of nothing passed. Return nothing. */
69 if (!string || !*string)
70 return;
72 string += skip_whitespace (string);
74 /* Check for (FILENAME)NODENAME. */
75 if (*string == '(')
77 i = 0;
78 /* Advance past the opening paren. */
79 string++;
81 /* Find the closing paren. */
82 while (string[i] && string[i] != ')')
83 i++;
85 /* Remember parsed filename. */
86 saven_filename (string, i);
88 /* Point directly at the nodename. */
89 string += i;
91 if (*string)
92 string++;
95 /* Parse out nodename. */
96 i = skip_node_characters (string, newlines_okay);
97 saven_nodename (string, i);
98 canonicalize_whitespace (info_parsed_nodename);
99 if (info_parsed_nodename && !*info_parsed_nodename)
101 free (info_parsed_nodename);
102 info_parsed_nodename = NULL;
105 /* Parse ``(line ...)'' part of menus, if any. */
107 char *rest = string + i;
109 /* Advance only if it's not already at end of string. */
110 if (*rest)
111 rest++;
113 /* Skip any whitespace first, and then a newline in case the item
114 was so long to contain the ``(line ...)'' string in the same
115 physical line. */
116 while (whitespace(*rest))
117 rest++;
118 if (*rest == '\n')
120 rest++;
121 while (whitespace(*rest))
122 rest++;
125 /* Are we looking at an opening parenthesis? That can only mean
126 we have a winner. :) */
127 if (strncmp (rest, "(line ", strlen ("(line ")) == 0)
129 rest += strlen ("(line ");
130 info_parsed_line_number = strtol (rest, NULL, 0);
132 else
133 info_parsed_line_number = 0;
137 /* Return the node addressed by LABEL in NODE (usually one of "Prev:",
138 "Next:", "Up:", "File:", or "Node:". After a call to this function,
139 the global INFO_PARSED_NODENAME and INFO_PARSED_FILENAME contain
140 the information. */
141 void
142 info_parse_label (char *label, NODE *node)
144 register int i;
145 char *nodeline;
147 /* Default answer to failure. */
148 save_nodename (NULL);
149 save_filename (NULL);
151 /* Find the label in the first line of this node. */
152 nodeline = node->contents;
153 i = string_in_line (label, nodeline);
155 if (i == -1)
156 return;
158 nodeline += i;
159 nodeline += skip_whitespace (nodeline);
160 info_parse_node (nodeline, DONT_SKIP_NEWLINES);
163 /* **************************************************************** */
164 /* */
165 /* Finding and Building Menus */
166 /* */
167 /* **************************************************************** */
169 /* Return a NULL terminated array of REFERENCE * which represents the menu
170 found in NODE. If there is no menu in NODE, just return a NULL pointer. */
171 REFERENCE **
172 info_menu_of_node (NODE *node)
174 long position;
175 SEARCH_BINDING tmp_search;
176 REFERENCE **menu = NULL;
178 tmp_search.buffer = node->contents;
179 tmp_search.start = 0;
180 tmp_search.end = node->nodelen;
181 tmp_search.flags = S_FoldCase;
183 /* Find the start of the menu. */
184 position = search_forward (INFO_MENU_LABEL, &tmp_search);
186 if (position == -1)
187 return NULL;
189 /* We have the start of the menu now. Glean menu items from the rest
190 of the node. */
191 tmp_search.start = position + strlen (INFO_MENU_LABEL);
192 tmp_search.start += skip_line (tmp_search.buffer + tmp_search.start);
193 tmp_search.start--;
194 menu = info_menu_items (&tmp_search);
195 return menu;
198 /* Return a NULL terminated array of REFERENCE * which represents the cross
199 refrences found in NODE. If there are no cross references in NODE, just
200 return a NULL pointer. */
201 REFERENCE **
202 info_xrefs_of_node (NODE *node)
204 SEARCH_BINDING tmp_search;
206 #if defined (HANDLE_MAN_PAGES)
207 if (node->flags & N_IsManPage)
208 return xrefs_of_manpage (node);
209 #endif
211 tmp_search.buffer = node->contents;
212 tmp_search.start = 0;
213 tmp_search.end = node->nodelen;
214 tmp_search.flags = S_FoldCase;
216 return info_xrefs (&tmp_search);
219 /* Glean menu entries from BINDING->buffer + BINDING->start until we
220 have looked at the entire contents of BINDING. Return an array
221 of REFERENCE * that represents each menu item in this range. */
222 REFERENCE **
223 info_menu_items (SEARCH_BINDING *binding)
225 return info_references_internal (INFO_MENU_ENTRY_LABEL, binding);
228 /* Glean cross references from BINDING->buffer + BINDING->start until
229 BINDING->end. Return an array of REFERENCE * that represents each
230 cross reference in this range. */
231 REFERENCE **
232 info_xrefs (SEARCH_BINDING *binding)
234 return info_references_internal (INFO_XREF_LABEL, binding);
237 /* Glean cross references or menu items from BINDING. Return an array
238 of REFERENCE * that represents the items found. */
239 static REFERENCE **
240 info_references_internal (char *label, SEARCH_BINDING *binding)
242 SEARCH_BINDING tmp_search;
243 REFERENCE **refs = NULL;
244 int refs_index = 0, refs_slots = 0;
245 int searching_for_menu_items = 0;
246 long position;
248 tmp_search.buffer = binding->buffer;
249 tmp_search.start = binding->start;
250 tmp_search.end = binding->end;
251 tmp_search.flags = S_FoldCase | S_SkipDest;
253 searching_for_menu_items = (mbscasecmp (label, INFO_MENU_ENTRY_LABEL) == 0);
255 while ((position = search_forward (label, &tmp_search)) != -1)
257 int offset, start;
258 char *refdef;
259 REFERENCE *entry;
261 tmp_search.start = position;
262 tmp_search.start += skip_whitespace (tmp_search.buffer + tmp_search.start);
263 start = tmp_search.start - binding->start;
264 refdef = tmp_search.buffer + tmp_search.start;
265 offset = string_in_line (":", refdef);
267 /* When searching for menu items, if no colon, there is no
268 menu item on this line. */
269 if (offset == -1)
271 if (searching_for_menu_items)
272 continue;
273 else
275 int temp;
277 temp = skip_line (refdef);
278 offset = string_in_line (":", refdef + temp);
279 if (offset == -1)
280 continue; /* Give up? */
281 else
282 offset += temp;
286 entry = xmalloc (sizeof (REFERENCE));
287 entry->filename = NULL;
288 entry->nodename = NULL;
289 entry->label = xmalloc (offset);
290 strncpy (entry->label, refdef, offset - 1);
291 entry->label[offset - 1] = '\0';
292 canonicalize_whitespace (entry->label);
293 entry->line_number = 0;
295 refdef += offset;
296 entry->start = start;
297 entry->end = refdef - binding->buffer;
299 /* If this reference entry continues with another ':' then the
300 nodename is the same as the label. */
301 if (*refdef == ':')
303 entry->nodename = xstrdup (entry->label);
305 else
307 /* This entry continues with a specific nodename. Parse the
308 nodename from the specification. */
310 refdef += skip_whitespace_and_newlines (refdef);
312 if (searching_for_menu_items)
313 info_parse_node (refdef, DONT_SKIP_NEWLINES);
314 else
315 info_parse_node (refdef, SKIP_NEWLINES);
317 if (info_parsed_filename)
318 entry->filename = xstrdup (info_parsed_filename);
320 if (info_parsed_nodename)
321 entry->nodename = xstrdup (info_parsed_nodename);
323 entry->line_number = info_parsed_line_number;
326 add_pointer_to_array
327 (entry, refs_index, refs, refs_slots, 50, REFERENCE *);
329 return refs;
332 /* Get the entry associated with LABEL in REFERENCES. Return a pointer
333 to the ENTRY if found, or NULL. */
334 REFERENCE *
335 info_get_labeled_reference (char *label, REFERENCE **references)
337 register int i;
338 REFERENCE *entry;
340 for (i = 0; references && (entry = references[i]); i++)
342 if (strcmp (label, entry->label) == 0)
343 return entry;
345 return NULL;
348 /* A utility function for concatenating REFERENCE **. Returns a new
349 REFERENCE ** which is the concatenation of REF1 and REF2. The REF1
350 and REF2 arrays are freed, but their contents are not. */
351 REFERENCE **
352 info_concatenate_references (REFERENCE **ref1, REFERENCE **ref2)
354 register int i, j;
355 REFERENCE **result;
356 int size;
358 /* With one argument passed as NULL, simply return the other arg. */
359 if (!ref1)
360 return ref2;
361 else if (!ref2)
362 return ref1;
364 /* Get the total size of the slots that we will need. */
365 for (i = 0; ref1[i]; i++);
366 size = i;
367 for (i = 0; ref2[i]; i++);
368 size += i;
370 result = xmalloc ((1 + size) * sizeof (REFERENCE *));
372 /* Copy the contents over. */
373 for (i = 0; ref1[i]; i++)
374 result[i] = ref1[i];
376 j = i;
377 for (i = 0; ref2[i]; i++)
378 result[j++] = ref2[i];
380 result[j] = NULL;
381 free (ref1);
382 free (ref2);
383 return result;
388 /* Copy a reference structure. Since we tend to free everything at
389 every opportunity, we don't share any points, but copy everything into
390 new memory. */
391 REFERENCE *
392 info_copy_reference (REFERENCE *src)
394 REFERENCE *dest = xmalloc (sizeof (REFERENCE));
395 dest->label = src->label ? xstrdup (src->label) : NULL;
396 dest->filename = src->filename ? xstrdup (src->filename) : NULL;
397 dest->nodename = src->nodename ? xstrdup (src->nodename) : NULL;
398 dest->start = src->start;
399 dest->end = src->end;
400 dest->line_number = 0;
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 = 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;
483 static size_t the_rep_size;
485 /* Return a pointer to a string which is the printed representation
486 of CHARACTER if it were printed at HPOS. */
487 char *
488 printed_representation (const unsigned char *cp, size_t len, size_t hpos,
489 /* Return: */
490 size_t *plen)
492 register int i = 0;
493 int printable_limit = ISO_Latin_p ? 255 : 127;
494 #define REPSPACE(s) \
495 do \
497 while (the_rep_size < s) \
499 if (the_rep_size == 0) \
500 the_rep_size = 8; /* Initial allocation */ \
501 the_rep = x2realloc (the_rep, &the_rep_size); \
504 while (0)
506 #define SC(c) \
507 do \
509 REPSPACE(i + 1); \
510 the_rep[i++] = c; \
512 while (0)
514 for (; len > 0; cp++, len--)
516 if (raw_escapes_p && *cp == '\033')
517 SC(*cp);
518 /* Show CTRL-x as ^X. */
519 else if (iscntrl (*cp) && *cp < 127)
521 switch (*cp)
523 case '\r':
524 case '\n':
525 SC(*cp);
526 break;
528 case '\t':
530 int tw;
532 tw = ((hpos + 8) & 0xf8) - hpos;
533 while (i < tw)
534 SC(' ');
535 break;
538 default:
539 SC('^');
540 SC(*cp | 0x40);
543 /* Show META-x as 0370. */
544 else if (*cp > printable_limit)
546 REPSPACE (i + 5);
547 sprintf (the_rep + i, "\\%0o", *cp);
548 i = strlen (the_rep);
550 else if (*cp == DEL)
552 SC('^');
553 SC('?');
555 else
556 SC(*cp);
559 SC(0);
560 *plen = i - 1;
561 return the_rep;
565 /* **************************************************************** */
566 /* */
567 /* Functions Static To This File */
568 /* */
569 /* **************************************************************** */
571 /* Amount of space allocated to INFO_PARSED_FILENAME via xmalloc (). */
572 static int parsed_filename_size = 0;
574 /* Amount of space allocated to INFO_PARSED_NODENAME via xmalloc (). */
575 static int parsed_nodename_size = 0;
577 static void save_string (char *string, char **string_p, int *string_size_p);
578 static void saven_string (char *string, int len, char **string_p,
579 int *string_size_p);
581 /* Remember FILENAME in PARSED_FILENAME. An empty FILENAME is translated
582 to a NULL pointer in PARSED_FILENAME. */
583 static void
584 save_filename (char *filename)
586 save_string (filename, &info_parsed_filename, &parsed_filename_size);
589 /* Just like save_filename (), but you pass the length of the string. */
590 static void
591 saven_filename (char *filename, int len)
593 saven_string (filename, len,
594 &info_parsed_filename, &parsed_filename_size);
597 /* Remember NODENAME in PARSED_NODENAME. An empty NODENAME is translated
598 to a NULL pointer in PARSED_NODENAME. */
599 static void
600 save_nodename (char *nodename)
602 save_string (nodename, &info_parsed_nodename, &parsed_nodename_size);
605 /* Just like save_nodename (), but you pass the length of the string. */
606 static void
607 saven_nodename (char *nodename, int len)
609 saven_string (nodename, len,
610 &info_parsed_nodename, &parsed_nodename_size);
613 /* Remember STRING in STRING_P. STRING_P should currently have STRING_SIZE_P
614 bytes allocated to it. An empty STRING is translated to a NULL pointer
615 in STRING_P. */
616 static void
617 save_string (char *string, char **string_p, int *string_size_p)
619 if (!string || !*string)
621 if (*string_p)
622 free (*string_p);
624 *string_p = NULL;
625 *string_size_p = 0;
627 else
629 if (strlen (string) >= (unsigned int) *string_size_p)
630 *string_p = xrealloc
631 (*string_p, (*string_size_p = 1 + strlen (string)));
633 strcpy (*string_p, string);
637 /* Just like save_string (), but you also pass the length of STRING. */
638 static void
639 saven_string (char *string, int len, char **string_p, int *string_size_p)
641 if (!string)
643 if (*string_p)
644 free (*string_p);
646 *string_p = NULL;
647 *string_size_p = 0;
649 else
651 if (len >= *string_size_p)
652 *string_p = xrealloc (*string_p, (*string_size_p = 1 + len));
654 strncpy (*string_p, string, len);
655 (*string_p)[len] = '\0';
659 /* Return a pointer to the part of PATHNAME that simply defines the file. */
660 char *
661 filename_non_directory (char *pathname)
663 register char *filename = pathname + strlen (pathname);
665 if (HAVE_DRIVE (pathname))
666 pathname += 2;
668 while (filename > pathname && !IS_SLASH (filename[-1]))
669 filename--;
671 return filename;
674 /* Return non-zero if NODE is one especially created by Info. */
676 internal_info_node_p (NODE *node)
678 #if defined (NEVER)
679 if (node &&
680 (node->filename && !*node->filename) &&
681 !node->parent && node->nodename)
682 return 1;
683 else
684 return 0;
685 #else
686 return (node != NULL) && ((node->flags & N_IsInternal) != 0);
687 #endif /* !NEVER */
690 /* Make NODE appear to be one especially created by Info. */
691 void
692 name_internal_node (NODE *node, char *name)
694 if (!node)
695 return;
697 node->filename = "";
698 node->parent = NULL;
699 node->nodename = name;
700 node->flags |= N_IsInternal;
703 /* Return the window displaying NAME, the name of an internally created
704 Info window. */
705 WINDOW *
706 get_internal_info_window (char *name)
708 WINDOW *win;
710 for (win = windows; win; win = win->next)
711 if (internal_info_node_p (win->node) &&
712 (strcmp (win->node->nodename, name) == 0))
713 break;
715 return win;
718 /* Return a window displaying the node NODE. */
719 WINDOW *
720 get_window_of_node (NODE *node)
722 WINDOW *win = NULL;
724 for (win = windows; win; win = win->next)
725 if (win->node == node)
726 break;
728 return win;