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