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