1*95c635efSGarrett D'Amore /* 2*95c635efSGarrett D'Amore * Copyright (c) 2002 John Rochester 3*95c635efSGarrett D'Amore * All rights reserved. 4*95c635efSGarrett D'Amore * 5*95c635efSGarrett D'Amore * Redistribution and use in source and binary forms, with or without 6*95c635efSGarrett D'Amore * modification, are permitted provided that the following conditions 7*95c635efSGarrett D'Amore * are met: 8*95c635efSGarrett D'Amore * 1. Redistributions of source code must retain the above copyright 9*95c635efSGarrett D'Amore * notice, this list of conditions and the following disclaimer, 10*95c635efSGarrett D'Amore * in this position and unchanged. 11*95c635efSGarrett D'Amore * 2. Redistributions in binary form must reproduce the above copyright 12*95c635efSGarrett D'Amore * notice, this list of conditions and the following disclaimer in the 13*95c635efSGarrett D'Amore * documentation and/or other materials provided with the distribution. 14*95c635efSGarrett D'Amore * 3. The name of the author may not be used to endorse or promote products 15*95c635efSGarrett D'Amore * derived from this software without specific prior written permission 16*95c635efSGarrett D'Amore * 17*95c635efSGarrett D'Amore * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18*95c635efSGarrett D'Amore * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19*95c635efSGarrett D'Amore * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20*95c635efSGarrett D'Amore * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21*95c635efSGarrett D'Amore * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22*95c635efSGarrett D'Amore * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23*95c635efSGarrett D'Amore * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24*95c635efSGarrett D'Amore * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25*95c635efSGarrett D'Amore * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26*95c635efSGarrett D'Amore * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27*95c635efSGarrett D'Amore */ 28*95c635efSGarrett D'Amore 29*95c635efSGarrett D'Amore /* 30*95c635efSGarrett D'Amore * Copyright 2012 Nexenta Systems, Inc. All rights reserved. 31*95c635efSGarrett D'Amore * Copyright 2014 Garrett D'Amore <garrett@damore.org> 32*95c635efSGarrett D'Amore */ 33*95c635efSGarrett D'Amore 34*95c635efSGarrett D'Amore #include <sys/types.h> 35*95c635efSGarrett D'Amore #include <sys/stat.h> 36*95c635efSGarrett D'Amore #include <sys/param.h> 37*95c635efSGarrett D'Amore 38*95c635efSGarrett D'Amore #include <ctype.h> 39*95c635efSGarrett D'Amore #include <dirent.h> 40*95c635efSGarrett D'Amore #include <err.h> 41*95c635efSGarrett D'Amore #include <signal.h> 42*95c635efSGarrett D'Amore #include <stddef.h> 43*95c635efSGarrett D'Amore #include <stdio.h> 44*95c635efSGarrett D'Amore #include <stdlib.h> 45*95c635efSGarrett D'Amore #include <string.h> 46*95c635efSGarrett D'Amore #include <unistd.h> 47*95c635efSGarrett D'Amore 48*95c635efSGarrett D'Amore #include "man.h" 49*95c635efSGarrett D'Amore #include "stringlist.h" 50*95c635efSGarrett D'Amore 51*95c635efSGarrett D'Amore 52*95c635efSGarrett D'Amore /* Information collected about each man page in a section */ 53*95c635efSGarrett D'Amore struct page_info { 54*95c635efSGarrett D'Amore char *filename; 55*95c635efSGarrett D'Amore char *name; 56*95c635efSGarrett D'Amore char *suffix; 57*95c635efSGarrett D'Amore ino_t inode; 58*95c635efSGarrett D'Amore }; 59*95c635efSGarrett D'Amore 60*95c635efSGarrett D'Amore /* An expanding string */ 61*95c635efSGarrett D'Amore struct sbuf { 62*95c635efSGarrett D'Amore char *content; /* the start of the buffer */ 63*95c635efSGarrett D'Amore char *end; /* just past the end of the content */ 64*95c635efSGarrett D'Amore char *last; /* the last allocated character */ 65*95c635efSGarrett D'Amore }; 66*95c635efSGarrett D'Amore 67*95c635efSGarrett D'Amore /* Remove the last amount characters from the sbuf */ 68*95c635efSGarrett D'Amore #define sbuf_retract(sbuf, amount) ((sbuf)->end -= (amount)) 69*95c635efSGarrett D'Amore /* Return the length of the sbuf content */ 70*95c635efSGarrett D'Amore #define sbuf_length(sbuf) ((sbuf)->end - (sbuf)->content) 71*95c635efSGarrett D'Amore 72*95c635efSGarrett D'Amore typedef char *edited_copy(char *from, char *to, int length); 73*95c635efSGarrett D'Amore 74*95c635efSGarrett D'Amore /* 75*95c635efSGarrett D'Amore * While the whatis line is being formed, it is stored in whatis_proto. 76*95c635efSGarrett D'Amore * When finished, it is reformatted into whatis_final and then appended 77*95c635efSGarrett D'Amore * to whatis_lines. 78*95c635efSGarrett D'Amore */ 79*95c635efSGarrett D'Amore static struct sbuf *whatis_proto; 80*95c635efSGarrett D'Amore static struct sbuf *whatis_final; 81*95c635efSGarrett D'Amore static stringlist *whatis_lines; /* collected output lines */ 82*95c635efSGarrett D'Amore 83*95c635efSGarrett D'Amore static char tempfile[MAXPATHLEN]; /* path of temporary file, if any */ 84*95c635efSGarrett D'Amore 85*95c635efSGarrett D'Amore #define MDOC_COMMANDS "ArDvErEvFlLiNmPa" 86*95c635efSGarrett D'Amore 87*95c635efSGarrett D'Amore 88*95c635efSGarrett D'Amore /* Free a struct page_info and its content */ 89*95c635efSGarrett D'Amore static void 90*95c635efSGarrett D'Amore free_page_info(struct page_info *info) 91*95c635efSGarrett D'Amore { 92*95c635efSGarrett D'Amore 93*95c635efSGarrett D'Amore free(info->filename); 94*95c635efSGarrett D'Amore free(info->name); 95*95c635efSGarrett D'Amore free(info->suffix); 96*95c635efSGarrett D'Amore free(info); 97*95c635efSGarrett D'Amore } 98*95c635efSGarrett D'Amore 99*95c635efSGarrett D'Amore /* 100*95c635efSGarrett D'Amore * Allocate and fill in a new struct page_info given the 101*95c635efSGarrett D'Amore * name of the man section directory and the dirent of the file. 102*95c635efSGarrett D'Amore * If the file is not a man page, return NULL. 103*95c635efSGarrett D'Amore */ 104*95c635efSGarrett D'Amore static struct page_info * 105*95c635efSGarrett D'Amore new_page_info(char *dir, struct dirent *dirent) 106*95c635efSGarrett D'Amore { 107*95c635efSGarrett D'Amore struct page_info *info; 108*95c635efSGarrett D'Amore int basename_length; 109*95c635efSGarrett D'Amore char *suffix; 110*95c635efSGarrett D'Amore struct stat st; 111*95c635efSGarrett D'Amore 112*95c635efSGarrett D'Amore if ((info = malloc(sizeof (struct page_info))) == NULL) 113*95c635efSGarrett D'Amore err(1, "malloc"); 114*95c635efSGarrett D'Amore basename_length = strlen(dirent->d_name); 115*95c635efSGarrett D'Amore suffix = &dirent->d_name[basename_length]; 116*95c635efSGarrett D'Amore if (asprintf(&info->filename, "%s/%s", dir, dirent->d_name) == -1) 117*95c635efSGarrett D'Amore err(1, "asprintf"); 118*95c635efSGarrett D'Amore for (;;) { 119*95c635efSGarrett D'Amore if (--suffix == dirent->d_name || !isalnum(*suffix)) { 120*95c635efSGarrett D'Amore if (*suffix == '.') 121*95c635efSGarrett D'Amore break; 122*95c635efSGarrett D'Amore free(info->filename); 123*95c635efSGarrett D'Amore free(info); 124*95c635efSGarrett D'Amore return (NULL); 125*95c635efSGarrett D'Amore } 126*95c635efSGarrett D'Amore } 127*95c635efSGarrett D'Amore *suffix++ = '\0'; 128*95c635efSGarrett D'Amore info->name = strdup(dirent->d_name); 129*95c635efSGarrett D'Amore info->suffix = strdup(suffix); 130*95c635efSGarrett D'Amore if (stat(info->filename, &st) < 0) { 131*95c635efSGarrett D'Amore warn("%s", info->filename); 132*95c635efSGarrett D'Amore free_page_info(info); 133*95c635efSGarrett D'Amore return (NULL); 134*95c635efSGarrett D'Amore } 135*95c635efSGarrett D'Amore if (!S_ISREG(st.st_mode)) { 136*95c635efSGarrett D'Amore free_page_info(info); 137*95c635efSGarrett D'Amore return (NULL); 138*95c635efSGarrett D'Amore } 139*95c635efSGarrett D'Amore info->inode = st.st_ino; 140*95c635efSGarrett D'Amore return (info); 141*95c635efSGarrett D'Amore } 142*95c635efSGarrett D'Amore 143*95c635efSGarrett D'Amore /* 144*95c635efSGarrett D'Amore * Reset sbuf length to 0. 145*95c635efSGarrett D'Amore */ 146*95c635efSGarrett D'Amore static void 147*95c635efSGarrett D'Amore sbuf_clear(struct sbuf *sbuf) 148*95c635efSGarrett D'Amore { 149*95c635efSGarrett D'Amore 150*95c635efSGarrett D'Amore sbuf->end = sbuf->content; 151*95c635efSGarrett D'Amore } 152*95c635efSGarrett D'Amore 153*95c635efSGarrett D'Amore /* 154*95c635efSGarrett D'Amore * Allocate a new sbuf. 155*95c635efSGarrett D'Amore */ 156*95c635efSGarrett D'Amore static struct sbuf * 157*95c635efSGarrett D'Amore new_sbuf(void) 158*95c635efSGarrett D'Amore { 159*95c635efSGarrett D'Amore struct sbuf *sbuf; 160*95c635efSGarrett D'Amore 161*95c635efSGarrett D'Amore if ((sbuf = malloc(sizeof (struct sbuf))) == NULL) 162*95c635efSGarrett D'Amore err(1, "malloc"); 163*95c635efSGarrett D'Amore if ((sbuf->content = (char *)malloc(LINE_ALLOC)) == NULL) 164*95c635efSGarrett D'Amore err(1, "malloc"); 165*95c635efSGarrett D'Amore sbuf->last = sbuf->content + LINE_ALLOC - 1; 166*95c635efSGarrett D'Amore sbuf_clear(sbuf); 167*95c635efSGarrett D'Amore 168*95c635efSGarrett D'Amore return (sbuf); 169*95c635efSGarrett D'Amore } 170*95c635efSGarrett D'Amore 171*95c635efSGarrett D'Amore /* 172*95c635efSGarrett D'Amore * Ensure that there is enough room in the sbuf 173*95c635efSGarrett D'Amore * for nchars more characters. 174*95c635efSGarrett D'Amore */ 175*95c635efSGarrett D'Amore static void 176*95c635efSGarrett D'Amore sbuf_need(struct sbuf *sbuf, int nchars) 177*95c635efSGarrett D'Amore { 178*95c635efSGarrett D'Amore char *new_content; 179*95c635efSGarrett D'Amore size_t size, cntsize; 180*95c635efSGarrett D'Amore size_t grow = 128; 181*95c635efSGarrett D'Amore 182*95c635efSGarrett D'Amore while (grow < nchars) { 183*95c635efSGarrett D'Amore grow += 128; /* we grow in chunks of 128 bytes */ 184*95c635efSGarrett D'Amore } 185*95c635efSGarrett D'Amore 186*95c635efSGarrett D'Amore /* Grow if the buffer isn't big enough */ 187*95c635efSGarrett D'Amore if (sbuf->end + nchars > sbuf->last) { 188*95c635efSGarrett D'Amore size = sbuf->last + 1 - sbuf->content; 189*95c635efSGarrett D'Amore size += grow; 190*95c635efSGarrett D'Amore cntsize = sbuf->end - sbuf->content; 191*95c635efSGarrett D'Amore 192*95c635efSGarrett D'Amore if ((new_content = realloc(sbuf->content, size)) == NULL) { 193*95c635efSGarrett D'Amore perror("realloc"); 194*95c635efSGarrett D'Amore if (tempfile[0] != '\0') 195*95c635efSGarrett D'Amore (void) unlink(tempfile); 196*95c635efSGarrett D'Amore exit(1); 197*95c635efSGarrett D'Amore } 198*95c635efSGarrett D'Amore sbuf->content = new_content; 199*95c635efSGarrett D'Amore sbuf->end = new_content + cntsize; 200*95c635efSGarrett D'Amore sbuf->last = new_content + size - 1; 201*95c635efSGarrett D'Amore } 202*95c635efSGarrett D'Amore } 203*95c635efSGarrett D'Amore 204*95c635efSGarrett D'Amore /* 205*95c635efSGarrett D'Amore * Append a string of a given length to the sbuf. 206*95c635efSGarrett D'Amore */ 207*95c635efSGarrett D'Amore static void 208*95c635efSGarrett D'Amore sbuf_append(struct sbuf *sbuf, const char *text, int length) 209*95c635efSGarrett D'Amore { 210*95c635efSGarrett D'Amore if (length > 0) { 211*95c635efSGarrett D'Amore sbuf_need(sbuf, length); 212*95c635efSGarrett D'Amore (void) memcpy(sbuf->end, text, length); 213*95c635efSGarrett D'Amore sbuf->end += length; 214*95c635efSGarrett D'Amore } 215*95c635efSGarrett D'Amore } 216*95c635efSGarrett D'Amore 217*95c635efSGarrett D'Amore /* 218*95c635efSGarrett D'Amore * Append a null-terminated string to the sbuf. 219*95c635efSGarrett D'Amore */ 220*95c635efSGarrett D'Amore static void 221*95c635efSGarrett D'Amore sbuf_append_str(struct sbuf *sbuf, char *text) 222*95c635efSGarrett D'Amore { 223*95c635efSGarrett D'Amore 224*95c635efSGarrett D'Amore sbuf_append(sbuf, text, strlen(text)); 225*95c635efSGarrett D'Amore } 226*95c635efSGarrett D'Amore 227*95c635efSGarrett D'Amore /* 228*95c635efSGarrett D'Amore * Append an edited null-terminated string to the sbuf. 229*95c635efSGarrett D'Amore */ 230*95c635efSGarrett D'Amore static void 231*95c635efSGarrett D'Amore sbuf_append_edited(struct sbuf *sbuf, char *text, edited_copy copy) 232*95c635efSGarrett D'Amore { 233*95c635efSGarrett D'Amore int length; 234*95c635efSGarrett D'Amore 235*95c635efSGarrett D'Amore if ((length = strlen(text)) > 0) { 236*95c635efSGarrett D'Amore sbuf_need(sbuf, length); 237*95c635efSGarrett D'Amore sbuf->end = copy(text, sbuf->end, length); 238*95c635efSGarrett D'Amore } 239*95c635efSGarrett D'Amore } 240*95c635efSGarrett D'Amore 241*95c635efSGarrett D'Amore /* 242*95c635efSGarrett D'Amore * Strip any of a set of chars from the end of the sbuf. 243*95c635efSGarrett D'Amore */ 244*95c635efSGarrett D'Amore static void 245*95c635efSGarrett D'Amore sbuf_strip(struct sbuf *sbuf, const char *set) 246*95c635efSGarrett D'Amore { 247*95c635efSGarrett D'Amore 248*95c635efSGarrett D'Amore while (sbuf->end > sbuf->content && strchr(set, sbuf->end[-1]) != NULL) 249*95c635efSGarrett D'Amore sbuf->end--; 250*95c635efSGarrett D'Amore } 251*95c635efSGarrett D'Amore 252*95c635efSGarrett D'Amore /* 253*95c635efSGarrett D'Amore * Return the null-terminated string built by the sbuf. 254*95c635efSGarrett D'Amore */ 255*95c635efSGarrett D'Amore static char * 256*95c635efSGarrett D'Amore sbuf_content(struct sbuf *sbuf) 257*95c635efSGarrett D'Amore { 258*95c635efSGarrett D'Amore 259*95c635efSGarrett D'Amore *sbuf->end = '\0'; 260*95c635efSGarrett D'Amore return (sbuf->content); 261*95c635efSGarrett D'Amore } 262*95c635efSGarrett D'Amore 263*95c635efSGarrett D'Amore /* 264*95c635efSGarrett D'Amore * Return true if no man page exists in the directory with 265*95c635efSGarrett D'Amore * any of the names in the stringlist. 266*95c635efSGarrett D'Amore */ 267*95c635efSGarrett D'Amore static int 268*95c635efSGarrett D'Amore no_page_exists(char *dir, stringlist *names, char *suffix) 269*95c635efSGarrett D'Amore { 270*95c635efSGarrett D'Amore char path[MAXPATHLEN]; 271*95c635efSGarrett D'Amore char *suffixes[] = { "", ".gz", ".bz2", NULL }; 272*95c635efSGarrett D'Amore size_t i; 273*95c635efSGarrett D'Amore int j; 274*95c635efSGarrett D'Amore 275*95c635efSGarrett D'Amore for (i = 0; i < names->sl_cur; i++) { 276*95c635efSGarrett D'Amore for (j = 0; suffixes[j] != NULL; j++) { 277*95c635efSGarrett D'Amore (void) snprintf(path, MAXPATHLEN, "%s/%s.%s%s", 278*95c635efSGarrett D'Amore dir, names->sl_str[i], suffix, suffixes[j]); 279*95c635efSGarrett D'Amore if (access(path, F_OK) == 0) { 280*95c635efSGarrett D'Amore return (0); 281*95c635efSGarrett D'Amore } 282*95c635efSGarrett D'Amore } 283*95c635efSGarrett D'Amore } 284*95c635efSGarrett D'Amore return (1); 285*95c635efSGarrett D'Amore } 286*95c635efSGarrett D'Amore 287*95c635efSGarrett D'Amore /* ARGSUSED sig */ 288*95c635efSGarrett D'Amore static void 289*95c635efSGarrett D'Amore trap_signal(int sig) 290*95c635efSGarrett D'Amore { 291*95c635efSGarrett D'Amore 292*95c635efSGarrett D'Amore if (tempfile[0] != '\0') 293*95c635efSGarrett D'Amore (void) unlink(tempfile); 294*95c635efSGarrett D'Amore 295*95c635efSGarrett D'Amore exit(1); 296*95c635efSGarrett D'Amore } 297*95c635efSGarrett D'Amore 298*95c635efSGarrett D'Amore /* 299*95c635efSGarrett D'Amore * Attempt to open an output file. 300*95c635efSGarrett D'Amore * Return NULL if unsuccessful. 301*95c635efSGarrett D'Amore */ 302*95c635efSGarrett D'Amore static FILE * 303*95c635efSGarrett D'Amore open_output(char *name) 304*95c635efSGarrett D'Amore { 305*95c635efSGarrett D'Amore FILE *output; 306*95c635efSGarrett D'Amore 307*95c635efSGarrett D'Amore whatis_lines = sl_init(); 308*95c635efSGarrett D'Amore (void) snprintf(tempfile, MAXPATHLEN, "%s.tmp", name); 309*95c635efSGarrett D'Amore name = tempfile; 310*95c635efSGarrett D'Amore if ((output = fopen(name, "w")) == NULL) { 311*95c635efSGarrett D'Amore warn("%s", name); 312*95c635efSGarrett D'Amore return (NULL); 313*95c635efSGarrett D'Amore } 314*95c635efSGarrett D'Amore return (output); 315*95c635efSGarrett D'Amore } 316*95c635efSGarrett D'Amore 317*95c635efSGarrett D'Amore static int 318*95c635efSGarrett D'Amore linesort(const void *a, const void *b) 319*95c635efSGarrett D'Amore { 320*95c635efSGarrett D'Amore 321*95c635efSGarrett D'Amore return (strcmp((*(const char * const *)a), (*(const char * const *)b))); 322*95c635efSGarrett D'Amore } 323*95c635efSGarrett D'Amore 324*95c635efSGarrett D'Amore /* 325*95c635efSGarrett D'Amore * Write the unique sorted lines to the output file. 326*95c635efSGarrett D'Amore */ 327*95c635efSGarrett D'Amore static void 328*95c635efSGarrett D'Amore finish_output(FILE *output, char *name) 329*95c635efSGarrett D'Amore { 330*95c635efSGarrett D'Amore size_t i; 331*95c635efSGarrett D'Amore char *prev = NULL; 332*95c635efSGarrett D'Amore 333*95c635efSGarrett D'Amore qsort(whatis_lines->sl_str, whatis_lines->sl_cur, sizeof (char *), 334*95c635efSGarrett D'Amore linesort); 335*95c635efSGarrett D'Amore for (i = 0; i < whatis_lines->sl_cur; i++) { 336*95c635efSGarrett D'Amore char *line = whatis_lines->sl_str[i]; 337*95c635efSGarrett D'Amore if (i > 0 && strcmp(line, prev) == 0) 338*95c635efSGarrett D'Amore continue; 339*95c635efSGarrett D'Amore prev = line; 340*95c635efSGarrett D'Amore (void) fputs(line, output); 341*95c635efSGarrett D'Amore (void) putc('\n', output); 342*95c635efSGarrett D'Amore } 343*95c635efSGarrett D'Amore (void) fclose(output); 344*95c635efSGarrett D'Amore sl_free(whatis_lines, 1); 345*95c635efSGarrett D'Amore (void) rename(tempfile, name); 346*95c635efSGarrett D'Amore (void) unlink(tempfile); 347*95c635efSGarrett D'Amore } 348*95c635efSGarrett D'Amore 349*95c635efSGarrett D'Amore static FILE * 350*95c635efSGarrett D'Amore open_whatis(char *mandir) 351*95c635efSGarrett D'Amore { 352*95c635efSGarrett D'Amore char filename[MAXPATHLEN]; 353*95c635efSGarrett D'Amore 354*95c635efSGarrett D'Amore (void) snprintf(filename, MAXPATHLEN, "%s/%s", mandir, WHATIS); 355*95c635efSGarrett D'Amore return (open_output(filename)); 356*95c635efSGarrett D'Amore } 357*95c635efSGarrett D'Amore 358*95c635efSGarrett D'Amore static void 359*95c635efSGarrett D'Amore finish_whatis(FILE *output, char *mandir) 360*95c635efSGarrett D'Amore { 361*95c635efSGarrett D'Amore char filename[MAXPATHLEN]; 362*95c635efSGarrett D'Amore 363*95c635efSGarrett D'Amore (void) snprintf(filename, MAXPATHLEN, "%s/%s", mandir, WHATIS); 364*95c635efSGarrett D'Amore finish_output(output, filename); 365*95c635efSGarrett D'Amore } 366*95c635efSGarrett D'Amore 367*95c635efSGarrett D'Amore /* 368*95c635efSGarrett D'Amore * Remove trailing spaces from a string, returning a pointer to just 369*95c635efSGarrett D'Amore * beyond the new last character. 370*95c635efSGarrett D'Amore */ 371*95c635efSGarrett D'Amore static char * 372*95c635efSGarrett D'Amore trim_rhs(char *str) 373*95c635efSGarrett D'Amore { 374*95c635efSGarrett D'Amore char *rhs; 375*95c635efSGarrett D'Amore 376*95c635efSGarrett D'Amore rhs = &str[strlen(str)]; 377*95c635efSGarrett D'Amore while (--rhs > str && isspace(*rhs)) 378*95c635efSGarrett D'Amore ; 379*95c635efSGarrett D'Amore *++rhs = '\0'; 380*95c635efSGarrett D'Amore return (rhs); 381*95c635efSGarrett D'Amore } 382*95c635efSGarrett D'Amore 383*95c635efSGarrett D'Amore /* 384*95c635efSGarrett D'Amore * Return a pointer to the next non-space character in the string. 385*95c635efSGarrett D'Amore */ 386*95c635efSGarrett D'Amore static char * 387*95c635efSGarrett D'Amore skip_spaces(char *s) 388*95c635efSGarrett D'Amore { 389*95c635efSGarrett D'Amore 390*95c635efSGarrett D'Amore while (*s != '\0' && isspace(*s)) 391*95c635efSGarrett D'Amore s++; 392*95c635efSGarrett D'Amore 393*95c635efSGarrett D'Amore return (s); 394*95c635efSGarrett D'Amore } 395*95c635efSGarrett D'Amore 396*95c635efSGarrett D'Amore /* 397*95c635efSGarrett D'Amore * Return whether the line is of one of the forms: 398*95c635efSGarrett D'Amore * .Sh NAME 399*95c635efSGarrett D'Amore * .Sh "NAME" 400*95c635efSGarrett D'Amore * etc. 401*95c635efSGarrett D'Amore * assuming that section_start is ".Sh". 402*95c635efSGarrett D'Amore */ 403*95c635efSGarrett D'Amore static int 404*95c635efSGarrett D'Amore name_section_line(char *line, const char *section_start) 405*95c635efSGarrett D'Amore { 406*95c635efSGarrett D'Amore char *rhs; 407*95c635efSGarrett D'Amore 408*95c635efSGarrett D'Amore if (strncmp(line, section_start, 3) != 0) 409*95c635efSGarrett D'Amore return (0); 410*95c635efSGarrett D'Amore line = skip_spaces(line + 3); 411*95c635efSGarrett D'Amore rhs = trim_rhs(line); 412*95c635efSGarrett D'Amore if (*line == '"') { 413*95c635efSGarrett D'Amore line++; 414*95c635efSGarrett D'Amore if (*--rhs == '"') 415*95c635efSGarrett D'Amore *rhs = '\0'; 416*95c635efSGarrett D'Amore } 417*95c635efSGarrett D'Amore if (strcmp(line, "NAME") == 0) 418*95c635efSGarrett D'Amore return (1); 419*95c635efSGarrett D'Amore 420*95c635efSGarrett D'Amore return (0); 421*95c635efSGarrett D'Amore } 422*95c635efSGarrett D'Amore 423*95c635efSGarrett D'Amore /* 424*95c635efSGarrett D'Amore * Copy characters while removing the most common nroff/troff markup: 425*95c635efSGarrett D'Amore * \(em, \(mi, \s[+-N], \& 426*95c635efSGarrett D'Amore * \fF, \f(fo, \f[font] 427*95c635efSGarrett D'Amore * \*s, \*(st, \*[stringvar] 428*95c635efSGarrett D'Amore */ 429*95c635efSGarrett D'Amore static char * 430*95c635efSGarrett D'Amore de_nroff_copy(char *from, char *to, int fromlen) 431*95c635efSGarrett D'Amore { 432*95c635efSGarrett D'Amore char *from_end = &from[fromlen]; 433*95c635efSGarrett D'Amore 434*95c635efSGarrett D'Amore while (from < from_end) { 435*95c635efSGarrett D'Amore switch (*from) { 436*95c635efSGarrett D'Amore case '\\': 437*95c635efSGarrett D'Amore switch (*++from) { 438*95c635efSGarrett D'Amore case '(': 439*95c635efSGarrett D'Amore if (strncmp(&from[1], "em", 2) == 0 || 440*95c635efSGarrett D'Amore strncmp(&from[1], "mi", 2) == 0) { 441*95c635efSGarrett D'Amore from += 3; 442*95c635efSGarrett D'Amore continue; 443*95c635efSGarrett D'Amore } 444*95c635efSGarrett D'Amore break; 445*95c635efSGarrett D'Amore case 's': 446*95c635efSGarrett D'Amore if (*++from == '-') 447*95c635efSGarrett D'Amore from++; 448*95c635efSGarrett D'Amore while (isdigit(*from)) 449*95c635efSGarrett D'Amore from++; 450*95c635efSGarrett D'Amore continue; 451*95c635efSGarrett D'Amore case 'f': 452*95c635efSGarrett D'Amore case '*': 453*95c635efSGarrett D'Amore if (*++from == '(') { 454*95c635efSGarrett D'Amore from += 3; 455*95c635efSGarrett D'Amore } else if (*from == '[') { 456*95c635efSGarrett D'Amore while (*++from != ']' && 457*95c635efSGarrett D'Amore from < from_end) 458*95c635efSGarrett D'Amore ; 459*95c635efSGarrett D'Amore from++; 460*95c635efSGarrett D'Amore } else { 461*95c635efSGarrett D'Amore from++; 462*95c635efSGarrett D'Amore } 463*95c635efSGarrett D'Amore continue; 464*95c635efSGarrett D'Amore case '&': 465*95c635efSGarrett D'Amore from++; 466*95c635efSGarrett D'Amore continue; 467*95c635efSGarrett D'Amore } 468*95c635efSGarrett D'Amore break; 469*95c635efSGarrett D'Amore } 470*95c635efSGarrett D'Amore *to++ = *from++; 471*95c635efSGarrett D'Amore } 472*95c635efSGarrett D'Amore return (to); 473*95c635efSGarrett D'Amore } 474*95c635efSGarrett D'Amore 475*95c635efSGarrett D'Amore /* 476*95c635efSGarrett D'Amore * Append a string with the nroff formatting removed. 477*95c635efSGarrett D'Amore */ 478*95c635efSGarrett D'Amore static void 479*95c635efSGarrett D'Amore add_nroff(char *text) 480*95c635efSGarrett D'Amore { 481*95c635efSGarrett D'Amore 482*95c635efSGarrett D'Amore sbuf_append_edited(whatis_proto, text, de_nroff_copy); 483*95c635efSGarrett D'Amore } 484*95c635efSGarrett D'Amore 485*95c635efSGarrett D'Amore /* 486*95c635efSGarrett D'Amore * Appends "name(suffix), " to whatis_final 487*95c635efSGarrett D'Amore */ 488*95c635efSGarrett D'Amore static void 489*95c635efSGarrett D'Amore add_whatis_name(char *name, char *suffix) 490*95c635efSGarrett D'Amore { 491*95c635efSGarrett D'Amore 492*95c635efSGarrett D'Amore if (*name != '\0') { 493*95c635efSGarrett D'Amore sbuf_append_str(whatis_final, name); 494*95c635efSGarrett D'Amore sbuf_append(whatis_final, "(", 1); 495*95c635efSGarrett D'Amore sbuf_append_str(whatis_final, suffix); 496*95c635efSGarrett D'Amore sbuf_append(whatis_final, "), ", 3); 497*95c635efSGarrett D'Amore } 498*95c635efSGarrett D'Amore } 499*95c635efSGarrett D'Amore 500*95c635efSGarrett D'Amore /* 501*95c635efSGarrett D'Amore * Processes an old-style man(7) line. This ignores commands with only 502*95c635efSGarrett D'Amore * a single number argument. 503*95c635efSGarrett D'Amore */ 504*95c635efSGarrett D'Amore static void 505*95c635efSGarrett D'Amore process_man_line(char *line) 506*95c635efSGarrett D'Amore { 507*95c635efSGarrett D'Amore char *p; 508*95c635efSGarrett D'Amore 509*95c635efSGarrett D'Amore if (*line == '.') { 510*95c635efSGarrett D'Amore while (isalpha(*++line)) 511*95c635efSGarrett D'Amore ; 512*95c635efSGarrett D'Amore p = line = skip_spaces(line); 513*95c635efSGarrett D'Amore while (*p != '\0') { 514*95c635efSGarrett D'Amore if (!isdigit(*p)) 515*95c635efSGarrett D'Amore break; 516*95c635efSGarrett D'Amore p++; 517*95c635efSGarrett D'Amore } 518*95c635efSGarrett D'Amore if (*p == '\0') 519*95c635efSGarrett D'Amore return; 520*95c635efSGarrett D'Amore } else 521*95c635efSGarrett D'Amore line = skip_spaces(line); 522*95c635efSGarrett D'Amore if (*line != '\0') { 523*95c635efSGarrett D'Amore add_nroff(line); 524*95c635efSGarrett D'Amore sbuf_append(whatis_proto, " ", 1); 525*95c635efSGarrett D'Amore } 526*95c635efSGarrett D'Amore } 527*95c635efSGarrett D'Amore 528*95c635efSGarrett D'Amore /* 529*95c635efSGarrett D'Amore * Processes a new-style mdoc(7) line. 530*95c635efSGarrett D'Amore */ 531*95c635efSGarrett D'Amore static void 532*95c635efSGarrett D'Amore process_mdoc_line(char *line) 533*95c635efSGarrett D'Amore { 534*95c635efSGarrett D'Amore int xref; 535*95c635efSGarrett D'Amore int arg = 0; 536*95c635efSGarrett D'Amore char *line_end = &line[strlen(line)]; 537*95c635efSGarrett D'Amore int orig_length = sbuf_length(whatis_proto); 538*95c635efSGarrett D'Amore char *next; 539*95c635efSGarrett D'Amore 540*95c635efSGarrett D'Amore if (*line == '\0') 541*95c635efSGarrett D'Amore return; 542*95c635efSGarrett D'Amore if (line[0] != '.' || !isupper(line[1]) || !islower(line[2])) { 543*95c635efSGarrett D'Amore add_nroff(skip_spaces(line)); 544*95c635efSGarrett D'Amore sbuf_append(whatis_proto, " ", 1); 545*95c635efSGarrett D'Amore return; 546*95c635efSGarrett D'Amore } 547*95c635efSGarrett D'Amore xref = strncmp(line, ".Xr", 3) == 0; 548*95c635efSGarrett D'Amore line += 3; 549*95c635efSGarrett D'Amore while ((line = skip_spaces(line)) < line_end) { 550*95c635efSGarrett D'Amore if (*line == '"') { 551*95c635efSGarrett D'Amore next = ++line; 552*95c635efSGarrett D'Amore for (;;) { 553*95c635efSGarrett D'Amore next = strchr(next, '"'); 554*95c635efSGarrett D'Amore if (next == NULL) 555*95c635efSGarrett D'Amore break; 556*95c635efSGarrett D'Amore (void) memmove(next, next + 1, strlen(next)); 557*95c635efSGarrett D'Amore line_end--; 558*95c635efSGarrett D'Amore if (*next != '"') 559*95c635efSGarrett D'Amore break; 560*95c635efSGarrett D'Amore next++; 561*95c635efSGarrett D'Amore } 562*95c635efSGarrett D'Amore } else { 563*95c635efSGarrett D'Amore next = strpbrk(line, " \t"); 564*95c635efSGarrett D'Amore } 565*95c635efSGarrett D'Amore if (next != NULL) 566*95c635efSGarrett D'Amore *next++ = '\0'; 567*95c635efSGarrett D'Amore else 568*95c635efSGarrett D'Amore next = line_end; 569*95c635efSGarrett D'Amore if (isupper(*line) && islower(line[1]) && line[2] == '\0') { 570*95c635efSGarrett D'Amore if (strcmp(line, "Ns") == 0) { 571*95c635efSGarrett D'Amore arg = 0; 572*95c635efSGarrett D'Amore line = next; 573*95c635efSGarrett D'Amore continue; 574*95c635efSGarrett D'Amore } 575*95c635efSGarrett D'Amore if (strstr(line, MDOC_COMMANDS) != NULL) { 576*95c635efSGarrett D'Amore line = next; 577*95c635efSGarrett D'Amore continue; 578*95c635efSGarrett D'Amore } 579*95c635efSGarrett D'Amore } 580*95c635efSGarrett D'Amore if (arg > 0 && strchr(",.:;?!)]", *line) == 0) { 581*95c635efSGarrett D'Amore if (xref) { 582*95c635efSGarrett D'Amore sbuf_append(whatis_proto, "(", 1); 583*95c635efSGarrett D'Amore add_nroff(line); 584*95c635efSGarrett D'Amore sbuf_append(whatis_proto, ")", 1); 585*95c635efSGarrett D'Amore xref = 0; 586*95c635efSGarrett D'Amore } else { 587*95c635efSGarrett D'Amore sbuf_append(whatis_proto, " ", 1); 588*95c635efSGarrett D'Amore } 589*95c635efSGarrett D'Amore } 590*95c635efSGarrett D'Amore add_nroff(line); 591*95c635efSGarrett D'Amore arg++; 592*95c635efSGarrett D'Amore line = next; 593*95c635efSGarrett D'Amore } 594*95c635efSGarrett D'Amore if (sbuf_length(whatis_proto) > orig_length) 595*95c635efSGarrett D'Amore sbuf_append(whatis_proto, " ", 1); 596*95c635efSGarrett D'Amore } 597*95c635efSGarrett D'Amore 598*95c635efSGarrett D'Amore /* 599*95c635efSGarrett D'Amore * Collect a list of comma-separated names from the text. 600*95c635efSGarrett D'Amore */ 601*95c635efSGarrett D'Amore static void 602*95c635efSGarrett D'Amore collect_names(stringlist *names, char *text) 603*95c635efSGarrett D'Amore { 604*95c635efSGarrett D'Amore char *arg; 605*95c635efSGarrett D'Amore 606*95c635efSGarrett D'Amore for (;;) { 607*95c635efSGarrett D'Amore arg = text; 608*95c635efSGarrett D'Amore text = strchr(text, ','); 609*95c635efSGarrett D'Amore if (text != NULL) 610*95c635efSGarrett D'Amore *text++ = '\0'; 611*95c635efSGarrett D'Amore (void) sl_add(names, arg); 612*95c635efSGarrett D'Amore if (text == NULL) 613*95c635efSGarrett D'Amore return; 614*95c635efSGarrett D'Amore if (*text == ' ') 615*95c635efSGarrett D'Amore text++; 616*95c635efSGarrett D'Amore } 617*95c635efSGarrett D'Amore } 618*95c635efSGarrett D'Amore 619*95c635efSGarrett D'Amore enum { STATE_UNKNOWN, STATE_MANSTYLE, STATE_MDOCNAME, STATE_MDOCDESC }; 620*95c635efSGarrett D'Amore 621*95c635efSGarrett D'Amore /* 622*95c635efSGarrett D'Amore * Process a man page source into a single whatis line and add it 623*95c635efSGarrett D'Amore * to whatis_lines. 624*95c635efSGarrett D'Amore */ 625*95c635efSGarrett D'Amore static void 626*95c635efSGarrett D'Amore process_page(struct page_info *page, char *section_dir) 627*95c635efSGarrett D'Amore { 628*95c635efSGarrett D'Amore FILE *fp; 629*95c635efSGarrett D'Amore stringlist *names; 630*95c635efSGarrett D'Amore char *descr; 631*95c635efSGarrett D'Amore int state = STATE_UNKNOWN; 632*95c635efSGarrett D'Amore size_t i; 633*95c635efSGarrett D'Amore char *line = NULL; 634*95c635efSGarrett D'Amore size_t linecap = 0; 635*95c635efSGarrett D'Amore 636*95c635efSGarrett D'Amore sbuf_clear(whatis_proto); 637*95c635efSGarrett D'Amore if ((fp = fopen(page->filename, "r")) == NULL) { 638*95c635efSGarrett D'Amore warn("%s", page->filename); 639*95c635efSGarrett D'Amore return; 640*95c635efSGarrett D'Amore } 641*95c635efSGarrett D'Amore while (getline(&line, &linecap, fp) > 0) { 642*95c635efSGarrett D'Amore /* Skip comments */ 643*95c635efSGarrett D'Amore if (strncmp(line, ".\\\"", 3) == 0) 644*95c635efSGarrett D'Amore continue; 645*95c635efSGarrett D'Amore switch (state) { 646*95c635efSGarrett D'Amore /* Haven't reached the NAME section yet */ 647*95c635efSGarrett D'Amore case STATE_UNKNOWN: 648*95c635efSGarrett D'Amore if (name_section_line(line, ".SH")) 649*95c635efSGarrett D'Amore state = STATE_MANSTYLE; 650*95c635efSGarrett D'Amore else if (name_section_line(line, ".Sh")) 651*95c635efSGarrett D'Amore state = STATE_MDOCNAME; 652*95c635efSGarrett D'Amore continue; 653*95c635efSGarrett D'Amore /* Inside an old-style .SH NAME section */ 654*95c635efSGarrett D'Amore case STATE_MANSTYLE: 655*95c635efSGarrett D'Amore if (strncmp(line, ".SH", 3) == 0 || 656*95c635efSGarrett D'Amore strncmp(line, ".SS", 3) == 0) 657*95c635efSGarrett D'Amore break; 658*95c635efSGarrett D'Amore (void) trim_rhs(line); 659*95c635efSGarrett D'Amore if (strcmp(line, ".") == 0) 660*95c635efSGarrett D'Amore continue; 661*95c635efSGarrett D'Amore if (strncmp(line, ".IX", 3) == 0) { 662*95c635efSGarrett D'Amore line += 3; 663*95c635efSGarrett D'Amore line = skip_spaces(line); 664*95c635efSGarrett D'Amore } 665*95c635efSGarrett D'Amore process_man_line(line); 666*95c635efSGarrett D'Amore continue; 667*95c635efSGarrett D'Amore /* Inside a new-style .Sh NAME section (the .Nm part) */ 668*95c635efSGarrett D'Amore case STATE_MDOCNAME: 669*95c635efSGarrett D'Amore (void) trim_rhs(line); 670*95c635efSGarrett D'Amore if (strncmp(line, ".Nm", 3) == 0) { 671*95c635efSGarrett D'Amore process_mdoc_line(line); 672*95c635efSGarrett D'Amore continue; 673*95c635efSGarrett D'Amore } else { 674*95c635efSGarrett D'Amore if (strcmp(line, ".") == 0) 675*95c635efSGarrett D'Amore continue; 676*95c635efSGarrett D'Amore sbuf_append(whatis_proto, "- ", 2); 677*95c635efSGarrett D'Amore state = STATE_MDOCDESC; 678*95c635efSGarrett D'Amore } 679*95c635efSGarrett D'Amore /* FALLTHROUGH */ 680*95c635efSGarrett D'Amore /* Inside a new-style .Sh NAME section (after the .Nm-s) */ 681*95c635efSGarrett D'Amore case STATE_MDOCDESC: 682*95c635efSGarrett D'Amore if (strncmp(line, ".Sh", 3) == 0) 683*95c635efSGarrett D'Amore break; 684*95c635efSGarrett D'Amore (void) trim_rhs(line); 685*95c635efSGarrett D'Amore if (strcmp(line, ".") == 0) 686*95c635efSGarrett D'Amore continue; 687*95c635efSGarrett D'Amore process_mdoc_line(line); 688*95c635efSGarrett D'Amore continue; 689*95c635efSGarrett D'Amore } 690*95c635efSGarrett D'Amore break; 691*95c635efSGarrett D'Amore } 692*95c635efSGarrett D'Amore (void) fclose(fp); 693*95c635efSGarrett D'Amore sbuf_strip(whatis_proto, " \t.-"); 694*95c635efSGarrett D'Amore line = sbuf_content(whatis_proto); 695*95c635efSGarrett D'Amore /* 696*95c635efSGarrett D'Amore * Line now contains the appropriate data, but without the 697*95c635efSGarrett D'Amore * proper indentation or the section appended to each name. 698*95c635efSGarrett D'Amore */ 699*95c635efSGarrett D'Amore descr = strstr(line, " - "); 700*95c635efSGarrett D'Amore if (descr == NULL) { 701*95c635efSGarrett D'Amore descr = strchr(line, ' '); 702*95c635efSGarrett D'Amore if (descr == NULL) 703*95c635efSGarrett D'Amore return; 704*95c635efSGarrett D'Amore *descr++ = '\0'; 705*95c635efSGarrett D'Amore } else { 706*95c635efSGarrett D'Amore *descr = '\0'; 707*95c635efSGarrett D'Amore descr += 3; 708*95c635efSGarrett D'Amore } 709*95c635efSGarrett D'Amore names = sl_init(); 710*95c635efSGarrett D'Amore collect_names(names, line); 711*95c635efSGarrett D'Amore sbuf_clear(whatis_final); 712*95c635efSGarrett D'Amore if (!sl_find(names, page->name) && 713*95c635efSGarrett D'Amore no_page_exists(section_dir, names, page->suffix)) { 714*95c635efSGarrett D'Amore /* 715*95c635efSGarrett D'Amore * Add the page name since that's the only 716*95c635efSGarrett D'Amore * thing that man(1) will find. 717*95c635efSGarrett D'Amore */ 718*95c635efSGarrett D'Amore add_whatis_name(page->name, page->suffix); 719*95c635efSGarrett D'Amore } 720*95c635efSGarrett D'Amore for (i = 0; i < names->sl_cur; i++) 721*95c635efSGarrett D'Amore add_whatis_name(names->sl_str[i], page->suffix); 722*95c635efSGarrett D'Amore sl_free(names, 0); 723*95c635efSGarrett D'Amore /* Remove last ", " */ 724*95c635efSGarrett D'Amore sbuf_retract(whatis_final, 2); 725*95c635efSGarrett D'Amore while (sbuf_length(whatis_final) < INDENT) 726*95c635efSGarrett D'Amore sbuf_append(whatis_final, " ", 1); 727*95c635efSGarrett D'Amore sbuf_append(whatis_final, " - ", 3); 728*95c635efSGarrett D'Amore sbuf_append_str(whatis_final, skip_spaces(descr)); 729*95c635efSGarrett D'Amore (void) sl_add(whatis_lines, strdup(sbuf_content(whatis_final))); 730*95c635efSGarrett D'Amore } 731*95c635efSGarrett D'Amore 732*95c635efSGarrett D'Amore /* 733*95c635efSGarrett D'Amore * Sort pages first by inode number, then by name. 734*95c635efSGarrett D'Amore */ 735*95c635efSGarrett D'Amore static int 736*95c635efSGarrett D'Amore pagesort(const void *a, const void *b) 737*95c635efSGarrett D'Amore { 738*95c635efSGarrett D'Amore const struct page_info *p1 = *(struct page_info * const *) a; 739*95c635efSGarrett D'Amore const struct page_info *p2 = *(struct page_info * const *) b; 740*95c635efSGarrett D'Amore 741*95c635efSGarrett D'Amore if (p1->inode == p2->inode) 742*95c635efSGarrett D'Amore return (strcmp(p1->name, p2->name)); 743*95c635efSGarrett D'Amore 744*95c635efSGarrett D'Amore return (p1->inode - p2->inode); 745*95c635efSGarrett D'Amore } 746*95c635efSGarrett D'Amore 747*95c635efSGarrett D'Amore /* 748*95c635efSGarrett D'Amore * Process a single man section. 749*95c635efSGarrett D'Amore */ 750*95c635efSGarrett D'Amore static void 751*95c635efSGarrett D'Amore process_section(char *section_dir) 752*95c635efSGarrett D'Amore { 753*95c635efSGarrett D'Amore struct dirent **entries; 754*95c635efSGarrett D'Amore int nentries; 755*95c635efSGarrett D'Amore struct page_info **pages; 756*95c635efSGarrett D'Amore int npages = 0; 757*95c635efSGarrett D'Amore int i; 758*95c635efSGarrett D'Amore ino_t prev_inode = 0; 759*95c635efSGarrett D'Amore 760*95c635efSGarrett D'Amore /* Scan the man section directory for pages */ 761*95c635efSGarrett D'Amore nentries = scandir(section_dir, &entries, NULL, alphasort); 762*95c635efSGarrett D'Amore 763*95c635efSGarrett D'Amore /* Collect information about man pages */ 764*95c635efSGarrett D'Amore pages = (struct page_info **)calloc(nentries, 765*95c635efSGarrett D'Amore sizeof (struct page_info *)); 766*95c635efSGarrett D'Amore for (i = 0; i < nentries; i++) { 767*95c635efSGarrett D'Amore struct page_info *info = new_page_info(section_dir, entries[i]); 768*95c635efSGarrett D'Amore if (info != NULL) 769*95c635efSGarrett D'Amore pages[npages++] = info; 770*95c635efSGarrett D'Amore free(entries[i]); 771*95c635efSGarrett D'Amore } 772*95c635efSGarrett D'Amore free(entries); 773*95c635efSGarrett D'Amore qsort(pages, npages, sizeof (struct page_info *), pagesort); 774*95c635efSGarrett D'Amore 775*95c635efSGarrett D'Amore /* Process each unique page */ 776*95c635efSGarrett D'Amore for (i = 0; i < npages; i++) { 777*95c635efSGarrett D'Amore struct page_info *page = pages[i]; 778*95c635efSGarrett D'Amore if (page->inode != prev_inode) { 779*95c635efSGarrett D'Amore prev_inode = page->inode; 780*95c635efSGarrett D'Amore process_page(page, section_dir); 781*95c635efSGarrett D'Amore } 782*95c635efSGarrett D'Amore free_page_info(page); 783*95c635efSGarrett D'Amore } 784*95c635efSGarrett D'Amore free(pages); 785*95c635efSGarrett D'Amore } 786*95c635efSGarrett D'Amore 787*95c635efSGarrett D'Amore /* 788*95c635efSGarrett D'Amore * Return whether the directory entry is a man page section. 789*95c635efSGarrett D'Amore */ 790*95c635efSGarrett D'Amore static int 791*95c635efSGarrett D'Amore select_sections(const struct dirent *entry) 792*95c635efSGarrett D'Amore { 793*95c635efSGarrett D'Amore const char *p = &entry->d_name[3]; 794*95c635efSGarrett D'Amore 795*95c635efSGarrett D'Amore if (strncmp(entry->d_name, "man", 3) != 0) 796*95c635efSGarrett D'Amore return (0); 797*95c635efSGarrett D'Amore while (*p != '\0') { 798*95c635efSGarrett D'Amore if (!isalnum(*p++)) 799*95c635efSGarrett D'Amore return (0); 800*95c635efSGarrett D'Amore } 801*95c635efSGarrett D'Amore return (1); 802*95c635efSGarrett D'Amore } 803*95c635efSGarrett D'Amore 804*95c635efSGarrett D'Amore /* 805*95c635efSGarrett D'Amore * Process a single top-level man directory by finding all the 806*95c635efSGarrett D'Amore * sub-directories named man* and processing each one in turn. 807*95c635efSGarrett D'Amore */ 808*95c635efSGarrett D'Amore void 809*95c635efSGarrett D'Amore mwpath(char *path) 810*95c635efSGarrett D'Amore { 811*95c635efSGarrett D'Amore FILE *fp = NULL; 812*95c635efSGarrett D'Amore struct dirent **entries; 813*95c635efSGarrett D'Amore int nsections; 814*95c635efSGarrett D'Amore int i; 815*95c635efSGarrett D'Amore 816*95c635efSGarrett D'Amore (void) signal(SIGINT, trap_signal); 817*95c635efSGarrett D'Amore (void) signal(SIGHUP, trap_signal); 818*95c635efSGarrett D'Amore (void) signal(SIGQUIT, trap_signal); 819*95c635efSGarrett D'Amore (void) signal(SIGTERM, trap_signal); 820*95c635efSGarrett D'Amore 821*95c635efSGarrett D'Amore whatis_proto = new_sbuf(); 822*95c635efSGarrett D'Amore whatis_final = new_sbuf(); 823*95c635efSGarrett D'Amore 824*95c635efSGarrett D'Amore nsections = scandir(path, &entries, select_sections, alphasort); 825*95c635efSGarrett D'Amore if ((fp = open_whatis(path)) == NULL) 826*95c635efSGarrett D'Amore return; 827*95c635efSGarrett D'Amore for (i = 0; i < nsections; i++) { 828*95c635efSGarrett D'Amore char section_dir[MAXPATHLEN]; 829*95c635efSGarrett D'Amore 830*95c635efSGarrett D'Amore (void) snprintf(section_dir, MAXPATHLEN, "%s/%s", 831*95c635efSGarrett D'Amore path, entries[i]->d_name); 832*95c635efSGarrett D'Amore process_section(section_dir); 833*95c635efSGarrett D'Amore free(entries[i]); 834*95c635efSGarrett D'Amore } 835*95c635efSGarrett D'Amore free(entries); 836*95c635efSGarrett D'Amore finish_whatis(fp, path); 837*95c635efSGarrett D'Amore } 838