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