source: trunk/texinfo/info/info-utils.c@ 3003

Last change on this file since 3003 was 2617, checked in by bird, 19 years ago

GNU Texinfo 4.8

File size: 18.8 KB
Line 
1/* info-utils.c -- miscellanous.
2 $Id: info-utils.c,v 1.4 2004/04/11 17:56:45 karl Exp $
3
4 Copyright (C) 1993, 1998, 2003, 2004 Free Software Foundation, Inc.
5
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)
9 any later version.
10
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.
15
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.
19
20 Originally written by Brian Fox (bfox@ai.mit.edu). */
21
22#include "info.h"
23#include "info-utils.h"
24#if defined (HANDLE_MAN_PAGES)
25# include "man.h"
26#endif /* HANDLE_MAN_PAGES */
27
28/* When non-zero, various display and input functions handle ISO Latin
29 character sets correctly. */
30int ISO_Latin_p = 1;
31
32/* Variable which holds the most recent filename parsed as a result of
33 calling info_parse_xxx (). */
34char *info_parsed_filename = (char *)NULL;
35
36/* Variable which holds the most recent nodename parsed as a result of
37 calling info_parse_xxx (). */
38char *info_parsed_nodename = (char *)NULL;
39
40/* Variable which holds the most recent line number parsed as a result of
41 calling info_parse_xxx (). */
42int info_parsed_line_number = 0;
43
44/* Functions to remember a filename or nodename for later return. */
45static void save_filename (char *filename);
46static void saven_filename (char *filename, int len);
47static void save_nodename (char *nodename);
48static void saven_nodename (char *nodename, int len);
49
50/* How to get a reference (either menu or cross). */
51static REFERENCE **info_references_internal (char *label,
52 SEARCH_BINDING *binding);
53
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). */
59void
60info_parse_node (char *string, int newlines_okay)
61{
62 register int i = 0;
63
64 /* Default the answer. */
65 save_filename ((char *)NULL);
66 save_nodename ((char *)NULL);
67
68 /* Special case of nothing passed. Return nothing. */
69 if (!string || !*string)
70 return;
71
72 string += skip_whitespace (string);
73
74 /* Check for (FILENAME)NODENAME. */
75 if (*string == '(')
76 {
77 i = 0;
78 /* Advance past the opening paren. */
79 string++;
80
81 /* Find the closing paren. */
82 while (string[i] && string[i] != ')')
83 i++;
84
85 /* Remember parsed filename. */
86 saven_filename (string, i);
87
88 /* Point directly at the nodename. */
89 string += i;
90
91 if (*string)
92 string++;
93 }
94
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)
100 {
101 free (info_parsed_nodename);
102 info_parsed_nodename = (char *)NULL;
103 }
104
105 /* Parse ``(line ...)'' part of menus, if any. */
106 {
107 char *rest = string + i;
108
109 /* Advance only if it's not already at end of string. */
110 if (*rest)
111 rest++;
112
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')
119 {
120 rest++;
121 while (whitespace(*rest))
122 rest++;
123 }
124
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)
128 {
129 rest += strlen ("(line ");
130 info_parsed_line_number = strtol (rest, NULL, 0);
131 }
132 else
133 info_parsed_line_number = 0;
134 }
135}
136
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. */
141void
142info_parse_label (char *label, NODE *node)
143{
144 register int i;
145 char *nodeline;
146
147 /* Default answer to failure. */
148 save_nodename ((char *)NULL);
149 save_filename ((char *)NULL);
150
151 /* Find the label in the first line of this node. */
152 nodeline = node->contents;
153 i = string_in_line (label, nodeline);
154
155 if (i == -1)
156 return;
157
158 nodeline += i;
159 nodeline += skip_whitespace (nodeline);
160 info_parse_node (nodeline, DONT_SKIP_NEWLINES);
161}
162
163
164/* **************************************************************** */
165/* */
166/* Finding and Building Menus */
167/* */
168/* **************************************************************** */
169
170/* Return a NULL terminated array of REFERENCE * which represents the menu
171 found in NODE. If there is no menu in NODE, just return a NULL pointer. */
172REFERENCE **
173info_menu_of_node (NODE *node)
174{
175 long position;
176 SEARCH_BINDING tmp_search;
177 REFERENCE **menu = (REFERENCE **)NULL;
178
179 tmp_search.buffer = node->contents;
180 tmp_search.start = 0;
181 tmp_search.end = node->nodelen;
182 tmp_search.flags = S_FoldCase;
183
184 /* Find the start of the menu. */
185 position = search_forward (INFO_MENU_LABEL, &tmp_search);
186
187 if (position == -1)
188 return ((REFERENCE **) NULL);
189
190 /* We have the start of the menu now. Glean menu items from the rest
191 of the node. */
192 tmp_search.start = position + strlen (INFO_MENU_LABEL);
193 tmp_search.start += skip_line (tmp_search.buffer + tmp_search.start);
194 tmp_search.start--;
195 menu = info_menu_items (&tmp_search);
196 return (menu);
197}
198
199/* Return a NULL terminated array of REFERENCE * which represents the cross
200 refrences found in NODE. If there are no cross references in NODE, just
201 return a NULL pointer. */
202REFERENCE **
203info_xrefs_of_node (NODE *node)
204{
205 SEARCH_BINDING tmp_search;
206
207#if defined (HANDLE_MAN_PAGES)
208 if (node->flags & N_IsManPage)
209 return (xrefs_of_manpage (node));
210#endif
211
212 tmp_search.buffer = node->contents;
213 tmp_search.start = 0;
214 tmp_search.end = node->nodelen;
215 tmp_search.flags = S_FoldCase;
216
217 return (info_xrefs (&tmp_search));
218}
219
220/* Glean menu entries from BINDING->buffer + BINDING->start until we
221 have looked at the entire contents of BINDING. Return an array
222 of REFERENCE * that represents each menu item in this range. */
223REFERENCE **
224info_menu_items (SEARCH_BINDING *binding)
225{
226 return (info_references_internal (INFO_MENU_ENTRY_LABEL, binding));
227}
228
229/* Glean cross references from BINDING->buffer + BINDING->start until
230 BINDING->end. Return an array of REFERENCE * that represents each
231 cross reference in this range. */
232REFERENCE **
233info_xrefs (SEARCH_BINDING *binding)
234{
235 return (info_references_internal (INFO_XREF_LABEL, binding));
236}
237
238/* Glean cross references or menu items from BINDING. Return an array
239 of REFERENCE * that represents the items found. */
240static REFERENCE **
241info_references_internal (char *label, SEARCH_BINDING *binding)
242{
243 SEARCH_BINDING tmp_search;
244 REFERENCE **refs = (REFERENCE **)NULL;
245 int refs_index = 0, refs_slots = 0;
246 int searching_for_menu_items = 0;
247 long position;
248
249 tmp_search.buffer = binding->buffer;
250 tmp_search.start = binding->start;
251 tmp_search.end = binding->end;
252 tmp_search.flags = S_FoldCase | S_SkipDest;
253
254 searching_for_menu_items = (strcasecmp (label, INFO_MENU_ENTRY_LABEL) == 0);
255
256 while ((position = search_forward (label, &tmp_search)) != -1)
257 {
258 int offset, start;
259 char *refdef;
260 REFERENCE *entry;
261
262 tmp_search.start = position;
263 tmp_search.start += skip_whitespace (tmp_search.buffer + tmp_search.start);
264 start = tmp_search.start - binding->start;
265 refdef = tmp_search.buffer + tmp_search.start;
266 offset = string_in_line (":", refdef);
267
268 /* When searching for menu items, if no colon, there is no
269 menu item on this line. */
270 if (offset == -1)
271 {
272 if (searching_for_menu_items)
273 continue;
274 else
275 {
276 int temp;
277
278 temp = skip_line (refdef);
279 offset = string_in_line (":", refdef + temp);
280 if (offset == -1)
281 continue; /* Give up? */
282 else
283 offset += temp;
284 }
285 }
286
287 entry = (REFERENCE *)xmalloc (sizeof (REFERENCE));
288 entry->filename = (char *)NULL;
289 entry->nodename = (char *)NULL;
290 entry->label = (char *)xmalloc (offset);
291 strncpy (entry->label, refdef, offset - 1);
292 entry->label[offset - 1] = '\0';
293 canonicalize_whitespace (entry->label);
294
295 refdef += offset;
296 entry->start = start;
297 entry->end = refdef - binding->buffer;
298
299 /* If this reference entry continues with another ':' then the
300 nodename is the same as the label. */
301 if (*refdef == ':')
302 {
303 entry->nodename = xstrdup (entry->label);
304 }
305 else
306 {
307 /* This entry continues with a specific nodename. Parse the
308 nodename from the specification. */
309
310 refdef += skip_whitespace_and_newlines (refdef);
311
312 if (searching_for_menu_items)
313 info_parse_node (refdef, DONT_SKIP_NEWLINES);
314 else
315 info_parse_node (refdef, SKIP_NEWLINES);
316
317 if (info_parsed_filename)
318 entry->filename = xstrdup (info_parsed_filename);
319
320 if (info_parsed_nodename)
321 entry->nodename = xstrdup (info_parsed_nodename);
322
323 entry->line_number = info_parsed_line_number;
324 }
325
326 add_pointer_to_array
327 (entry, refs_index, refs, refs_slots, 50, REFERENCE *);
328 }
329 return (refs);
330}
331
332/* Get the entry associated with LABEL in REFERENCES. Return a pointer
333 to the ENTRY if found, or NULL. */
334REFERENCE *
335info_get_labeled_reference (char *label, REFERENCE **references)
336{
337 register int i;
338 REFERENCE *entry;
339
340 for (i = 0; references && (entry = references[i]); i++)
341 {
342 if (strcmp (label, entry->label) == 0)
343 return (entry);
344 }
345 return ((REFERENCE *)NULL);
346}
347
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. */
351REFERENCE **
352info_concatenate_references (REFERENCE **ref1, REFERENCE **ref2)
353{
354 register int i, j;
355 REFERENCE **result;
356 int size;
357
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);
363
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;
369
370 result = (REFERENCE **)xmalloc ((1 + size) * sizeof (REFERENCE *));
371
372 /* Copy the contents over. */
373 for (i = 0; ref1[i]; i++)
374 result[i] = ref1[i];
375
376 j = i;
377 for (i = 0; ref2[i]; i++)
378 result[j++] = ref2[i];
379
380 result[j] = (REFERENCE *)NULL;
381 free (ref1);
382 free (ref2);
383 return (result);
384}
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. */
392REFERENCE *
393info_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
408/* Free the data associated with REFERENCES. */
409void
410info_free_references (REFERENCE **references)
411{
412 register int i;
413 REFERENCE *entry;
414
415 if (references)
416 {
417 for (i = 0; references && (entry = references[i]); i++)
418 {
419 maybe_free (entry->label);
420 maybe_free (entry->filename);
421 maybe_free (entry->nodename);
422
423 free (entry);
424 }
425
426 free (references);
427 }
428}
429
430/* Search for sequences of whitespace or newlines in STRING, replacing
431 all such sequences with just a single space. Remove whitespace from
432 start and end of string. */
433void
434canonicalize_whitespace (char *string)
435{
436 register int i, j;
437 int len, whitespace_found, whitespace_loc = 0;
438 char *temp;
439
440 if (!string)
441 return;
442
443 len = strlen (string);
444 temp = (char *)xmalloc (1 + len);
445
446 /* Search for sequences of whitespace or newlines. Replace all such
447 sequences in the string with just a single space. */
448
449 whitespace_found = 0;
450 for (i = 0, j = 0; string[i]; i++)
451 {
452 if (whitespace_or_newline (string[i]))
453 {
454 whitespace_found++;
455 whitespace_loc = i;
456 continue;
457 }
458 else
459 {
460 if (whitespace_found && whitespace_loc)
461 {
462 whitespace_found = 0;
463
464 /* Suppress whitespace at start of string. */
465 if (j)
466 temp[j++] = ' ';
467 }
468
469 temp[j++] = string[i];
470 }
471 }
472
473 /* Kill trailing whitespace. */
474 if (j && whitespace (temp[j - 1]))
475 j--;
476
477 temp[j] = '\0';
478 strcpy (string, temp);
479 free (temp);
480}
481
482/* String representation of a char returned by printed_representation (). */
483static char the_rep[10];
484
485/* Return a pointer to a string which is the printed representation
486 of CHARACTER if it were printed at HPOS. */
487char *
488printed_representation (unsigned char character, int hpos)
489{
490 register int i = 0;
491 int printable_limit = ISO_Latin_p ? 255 : 127;
492
493 if (raw_escapes_p && character == '\033')
494 the_rep[i++] = character;
495 /* Show CTRL-x as ^X. */
496 else if (iscntrl (character) && character < 127)
497 {
498 switch (character)
499 {
500 case '\r':
501 case '\n':
502 the_rep[i++] = character;
503 break;
504
505 case '\t':
506 {
507 int tw;
508
509 tw = ((hpos + 8) & 0xf8) - hpos;
510 while (i < tw)
511 the_rep[i++] = ' ';
512 }
513 break;
514
515 default:
516 the_rep[i++] = '^';
517 the_rep[i++] = (character | 0x40);
518 }
519 }
520 /* Show META-x as 0370. */
521 else if (character > printable_limit)
522 {
523 sprintf (the_rep + i, "\\%0o", character);
524 i = strlen (the_rep);
525 }
526 else if (character == DEL)
527 {
528 the_rep[i++] = '^';
529 the_rep[i++] = '?';
530 }
531 else
532 the_rep[i++] = character;
533
534 the_rep[i] = 0;
535
536 return the_rep;
537}
538
539
540
541/* **************************************************************** */
542/* */
543/* Functions Static To This File */
544/* */
545/* **************************************************************** */
546
547/* Amount of space allocated to INFO_PARSED_FILENAME via xmalloc (). */
548static int parsed_filename_size = 0;
549
550/* Amount of space allocated to INFO_PARSED_NODENAME via xmalloc (). */
551static int parsed_nodename_size = 0;
552
553static void save_string (char *string, char **string_p, int *string_size_p);
554static void saven_string (char *string, int len, char **string_p,
555 int *string_size_p);
556
557/* Remember FILENAME in PARSED_FILENAME. An empty FILENAME is translated
558 to a NULL pointer in PARSED_FILENAME. */
559static void
560save_filename (char *filename)
561{
562 save_string (filename, &info_parsed_filename, &parsed_filename_size);
563}
564
565/* Just like save_filename (), but you pass the length of the string. */
566static void
567saven_filename (char *filename, int len)
568{
569 saven_string (filename, len,
570 &info_parsed_filename, &parsed_filename_size);
571}
572
573/* Remember NODENAME in PARSED_NODENAME. An empty NODENAME is translated
574 to a NULL pointer in PARSED_NODENAME. */
575static void
576save_nodename (char *nodename)
577{
578 save_string (nodename, &info_parsed_nodename, &parsed_nodename_size);
579}
580
581/* Just like save_nodename (), but you pass the length of the string. */
582static void
583saven_nodename (char *nodename, int len)
584{
585 saven_string (nodename, len,
586 &info_parsed_nodename, &parsed_nodename_size);
587}
588
589/* Remember STRING in STRING_P. STRING_P should currently have STRING_SIZE_P
590 bytes allocated to it. An empty STRING is translated to a NULL pointer
591 in STRING_P. */
592static void
593save_string (char *string, char **string_p, int *string_size_p)
594{
595 if (!string || !*string)
596 {
597 if (*string_p)
598 free (*string_p);
599
600 *string_p = (char *)NULL;
601 *string_size_p = 0;
602 }
603 else
604 {
605 if (strlen (string) >= (unsigned int) *string_size_p)
606 *string_p = (char *)xrealloc
607 (*string_p, (*string_size_p = 1 + strlen (string)));
608
609 strcpy (*string_p, string);
610 }
611}
612
613/* Just like save_string (), but you also pass the length of STRING. */
614static void
615saven_string (char *string, int len, char **string_p, int *string_size_p)
616{
617 if (!string)
618 {
619 if (*string_p)
620 free (*string_p);
621
622 *string_p = (char *)NULL;
623 *string_size_p = 0;
624 }
625 else
626 {
627 if (len >= *string_size_p)
628 *string_p = (char *)xrealloc (*string_p, (*string_size_p = 1 + len));
629
630 strncpy (*string_p, string, len);
631 (*string_p)[len] = '\0';
632 }
633}
634
635/* Return a pointer to the part of PATHNAME that simply defines the file. */
636char *
637filename_non_directory (char *pathname)
638{
639 register char *filename = pathname + strlen (pathname);
640
641 if (HAVE_DRIVE (pathname))
642 pathname += 2;
643
644 while (filename > pathname && !IS_SLASH (filename[-1]))
645 filename--;
646
647 return (filename);
648}
649
650/* Return non-zero if NODE is one especially created by Info. */
651int
652internal_info_node_p (NODE *node)
653{
654#if defined (NEVER)
655 if (node &&
656 (node->filename && !*node->filename) &&
657 !node->parent && node->nodename)
658 return (1);
659 else
660 return (0);
661#else
662 return ((node != (NODE *)NULL) && ((node->flags & N_IsInternal) != 0));
663#endif /* !NEVER */
664}
665
666/* Make NODE appear to be one especially created by Info. */
667void
668name_internal_node (NODE *node, char *name)
669{
670 if (!node)
671 return;
672
673 node->filename = "";
674 node->parent = (char *)NULL;
675 node->nodename = name;
676 node->flags |= N_IsInternal;
677}
678
679/* Return the window displaying NAME, the name of an internally created
680 Info window. */
681WINDOW *
682get_internal_info_window (char *name)
683{
684 WINDOW *win;
685
686 for (win = windows; win; win = win->next)
687 if (internal_info_node_p (win->node) &&
688 (strcmp (win->node->nodename, name) == 0))
689 break;
690
691 return (win);
692}
693
694/* Return a window displaying the node NODE. */
695WINDOW *
696get_window_of_node (NODE *node)
697{
698 WINDOW *win = (WINDOW *)NULL;
699
700 for (win = windows; win; win = win->next)
701 if (win->node == node)
702 break;
703
704 return (win);
705}
Note: See TracBrowser for help on using the repository browser.