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)
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). */
25 #include "info-utils.h"
26 #if defined (HANDLE_MAN_PAGES)
28 #endif /* HANDLE_MAN_PAGES */
30 /* When non-zero, various display and input functions handle ISO Latin
31 character sets correctly. */
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). */
62 info_parse_node (char *string
, int newlines_okay
)
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
)
74 string
+= skip_whitespace (string
);
76 /* Check for (FILENAME)NODENAME. */
80 /* Advance past the opening paren. */
83 /* Find the closing paren. */
84 while (string
[i
] && string
[i
] != ')')
87 /* Remember parsed filename. */
88 saven_filename (string
, i
);
90 /* Point directly at the nodename. */
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. */
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
118 while (whitespace(*rest
))
123 while (whitespace(*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);
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
144 info_parse_label (char *label
, NODE
*node
)
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
);
161 nodeline
+= skip_whitespace (nodeline
);
162 info_parse_node (nodeline
, DONT_SKIP_NEWLINES
);
165 /* **************************************************************** */
167 /* Finding and Building Menus */
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. */
174 info_menu_of_node (NODE
*node
)
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
);
189 return ((REFERENCE
**) NULL
);
191 /* We have the start of the menu now. Glean menu items from the rest
193 tmp_search
.start
= position
+ strlen (INFO_MENU_LABEL
);
194 tmp_search
.start
+= skip_line (tmp_search
.buffer
+ tmp_search
.start
);
196 menu
= info_menu_items (&tmp_search
);
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. */
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
));
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. */
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. */
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. */
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;
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)
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. */
273 if (searching_for_menu_items
)
279 temp
= skip_line (refdef
);
280 offset
= string_in_line (":", refdef
+ temp
);
282 continue; /* Give up? */
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
);
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. */
304 entry
->nodename
= xstrdup (entry
->label
);
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
);
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
;
328 (entry
, refs_index
, refs
, refs_slots
, 50, REFERENCE
*);
333 /* Get the entry associated with LABEL in REFERENCES. Return a pointer
334 to the ENTRY if found, or NULL. */
336 info_get_labeled_reference (char *label
, REFERENCE
**references
)
341 for (i
= 0; references
&& (entry
= references
[i
]); i
++)
343 if (strcmp (label
, entry
->label
) == 0)
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. */
353 info_concatenate_references (REFERENCE
**ref1
, REFERENCE
**ref2
)
359 /* With one argument passed as NULL, simply return the other arg. */
365 /* Get the total size of the slots that we will need. */
366 for (i
= 0; ref1
[i
]; i
++);
368 for (i
= 0; ref2
[i
]; i
++);
371 result
= (REFERENCE
**)xmalloc ((1 + size
) * sizeof (REFERENCE
*));
373 /* Copy the contents over. */
374 for (i
= 0; ref1
[i
]; i
++)
378 for (i
= 0; ref2
[i
]; i
++)
379 result
[j
++] = ref2
[i
];
381 result
[j
] = (REFERENCE
*)NULL
;
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
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
;
407 /* Free the data associated with REFERENCES. */
409 info_free_references (REFERENCE
**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
);
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. */
433 canonicalize_whitespace (char *string
)
436 int len
, whitespace_found
, whitespace_loc
= 0;
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
]))
459 if (whitespace_found
&& whitespace_loc
)
461 whitespace_found
= 0;
463 /* Suppress whitespace at start of string. */
468 temp
[j
++] = string
[i
];
472 /* Kill trailing whitespace. */
473 if (j
&& whitespace (temp
[j
- 1]))
477 strcpy (string
, 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. */
487 printed_representation (unsigned char character
, int hpos
)
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)
501 the_rep
[i
++] = character
;
508 tw
= ((hpos
+ 8) & 0xf8) - hpos
;
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
)
531 the_rep
[i
++] = character
;
539 /* **************************************************************** */
541 /* Functions Static To This File */
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
,
555 /* Remember FILENAME in PARSED_FILENAME. An empty FILENAME is translated
556 to a NULL pointer in PARSED_FILENAME. */
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. */
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. */
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. */
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
591 save_string (char *string
, char **string_p
, int *string_size_p
)
593 if (!string
|| !*string
)
598 *string_p
= (char *)NULL
;
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. */
613 saven_string (char *string
, int len
, char **string_p
, int *string_size_p
)
620 *string_p
= (char *)NULL
;
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. */
635 filename_non_directory (char *pathname
)
637 register char *filename
= pathname
+ strlen (pathname
);
639 if (HAVE_DRIVE (pathname
))
642 while (filename
> pathname
&& !IS_SLASH (filename
[-1]))
648 /* Return non-zero if NODE is one especially created by Info. */
650 internal_info_node_p (NODE
*node
)
654 (node
->filename
&& !*node
->filename
) &&
655 !node
->parent
&& node
->nodename
)
660 return ((node
!= (NODE
*)NULL
) && ((node
->flags
& N_IsInternal
) != 0));
664 /* Make NODE appear to be one especially created by Info. */
666 name_internal_node (NODE
*node
, char *name
)
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
680 get_internal_info_window (char *name
)
684 for (win
= windows
; win
; win
= win
->next
)
685 if (internal_info_node_p (win
->node
) &&
686 (strcmp (win
->node
->nodename
, name
) == 0))
692 /* Return a window displaying the node NODE. */
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
)