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