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