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. */ 32int ISO_Latin_p = 1; 33 34/* Variable which holds the most recent filename parsed as a result of 35 calling info_parse_xxx (). */ 36char *info_parsed_filename = (char *)NULL; 37 38/* Variable which holds the most recent nodename parsed as a result of 39 calling info_parse_xxx (). */ 40char *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 (). */ 44int info_parsed_line_number = 0; 45 46/* Functions to remember a filename or nodename for later return. */ 47static void save_filename (char *filename); 48static void saven_filename (char *filename, int len); 49static void save_nodename (char *nodename); 50static void saven_nodename (char *nodename, int len); 51 52/* How to get a reference (either menu or cross). */ 53static 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). */ 61void 62info_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. */ 143void 144info_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. */ 173REFERENCE ** 174info_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. */ 203REFERENCE ** 204info_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. */ 224REFERENCE ** 225info_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. */ 233REFERENCE ** 234info_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. */ 241static REFERENCE ** 242info_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. */ 335REFERENCE * 336info_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. */ 352REFERENCE ** 353info_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. */ 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/* Free the data associated with REFERENCES. */ 408void 409info_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. */ 432void 433canonicalize_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 (). */ 482static 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. */ 486char * 487printed_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 (). */ 546static int parsed_filename_size = 0; 547 548/* Amount of space allocated to INFO_PARSED_NODENAME via xmalloc (). */ 549static int parsed_nodename_size = 0; 550 551static void save_string (char *string, char **string_p, int *string_size_p); 552static 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. */ 557static void 558save_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. */ 564static void 565saven_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. */ 573static void 574save_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. */ 580static void 581saven_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. */ 590static void 591save_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. */ 612static void 613saven_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. */ 634char * 635filename_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. */ 649int 650internal_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. */ 665void 666name_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. */ 679WINDOW * 680get_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. */ 693WINDOW * 694get_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