195c635efSGarrett D'Amore /*
295c635efSGarrett D'Amore * Copyright (c) 2002 John Rochester
395c635efSGarrett D'Amore * All rights reserved.
495c635efSGarrett D'Amore *
595c635efSGarrett D'Amore * Redistribution and use in source and binary forms, with or without
695c635efSGarrett D'Amore * modification, are permitted provided that the following conditions
795c635efSGarrett D'Amore * are met:
895c635efSGarrett D'Amore * 1. Redistributions of source code must retain the above copyright
995c635efSGarrett D'Amore * notice, this list of conditions and the following disclaimer,
1095c635efSGarrett D'Amore * in this position and unchanged.
1195c635efSGarrett D'Amore * 2. Redistributions in binary form must reproduce the above copyright
1295c635efSGarrett D'Amore * notice, this list of conditions and the following disclaimer in the
1395c635efSGarrett D'Amore * documentation and/or other materials provided with the distribution.
1495c635efSGarrett D'Amore * 3. The name of the author may not be used to endorse or promote products
1595c635efSGarrett D'Amore * derived from this software without specific prior written permission
1695c635efSGarrett D'Amore *
1795c635efSGarrett D'Amore * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1895c635efSGarrett D'Amore * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1995c635efSGarrett D'Amore * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2095c635efSGarrett D'Amore * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2195c635efSGarrett D'Amore * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2295c635efSGarrett D'Amore * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2395c635efSGarrett D'Amore * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2495c635efSGarrett D'Amore * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2595c635efSGarrett D'Amore * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2695c635efSGarrett D'Amore * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2795c635efSGarrett D'Amore */
2895c635efSGarrett D'Amore
2995c635efSGarrett D'Amore /*
3095c635efSGarrett D'Amore * Copyright 2012 Nexenta Systems, Inc. All rights reserved.
3195c635efSGarrett D'Amore * Copyright 2014 Garrett D'Amore <garrett@damore.org>
32*186c806eSRobert Mustacchi * Copyright 2022 Oxide Computer Company
3395c635efSGarrett D'Amore */
3495c635efSGarrett D'Amore
3595c635efSGarrett D'Amore #include <sys/types.h>
3695c635efSGarrett D'Amore #include <sys/stat.h>
3795c635efSGarrett D'Amore #include <sys/param.h>
3895c635efSGarrett D'Amore
3995c635efSGarrett D'Amore #include <ctype.h>
4095c635efSGarrett D'Amore #include <dirent.h>
4195c635efSGarrett D'Amore #include <err.h>
4295c635efSGarrett D'Amore #include <signal.h>
4395c635efSGarrett D'Amore #include <stddef.h>
4495c635efSGarrett D'Amore #include <stdio.h>
4595c635efSGarrett D'Amore #include <stdlib.h>
4695c635efSGarrett D'Amore #include <string.h>
4795c635efSGarrett D'Amore #include <unistd.h>
4895c635efSGarrett D'Amore
4995c635efSGarrett D'Amore #include "man.h"
5095c635efSGarrett D'Amore #include "stringlist.h"
5195c635efSGarrett D'Amore
5295c635efSGarrett D'Amore
5395c635efSGarrett D'Amore /* Information collected about each man page in a section */
5495c635efSGarrett D'Amore struct page_info {
5595c635efSGarrett D'Amore char *filename;
5695c635efSGarrett D'Amore char *name;
5795c635efSGarrett D'Amore char *suffix;
5895c635efSGarrett D'Amore ino_t inode;
5995c635efSGarrett D'Amore };
6095c635efSGarrett D'Amore
6195c635efSGarrett D'Amore /* An expanding string */
6295c635efSGarrett D'Amore struct sbuf {
6395c635efSGarrett D'Amore char *content; /* the start of the buffer */
6495c635efSGarrett D'Amore char *end; /* just past the end of the content */
6595c635efSGarrett D'Amore char *last; /* the last allocated character */
6695c635efSGarrett D'Amore };
6795c635efSGarrett D'Amore
6895c635efSGarrett D'Amore /* Remove the last amount characters from the sbuf */
6995c635efSGarrett D'Amore #define sbuf_retract(sbuf, amount) ((sbuf)->end -= (amount))
7095c635efSGarrett D'Amore /* Return the length of the sbuf content */
7195c635efSGarrett D'Amore #define sbuf_length(sbuf) ((sbuf)->end - (sbuf)->content)
7295c635efSGarrett D'Amore
7395c635efSGarrett D'Amore typedef char *edited_copy(char *from, char *to, int length);
7495c635efSGarrett D'Amore
7595c635efSGarrett D'Amore /*
7695c635efSGarrett D'Amore * While the whatis line is being formed, it is stored in whatis_proto.
7795c635efSGarrett D'Amore * When finished, it is reformatted into whatis_final and then appended
7895c635efSGarrett D'Amore * to whatis_lines.
7995c635efSGarrett D'Amore */
8095c635efSGarrett D'Amore static struct sbuf *whatis_proto;
8195c635efSGarrett D'Amore static struct sbuf *whatis_final;
8295c635efSGarrett D'Amore static stringlist *whatis_lines; /* collected output lines */
8395c635efSGarrett D'Amore
8495c635efSGarrett D'Amore static char tempfile[MAXPATHLEN]; /* path of temporary file, if any */
8595c635efSGarrett D'Amore
8695c635efSGarrett D'Amore #define MDOC_COMMANDS "ArDvErEvFlLiNmPa"
8795c635efSGarrett D'Amore
8895c635efSGarrett D'Amore
8995c635efSGarrett D'Amore /* Free a struct page_info and its content */
9095c635efSGarrett D'Amore static void
free_page_info(struct page_info * info)9195c635efSGarrett D'Amore free_page_info(struct page_info *info)
9295c635efSGarrett D'Amore {
9395c635efSGarrett D'Amore
9495c635efSGarrett D'Amore free(info->filename);
9595c635efSGarrett D'Amore free(info->name);
9695c635efSGarrett D'Amore free(info->suffix);
9795c635efSGarrett D'Amore free(info);
9895c635efSGarrett D'Amore }
9995c635efSGarrett D'Amore
10095c635efSGarrett D'Amore /*
10195c635efSGarrett D'Amore * Allocate and fill in a new struct page_info given the
10295c635efSGarrett D'Amore * name of the man section directory and the dirent of the file.
10395c635efSGarrett D'Amore * If the file is not a man page, return NULL.
10495c635efSGarrett D'Amore */
10595c635efSGarrett D'Amore static struct page_info *
new_page_info(char * dir,struct dirent * dirent)10695c635efSGarrett D'Amore new_page_info(char *dir, struct dirent *dirent)
10795c635efSGarrett D'Amore {
10895c635efSGarrett D'Amore struct page_info *info;
10995c635efSGarrett D'Amore int basename_length;
11095c635efSGarrett D'Amore char *suffix;
11195c635efSGarrett D'Amore struct stat st;
11295c635efSGarrett D'Amore
11395c635efSGarrett D'Amore if ((info = malloc(sizeof (struct page_info))) == NULL)
11495c635efSGarrett D'Amore err(1, "malloc");
11595c635efSGarrett D'Amore basename_length = strlen(dirent->d_name);
11695c635efSGarrett D'Amore suffix = &dirent->d_name[basename_length];
11795c635efSGarrett D'Amore if (asprintf(&info->filename, "%s/%s", dir, dirent->d_name) == -1)
11895c635efSGarrett D'Amore err(1, "asprintf");
11995c635efSGarrett D'Amore for (;;) {
12095c635efSGarrett D'Amore if (--suffix == dirent->d_name || !isalnum(*suffix)) {
12195c635efSGarrett D'Amore if (*suffix == '.')
12295c635efSGarrett D'Amore break;
12395c635efSGarrett D'Amore free(info->filename);
12495c635efSGarrett D'Amore free(info);
12595c635efSGarrett D'Amore return (NULL);
12695c635efSGarrett D'Amore }
12795c635efSGarrett D'Amore }
12895c635efSGarrett D'Amore *suffix++ = '\0';
12995c635efSGarrett D'Amore info->name = strdup(dirent->d_name);
13095c635efSGarrett D'Amore info->suffix = strdup(suffix);
13195c635efSGarrett D'Amore if (stat(info->filename, &st) < 0) {
13295c635efSGarrett D'Amore warn("%s", info->filename);
13395c635efSGarrett D'Amore free_page_info(info);
13495c635efSGarrett D'Amore return (NULL);
13595c635efSGarrett D'Amore }
13695c635efSGarrett D'Amore if (!S_ISREG(st.st_mode)) {
13795c635efSGarrett D'Amore free_page_info(info);
13895c635efSGarrett D'Amore return (NULL);
13995c635efSGarrett D'Amore }
14095c635efSGarrett D'Amore info->inode = st.st_ino;
14195c635efSGarrett D'Amore return (info);
14295c635efSGarrett D'Amore }
14395c635efSGarrett D'Amore
14495c635efSGarrett D'Amore /*
14595c635efSGarrett D'Amore * Reset sbuf length to 0.
14695c635efSGarrett D'Amore */
14795c635efSGarrett D'Amore static void
sbuf_clear(struct sbuf * sbuf)14895c635efSGarrett D'Amore sbuf_clear(struct sbuf *sbuf)
14995c635efSGarrett D'Amore {
15095c635efSGarrett D'Amore
15195c635efSGarrett D'Amore sbuf->end = sbuf->content;
15295c635efSGarrett D'Amore }
15395c635efSGarrett D'Amore
15495c635efSGarrett D'Amore /*
15595c635efSGarrett D'Amore * Allocate a new sbuf.
15695c635efSGarrett D'Amore */
15795c635efSGarrett D'Amore static struct sbuf *
new_sbuf(void)15895c635efSGarrett D'Amore new_sbuf(void)
15995c635efSGarrett D'Amore {
16095c635efSGarrett D'Amore struct sbuf *sbuf;
16195c635efSGarrett D'Amore
16295c635efSGarrett D'Amore if ((sbuf = malloc(sizeof (struct sbuf))) == NULL)
16395c635efSGarrett D'Amore err(1, "malloc");
16495c635efSGarrett D'Amore if ((sbuf->content = (char *)malloc(LINE_ALLOC)) == NULL)
16595c635efSGarrett D'Amore err(1, "malloc");
16695c635efSGarrett D'Amore sbuf->last = sbuf->content + LINE_ALLOC - 1;
16795c635efSGarrett D'Amore sbuf_clear(sbuf);
16895c635efSGarrett D'Amore
16995c635efSGarrett D'Amore return (sbuf);
17095c635efSGarrett D'Amore }
17195c635efSGarrett D'Amore
17295c635efSGarrett D'Amore /*
17395c635efSGarrett D'Amore * Ensure that there is enough room in the sbuf
17495c635efSGarrett D'Amore * for nchars more characters.
17595c635efSGarrett D'Amore */
17695c635efSGarrett D'Amore static void
sbuf_need(struct sbuf * sbuf,int nchars)17795c635efSGarrett D'Amore sbuf_need(struct sbuf *sbuf, int nchars)
17895c635efSGarrett D'Amore {
17995c635efSGarrett D'Amore char *new_content;
18095c635efSGarrett D'Amore size_t size, cntsize;
18195c635efSGarrett D'Amore size_t grow = 128;
18295c635efSGarrett D'Amore
18395c635efSGarrett D'Amore while (grow < nchars) {
18495c635efSGarrett D'Amore grow += 128; /* we grow in chunks of 128 bytes */
18595c635efSGarrett D'Amore }
18695c635efSGarrett D'Amore
18795c635efSGarrett D'Amore /* Grow if the buffer isn't big enough */
18895c635efSGarrett D'Amore if (sbuf->end + nchars > sbuf->last) {
18995c635efSGarrett D'Amore size = sbuf->last + 1 - sbuf->content;
19095c635efSGarrett D'Amore size += grow;
19195c635efSGarrett D'Amore cntsize = sbuf->end - sbuf->content;
19295c635efSGarrett D'Amore
19395c635efSGarrett D'Amore if ((new_content = realloc(sbuf->content, size)) == NULL) {
19495c635efSGarrett D'Amore perror("realloc");
19595c635efSGarrett D'Amore if (tempfile[0] != '\0')
19695c635efSGarrett D'Amore (void) unlink(tempfile);
19795c635efSGarrett D'Amore exit(1);
19895c635efSGarrett D'Amore }
19995c635efSGarrett D'Amore sbuf->content = new_content;
20095c635efSGarrett D'Amore sbuf->end = new_content + cntsize;
20195c635efSGarrett D'Amore sbuf->last = new_content + size - 1;
20295c635efSGarrett D'Amore }
20395c635efSGarrett D'Amore }
20495c635efSGarrett D'Amore
20595c635efSGarrett D'Amore /*
20695c635efSGarrett D'Amore * Append a string of a given length to the sbuf.
20795c635efSGarrett D'Amore */
20895c635efSGarrett D'Amore static void
sbuf_append(struct sbuf * sbuf,const char * text,int length)20995c635efSGarrett D'Amore sbuf_append(struct sbuf *sbuf, const char *text, int length)
21095c635efSGarrett D'Amore {
21195c635efSGarrett D'Amore if (length > 0) {
21295c635efSGarrett D'Amore sbuf_need(sbuf, length);
21395c635efSGarrett D'Amore (void) memcpy(sbuf->end, text, length);
21495c635efSGarrett D'Amore sbuf->end += length;
21595c635efSGarrett D'Amore }
21695c635efSGarrett D'Amore }
21795c635efSGarrett D'Amore
21895c635efSGarrett D'Amore /*
21995c635efSGarrett D'Amore * Append a null-terminated string to the sbuf.
22095c635efSGarrett D'Amore */
22195c635efSGarrett D'Amore static void
sbuf_append_str(struct sbuf * sbuf,char * text)22295c635efSGarrett D'Amore sbuf_append_str(struct sbuf *sbuf, char *text)
22395c635efSGarrett D'Amore {
22495c635efSGarrett D'Amore
22595c635efSGarrett D'Amore sbuf_append(sbuf, text, strlen(text));
22695c635efSGarrett D'Amore }
22795c635efSGarrett D'Amore
22895c635efSGarrett D'Amore /*
22995c635efSGarrett D'Amore * Append an edited null-terminated string to the sbuf.
23095c635efSGarrett D'Amore */
23195c635efSGarrett D'Amore static void
sbuf_append_edited(struct sbuf * sbuf,char * text,edited_copy copy)23295c635efSGarrett D'Amore sbuf_append_edited(struct sbuf *sbuf, char *text, edited_copy copy)
23395c635efSGarrett D'Amore {
23495c635efSGarrett D'Amore int length;
23595c635efSGarrett D'Amore
23695c635efSGarrett D'Amore if ((length = strlen(text)) > 0) {
23795c635efSGarrett D'Amore sbuf_need(sbuf, length);
23895c635efSGarrett D'Amore sbuf->end = copy(text, sbuf->end, length);
23995c635efSGarrett D'Amore }
24095c635efSGarrett D'Amore }
24195c635efSGarrett D'Amore
24295c635efSGarrett D'Amore /*
24395c635efSGarrett D'Amore * Strip any of a set of chars from the end of the sbuf.
24495c635efSGarrett D'Amore */
24595c635efSGarrett D'Amore static void
sbuf_strip(struct sbuf * sbuf,const char * set)24695c635efSGarrett D'Amore sbuf_strip(struct sbuf *sbuf, const char *set)
24795c635efSGarrett D'Amore {
24895c635efSGarrett D'Amore
24995c635efSGarrett D'Amore while (sbuf->end > sbuf->content && strchr(set, sbuf->end[-1]) != NULL)
25095c635efSGarrett D'Amore sbuf->end--;
25195c635efSGarrett D'Amore }
25295c635efSGarrett D'Amore
25395c635efSGarrett D'Amore /*
25495c635efSGarrett D'Amore * Return the null-terminated string built by the sbuf.
25595c635efSGarrett D'Amore */
25695c635efSGarrett D'Amore static char *
sbuf_content(struct sbuf * sbuf)25795c635efSGarrett D'Amore sbuf_content(struct sbuf *sbuf)
25895c635efSGarrett D'Amore {
25995c635efSGarrett D'Amore
26095c635efSGarrett D'Amore *sbuf->end = '\0';
26195c635efSGarrett D'Amore return (sbuf->content);
26295c635efSGarrett D'Amore }
26395c635efSGarrett D'Amore
26495c635efSGarrett D'Amore /*
26595c635efSGarrett D'Amore * Return true if no man page exists in the directory with
26695c635efSGarrett D'Amore * any of the names in the stringlist.
26795c635efSGarrett D'Amore */
26895c635efSGarrett D'Amore static int
no_page_exists(char * dir,stringlist * names,char * suffix)26995c635efSGarrett D'Amore no_page_exists(char *dir, stringlist *names, char *suffix)
27095c635efSGarrett D'Amore {
27195c635efSGarrett D'Amore char path[MAXPATHLEN];
27295c635efSGarrett D'Amore char *suffixes[] = { "", ".gz", ".bz2", NULL };
27395c635efSGarrett D'Amore size_t i;
27495c635efSGarrett D'Amore int j;
27595c635efSGarrett D'Amore
27695c635efSGarrett D'Amore for (i = 0; i < names->sl_cur; i++) {
27795c635efSGarrett D'Amore for (j = 0; suffixes[j] != NULL; j++) {
27895c635efSGarrett D'Amore (void) snprintf(path, MAXPATHLEN, "%s/%s.%s%s",
27995c635efSGarrett D'Amore dir, names->sl_str[i], suffix, suffixes[j]);
28095c635efSGarrett D'Amore if (access(path, F_OK) == 0) {
28195c635efSGarrett D'Amore return (0);
28295c635efSGarrett D'Amore }
28395c635efSGarrett D'Amore }
28495c635efSGarrett D'Amore }
28595c635efSGarrett D'Amore return (1);
28695c635efSGarrett D'Amore }
28795c635efSGarrett D'Amore
28895c635efSGarrett D'Amore /* ARGSUSED sig */
28995c635efSGarrett D'Amore static void
trap_signal(int sig)29095c635efSGarrett D'Amore trap_signal(int sig)
29195c635efSGarrett D'Amore {
29295c635efSGarrett D'Amore
29395c635efSGarrett D'Amore if (tempfile[0] != '\0')
29495c635efSGarrett D'Amore (void) unlink(tempfile);
29595c635efSGarrett D'Amore
29695c635efSGarrett D'Amore exit(1);
29795c635efSGarrett D'Amore }
29895c635efSGarrett D'Amore
29995c635efSGarrett D'Amore /*
30095c635efSGarrett D'Amore * Attempt to open an output file.
30195c635efSGarrett D'Amore * Return NULL if unsuccessful.
30295c635efSGarrett D'Amore */
30395c635efSGarrett D'Amore static FILE *
open_output(char * name)30495c635efSGarrett D'Amore open_output(char *name)
30595c635efSGarrett D'Amore {
30695c635efSGarrett D'Amore FILE *output;
30795c635efSGarrett D'Amore
30895c635efSGarrett D'Amore whatis_lines = sl_init();
30995c635efSGarrett D'Amore (void) snprintf(tempfile, MAXPATHLEN, "%s.tmp", name);
31095c635efSGarrett D'Amore name = tempfile;
31195c635efSGarrett D'Amore if ((output = fopen(name, "w")) == NULL) {
31295c635efSGarrett D'Amore warn("%s", name);
31395c635efSGarrett D'Amore return (NULL);
31495c635efSGarrett D'Amore }
31595c635efSGarrett D'Amore return (output);
31695c635efSGarrett D'Amore }
31795c635efSGarrett D'Amore
31895c635efSGarrett D'Amore static int
linesort(const void * a,const void * b)31995c635efSGarrett D'Amore linesort(const void *a, const void *b)
32095c635efSGarrett D'Amore {
32195c635efSGarrett D'Amore
32295c635efSGarrett D'Amore return (strcmp((*(const char * const *)a), (*(const char * const *)b)));
32395c635efSGarrett D'Amore }
32495c635efSGarrett D'Amore
32595c635efSGarrett D'Amore /*
32695c635efSGarrett D'Amore * Write the unique sorted lines to the output file.
32795c635efSGarrett D'Amore */
32895c635efSGarrett D'Amore static void
finish_output(FILE * output,char * name)32995c635efSGarrett D'Amore finish_output(FILE *output, char *name)
33095c635efSGarrett D'Amore {
33195c635efSGarrett D'Amore size_t i;
33295c635efSGarrett D'Amore char *prev = NULL;
33395c635efSGarrett D'Amore
33495c635efSGarrett D'Amore qsort(whatis_lines->sl_str, whatis_lines->sl_cur, sizeof (char *),
33595c635efSGarrett D'Amore linesort);
33695c635efSGarrett D'Amore for (i = 0; i < whatis_lines->sl_cur; i++) {
33795c635efSGarrett D'Amore char *line = whatis_lines->sl_str[i];
33895c635efSGarrett D'Amore if (i > 0 && strcmp(line, prev) == 0)
33995c635efSGarrett D'Amore continue;
34095c635efSGarrett D'Amore prev = line;
34195c635efSGarrett D'Amore (void) fputs(line, output);
34295c635efSGarrett D'Amore (void) putc('\n', output);
34395c635efSGarrett D'Amore }
34495c635efSGarrett D'Amore (void) fclose(output);
34595c635efSGarrett D'Amore sl_free(whatis_lines, 1);
34695c635efSGarrett D'Amore (void) rename(tempfile, name);
34795c635efSGarrett D'Amore (void) unlink(tempfile);
34895c635efSGarrett D'Amore }
34995c635efSGarrett D'Amore
35095c635efSGarrett D'Amore static FILE *
open_whatis(char * mandir)35195c635efSGarrett D'Amore open_whatis(char *mandir)
35295c635efSGarrett D'Amore {
35395c635efSGarrett D'Amore char filename[MAXPATHLEN];
35495c635efSGarrett D'Amore
35595c635efSGarrett D'Amore (void) snprintf(filename, MAXPATHLEN, "%s/%s", mandir, WHATIS);
35695c635efSGarrett D'Amore return (open_output(filename));
35795c635efSGarrett D'Amore }
35895c635efSGarrett D'Amore
35995c635efSGarrett D'Amore static void
finish_whatis(FILE * output,char * mandir)36095c635efSGarrett D'Amore finish_whatis(FILE *output, char *mandir)
36195c635efSGarrett D'Amore {
36295c635efSGarrett D'Amore char filename[MAXPATHLEN];
36395c635efSGarrett D'Amore
36495c635efSGarrett D'Amore (void) snprintf(filename, MAXPATHLEN, "%s/%s", mandir, WHATIS);
36595c635efSGarrett D'Amore finish_output(output, filename);
36695c635efSGarrett D'Amore }
36795c635efSGarrett D'Amore
36895c635efSGarrett D'Amore /*
36995c635efSGarrett D'Amore * Remove trailing spaces from a string, returning a pointer to just
37095c635efSGarrett D'Amore * beyond the new last character.
37195c635efSGarrett D'Amore */
37295c635efSGarrett D'Amore static char *
trim_rhs(char * str)37395c635efSGarrett D'Amore trim_rhs(char *str)
37495c635efSGarrett D'Amore {
37595c635efSGarrett D'Amore char *rhs;
37695c635efSGarrett D'Amore
37795c635efSGarrett D'Amore rhs = &str[strlen(str)];
37895c635efSGarrett D'Amore while (--rhs > str && isspace(*rhs))
37995c635efSGarrett D'Amore ;
38095c635efSGarrett D'Amore *++rhs = '\0';
38195c635efSGarrett D'Amore return (rhs);
38295c635efSGarrett D'Amore }
38395c635efSGarrett D'Amore
38495c635efSGarrett D'Amore /*
38595c635efSGarrett D'Amore * Return a pointer to the next non-space character in the string.
38695c635efSGarrett D'Amore */
38795c635efSGarrett D'Amore static char *
skip_spaces(char * s)38895c635efSGarrett D'Amore skip_spaces(char *s)
38995c635efSGarrett D'Amore {
39095c635efSGarrett D'Amore
39195c635efSGarrett D'Amore while (*s != '\0' && isspace(*s))
39295c635efSGarrett D'Amore s++;
39395c635efSGarrett D'Amore
39495c635efSGarrett D'Amore return (s);
39595c635efSGarrett D'Amore }
39695c635efSGarrett D'Amore
39795c635efSGarrett D'Amore /*
39895c635efSGarrett D'Amore * Return whether the line is of one of the forms:
39995c635efSGarrett D'Amore * .Sh NAME
40095c635efSGarrett D'Amore * .Sh "NAME"
40195c635efSGarrett D'Amore * etc.
40295c635efSGarrett D'Amore * assuming that section_start is ".Sh".
40395c635efSGarrett D'Amore */
40495c635efSGarrett D'Amore static int
name_section_line(char * line,const char * section_start)40595c635efSGarrett D'Amore name_section_line(char *line, const char *section_start)
40695c635efSGarrett D'Amore {
40795c635efSGarrett D'Amore char *rhs;
40895c635efSGarrett D'Amore
40995c635efSGarrett D'Amore if (strncmp(line, section_start, 3) != 0)
41095c635efSGarrett D'Amore return (0);
41195c635efSGarrett D'Amore line = skip_spaces(line + 3);
41295c635efSGarrett D'Amore rhs = trim_rhs(line);
41395c635efSGarrett D'Amore if (*line == '"') {
41495c635efSGarrett D'Amore line++;
41595c635efSGarrett D'Amore if (*--rhs == '"')
41695c635efSGarrett D'Amore *rhs = '\0';
41795c635efSGarrett D'Amore }
41895c635efSGarrett D'Amore if (strcmp(line, "NAME") == 0)
41995c635efSGarrett D'Amore return (1);
42095c635efSGarrett D'Amore
42195c635efSGarrett D'Amore return (0);
42295c635efSGarrett D'Amore }
42395c635efSGarrett D'Amore
42495c635efSGarrett D'Amore /*
42595c635efSGarrett D'Amore * Copy characters while removing the most common nroff/troff markup:
42695c635efSGarrett D'Amore * \(em, \(mi, \s[+-N], \&
42795c635efSGarrett D'Amore * \fF, \f(fo, \f[font]
42895c635efSGarrett D'Amore * \*s, \*(st, \*[stringvar]
42995c635efSGarrett D'Amore */
43095c635efSGarrett D'Amore static char *
de_nroff_copy(char * from,char * to,int fromlen)43195c635efSGarrett D'Amore de_nroff_copy(char *from, char *to, int fromlen)
43295c635efSGarrett D'Amore {
43395c635efSGarrett D'Amore char *from_end = &from[fromlen];
43495c635efSGarrett D'Amore
43595c635efSGarrett D'Amore while (from < from_end) {
43695c635efSGarrett D'Amore switch (*from) {
43795c635efSGarrett D'Amore case '\\':
43895c635efSGarrett D'Amore switch (*++from) {
43995c635efSGarrett D'Amore case '(':
44095c635efSGarrett D'Amore if (strncmp(&from[1], "em", 2) == 0 ||
44195c635efSGarrett D'Amore strncmp(&from[1], "mi", 2) == 0) {
44295c635efSGarrett D'Amore from += 3;
44395c635efSGarrett D'Amore continue;
44495c635efSGarrett D'Amore }
44595c635efSGarrett D'Amore break;
44695c635efSGarrett D'Amore case 's':
44795c635efSGarrett D'Amore if (*++from == '-')
44895c635efSGarrett D'Amore from++;
44995c635efSGarrett D'Amore while (isdigit(*from))
45095c635efSGarrett D'Amore from++;
45195c635efSGarrett D'Amore continue;
45295c635efSGarrett D'Amore case 'f':
45395c635efSGarrett D'Amore case '*':
45495c635efSGarrett D'Amore if (*++from == '(') {
45595c635efSGarrett D'Amore from += 3;
45695c635efSGarrett D'Amore } else if (*from == '[') {
45795c635efSGarrett D'Amore while (*++from != ']' &&
45895c635efSGarrett D'Amore from < from_end)
45995c635efSGarrett D'Amore ;
46095c635efSGarrett D'Amore from++;
46195c635efSGarrett D'Amore } else {
46295c635efSGarrett D'Amore from++;
46395c635efSGarrett D'Amore }
46495c635efSGarrett D'Amore continue;
46595c635efSGarrett D'Amore case '&':
46695c635efSGarrett D'Amore from++;
46795c635efSGarrett D'Amore continue;
46895c635efSGarrett D'Amore }
46995c635efSGarrett D'Amore break;
47095c635efSGarrett D'Amore }
47195c635efSGarrett D'Amore *to++ = *from++;
47295c635efSGarrett D'Amore }
47395c635efSGarrett D'Amore return (to);
47495c635efSGarrett D'Amore }
47595c635efSGarrett D'Amore
47695c635efSGarrett D'Amore /*
47795c635efSGarrett D'Amore * Append a string with the nroff formatting removed.
47895c635efSGarrett D'Amore */
47995c635efSGarrett D'Amore static void
add_nroff(char * text)48095c635efSGarrett D'Amore add_nroff(char *text)
48195c635efSGarrett D'Amore {
48295c635efSGarrett D'Amore
48395c635efSGarrett D'Amore sbuf_append_edited(whatis_proto, text, de_nroff_copy);
48495c635efSGarrett D'Amore }
48595c635efSGarrett D'Amore
48695c635efSGarrett D'Amore /*
48795c635efSGarrett D'Amore * Appends "name(suffix), " to whatis_final
48895c635efSGarrett D'Amore */
48995c635efSGarrett D'Amore static void
add_whatis_name(char * name,char * suffix)49095c635efSGarrett D'Amore add_whatis_name(char *name, char *suffix)
49195c635efSGarrett D'Amore {
49295c635efSGarrett D'Amore
49395c635efSGarrett D'Amore if (*name != '\0') {
49495c635efSGarrett D'Amore sbuf_append_str(whatis_final, name);
49595c635efSGarrett D'Amore sbuf_append(whatis_final, "(", 1);
49695c635efSGarrett D'Amore sbuf_append_str(whatis_final, suffix);
49795c635efSGarrett D'Amore sbuf_append(whatis_final, "), ", 3);
49895c635efSGarrett D'Amore }
49995c635efSGarrett D'Amore }
50095c635efSGarrett D'Amore
50195c635efSGarrett D'Amore /*
50295c635efSGarrett D'Amore * Processes an old-style man(7) line. This ignores commands with only
50395c635efSGarrett D'Amore * a single number argument.
50495c635efSGarrett D'Amore */
50595c635efSGarrett D'Amore static void
process_man_line(char * line)50695c635efSGarrett D'Amore process_man_line(char *line)
50795c635efSGarrett D'Amore {
50895c635efSGarrett D'Amore char *p;
50995c635efSGarrett D'Amore
51095c635efSGarrett D'Amore if (*line == '.') {
51195c635efSGarrett D'Amore while (isalpha(*++line))
51295c635efSGarrett D'Amore ;
51395c635efSGarrett D'Amore p = line = skip_spaces(line);
51495c635efSGarrett D'Amore while (*p != '\0') {
51595c635efSGarrett D'Amore if (!isdigit(*p))
51695c635efSGarrett D'Amore break;
51795c635efSGarrett D'Amore p++;
51895c635efSGarrett D'Amore }
51995c635efSGarrett D'Amore if (*p == '\0')
52095c635efSGarrett D'Amore return;
52195c635efSGarrett D'Amore } else
52295c635efSGarrett D'Amore line = skip_spaces(line);
52395c635efSGarrett D'Amore if (*line != '\0') {
52495c635efSGarrett D'Amore add_nroff(line);
52595c635efSGarrett D'Amore sbuf_append(whatis_proto, " ", 1);
52695c635efSGarrett D'Amore }
52795c635efSGarrett D'Amore }
52895c635efSGarrett D'Amore
52995c635efSGarrett D'Amore /*
53095c635efSGarrett D'Amore * Processes a new-style mdoc(7) line.
53195c635efSGarrett D'Amore */
53295c635efSGarrett D'Amore static void
process_mdoc_line(char * line)53395c635efSGarrett D'Amore process_mdoc_line(char *line)
53495c635efSGarrett D'Amore {
53595c635efSGarrett D'Amore int xref;
53695c635efSGarrett D'Amore int arg = 0;
53795c635efSGarrett D'Amore char *line_end = &line[strlen(line)];
53895c635efSGarrett D'Amore int orig_length = sbuf_length(whatis_proto);
53995c635efSGarrett D'Amore char *next;
54095c635efSGarrett D'Amore
54195c635efSGarrett D'Amore if (*line == '\0')
54295c635efSGarrett D'Amore return;
54395c635efSGarrett D'Amore if (line[0] != '.' || !isupper(line[1]) || !islower(line[2])) {
54495c635efSGarrett D'Amore add_nroff(skip_spaces(line));
54595c635efSGarrett D'Amore sbuf_append(whatis_proto, " ", 1);
54695c635efSGarrett D'Amore return;
54795c635efSGarrett D'Amore }
54895c635efSGarrett D'Amore xref = strncmp(line, ".Xr", 3) == 0;
54995c635efSGarrett D'Amore line += 3;
55095c635efSGarrett D'Amore while ((line = skip_spaces(line)) < line_end) {
55195c635efSGarrett D'Amore if (*line == '"') {
55295c635efSGarrett D'Amore next = ++line;
55395c635efSGarrett D'Amore for (;;) {
55495c635efSGarrett D'Amore next = strchr(next, '"');
55595c635efSGarrett D'Amore if (next == NULL)
55695c635efSGarrett D'Amore break;
55795c635efSGarrett D'Amore (void) memmove(next, next + 1, strlen(next));
55895c635efSGarrett D'Amore line_end--;
55995c635efSGarrett D'Amore if (*next != '"')
56095c635efSGarrett D'Amore break;
56195c635efSGarrett D'Amore next++;
56295c635efSGarrett D'Amore }
56395c635efSGarrett D'Amore } else {
56495c635efSGarrett D'Amore next = strpbrk(line, " \t");
56595c635efSGarrett D'Amore }
56695c635efSGarrett D'Amore if (next != NULL)
56795c635efSGarrett D'Amore *next++ = '\0';
56895c635efSGarrett D'Amore else
56995c635efSGarrett D'Amore next = line_end;
57095c635efSGarrett D'Amore if (isupper(*line) && islower(line[1]) && line[2] == '\0') {
57195c635efSGarrett D'Amore if (strcmp(line, "Ns") == 0) {
57295c635efSGarrett D'Amore arg = 0;
57395c635efSGarrett D'Amore line = next;
57495c635efSGarrett D'Amore continue;
57595c635efSGarrett D'Amore }
57695c635efSGarrett D'Amore if (strstr(line, MDOC_COMMANDS) != NULL) {
57795c635efSGarrett D'Amore line = next;
57895c635efSGarrett D'Amore continue;
57995c635efSGarrett D'Amore }
58095c635efSGarrett D'Amore }
58195c635efSGarrett D'Amore if (arg > 0 && strchr(",.:;?!)]", *line) == 0) {
58295c635efSGarrett D'Amore if (xref) {
58395c635efSGarrett D'Amore sbuf_append(whatis_proto, "(", 1);
58495c635efSGarrett D'Amore add_nroff(line);
58595c635efSGarrett D'Amore sbuf_append(whatis_proto, ")", 1);
58695c635efSGarrett D'Amore xref = 0;
58795c635efSGarrett D'Amore } else {
58895c635efSGarrett D'Amore sbuf_append(whatis_proto, " ", 1);
58995c635efSGarrett D'Amore }
59095c635efSGarrett D'Amore }
59195c635efSGarrett D'Amore add_nroff(line);
59295c635efSGarrett D'Amore arg++;
59395c635efSGarrett D'Amore line = next;
59495c635efSGarrett D'Amore }
59595c635efSGarrett D'Amore if (sbuf_length(whatis_proto) > orig_length)
59695c635efSGarrett D'Amore sbuf_append(whatis_proto, " ", 1);
59795c635efSGarrett D'Amore }
59895c635efSGarrett D'Amore
59995c635efSGarrett D'Amore /*
60095c635efSGarrett D'Amore * Collect a list of comma-separated names from the text.
60195c635efSGarrett D'Amore */
60295c635efSGarrett D'Amore static void
collect_names(stringlist * names,char * text)60395c635efSGarrett D'Amore collect_names(stringlist *names, char *text)
60495c635efSGarrett D'Amore {
60595c635efSGarrett D'Amore char *arg;
60695c635efSGarrett D'Amore
60795c635efSGarrett D'Amore for (;;) {
60895c635efSGarrett D'Amore arg = text;
60995c635efSGarrett D'Amore text = strchr(text, ',');
61095c635efSGarrett D'Amore if (text != NULL)
61195c635efSGarrett D'Amore *text++ = '\0';
61295c635efSGarrett D'Amore (void) sl_add(names, arg);
61395c635efSGarrett D'Amore if (text == NULL)
61495c635efSGarrett D'Amore return;
61595c635efSGarrett D'Amore if (*text == ' ')
61695c635efSGarrett D'Amore text++;
61795c635efSGarrett D'Amore }
61895c635efSGarrett D'Amore }
61995c635efSGarrett D'Amore
62095c635efSGarrett D'Amore enum { STATE_UNKNOWN, STATE_MANSTYLE, STATE_MDOCNAME, STATE_MDOCDESC };
62195c635efSGarrett D'Amore
62295c635efSGarrett D'Amore /*
62395c635efSGarrett D'Amore * Process a man page source into a single whatis line and add it
62495c635efSGarrett D'Amore * to whatis_lines.
62595c635efSGarrett D'Amore */
62695c635efSGarrett D'Amore static void
process_page(struct page_info * page,char * section_dir)62795c635efSGarrett D'Amore process_page(struct page_info *page, char *section_dir)
62895c635efSGarrett D'Amore {
62995c635efSGarrett D'Amore FILE *fp;
63095c635efSGarrett D'Amore stringlist *names;
63195c635efSGarrett D'Amore char *descr;
63295c635efSGarrett D'Amore int state = STATE_UNKNOWN;
63395c635efSGarrett D'Amore size_t i;
63495c635efSGarrett D'Amore char *line = NULL;
63595c635efSGarrett D'Amore size_t linecap = 0;
63695c635efSGarrett D'Amore
63795c635efSGarrett D'Amore sbuf_clear(whatis_proto);
63895c635efSGarrett D'Amore if ((fp = fopen(page->filename, "r")) == NULL) {
63995c635efSGarrett D'Amore warn("%s", page->filename);
64095c635efSGarrett D'Amore return;
64195c635efSGarrett D'Amore }
64295c635efSGarrett D'Amore while (getline(&line, &linecap, fp) > 0) {
64395c635efSGarrett D'Amore /* Skip comments */
64495c635efSGarrett D'Amore if (strncmp(line, ".\\\"", 3) == 0)
64595c635efSGarrett D'Amore continue;
64695c635efSGarrett D'Amore switch (state) {
64795c635efSGarrett D'Amore /* Haven't reached the NAME section yet */
64895c635efSGarrett D'Amore case STATE_UNKNOWN:
64995c635efSGarrett D'Amore if (name_section_line(line, ".SH"))
65095c635efSGarrett D'Amore state = STATE_MANSTYLE;
65195c635efSGarrett D'Amore else if (name_section_line(line, ".Sh"))
65295c635efSGarrett D'Amore state = STATE_MDOCNAME;
65395c635efSGarrett D'Amore continue;
65495c635efSGarrett D'Amore /* Inside an old-style .SH NAME section */
655*186c806eSRobert Mustacchi case STATE_MANSTYLE: {
656*186c806eSRobert Mustacchi char *altline;
657*186c806eSRobert Mustacchi
65895c635efSGarrett D'Amore if (strncmp(line, ".SH", 3) == 0 ||
65995c635efSGarrett D'Amore strncmp(line, ".SS", 3) == 0)
66095c635efSGarrett D'Amore break;
66195c635efSGarrett D'Amore (void) trim_rhs(line);
66295c635efSGarrett D'Amore if (strcmp(line, ".") == 0)
66395c635efSGarrett D'Amore continue;
664*186c806eSRobert Mustacchi altline = line;
665*186c806eSRobert Mustacchi if (strncmp(altline, ".IX", 3) == 0) {
666*186c806eSRobert Mustacchi altline += 3;
667*186c806eSRobert Mustacchi altline = skip_spaces(altline);
66895c635efSGarrett D'Amore }
669*186c806eSRobert Mustacchi process_man_line(altline);
67095c635efSGarrett D'Amore continue;
671*186c806eSRobert Mustacchi }
67295c635efSGarrett D'Amore /* Inside a new-style .Sh NAME section (the .Nm part) */
67395c635efSGarrett D'Amore case STATE_MDOCNAME:
67495c635efSGarrett D'Amore (void) trim_rhs(line);
67595c635efSGarrett D'Amore if (strncmp(line, ".Nm", 3) == 0) {
67695c635efSGarrett D'Amore process_mdoc_line(line);
67795c635efSGarrett D'Amore continue;
67895c635efSGarrett D'Amore } else {
67995c635efSGarrett D'Amore if (strcmp(line, ".") == 0)
68095c635efSGarrett D'Amore continue;
68195c635efSGarrett D'Amore sbuf_append(whatis_proto, "- ", 2);
68295c635efSGarrett D'Amore state = STATE_MDOCDESC;
68395c635efSGarrett D'Amore }
68495c635efSGarrett D'Amore /* FALLTHROUGH */
68595c635efSGarrett D'Amore /* Inside a new-style .Sh NAME section (after the .Nm-s) */
68695c635efSGarrett D'Amore case STATE_MDOCDESC:
68795c635efSGarrett D'Amore if (strncmp(line, ".Sh", 3) == 0)
68895c635efSGarrett D'Amore break;
68995c635efSGarrett D'Amore (void) trim_rhs(line);
69095c635efSGarrett D'Amore if (strcmp(line, ".") == 0)
69195c635efSGarrett D'Amore continue;
69295c635efSGarrett D'Amore process_mdoc_line(line);
69395c635efSGarrett D'Amore continue;
69495c635efSGarrett D'Amore }
69595c635efSGarrett D'Amore break;
69695c635efSGarrett D'Amore }
69795c635efSGarrett D'Amore (void) fclose(fp);
69895c635efSGarrett D'Amore sbuf_strip(whatis_proto, " \t.-");
69995c635efSGarrett D'Amore line = sbuf_content(whatis_proto);
70095c635efSGarrett D'Amore /*
70195c635efSGarrett D'Amore * Line now contains the appropriate data, but without the
70295c635efSGarrett D'Amore * proper indentation or the section appended to each name.
70395c635efSGarrett D'Amore */
70495c635efSGarrett D'Amore descr = strstr(line, " - ");
70595c635efSGarrett D'Amore if (descr == NULL) {
70695c635efSGarrett D'Amore descr = strchr(line, ' ');
70795c635efSGarrett D'Amore if (descr == NULL)
70895c635efSGarrett D'Amore return;
70995c635efSGarrett D'Amore *descr++ = '\0';
71095c635efSGarrett D'Amore } else {
71195c635efSGarrett D'Amore *descr = '\0';
71295c635efSGarrett D'Amore descr += 3;
71395c635efSGarrett D'Amore }
71495c635efSGarrett D'Amore names = sl_init();
71595c635efSGarrett D'Amore collect_names(names, line);
71695c635efSGarrett D'Amore sbuf_clear(whatis_final);
71795c635efSGarrett D'Amore if (!sl_find(names, page->name) &&
71895c635efSGarrett D'Amore no_page_exists(section_dir, names, page->suffix)) {
71995c635efSGarrett D'Amore /*
72095c635efSGarrett D'Amore * Add the page name since that's the only
72195c635efSGarrett D'Amore * thing that man(1) will find.
72295c635efSGarrett D'Amore */
72395c635efSGarrett D'Amore add_whatis_name(page->name, page->suffix);
72495c635efSGarrett D'Amore }
72595c635efSGarrett D'Amore for (i = 0; i < names->sl_cur; i++)
72695c635efSGarrett D'Amore add_whatis_name(names->sl_str[i], page->suffix);
72795c635efSGarrett D'Amore sl_free(names, 0);
72895c635efSGarrett D'Amore /* Remove last ", " */
72995c635efSGarrett D'Amore sbuf_retract(whatis_final, 2);
73095c635efSGarrett D'Amore while (sbuf_length(whatis_final) < INDENT)
73195c635efSGarrett D'Amore sbuf_append(whatis_final, " ", 1);
73295c635efSGarrett D'Amore sbuf_append(whatis_final, " - ", 3);
73395c635efSGarrett D'Amore sbuf_append_str(whatis_final, skip_spaces(descr));
73495c635efSGarrett D'Amore (void) sl_add(whatis_lines, strdup(sbuf_content(whatis_final)));
73595c635efSGarrett D'Amore }
73695c635efSGarrett D'Amore
73795c635efSGarrett D'Amore /*
73895c635efSGarrett D'Amore * Sort pages first by inode number, then by name.
73995c635efSGarrett D'Amore */
74095c635efSGarrett D'Amore static int
pagesort(const void * a,const void * b)74195c635efSGarrett D'Amore pagesort(const void *a, const void *b)
74295c635efSGarrett D'Amore {
74395c635efSGarrett D'Amore const struct page_info *p1 = *(struct page_info * const *) a;
74495c635efSGarrett D'Amore const struct page_info *p2 = *(struct page_info * const *) b;
74595c635efSGarrett D'Amore
74695c635efSGarrett D'Amore if (p1->inode == p2->inode)
74795c635efSGarrett D'Amore return (strcmp(p1->name, p2->name));
74895c635efSGarrett D'Amore
74995c635efSGarrett D'Amore return (p1->inode - p2->inode);
75095c635efSGarrett D'Amore }
75195c635efSGarrett D'Amore
75295c635efSGarrett D'Amore /*
75395c635efSGarrett D'Amore * Process a single man section.
75495c635efSGarrett D'Amore */
75595c635efSGarrett D'Amore static void
process_section(char * section_dir)75695c635efSGarrett D'Amore process_section(char *section_dir)
75795c635efSGarrett D'Amore {
75895c635efSGarrett D'Amore struct dirent **entries;
75995c635efSGarrett D'Amore int nentries;
76095c635efSGarrett D'Amore struct page_info **pages;
76195c635efSGarrett D'Amore int npages = 0;
76295c635efSGarrett D'Amore int i;
76395c635efSGarrett D'Amore ino_t prev_inode = 0;
76495c635efSGarrett D'Amore
76595c635efSGarrett D'Amore /* Scan the man section directory for pages */
76695c635efSGarrett D'Amore nentries = scandir(section_dir, &entries, NULL, alphasort);
76795c635efSGarrett D'Amore
76895c635efSGarrett D'Amore /* Collect information about man pages */
76995c635efSGarrett D'Amore pages = (struct page_info **)calloc(nentries,
77095c635efSGarrett D'Amore sizeof (struct page_info *));
77195c635efSGarrett D'Amore for (i = 0; i < nentries; i++) {
77295c635efSGarrett D'Amore struct page_info *info = new_page_info(section_dir, entries[i]);
77395c635efSGarrett D'Amore if (info != NULL)
77495c635efSGarrett D'Amore pages[npages++] = info;
77595c635efSGarrett D'Amore free(entries[i]);
77695c635efSGarrett D'Amore }
77795c635efSGarrett D'Amore free(entries);
77895c635efSGarrett D'Amore qsort(pages, npages, sizeof (struct page_info *), pagesort);
77995c635efSGarrett D'Amore
78095c635efSGarrett D'Amore /* Process each unique page */
78195c635efSGarrett D'Amore for (i = 0; i < npages; i++) {
78295c635efSGarrett D'Amore struct page_info *page = pages[i];
78395c635efSGarrett D'Amore if (page->inode != prev_inode) {
78495c635efSGarrett D'Amore prev_inode = page->inode;
78595c635efSGarrett D'Amore process_page(page, section_dir);
78695c635efSGarrett D'Amore }
78795c635efSGarrett D'Amore free_page_info(page);
78895c635efSGarrett D'Amore }
78995c635efSGarrett D'Amore free(pages);
79095c635efSGarrett D'Amore }
79195c635efSGarrett D'Amore
79295c635efSGarrett D'Amore /*
79395c635efSGarrett D'Amore * Return whether the directory entry is a man page section.
79495c635efSGarrett D'Amore */
79595c635efSGarrett D'Amore static int
select_sections(const struct dirent * entry)79695c635efSGarrett D'Amore select_sections(const struct dirent *entry)
79795c635efSGarrett D'Amore {
79895c635efSGarrett D'Amore const char *p = &entry->d_name[3];
79995c635efSGarrett D'Amore
80095c635efSGarrett D'Amore if (strncmp(entry->d_name, "man", 3) != 0)
80195c635efSGarrett D'Amore return (0);
80295c635efSGarrett D'Amore while (*p != '\0') {
80395c635efSGarrett D'Amore if (!isalnum(*p++))
80495c635efSGarrett D'Amore return (0);
80595c635efSGarrett D'Amore }
80695c635efSGarrett D'Amore return (1);
80795c635efSGarrett D'Amore }
80895c635efSGarrett D'Amore
80995c635efSGarrett D'Amore /*
81095c635efSGarrett D'Amore * Process a single top-level man directory by finding all the
81195c635efSGarrett D'Amore * sub-directories named man* and processing each one in turn.
81295c635efSGarrett D'Amore */
81395c635efSGarrett D'Amore void
mwpath(char * path)81495c635efSGarrett D'Amore mwpath(char *path)
81595c635efSGarrett D'Amore {
81695c635efSGarrett D'Amore FILE *fp = NULL;
81795c635efSGarrett D'Amore struct dirent **entries;
81895c635efSGarrett D'Amore int nsections;
81995c635efSGarrett D'Amore int i;
82095c635efSGarrett D'Amore
82195c635efSGarrett D'Amore (void) signal(SIGINT, trap_signal);
82295c635efSGarrett D'Amore (void) signal(SIGHUP, trap_signal);
82395c635efSGarrett D'Amore (void) signal(SIGQUIT, trap_signal);
82495c635efSGarrett D'Amore (void) signal(SIGTERM, trap_signal);
82595c635efSGarrett D'Amore
82695c635efSGarrett D'Amore whatis_proto = new_sbuf();
82795c635efSGarrett D'Amore whatis_final = new_sbuf();
82895c635efSGarrett D'Amore
82995c635efSGarrett D'Amore nsections = scandir(path, &entries, select_sections, alphasort);
83095c635efSGarrett D'Amore if ((fp = open_whatis(path)) == NULL)
83195c635efSGarrett D'Amore return;
83295c635efSGarrett D'Amore for (i = 0; i < nsections; i++) {
83395c635efSGarrett D'Amore char section_dir[MAXPATHLEN];
83495c635efSGarrett D'Amore
83595c635efSGarrett D'Amore (void) snprintf(section_dir, MAXPATHLEN, "%s/%s",
83695c635efSGarrett D'Amore path, entries[i]->d_name);
83795c635efSGarrett D'Amore process_section(section_dir);
83895c635efSGarrett D'Amore free(entries[i]);
83995c635efSGarrett D'Amore }
84095c635efSGarrett D'Amore free(entries);
84195c635efSGarrett D'Amore finish_whatis(fp, path);
84295c635efSGarrett D'Amore }
843