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 (at) 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 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 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 /* **************************************************************** */ 167 /* */ 168 /* Finding and Building Menus */ 169 /* */ 170 /* **************************************************************** */ 171 172 /* Return a NULL terminated array of REFERENCE * which represents the menu 173 found in NODE. If there is no menu in NODE, just return a NULL pointer. */ 174 REFERENCE ** 175 info_menu_of_node (NODE *node) 176 { 177 long position; 178 SEARCH_BINDING tmp_search; 179 REFERENCE **menu = (REFERENCE **)NULL; 180 181 tmp_search.buffer = node->contents; 182 tmp_search.start = 0; 183 tmp_search.end = node->nodelen; 184 tmp_search.flags = S_FoldCase; 185 186 /* Find the start of the menu. */ 187 position = search_forward (INFO_MENU_LABEL, &tmp_search); 188 189 if (position == -1) 190 return ((REFERENCE **) NULL); 191 192 /* We have the start of the menu now. Glean menu items from the rest 193 of the node. */ 194 tmp_search.start = position + strlen (INFO_MENU_LABEL); 195 tmp_search.start += skip_line (tmp_search.buffer + tmp_search.start); 196 tmp_search.start--; 197 menu = info_menu_items (&tmp_search); 198 return (menu); 199 } 200 201 /* Return a NULL terminated array of REFERENCE * which represents the cross 202 refrences found in NODE. If there are no cross references in NODE, just 203 return a NULL pointer. */ 204 REFERENCE ** 205 info_xrefs_of_node (NODE *node) 206 { 207 SEARCH_BINDING tmp_search; 208 209 #if defined (HANDLE_MAN_PAGES) 210 if (node->flags & N_IsManPage) 211 return (xrefs_of_manpage (node)); 212 #endif 213 214 tmp_search.buffer = node->contents; 215 tmp_search.start = 0; 216 tmp_search.end = node->nodelen; 217 tmp_search.flags = S_FoldCase; 218 219 return (info_xrefs (&tmp_search)); 220 } 221 222 /* Glean menu entries from BINDING->buffer + BINDING->start until we 223 have looked at the entire contents of BINDING. Return an array 224 of REFERENCE * that represents each menu item in this range. */ 225 REFERENCE ** 226 info_menu_items (SEARCH_BINDING *binding) 227 { 228 return (info_references_internal (INFO_MENU_ENTRY_LABEL, binding)); 229 } 230 231 /* Glean cross references from BINDING->buffer + BINDING->start until 232 BINDING->end. Return an array of REFERENCE * that represents each 233 cross reference in this range. */ 234 REFERENCE ** 235 info_xrefs (SEARCH_BINDING *binding) 236 { 237 return (info_references_internal (INFO_XREF_LABEL, binding)); 238 } 239 240 /* Glean cross references or menu items from BINDING. Return an array 241 of REFERENCE * that represents the items found. */ 242 static REFERENCE ** 243 info_references_internal (char *label, SEARCH_BINDING *binding) 244 { 245 SEARCH_BINDING tmp_search; 246 REFERENCE **refs = (REFERENCE **)NULL; 247 int refs_index = 0, refs_slots = 0; 248 int searching_for_menu_items = 0; 249 long position; 250 251 tmp_search.buffer = binding->buffer; 252 tmp_search.start = binding->start; 253 tmp_search.end = binding->end; 254 tmp_search.flags = S_FoldCase | S_SkipDest; 255 256 searching_for_menu_items = (strcasecmp (label, INFO_MENU_ENTRY_LABEL) == 0); 257 258 while ((position = search_forward (label, &tmp_search)) != -1) 259 { 260 int offset, start; 261 char *refdef; 262 REFERENCE *entry; 263 264 tmp_search.start = position; 265 tmp_search.start += skip_whitespace (tmp_search.buffer + tmp_search.start); 266 start = tmp_search.start - binding->start; 267 refdef = tmp_search.buffer + tmp_search.start; 268 offset = string_in_line (":", refdef); 269 270 /* When searching for menu items, if no colon, there is no 271 menu item on this line. */ 272 if (offset == -1) 273 { 274 if (searching_for_menu_items) 275 continue; 276 else 277 { 278 int temp; 279 280 temp = skip_line (refdef); 281 offset = string_in_line (":", refdef + temp); 282 if (offset == -1) 283 continue; /* Give up? */ 284 else 285 offset += temp; 286 } 287 } 288 289 entry = (REFERENCE *)xmalloc (sizeof (REFERENCE)); 290 entry->filename = (char *)NULL; 291 entry->nodename = (char *)NULL; 292 entry->label = (char *)xmalloc (offset); 293 strncpy (entry->label, refdef, offset - 1); 294 entry->label[offset - 1] = '\0'; 295 canonicalize_whitespace (entry->label); 296 297 refdef += offset; 298 entry->start = start; 299 entry->end = refdef - binding->buffer; 300 301 /* If this reference entry continues with another ':' then the 302 nodename is the same as the label. */ 303 if (*refdef == ':') 304 { 305 entry->nodename = xstrdup (entry->label); 306 } 307 else 308 { 309 /* This entry continues with a specific nodename. Parse the 310 nodename from the specification. */ 311 312 refdef += skip_whitespace_and_newlines (refdef); 313 314 if (searching_for_menu_items) 315 info_parse_node (refdef, DONT_SKIP_NEWLINES); 316 else 317 info_parse_node (refdef, SKIP_NEWLINES); 318 319 if (info_parsed_filename) 320 entry->filename = xstrdup (info_parsed_filename); 321 322 if (info_parsed_nodename) 323 entry->nodename = xstrdup (info_parsed_nodename); 324 325 entry->line_number = info_parsed_line_number; 326 } 327 328 add_pointer_to_array 329 (entry, refs_index, refs, refs_slots, 50, REFERENCE *); 330 } 331 return (refs); 332 } 333 334 /* Get the entry associated with LABEL in REFERENCES. Return a pointer 335 to the ENTRY if found, or NULL. */ 336 REFERENCE * 337 info_get_labeled_reference (char *label, REFERENCE **references) 338 { 339 register int i; 340 REFERENCE *entry; 341 342 for (i = 0; references && (entry = references[i]); i++) 343 { 344 if (strcmp (label, entry->label) == 0) 345 return (entry); 346 } 347 return ((REFERENCE *)NULL); 348 } 349 350 /* A utility function for concatenating REFERENCE **. Returns a new 351 REFERENCE ** which is the concatenation of REF1 and REF2. The REF1 352 and REF2 arrays are freed, but their contents are not. */ 353 REFERENCE ** 354 info_concatenate_references (REFERENCE **ref1, REFERENCE **ref2) 355 { 356 register int i, j; 357 REFERENCE **result; 358 int size; 359 360 /* With one argument passed as NULL, simply return the other arg. */ 361 if (!ref1) 362 return (ref2); 363 else if (!ref2) 364 return (ref1); 365 366 /* Get the total size of the slots that we will need. */ 367 for (i = 0; ref1[i]; i++); 368 size = i; 369 for (i = 0; ref2[i]; i++); 370 size += i; 371 372 result = (REFERENCE **)xmalloc ((1 + size) * sizeof (REFERENCE *)); 373 374 /* Copy the contents over. */ 375 for (i = 0; ref1[i]; i++) 376 result[i] = ref1[i]; 377 378 j = i; 379 for (i = 0; ref2[i]; i++) 380 result[j++] = ref2[i]; 381 382 result[j] = (REFERENCE *)NULL; 383 free (ref1); 384 free (ref2); 385 return (result); 386 } 387 388 389 390 /* Copy a reference structure. Since we tend to free everything at 392 every opportunity, we don't share any points, but copy everything into 393 new memory. */ 394 REFERENCE * 395 info_copy_reference (REFERENCE *src) 396 { 397 REFERENCE *dest = xmalloc (sizeof (REFERENCE)); 398 dest->label = src->label ? xstrdup (src->label) : NULL; 399 dest->filename = src->filename ? xstrdup (src->filename) : NULL; 400 dest->nodename = src->nodename ? xstrdup (src->nodename) : NULL; 401 dest->start = src->start; 402 dest->end = src->end; 403 404 return dest; 405 } 406 407 408 409 /* Free the data associated with REFERENCES. */ 411 void 412 info_free_references (REFERENCE **references) 413 { 414 register int i; 415 REFERENCE *entry; 416 417 if (references) 418 { 419 for (i = 0; references && (entry = references[i]); i++) 420 { 421 maybe_free (entry->label); 422 maybe_free (entry->filename); 423 maybe_free (entry->nodename); 424 425 free (entry); 426 } 427 428 free (references); 429 } 430 } 431 432 /* Search for sequences of whitespace or newlines in STRING, replacing 433 all such sequences with just a single space. Remove whitespace from 434 start and end of string. */ 435 void 436 canonicalize_whitespace (char *string) 437 { 438 register int i, j; 439 int len, whitespace_found, whitespace_loc = 0; 440 char *temp; 441 442 if (!string) 443 return; 444 445 len = strlen (string); 446 temp = (char *)xmalloc (1 + len); 447 448 /* Search for sequences of whitespace or newlines. Replace all such 449 sequences in the string with just a single space. */ 450 451 whitespace_found = 0; 452 for (i = 0, j = 0; string[i]; i++) 453 { 454 if (whitespace_or_newline (string[i])) 455 { 456 whitespace_found++; 457 whitespace_loc = i; 458 continue; 459 } 460 else 461 { 462 if (whitespace_found && whitespace_loc) 463 { 464 whitespace_found = 0; 465 466 /* Suppress whitespace at start of string. */ 467 if (j) 468 temp[j++] = ' '; 469 } 470 471 temp[j++] = string[i]; 472 } 473 } 474 475 /* Kill trailing whitespace. */ 476 if (j && whitespace (temp[j - 1])) 477 j--; 478 479 temp[j] = '\0'; 480 strcpy (string, temp); 481 free (temp); 482 } 483 484 /* String representation of a char returned by printed_representation (). */ 485 static char the_rep[10]; 486 487 /* Return a pointer to a string which is the printed representation 488 of CHARACTER if it were printed at HPOS. */ 489 char * 490 printed_representation (unsigned char character, int hpos) 491 { 492 register int i = 0; 493 int printable_limit = ISO_Latin_p ? 255 : 127; 494 495 if (raw_escapes_p && character == '\033') 496 the_rep[i++] = character; 497 /* Show CTRL-x as ^X. */ 498 else if (iscntrl (character) && character < 127) 499 { 500 switch (character) 501 { 502 case '\r': 503 case '\n': 504 the_rep[i++] = character; 505 break; 506 507 case '\t': 508 { 509 int tw; 510 511 tw = ((hpos + 8) & 0xf8) - hpos; 512 while (i < tw) 513 the_rep[i++] = ' '; 514 } 515 break; 516 517 default: 518 the_rep[i++] = '^'; 519 the_rep[i++] = (character | 0x40); 520 } 521 } 522 /* Show META-x as 0370. */ 523 else if (character > printable_limit) 524 { 525 sprintf (the_rep + i, "\\%0o", character); 526 i = strlen (the_rep); 527 } 528 else if (character == DEL) 529 { 530 the_rep[i++] = '^'; 531 the_rep[i++] = '?'; 532 } 533 else 534 the_rep[i++] = character; 535 536 the_rep[i] = 0; 537 538 return the_rep; 539 } 540 541 542 /* **************************************************************** */ 544 /* */ 545 /* Functions Static To This File */ 546 /* */ 547 /* **************************************************************** */ 548 549 /* Amount of space allocated to INFO_PARSED_FILENAME via xmalloc (). */ 550 static int parsed_filename_size = 0; 551 552 /* Amount of space allocated to INFO_PARSED_NODENAME via xmalloc (). */ 553 static int parsed_nodename_size = 0; 554 555 static void save_string (char *string, char **string_p, int *string_size_p); 556 static void saven_string (char *string, int len, char **string_p, 557 int *string_size_p); 558 559 /* Remember FILENAME in PARSED_FILENAME. An empty FILENAME is translated 560 to a NULL pointer in PARSED_FILENAME. */ 561 static void 562 save_filename (char *filename) 563 { 564 save_string (filename, &info_parsed_filename, &parsed_filename_size); 565 } 566 567 /* Just like save_filename (), but you pass the length of the string. */ 568 static void 569 saven_filename (char *filename, int len) 570 { 571 saven_string (filename, len, 572 &info_parsed_filename, &parsed_filename_size); 573 } 574 575 /* Remember NODENAME in PARSED_NODENAME. An empty NODENAME is translated 576 to a NULL pointer in PARSED_NODENAME. */ 577 static void 578 save_nodename (char *nodename) 579 { 580 save_string (nodename, &info_parsed_nodename, &parsed_nodename_size); 581 } 582 583 /* Just like save_nodename (), but you pass the length of the string. */ 584 static void 585 saven_nodename (char *nodename, int len) 586 { 587 saven_string (nodename, len, 588 &info_parsed_nodename, &parsed_nodename_size); 589 } 590 591 /* Remember STRING in STRING_P. STRING_P should currently have STRING_SIZE_P 592 bytes allocated to it. An empty STRING is translated to a NULL pointer 593 in STRING_P. */ 594 static void 595 save_string (char *string, char **string_p, int *string_size_p) 596 { 597 if (!string || !*string) 598 { 599 if (*string_p) 600 free (*string_p); 601 602 *string_p = (char *)NULL; 603 *string_size_p = 0; 604 } 605 else 606 { 607 if (strlen (string) >= (unsigned int) *string_size_p) 608 *string_p = (char *)xrealloc 609 (*string_p, (*string_size_p = 1 + strlen (string))); 610 611 strcpy (*string_p, string); 612 } 613 } 614 615 /* Just like save_string (), but you also pass the length of STRING. */ 616 static void 617 saven_string (char *string, int len, char **string_p, int *string_size_p) 618 { 619 if (!string) 620 { 621 if (*string_p) 622 free (*string_p); 623 624 *string_p = (char *)NULL; 625 *string_size_p = 0; 626 } 627 else 628 { 629 if (len >= *string_size_p) 630 *string_p = (char *)xrealloc (*string_p, (*string_size_p = 1 + len)); 631 632 strncpy (*string_p, string, len); 633 (*string_p)[len] = '\0'; 634 } 635 } 636 637 /* Return a pointer to the part of PATHNAME that simply defines the file. */ 638 char * 639 filename_non_directory (char *pathname) 640 { 641 register char *filename = pathname + strlen (pathname); 642 643 if (HAVE_DRIVE (pathname)) 644 pathname += 2; 645 646 while (filename > pathname && !IS_SLASH (filename[-1])) 647 filename--; 648 649 return (filename); 650 } 651 652 /* Return non-zero if NODE is one especially created by Info. */ 653 int 654 internal_info_node_p (NODE *node) 655 { 656 #if defined (NEVER) 657 if (node && 658 (node->filename && !*node->filename) && 659 !node->parent && node->nodename) 660 return (1); 661 else 662 return (0); 663 #else 664 return ((node != (NODE *)NULL) && ((node->flags & N_IsInternal) != 0)); 665 #endif /* !NEVER */ 666 } 667 668 /* Make NODE appear to be one especially created by Info. */ 669 void 670 name_internal_node (NODE *node, char *name) 671 { 672 if (!node) 673 return; 674 675 node->filename = ""; 676 node->parent = (char *)NULL; 677 node->nodename = name; 678 node->flags |= N_IsInternal; 679 } 680 681 /* Return the window displaying NAME, the name of an internally created 682 Info window. */ 683 WINDOW * 684 get_internal_info_window (char *name) 685 { 686 WINDOW *win; 687 688 for (win = windows; win; win = win->next) 689 if (internal_info_node_p (win->node) && 690 (strcmp (win->node->nodename, name) == 0)) 691 break; 692 693 return (win); 694 } 695 696 /* Return a window displaying the node NODE. */ 697 WINDOW * 698 get_window_of_node (NODE *node) 699 { 700 WINDOW *win = (WINDOW *)NULL; 701 702 for (win = windows; win; win = win->next) 703 if (win->node == node) 704 break; 705 706 return (win); 707 } 708