man.c revision 8b6d6341
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 = NULL, 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 != NULL) {
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 != NULL) {
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 = NULL ; (local->next != NULL) && (local->flags)
243	   ; old = local, local = local->next);
244
245      if (old != NULL) {
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#ifdef BZIP2_EXTENSION
451    else if (streq(ptr + 1, BZIP2_EXTENSION))
452      *ptr = '\0';
453#endif
454#ifdef LZMA_EXTENSION
455    else if (streq(ptr + 1, LZMA_EXTENSION))
456      *ptr = '\0';
457#endif
458  }
459
460  nentries = local_manual->nentries;
461  nalloc = local_manual->nalloc;
462
463  while( (dp = readdir(dir)) != NULL ) {
464    char * name = dp->d_name;
465    if (name[0] == '.')
466      continue;
467#ifndef CRAY
468    if (index(name, '.') == NULL)
469      continue;
470#endif
471    if( nentries >= nalloc ) {
472      nalloc += ENTRYALLOC;
473      local_manual->entries =(char **) XtRealloc((char *)local_manual->entries,
474						 nalloc * sizeof(char *));
475      local_manual->entries_less_paths =
476	(char **) XtRealloc((char *)local_manual->entries_less_paths,
477			    nalloc * sizeof(char *));
478    }
479
480    snprintf(full_name, sizeof(full_name), "%s/%s", path, name);
481/*
482 * Remove the compression extension from the entry name.
483 */
484
485    if ( (ptr = rindex(full_name, '.')) != NULL) {
486#if !defined(__SCO__) && !defined(ISC)
487      if (streq(ptr + 1, COMPRESSION_EXTENSION))
488#else
489      if (strpbrk(ptr + 1, COMPRESSION_EXTENSIONS) != NULL)
490#endif
491	*ptr = '\0';
492#ifdef GZIP_EXTENSION
493      else if (streq(ptr + 1, GZIP_EXTENSION))
494	*ptr = '\0';
495#endif
496#ifdef BZIP2_EXTENSION
497      else if (streq(ptr + 1, BZIP2_EXTENSION))
498	*ptr = '\0';
499#endif
500#ifdef LZMA_EXTENSION
501      else if (streq(ptr + 1, LZMA_EXTENSION))
502	*ptr = '\0';
503#endif
504#ifdef IGNORE_EXTENSION
505      /* skip files with specified extension - they're not real man pages */
506      else if (streq(ptr + 1, IGNORE_EXTENSION)) {
507	continue;
508      }
509#endif /* IGNORE_EXTENSION */
510    }
511    local_manual->entries[nentries] = StrAlloc(full_name);
512    local_manual->entries_less_paths[nentries] =
513      rindex(local_manual->entries[nentries], '/');
514    if ( local_manual->entries_less_paths[nentries] == NULL )
515      PrintError("Internal error while cataloging manual pages.");
516    ++ nentries;
517  }
518
519  local_manual->nentries = nentries;
520  local_manual->nalloc = nalloc;
521
522  closedir(dir);
523}
524
525/*	Function Name: SortAndRemove
526 *	Description: This function sorts all the entry names and
527 *                   then removes all the duplicate entries.
528 *	Arguments: man - a pointer to the manual structure.
529 *                 number - the number of manual sections.
530 *	Returns: an improved manual stucure
531 */
532
533static void
534SortAndRemove(Manual *man, int number)
535{
536  int i;
537  char *l1, *l2, **s1;
538
539  for ( i = 0; i < number; man++, i++) { /* sort each section */
540    register int i2 = 0;
541
542#ifdef DEBUG
543    printf("sorting section %d - %s\n", i, man->blabel);
544#endif /* DEBUG */
545
546    s1 = (char **)malloc(man->nentries * sizeof(char *));
547
548    /* temporarily remove suffixes of entries, preventing them from */
549    /* being used in alpabetic comparison ie sccs-delta.1 vs sccs.1 */
550    for (i2=0; i2<man->nentries; i2++)
551      if ((s1[i2] = rindex(man->entries_less_paths[i2], '.')) != NULL)
552	*s1[i2] = '\0';
553
554    sortstrs ( (Byte **)man->entries_less_paths, man->nentries, (Byte **)man->entries );
555
556    /* put back suffixes */
557    for (i2=0; i2<man->nentries; i2++)
558      if (s1[i2] != NULL) *s1[i2] = '.';
559
560    free(s1);
561
562#ifdef DEBUG
563    printf("removing from section %d.\n", i);
564#endif /* DEBUG */
565
566    {
567      register int   j, k, nent, nentm1;
568      int     j2;
569      nent   = man -> nentries;
570      nentm1 = nent - 1;
571      j = 0;
572      l2 = man->entries_less_paths[j++];
573      if ( l2 == NULL )
574        PrintError("Internal error while removing duplicate manual pages.");
575      while ( j < nentm1 )
576	{
577	  l1 = l2;
578	  l2 = man->entries_less_paths[j++];
579	  if ( l2 == NULL )
580	    PrintError("Internal error while removing duplicate manual pages."
581		       );
582	  if ( streq(l1,l2) )
583	    {
584	      j2 = j-1;
585	      k  = j2;
586	      while ( j < nent )
587                {
588		  man -> entries_less_paths[k] = man -> entries_less_paths[j];
589                man -> entries[k++] = man -> entries[j++];
590                }
591	      j = j2;
592	      -- man -> nentries;
593	      -- nent;
594	      -- nentm1;
595	    }
596	}
597    }
598  }
599}
600
601 /*
602       *******  Replacement for qsort to keep
603       *******  identical entries in order
604
605       A somewhat ugly hack of something that was once simpler...
606 */
607 /*
608       Sort an array of pointers to strings, keeping it
609       in ascending order by (1) string comparison and
610       (2) original entry order in the pointer array.
611
612       This is a modified radix exchange algorithm.
613
614       In case there's insufficient memory for a temporary copy
615       of the pointer array, the original order of identical strings
616       isn't preserved.
617 */
618
619static void
620sortstrs (Byte *data[], int size, Byte *otherdata[])
621{
622       Byte   **sp, **ep;
623       Byte   **othersp, **otherep;
624       int     *origorder;
625
626 origorder = (int *) calloc (size, sizeof(int));
627 if ( origorder )
628    {
629    reg int     i;
630
631    for ( i=0; i < size; ++i )
632       origorder[i] = i;
633    }
634
635 sp = data;
636 ep = &data[size-1];
637 othersp = otherdata;
638 otherep = &otherdata[size-1];
639 if ( origorder )
640    {
641    sortstrs_block_oo ( sp, ep, 0, 0x80, origorder, &origorder[size-1],
642       othersp, otherep );
643    free (origorder);
644    }
645 else
646    sortstrs_block ( sp, ep, 0, 0x80, othersp, otherep );
647}
648
649
650
651 /*---------------------------------*/
652 /*  Sort 1 block of data on 1 bit  */
653 /*---------------------------------*/
654
655static void
656sortstrs_block (
657       Byte   **start,
658       Byte   **end,
659       int      offset,
660       Byte     mask,
661       Byte   **otherstart,
662       Byte   **otherend)
663
664{
665 reg   Byte   **sp, **ep;
666 reg   Byte     m;
667 reg   int      off;
668 reg   Byte    *t;
669 reg   int      curstrlen;
670       int      maxstrlen;
671       Byte   **othersp, **otherep;
672
673
674#define       newstring(ptr) \
675 { \
676 t = *ptr; \
677 curstrlen = 0; \
678 while ( *t++ ) ++ curstrlen; \
679 if ( curstrlen > maxstrlen ) maxstrlen = curstrlen; \
680 t = *ptr; \
681 }
682
683
684 maxstrlen = 0;
685 sp  = start;
686 ep  = end;
687 off = offset;
688 m   = mask;
689 othersp = otherstart;
690 otherep = otherend;
691
692 while (1)
693     {
694     newstring(sp)
695     while (((sp != ep) && ((curstrlen < off) || ((t[off] & m) == 0))))
696       {
697       ++ sp;
698       ++ othersp;
699       newstring(sp)
700       }
701     if ( sp == ep )
702       break;
703
704     newstring(ep);
705     while (((sp != ep) && (curstrlen >= off) && ((t[off] & m) != 0)))
706       {
707       -- ep;
708       -- otherep;
709       newstring(ep)
710       }
711     if ( sp == ep )
712       break;
713
714     t = *sp;
715     *sp = *ep;
716     *ep = t;
717
718     t      = *othersp;
719     *othersp = *otherep;
720     *otherep = t;
721     }
722
723 t = *sp;
724 if ((curstrlen < off) || ((t[off] & m) == 0))
725    {
726    if ( ep != end )
727       {
728       ++ ep;
729       ++ otherep;
730       }
731    }
732 else
733    {
734    if ( sp != start )
735       {
736       -- sp;
737       -- othersp;
738       }
739    }
740
741 m >>= 1;
742 if ( m == 0 )
743    {
744    m = 0x80;
745    if ( ++off >= maxstrlen )
746       return;
747    }
748
749
750 if ( sp != start )
751    sortstrs_block ( start, sp, off, m, otherstart, othersp );
752 if ( ep != end )
753    sortstrs_block ( ep, end, off, m, otherep, otherend );
754}
755
756
757
758 /*-----------------------------------------------------------------*/
759 /*  Sort 1 block of data on 1 bit; check for out-of-order entries  */
760 /*-----------------------------------------------------------------*/
761
762static void
763 sortstrs_block_oo (
764       Byte   **start,
765       Byte   **end,
766       int      offset,
767       Byte     mask,
768       int     *ostart,
769       int     *oend,
770       Byte   **otherstart,
771       Byte   **otherend)
772
773{
774 reg   Byte   **sp, **ep;
775 reg   int     *osp, *oep;
776 reg   Byte     m;
777 reg   int      off;
778 reg   Byte    *t;
779 reg   int      u;
780 reg   int      curstrlen;
781       int      maxstrlen;
782       Byte   **othersp, **otherep;
783
784
785#define       newstring(ptr) \
786 { \
787 t = *ptr; \
788 curstrlen = 0; \
789 while ( *t++ ) ++ curstrlen; \
790 if ( curstrlen > maxstrlen ) maxstrlen = curstrlen; \
791 t = *ptr; \
792 }
793
794
795 maxstrlen = 0;
796 sp  = start;
797 ep  = end;
798 osp = ostart;
799 oep = oend;
800 off = offset;
801 m   = mask;
802 othersp = otherstart;
803 otherep = otherend;
804
805 while (1)
806     {
807     newstring(sp)
808     while (((sp != ep) && ((curstrlen < off) || ((t[off] & m) == 0))))
809       {
810       ++ sp;
811       ++ osp;
812       ++ othersp;
813       newstring(sp)
814       }
815     if ( sp == ep )
816       break;
817
818     newstring(ep);
819     while (((sp != ep) && (curstrlen >= off) && ((t[off] & m) != 0)))
820       {
821       -- ep;
822       -- oep;
823       -- otherep;
824       newstring(ep)
825       }
826     if ( sp == ep )
827       break;
828
829     t   = *sp;
830     *sp = *ep;
831     *ep = t;
832
833     t      = *othersp;
834     *othersp = *otherep;
835     *otherep = t;
836
837     u    = *osp;
838     *osp = *oep;
839     *oep = u;
840     }
841
842 t = *sp;
843 if ((curstrlen < off) || ((t[off] & m) == 0))
844    {
845    if ( ep != end )
846       {
847       ++ ep;
848       ++ oep;
849       ++ otherep;
850       }
851    }
852 else
853    {
854    if ( sp != start )
855       {
856       -- sp;
857       -- osp;
858       -- othersp;
859       }
860    }
861
862 m >>= 1;
863 if ( m == 0 )
864    {
865    m = 0x80;
866    if ( ++off >= maxstrlen )  /*  Finished sorting block of strings:    */
867       {                               /*  Restore duplicates to
868riginal order  */
869       reg Byte **cp;
870       reg int *ocp;
871         Byte **othercp;
872
873
874       if ( sp != start )
875        {
876        cp  = start;
877        ocp = ostart;
878        othercp = otherstart;
879        while ( cp != sp )
880           {
881           if ( *ocp > *(ocp+1) )
882               {
883               t       = *(cp+1);
884               *(cp+1) = *cp;
885               *cp     = t;
886
887               t               = *(othercp+1);
888               *(othercp+1)    = *othercp;
889               *othercp        = t;
890
891               u        = *(ocp+1);
892               *(ocp+1) = *ocp;
893               *ocp     = u;
894
895               if ( cp != start )
896                  {
897                  -- cp;
898                  -- ocp;
899                  -- othercp;
900                  continue;
901                  }
902               }
903           ++ cp;
904           ++ ocp;
905           ++ othercp;
906           }
907        }
908       if ( ep != end )
909        {
910        cp  = ep;
911        ocp = oep;
912        othercp = otherep;
913        while ( cp != end )
914           {
915           if ( *ocp > *(ocp+1) )
916               {
917               t       = *(cp+1);
918               *(cp+1) = *cp;
919               *cp     = t;
920
921               t               = *(othercp+1);
922               *(othercp+1)    = *othercp;
923               *othercp        = t;
924
925               u        = *(ocp+1);
926               *(ocp+1) = *ocp;
927               *ocp     = u;
928
929               if ( cp != ep )
930                  {
931                  -- cp;
932                  -- ocp;
933                  -- othercp;
934                  continue;
935                  }
936               }
937           ++ cp;
938           ++ ocp;
939           ++ othercp;
940           }
941        }
942       return;
943       }
944    }
945
946
947 if ( sp != start )
948    sortstrs_block_oo ( start, sp, off, m, ostart, osp, otherstart, othersp );
949 if ( ep != end )
950    sortstrs_block_oo ( ep, end, off, m, oep, oend, otherep, otherend );
951}
952
953
954/*	Function Name: InitManual
955 *	Description: Initializes this manual section.
956 *	Arguments: l_manual - local copy of the manual structure.
957 *                 label - the button label for this section.
958 *	Returns: none.
959 */
960
961static void
962InitManual(Manual * l_manual, char * label)
963{
964  bzero( l_manual, sizeof(Manual) );	        /* clear it. */
965  l_manual->blabel = label;	                /* set label. */
966}
967
968#if defined(DEBUG)
969
970/*	Function Name: DumpManual
971 *	Description: Debugging function that dumps the entire manual page
972 *                   structure.
973 *	Arguments: number - the number of sections.
974 *	Returns: none.
975 */
976
977void
978DumpManual(int number)
979{
980  register int i,j;
981
982  for ( i = 0; i < number; i++) {
983    printf("label: %s\n", manual[i].blabel);
984    for (j = 0; j < manual[i].nentries; j++)
985      printf("%s\n", manual[i].entries[j]);
986  }
987}
988
989#endif /* DEBUG */
990
991
992
993#ifdef MANCONF
994
995#if defined(MANCONFIGSTYLE_FreeBSD)
996
997/*    Function Name: ReadManConfig
998 *    Description: Reads man.conf file used by FreeBSD man
999 *      Argument: manpath - char array to return path in.
1000 *    Returns: TRUE if read was successful.
1001 */
1002
1003Bool
1004ReadManConfig(char manpath[])
1005{
1006  FILE        *fp;
1007  char        line[BUFSIZ];
1008  char        *path;
1009  Bool  firstpath = TRUE;
1010
1011  if (!(fp = fopen(MANCONF, "r")))
1012    return(FALSE);
1013
1014  while (fgets(line, sizeof(line), fp)) {
1015    path = strtok(line, " \t\n");
1016    if (!path || *path == '#')
1017      continue;
1018    if (strcmp(path, "MANPATH_MAP") == 0)
1019      path = strtok((char *)NULL, " \t\n");
1020    else if (strcmp(path, "MANDATORY_MANPATH") != 0 &&
1021	     strcmp(path, "OPTIONAL_MANPATH") != 0)
1022      return(FALSE);
1023    path = strtok((char *)NULL, " \t\n");
1024    if (!path || *path == '#')
1025      return FALSE;
1026    if (firstpath) {
1027      strcpy(manpath, path);
1028      firstpath = FALSE;
1029    }
1030    else if (!strstr(manpath,path)) {
1031      strcat(manpath, ":");
1032      strcat(manpath, path);
1033    }
1034  }
1035  fclose(fp);
1036  return(!firstpath);
1037}
1038
1039
1040#elif defined(MANCONFIGSTYLE_Linux) /* not FreeBSD */
1041
1042/*    Function Name: ReadManConfig
1043 *    Description: Reads man.conf file used by Linux man
1044 *      Argument: manpath - char array to return path in.
1045 *    Returns: TRUE if read was successful.
1046 */
1047
1048
1049Bool
1050ReadManConfig(char manpath[])
1051{
1052  FILE        *fp;
1053  char        line[BUFSIZ];
1054  char        *path;
1055  Bool  firstpath = TRUE;
1056
1057  if (!(fp = fopen(MANCONF, "r")))
1058    return(FALSE);
1059
1060  while (fgets(line, sizeof(line), fp)) {
1061    path = strtok(line, " \t\n");
1062    if (!path || *path == '#' || (strcmp(path, "MANPATH") != 0))
1063      continue;
1064    path = strtok((char *)NULL, " \t\n");
1065    if (!path || *path == '#')
1066      return FALSE;
1067    if (firstpath) {
1068      strcpy(manpath, path);
1069      firstpath = FALSE;
1070    }
1071    else {
1072      strcat(manpath, ":");
1073      strcat(manpath, path);
1074    }
1075  }
1076  fclose(fp);
1077  return(!firstpath);
1078}
1079
1080#elif defined(MANCONFIGSTYLE_OpenBSD) /* not FreeBSD or Linux */
1081
1082/*    Function Name: ReadManConfig
1083 *    Description: Reads man.conf file used by Open/NetBSD
1084 *      Argument: manpath - char array to return path in.
1085 *    Returns: TRUE if read was successful.
1086 *
1087 *     This version expands the glob pattern that can be found
1088 *     in man.conf
1089 */
1090#include <glob.h>
1091
1092Bool
1093ReadManConfig(char manpath[])
1094{
1095    FILE        *fp;
1096    char        line[BUFSIZ];
1097    char        *path;
1098    Bool        firstpath = TRUE;
1099    glob_t      gs;
1100    int         i;
1101
1102    if (!(fp = fopen(MANCONF, "r")))
1103	return(FALSE);
1104
1105    while (fgets(line, sizeof(line), fp)) {
1106	path = strtok(line, " \t\n");
1107	if (!path || *path == '#')
1108	    continue;
1109	if (strcmp(path, "_default")) {
1110	    /* for now */
1111	    continue;
1112	}
1113	memset(&gs, 0, sizeof(glob_t));
1114	while ((path = strtok((char *)NULL, " \t\n"))) {
1115	    if (glob(path, GLOB_BRACE, NULL, &gs) < 0) {
1116		fclose(fp);
1117		return FALSE;
1118	    }
1119	} /* while */
1120	for (i = 0; i < gs.gl_pathc; i++) {
1121
1122	    if (firstpath) {
1123		strcpy(manpath, gs.gl_pathv[i]);
1124		firstpath = FALSE;
1125	    }
1126	    else {
1127		strcat(manpath, ":");
1128		strcat(manpath, gs.gl_pathv[i]);
1129	    }
1130	} /* for */
1131	globfree(&gs);
1132    }
1133    fclose(fp);
1134    return(!firstpath);
1135}
1136
1137#elif defined(MANCONFIGSTYLE_BSD) /* not FreeBSD, Linux, or OpenBSD */
1138
1139/*    Function Name: ReadManConfig
1140 *    Description: Reads man.conf file used by BSD 4.4
1141 *      Argument: manpath - char array to return path in.
1142 *    Returns: TRUE if read was successful.
1143 */
1144
1145Bool
1146ReadManConfig(manpath)
1147
1148char  manpath[];
1149
1150{
1151  FILE        *fp;
1152  char        line[BUFSIZ];
1153  char        *path;
1154  Bool  firstpath = TRUE;
1155
1156  if (!(fp = fopen(MANCONF, "r")))
1157    return(FALSE);
1158
1159  while (fgets(line, sizeof(line), fp)) {
1160    path = strtok(line, " \t\n");
1161    if (!path || *path == '#' || strcmp(path, "_default"))
1162      continue;
1163    while ((path = strtok((char *)NULL, " \t\n"))) {
1164      if (firstpath) {
1165        strcpy(manpath, path);
1166        firstpath = FALSE;
1167      }
1168      else {
1169        strcat(manpath, ":");
1170        strcat(manpath, path);
1171      }
1172    }
1173  }
1174  fclose(fp);
1175  return(!firstpath);
1176}
1177
1178#else /* not BSD */
1179
1180#error "MANCONF defined (in vendor.h) for unknown operating system."
1181
1182#endif /* MANCONFIGSTYLE == FreeBSD ... BSD */
1183
1184#endif /* MANCONF */
1185