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