xref: /illumos-gate/usr/src/cmd/man/makewhatis.c (revision 186c806e)
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