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