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