1/* 2 3Copyright (c) 1987, 1988 X Consortium 4 5Permission is hereby granted, free of charge, to any person obtaining 6a copy of this software and associated documentation files (the 7"Software"), to deal in the Software without restriction, including 8without limitation the rights to use, copy, modify, merge, publish, 9distribute, sublicense, and/or sell copies of the Software, and to 10permit persons to whom the Software is furnished to do so, subject to 11the following conditions: 12 13The above copyright notice and this permission notice shall be included 14in all copies or substantial portions of the Software. 15 16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 17OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19IN NO EVENT SHALL THE X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR 20OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22OTHER DEALINGS IN THE SOFTWARE. 23 24Except as contained in this notice, the name of the X Consortium shall 25not be used in advertising or otherwise to promote the sale, use or 26other dealings in this Software without prior written authorization 27from the X Consortium. 28 29*/ 30 31 32#include "globals.h" 33#include "vendor.h" /* vendor-specific defines and data */ 34 35#include <dirent.h> 36 37#ifdef DEBUG 38static char error_buf[BUFSIZ]; /* The buffer for error messages. */ 39#endif /* DEBUG */ 40 41static void AddToCurrentSection(Manual * local_manual, char *path); 42static void InitManual(Manual * l_manual, char *label); 43static void ReadCurrentSection(Manual * local_manual, char *path); 44static void ReadMandescFile(SectionList ** section_list, char *path); 45static void SortAndRemove(Manual * man, int number); 46static void SortList(SectionList ** list); 47 48#define SECT_ERROR -1 49 50#ifndef Byte 51#define Byte unsigned char 52#endif 53 54#ifndef reg 55#define reg register 56#endif 57 58static void sortstrs(Byte * data[], int size, Byte * otherdata[]); 59static void sortstrs_block(Byte **, Byte **, int, Byte, Byte **, Byte **); 60static void sortstrs_block_oo(Byte **, Byte **, int, Byte, int *, int *, 61 Byte **, Byte **); 62 63/* Function Name: Man 64 * Description: Builds a list of all manual directories and files. 65 * Arguments: none. 66 * Returns: the number of manual sections. 67 */ 68 69int 70Man(void) 71{ 72 SectionList *list = NULL; 73 74 char *ptr, *lang = NULL, manpath[BUFSIZ], buf[BUFSIZ], 75 *path, *current_label; 76 int sect, num_alloced; 77 78/* 79 * Get the environment variable MANPATH, and if it doesn't exist then use 80 * SYSMANPATH and LOCALMANPATH. 81 */ 82 83 /* if MANPATH variable ends in ':'. So, should extend it's value to the 84 * default search path. 85 */ 86 87 *manpath = '\0'; 88 if ((ptr = getenv("MANPATH")) != NULL) 89 strcpy(manpath, ptr); 90 if (ptr == NULL || streq(ptr, "") || ptr[strlen(ptr) - 1] == ':') { 91 lang = getenv("LANG"); 92#ifdef MANCONF 93 if (!ReadManConfig(manpath + strlen(manpath))) 94#endif 95 { 96#ifdef MANCONF 97 if (manpath[strlen(manpath) - 1] != ':') 98 strcat(manpath, ":"); 99#endif 100 strcat(manpath, SYSMANPATH); 101#ifdef LOCALMANPATH 102 strcat(manpath, ":"); 103 strcat(manpath, LOCALMANPATH); 104#endif 105 } 106 } 107 108/* 109 * Get the list of manual directories in the users MANPATH that we should 110 * open to look for manual pages. The ``mandesc'' file is read here. 111 */ 112 113 for (path = manpath; (ptr = strchr(path, ':')) != NULL; path = ++ptr) { 114 *ptr = '\0'; 115 if (lang != NULL) { 116 strcpy(buf, path); 117 strcat(buf, "/"); 118 strncat(buf, lang, sizeof(buf) - strlen(path) + 1); 119 buf[sizeof(buf) - strlen(path) + 1] = '\0'; 120 ReadMandescFile(&list, buf); 121 } 122 ReadMandescFile(&list, path); 123 } 124 if (lang != NULL) { 125 strcpy(buf, path); 126 strcat(buf, "/"); 127 strncat(buf, lang, sizeof(buf) - strlen(path) + 1); 128 buf[sizeof(buf) - strlen(path) + 1] = '\0'; 129 ReadMandescFile(&list, buf); 130 } 131 ReadMandescFile(&list, path); 132 133 SortList(&list); 134 135 sect = 0; 136 num_alloced = SECTALLOC; 137 manual = (Manual *) XtMalloc(sizeof(Manual) * num_alloced); 138 InitManual(manual, list->label); 139 manual[sect].flags = list->flags; 140 current_label = NULL; 141 142 while (list != NULL) { 143 SectionList *old_list; 144 145 if (current_label == NULL || streq(list->label, current_label)) 146 AddToCurrentSection(manual + sect, list->directory); 147 else { 148 if (manual[sect].nentries == 0) { /* empty section, re-use it. */ 149 XtFree(manual[sect].blabel); 150 manual[sect].blabel = list->label; 151 manual[sect].flags = list->flags; 152 } 153 else { 154 if (++sect >= num_alloced) { 155 num_alloced += SECTALLOC; 156 manual = (Manual *) XtRealloc((char *) manual, 157 (sizeof(Manual) * num_alloced)); 158 if (manual == NULL) 159 PrintError 160 ("Could not allocate memory for manual sections."); 161 } 162 InitManual(manual + sect, list->label); 163 manual[sect].flags = list->flags; 164 } 165 AddToCurrentSection(manual + sect, list->directory); 166 } 167 /* Save label to see if it matches next entry. */ 168 current_label = list->label; 169 old_list = list; 170 list = list->next; 171 XtFree((char *) old_list); /* free what you allocate. */ 172 } 173 if (manual[sect].nentries != 0) 174 sect++; /* don't forget that last section. */ 175 176 SortAndRemove(manual, sect); 177 178#ifdef notdef /* dump info. */ 179 DumpManual(sect); 180#endif 181 182/* 183 * realloc manual to be minimum space necessary. 184 */ 185 186 if (sect == 0) 187 PrintError("No manual pages found."); 188 manual = (Manual *) XtRealloc((char *) manual, (sizeof(Manual) * sect)); 189 if (manual == NULL) 190 PrintError("Could not allocate memory for manual sections."); 191 192 return (sect); /* return the number of man sections. */ 193} 194 195/* Function Name: SortList 196 * Description: Sorts the list of sections to search. 197 * Arguments: list - a pointer to the list to sort. 198 * Returns: a sorted list. 199 * 200 * This is the most complicated part of the entire operation. 201 * all sections with the same label must by right next to each other, 202 * but the sections that are in the standard list have to come first. 203 */ 204 205static void 206SortList(SectionList ** list) 207{ 208 SectionList *local; 209 SectionList *head, *last, *inner, *old; 210 211 if (*list == NULL) 212 PrintError("No manual sections to read, exiting."); 213 214/* 215 * First step 216 * 217 * Look for standard list items, and more them to the top of the list. 218 */ 219 220 last = NULL; /* keep Saber happy. */ 221 for (local = *list; local->next != NULL; local = local->next) { 222 if (local->flags) { 223 if (local == *list) /* top element is already standard. */ 224 break; 225 head = local; 226 227 /* Find end of standard block */ 228 for (old = NULL; (local->next != NULL) && (local->flags); 229 old = local, local = local->next); 230 231 if (old != NULL) { 232 last->next = old->next; /* Move the block. */ 233 old->next = *list; 234 *list = head; 235 } 236 237 break; /* First step accomplished. */ 238 } 239 last = local; 240 } 241 242/* 243 * Second step 244 * 245 * Move items with duplicate labels right next to each other. 246 * 247 * Changed to keep the order of the list entries unchanged. 248 */ 249 250 for (local = *list; local->next != NULL; local = local->next) { 251 head = local; 252 old = inner = local->next; 253 while (inner != NULL) { 254 if (streq(inner->label, local->label)) { 255 if (old != inner) { 256 old->next = inner->next; 257 last = inner->next; 258 inner->next = head->next; 259 head->next = inner; 260 head = inner; 261 old = inner = last; 262 continue; 263 } 264 else 265 head = inner; 266 } 267 old = inner; 268 inner = inner->next; 269 } 270 } 271} 272 273/* Function Name: ReadMandescFile 274 * Description: Reads the mandesc file, and adds more sections as 275 * necessary. 276 * Arguments: path - path name if the current search directory. 277 * section_list - pointer to the list of sections. 278 * Returns: TRUE in we should use default sections 279 */ 280 281static void 282ReadMandescFile(SectionList ** section_list, char *path) 283{ 284 char mandesc_file[BUFSIZ]; /* full path to the mandesc file. */ 285 FILE *descfile; 286 char string[BUFSIZ], local_file[BUFSIZ]; 287 Boolean use_defaults = TRUE; 288 char *cp; 289 290 snprintf(mandesc_file, sizeof(mandesc_file), "%s/%s", path, MANDESC); 291 if ((descfile = fopen(mandesc_file, "r")) != NULL) { 292 while (fgets(string, BUFSIZ, descfile) != NULL) { 293 size_t len = strlen(string); 294 295 if (len == 0) 296 continue; 297 if (string[len - 1] == '\n') 298 string[len - 1] = '\0'; /* Strip off the CR. */ 299 300 if (streq(string, NO_SECTION_DEFAULTS)) { 301 use_defaults = FALSE; 302 continue; 303 } 304 305 if ((cp = strchr(string, '\t')) != NULL) { 306 char *s; 307 308 *cp++ = '\0'; 309 strcpy(local_file, MAN); 310 strcat(local_file, string); 311 if ((s = strchr(cp, '\t')) != NULL) { 312 *s++ = '\0'; 313 if (streq(s, SUFFIX)) 314 AddNewSection(section_list, path, local_file, cp, 315 MSUFFIX); 316 else if (streq(s, FOLD)) 317 AddNewSection(section_list, path, local_file, cp, 318 MFOLD); 319 else if (streq(s, FOLDSUFFIX)) 320 AddNewSection(section_list, path, local_file, cp, 321 MFOLDSUFFIX); 322 else 323 AddNewSection(section_list, path, local_file, cp, 324 MNULL); 325 } 326 else 327 AddNewSection(section_list, path, local_file, cp, MNULL); 328 } 329 else { 330 snprintf(local_file, sizeof(local_file), "%s%c", MAN, 331 string[0]); 332 AddNewSection(section_list, path, local_file, (string + 1), 333 FALSE); 334#ifdef SEARCHOTHER 335 snprintf(local_file, sizeof(local_file), "%s%c", SEARCHOTHER, 336 string[0]); 337 AddNewSection(section_list, path, local_file, (string + 1), 338 FALSE); 339#endif 340 } 341 } 342 343 fclose(descfile); 344 } 345 if (use_defaults) 346 AddStandardSections(section_list, path); 347} 348 349/* Function Name: AddNewSection 350 * Description: Adds the new section onto the current section list. 351 * Arguments: list - pointer to the section list. 352 * path - the path to the current manual section. 353 * file - the file to save. 354 * label - the current section label. 355 * flags = 1 - add a suffix 356 * = 2 - fold to lower case 357 * Returns: none. 358 */ 359 360void 361AddNewSection(SectionList ** list, const char *path, const char *file, 362 const char *label, int flags) 363{ 364 SectionList *local_list, *end; 365 char full_path[BUFSIZ]; 366 367/* Allocate a new list element */ 368 369 local_list = (SectionList *) XtMalloc(sizeof(SectionList)); 370 371 if (*list != NULL) { 372 for (end = *list; end->next != NULL; end = end->next); 373 end->next = local_list; 374 } 375 else 376 *list = local_list; 377 378 local_list->next = NULL; 379 local_list->label = XtNewString(label); 380 snprintf(full_path, sizeof(full_path), "%s/%s", path, file); 381 local_list->directory = XtNewString(full_path); 382 local_list->flags = flags; 383} 384 385/* Function Name: AddToCurrentSection 386 * Description: This function gets the names of the manual page 387 * directories, then closes the directory. 388 * Arguments: local_manual - a pointer to a manual pages structure. 389 * path - the path to this directory. 390 * Returns: none. 391 */ 392 393static void 394AddToCurrentSection(Manual * local_manual, char *path) 395{ 396 char temp_path[BUFSIZ]; 397 398#if defined(__OpenBSD__) || defined(__NetBSD__) 399 snprintf(temp_path, sizeof(temp_path), "%s/%s", path, MACHINE); 400 ReadCurrentSection(local_manual, temp_path); 401#endif 402 ReadCurrentSection(local_manual, path); 403 snprintf(temp_path, sizeof(temp_path), "%s.%s", path, 404 COMPRESSION_EXTENSION); 405 ReadCurrentSection(local_manual, temp_path); 406} 407 408/* Function Name: ReadCurrentSection 409 * Description: Actually does the work of adding entries to the 410 * new section 411 * Arguments: local_manual - a pointer to a manual pages structure. 412 * path - the path to this directory. 413 * compressed - Is this a compressed directory? 414 * Returns: TRUE if any entries are found. 415 */ 416 417static void 418ReadCurrentSection(Manual * local_manual, char *path) 419{ 420 DIR *dir; 421 register struct dirent *dp; 422 423 register int nentries; 424 register int nalloc; 425 char full_name[BUFSIZ], *ptr; 426 427 if ((dir = opendir(path)) == NULL) { 428#ifdef DEBUG 429 snprintf(error_buf, sizeof(error_buf), "Can't open directory %s", path); 430 PopupWarning(NULL, error_buf); 431#endif /* DEBUG */ 432 return; 433 } 434 435/* 436 * Remove the compression extension from the path name. 437 */ 438 439 if ((ptr = strrchr(path, '.')) != NULL) { 440 if (streq(ptr + 1, COMPRESSION_EXTENSION)) 441 *ptr = '\0'; 442#ifdef GZIP_EXTENSION 443 else if (streq(ptr + 1, GZIP_EXTENSION)) 444 *ptr = '\0'; 445#endif 446#ifdef BZIP2_EXTENSION 447 else if (streq(ptr + 1, BZIP2_EXTENSION)) 448 *ptr = '\0'; 449#endif 450#ifdef LZMA_EXTENSION 451 else if (streq(ptr + 1, LZMA_EXTENSION)) 452 *ptr = '\0'; 453#endif 454 } 455 456 nentries = local_manual->nentries; 457 nalloc = local_manual->nalloc; 458 459 while ((dp = readdir(dir)) != NULL) { 460 char *name = dp->d_name; 461 462 if (name[0] == '.') 463 continue; 464 if (strchr(name, '.') == NULL) 465 continue; 466 if (nentries >= nalloc) { 467 nalloc += ENTRYALLOC; 468 local_manual->entries = 469 (char **) XtRealloc((char *) local_manual->entries, 470 nalloc * sizeof(char *)); 471 local_manual->entries_less_paths = 472 (char **) XtRealloc((char *) local_manual->entries_less_paths, 473 nalloc * sizeof(char *)); 474 } 475 476 snprintf(full_name, sizeof(full_name), "%s/%s", path, name); 477/* 478 * Remove the compression extension from the entry name. 479 */ 480 481 if ((ptr = strrchr(full_name, '.')) != NULL) { 482 if (streq(ptr + 1, COMPRESSION_EXTENSION)) 483 *ptr = '\0'; 484#ifdef GZIP_EXTENSION 485 else if (streq(ptr + 1, GZIP_EXTENSION)) 486 *ptr = '\0'; 487#endif 488#ifdef BZIP2_EXTENSION 489 else if (streq(ptr + 1, BZIP2_EXTENSION)) 490 *ptr = '\0'; 491#endif 492#ifdef LZMA_EXTENSION 493 else if (streq(ptr + 1, LZMA_EXTENSION)) 494 *ptr = '\0'; 495#endif 496#ifdef IGNORE_EXTENSION 497 /* skip files with specified extension - they're not real man pages */ 498 else if (streq(ptr + 1, IGNORE_EXTENSION)) { 499 continue; 500 } 501#endif /* IGNORE_EXTENSION */ 502 } 503 local_manual->entries[nentries] = XtNewString(full_name); 504 local_manual->entries_less_paths[nentries] = 505 strrchr(local_manual->entries[nentries], '/'); 506 if (local_manual->entries_less_paths[nentries] == NULL) 507 PrintError("Internal error while cataloging manual pages."); 508 ++nentries; 509 } 510 511 local_manual->nentries = nentries; 512 local_manual->nalloc = nalloc; 513 514 closedir(dir); 515} 516 517/* Function Name: SortAndRemove 518 * Description: This function sorts all the entry names and 519 * then removes all the duplicate entries. 520 * Arguments: man - a pointer to the manual structure. 521 * number - the number of manual sections. 522 * Returns: an improved manual stucure 523 */ 524 525static void 526SortAndRemove(Manual * man, int number) 527{ 528 int i; 529 char *l1, *l2, **s1; 530 531 for (i = 0; i < number; man++, i++) { /* sort each section */ 532 register int i2 = 0; 533 534#ifdef DEBUG 535 printf("sorting section %d - %s\n", i, man->blabel); 536#endif /* DEBUG */ 537 538 s1 = (char **) malloc(man->nentries * sizeof(char *)); 539 540 /* temporarily remove suffixes of entries, preventing them from */ 541 /* being used in alphabetic comparison ie sccs-delta.1 vs sccs.1 */ 542 for (i2 = 0; i2 < man->nentries; i2++) 543 if ((s1[i2] = strrchr(man->entries_less_paths[i2], '.')) != NULL) 544 *s1[i2] = '\0'; 545 546 sortstrs((Byte **) man->entries_less_paths, man->nentries, 547 (Byte **) man->entries); 548 549 /* put back suffixes */ 550 for (i2 = 0; i2 < man->nentries; i2++) 551 if (s1[i2] != NULL) 552 *s1[i2] = '.'; 553 554 free(s1); 555 556#ifdef DEBUG 557 printf("removing from section %d.\n", i); 558#endif /* DEBUG */ 559 560 { 561 register int j, k, nent, nentm1; 562 int j2; 563 564 nent = man->nentries; 565 nentm1 = nent - 1; 566 j = 0; 567 l2 = man->entries_less_paths[j++]; 568 if (l2 == NULL) 569 PrintError 570 ("Internal error while removing duplicate manual pages."); 571 while (j < nentm1) { 572 l1 = l2; 573 l2 = man->entries_less_paths[j++]; 574 if (l2 == NULL) 575 PrintError 576 ("Internal error while removing duplicate manual pages."); 577 if (streq(l1, l2)) { 578 j2 = j - 1; 579 k = j2; 580 while (j < nent) { 581 man->entries_less_paths[k] = man->entries_less_paths[j]; 582 man->entries[k++] = man->entries[j++]; 583 } 584 j = j2; 585 --man->nentries; 586 --nent; 587 --nentm1; 588 } 589 } 590 } 591 } 592} 593 594 /* 595 ******* Replacement for qsort to keep 596 ******* identical entries in order 597 598 A somewhat ugly hack of something that was once simpler... 599 */ 600 /* 601 Sort an array of pointers to strings, keeping it 602 in ascending order by (1) string comparison and 603 (2) original entry order in the pointer array. 604 605 This is a modified radix exchange algorithm. 606 607 In case there's insufficient memory for a temporary copy 608 of the pointer array, the original order of identical strings 609 isn't preserved. 610 */ 611 612static void 613sortstrs(Byte * data[], int size, Byte * otherdata[]) 614{ 615 Byte **sp, **ep; 616 Byte **othersp, **otherep; 617 int *origorder; 618 619 origorder = (int *) calloc(size, sizeof(int)); 620 if (origorder) { 621 reg int i; 622 623 for (i = 0; i < size; ++i) 624 origorder[i] = i; 625 } 626 627 sp = data; 628 ep = &data[size - 1]; 629 othersp = otherdata; 630 otherep = &otherdata[size - 1]; 631 if (origorder) { 632 sortstrs_block_oo(sp, ep, 0, 0x80, origorder, &origorder[size - 1], 633 othersp, otherep); 634 free(origorder); 635 } 636 else 637 sortstrs_block(sp, ep, 0, 0x80, othersp, otherep); 638} 639 640 641 642 /*---------------------------------*/ 643 /* Sort 1 block of data on 1 bit */ 644 /*---------------------------------*/ 645 646static void 647sortstrs_block(Byte ** start, Byte ** end, int offset, Byte mask, 648 Byte ** otherstart, Byte ** otherend) 649{ 650 reg Byte **sp, **ep; 651 reg Byte m; 652 reg int off; 653 reg Byte *t; 654 reg int curstrlen; 655 int maxstrlen; 656 Byte **othersp, **otherep; 657 658 659#define newstring(ptr) \ 660 { \ 661 t = *ptr; \ 662 curstrlen = 0; \ 663 while ( *t++ ) ++ curstrlen; \ 664 if ( curstrlen > maxstrlen ) maxstrlen = curstrlen; \ 665 t = *ptr; \ 666 } 667 668 669 maxstrlen = 0; 670 sp = start; 671 ep = end; 672 off = offset; 673 m = mask; 674 othersp = otherstart; 675 otherep = otherend; 676 677 while (1) { 678 newstring(sp) 679 while (((sp != ep) && ((curstrlen < off) || ((t[off] & m) == 0)))) { 680 ++sp; 681 ++othersp; 682 newstring(sp) 683 } 684 if (sp == ep) 685 break; 686 687 newstring(ep); 688 while (((sp != ep) && (curstrlen >= off) && ((t[off] & m) != 0))) { 689 --ep; 690 --otherep; 691 newstring(ep) 692 } 693 if (sp == ep) 694 break; 695 696 t = *sp; 697 *sp = *ep; 698 *ep = t; 699 700 t = *othersp; 701 *othersp = *otherep; 702 *otherep = t; 703 } 704 705 t = *sp; 706 if ((curstrlen < off) || ((t[off] & m) == 0)) { 707 if (ep != end) { 708 ++ep; 709 ++otherep; 710 } 711 } 712 else { 713 if (sp != start) { 714 --sp; 715 --othersp; 716 } 717 } 718 719 m >>= 1; 720 if (m == 0) { 721 m = 0x80; 722 if (++off >= maxstrlen) 723 return; 724 } 725 726 if (sp != start) 727 sortstrs_block(start, sp, off, m, otherstart, othersp); 728 if (ep != end) 729 sortstrs_block(ep, end, off, m, otherep, otherend); 730} 731 732 733 734 /*-----------------------------------------------------------------*/ 735 /* Sort 1 block of data on 1 bit; check for out-of-order entries */ 736 /*-----------------------------------------------------------------*/ 737 738static void 739sortstrs_block_oo(Byte ** start, Byte ** end, int offset, Byte mask, 740 int *ostart, int *oend, Byte ** otherstart, Byte ** otherend) 741{ 742 reg Byte **sp, **ep; 743 reg int *osp, *oep; 744 reg Byte m; 745 reg int off; 746 reg Byte *t; 747 reg int u; 748 reg int curstrlen; 749 int maxstrlen; 750 Byte **othersp, **otherep; 751 752 753#define newstring(ptr) \ 754 { \ 755 t = *ptr; \ 756 curstrlen = 0; \ 757 while ( *t++ ) ++ curstrlen; \ 758 if ( curstrlen > maxstrlen ) maxstrlen = curstrlen; \ 759 t = *ptr; \ 760 } 761 762 763 maxstrlen = 0; 764 sp = start; 765 ep = end; 766 osp = ostart; 767 oep = oend; 768 off = offset; 769 m = mask; 770 othersp = otherstart; 771 otherep = otherend; 772 773 while (1) { 774 newstring(sp) 775 while (((sp != ep) && ((curstrlen < off) || ((t[off] & m) == 0)))) { 776 ++sp; 777 ++osp; 778 ++othersp; 779 newstring(sp) 780 } 781 if (sp == ep) 782 break; 783 784 newstring(ep); 785 while (((sp != ep) && (curstrlen >= off) && ((t[off] & m) != 0))) { 786 --ep; 787 --oep; 788 --otherep; 789 newstring(ep) 790 } 791 if (sp == ep) 792 break; 793 794 t = *sp; 795 *sp = *ep; 796 *ep = t; 797 798 t = *othersp; 799 *othersp = *otherep; 800 *otherep = t; 801 802 u = *osp; 803 *osp = *oep; 804 *oep = u; 805 } 806 807 t = *sp; 808 if ((curstrlen < off) || ((t[off] & m) == 0)) { 809 if (ep != end) { 810 ++ep; 811 ++oep; 812 ++otherep; 813 } 814 } 815 else { 816 if (sp != start) { 817 --sp; 818 --osp; 819 --othersp; 820 } 821 } 822 823 m >>= 1; 824 if (m == 0) { 825 m = 0x80; 826 if (++off >= maxstrlen) { /* Finished sorting block of strings: *//* Restore duplicates to original order */ 827 reg Byte **cp; 828 reg int *ocp; 829 Byte **othercp; 830 831 if (sp != start) { 832 cp = start; 833 ocp = ostart; 834 othercp = otherstart; 835 while (cp != sp) { 836 if (*ocp > *(ocp + 1)) { 837 t = *(cp + 1); 838 *(cp + 1) = *cp; 839 *cp = t; 840 841 t = *(othercp + 1); 842 *(othercp + 1) = *othercp; 843 *othercp = t; 844 845 u = *(ocp + 1); 846 *(ocp + 1) = *ocp; 847 *ocp = u; 848 849 if (cp != start) { 850 --cp; 851 --ocp; 852 --othercp; 853 continue; 854 } 855 } 856 ++cp; 857 ++ocp; 858 ++othercp; 859 } 860 } 861 if (ep != end) { 862 cp = ep; 863 ocp = oep; 864 othercp = otherep; 865 while (cp != end) { 866 if (*ocp > *(ocp + 1)) { 867 t = *(cp + 1); 868 *(cp + 1) = *cp; 869 *cp = t; 870 871 t = *(othercp + 1); 872 *(othercp + 1) = *othercp; 873 *othercp = t; 874 875 u = *(ocp + 1); 876 *(ocp + 1) = *ocp; 877 *ocp = u; 878 879 if (cp != ep) { 880 --cp; 881 --ocp; 882 --othercp; 883 continue; 884 } 885 } 886 ++cp; 887 ++ocp; 888 ++othercp; 889 } 890 } 891 return; 892 } 893 } 894 895 if (sp != start) 896 sortstrs_block_oo(start, sp, off, m, ostart, osp, otherstart, othersp); 897 if (ep != end) 898 sortstrs_block_oo(ep, end, off, m, oep, oend, otherep, otherend); 899} 900 901 902/* Function Name: InitManual 903 * Description: Initializes this manual section. 904 * Arguments: l_manual - local copy of the manual structure. 905 * label - the button label for this section. 906 * Returns: none. 907 */ 908 909static void 910InitManual(Manual * l_manual, char *label) 911{ 912 bzero(l_manual, sizeof(Manual)); /* clear it. */ 913 l_manual->blabel = label; /* set label. */ 914} 915 916#if defined(DEBUG) 917 918/* Function Name: DumpManual 919 * Description: Debugging function that dumps the entire manual page 920 * structure. 921 * Arguments: number - the number of sections. 922 * Returns: none. 923 */ 924 925void 926DumpManual(int number) 927{ 928 register int i, j; 929 930 for (i = 0; i < number; i++) { 931 printf("label: %s\n", manual[i].blabel); 932 for (j = 0; j < manual[i].nentries; j++) 933 printf("%s\n", manual[i].entries[j]); 934 } 935} 936 937#endif /* DEBUG */ 938 939 940 941#ifdef MANCONF 942 943#if defined(MANCONFIGSTYLE_FreeBSD) 944 945/* Function Name: ReadManConfig 946 * Description: Reads man.conf file used by FreeBSD man 947 * Argument: manpath - char array to return path in. 948 * Returns: TRUE if read was successful. 949 */ 950 951Bool 952ReadManConfig(char manpath[]) 953{ 954 FILE *fp; 955 char line[BUFSIZ]; 956 char *path; 957 Bool firstpath = TRUE; 958 959 if (!(fp = fopen(MANCONF, "r"))) 960 return (FALSE); 961 962 while (fgets(line, sizeof(line), fp)) { 963 path = strtok(line, " \t\n"); 964 if (!path || *path == '#') 965 continue; 966 if (strcmp(path, "MANPATH_MAP") == 0) 967 path = strtok((char *) NULL, " \t\n"); 968 else if (strcmp(path, "MANDATORY_MANPATH") != 0 && 969 strcmp(path, "OPTIONAL_MANPATH") != 0) 970 return (FALSE); 971 path = strtok((char *) NULL, " \t\n"); 972 if (!path || *path == '#') 973 return FALSE; 974 if (firstpath) { 975 strcpy(manpath, path); 976 firstpath = FALSE; 977 } 978 else if (!strstr(manpath, path)) { 979 strcat(manpath, ":"); 980 strcat(manpath, path); 981 } 982 } 983 fclose(fp); 984 return (!firstpath); 985} 986 987 988#elif defined(MANCONFIGSTYLE_Linux) /* not FreeBSD */ 989 990/* Function Name: ReadManConfig 991 * Description: Reads man.conf file used by Linux man 992 * Argument: manpath - char array to return path in. 993 * Returns: TRUE if read was successful. 994 */ 995 996Bool 997ReadManConfig(char manpath[]) 998{ 999 FILE *fp; 1000 char line[BUFSIZ]; 1001 char *path; 1002 Bool firstpath = TRUE; 1003 1004 if (!(fp = fopen(MANCONF, "r"))) 1005 return (FALSE); 1006 1007 while (fgets(line, sizeof(line), fp)) { 1008 path = strtok(line, " \t\n"); 1009 if (!path || *path == '#' || (strcmp(path, "MANPATH") != 0)) 1010 continue; 1011 path = strtok((char *) NULL, " \t\n"); 1012 if (!path || *path == '#') 1013 return FALSE; 1014 if (firstpath) { 1015 strcpy(manpath, path); 1016 firstpath = FALSE; 1017 } 1018 else { 1019 strcat(manpath, ":"); 1020 strcat(manpath, path); 1021 } 1022 } 1023 fclose(fp); 1024 return (!firstpath); 1025} 1026 1027#elif defined(MANCONFIGSTYLE_OpenBSD) /* not FreeBSD or Linux */ 1028 1029/* Function Name: ReadManConfig 1030 * Description: Reads man.conf file used by Open/NetBSD 1031 * Argument: manpath - char array to return path in. 1032 * Returns: TRUE if read was successful. 1033 * 1034 * This version expands the glob pattern that can be found 1035 * in man.conf 1036 */ 1037#include <glob.h> 1038 1039Bool 1040ReadManConfig(char manpath[]) 1041{ 1042 FILE *fp; 1043 char line[BUFSIZ]; 1044 char *path; 1045 Bool firstpath = TRUE; 1046 glob_t gs; 1047 int i; 1048 1049 if (!(fp = fopen(MANCONF, "r"))) 1050 return (FALSE); 1051 1052 while (fgets(line, sizeof(line), fp)) { 1053 path = strtok(line, " \t\n"); 1054 if (!path || *path == '#') 1055 continue; 1056 if (strcmp(path, "_default")) { 1057 /* for now */ 1058 continue; 1059 } 1060 memset(&gs, 0, sizeof(glob_t)); 1061 while ((path = strtok((char *) NULL, " \t\n"))) { 1062 if (glob(path, GLOB_BRACE, NULL, &gs) < 0) { 1063 fclose(fp); 1064 return FALSE; 1065 } 1066 } /* while */ 1067 for (i = 0; i < gs.gl_pathc; i++) { 1068 1069 if (firstpath) { 1070 strcpy(manpath, gs.gl_pathv[i]); 1071 firstpath = FALSE; 1072 } 1073 else { 1074 strcat(manpath, ":"); 1075 strcat(manpath, gs.gl_pathv[i]); 1076 } 1077 } /* for */ 1078 globfree(&gs); 1079 } 1080 fclose(fp); 1081 return (!firstpath); 1082} 1083 1084#elif defined(MANCONFIGSTYLE_BSD) /* not FreeBSD, Linux, or OpenBSD */ 1085 1086/* Function Name: ReadManConfig 1087 * Description: Reads man.conf file used by BSD 4.4 1088 * Argument: manpath - char array to return path in. 1089 * Returns: TRUE if read was successful. 1090 */ 1091 1092Bool 1093ReadManConfig(char manpath[]) 1094{ 1095 FILE *fp; 1096 char line[BUFSIZ]; 1097 char *path; 1098 Bool firstpath = TRUE; 1099 1100 if (!(fp = fopen(MANCONF, "r"))) 1101 return (FALSE); 1102 1103 while (fgets(line, sizeof(line), fp)) { 1104 path = strtok(line, " \t\n"); 1105 if (!path || *path == '#' || strcmp(path, "_default")) 1106 continue; 1107 while ((path = strtok((char *) NULL, " \t\n"))) { 1108 if (firstpath) { 1109 strcpy(manpath, path); 1110 firstpath = FALSE; 1111 } 1112 else { 1113 strcat(manpath, ":"); 1114 strcat(manpath, path); 1115 } 1116 } 1117 } 1118 fclose(fp); 1119 return (!firstpath); 1120} 1121 1122#else /* not BSD */ 1123 1124#error "MANCONF defined (in vendor.h) for unknown operating system." 1125 1126#endif /* MANCONFIGSTYLE == FreeBSD ... BSD */ 1127 1128#endif /* MANCONF */ 1129