1 /*
2 * descriptions.c: manipulate man page descriptions
3 *
4 * Copyright (C) 2002, 2003, 2006, 2007, 2008, 2009, 2010, 2011 Colin Watson.
5 *
6 * This file is part of man-db.
7 *
8 * man-db is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * man-db is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with man-db; if not, write to the Free Software Foundation,
20 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21 */
22
23 #ifdef HAVE_CONFIG_H
24 # include "config.h"
25 #endif /* HAVE_CONFIG_H */
26
27 #include <stdbool.h>
28 #include <string.h>
29 #include <stdlib.h>
30
31 #include "gl_array_list.h"
32 #include "gl_xlist.h"
33 #include "xalloc.h"
34 #include "xstrndup.h"
35
36 #include "manconfig.h"
37
38 #include "debug.h"
39 #include "util.h"
40
41 #include "descriptions.h"
42
43 /* Free a page description. */
44 static void page_description_free (const void *value)
45 {
46 struct page_description *desc = (struct page_description *) value;
47
48 free (desc->name);
49 free (desc->whatis);
50 free (desc);
51 }
52
53 /* Parse the description in a whatis line returned by find_name() into a
54 * list of names and whatis descriptions.
55 */
56 gl_list_t parse_descriptions (const char *base, const char *whatis)
57 {
58 const char *sep, *nextsep;
59 gl_list_t descs;
60 bool seen_base = false;
61
62 descs = gl_list_create_empty (GL_ARRAY_LIST, NULL, NULL,
63 page_description_free, true);
64
65 if (!whatis)
66 return descs;
67
68 sep = whatis;
69
70 while (sep) {
71 char *record;
72 size_t length;
73 const char *dash;
74 char *names;
75 const char *token;
76
77 /* Use a while loop so that we skip over things like the
78 * result of double line breaks.
79 */
80 while (*sep == 0x11 || *sep == ' ')
81 ++sep;
82 nextsep = strchr (sep, 0x11);
83
84 /* Get this record as a null-terminated string. */
85 if (nextsep)
86 length = (size_t) (nextsep - sep);
87 else
88 length = strlen (sep);
89 if (length == 0)
90 break;
91
92 record = xstrndup (sep, length);
93 debug ("record = '%s'\n", record);
94
95 /* Split the record into name and whatis description. */
96 dash = strstr (record, " - ");
97 if (dash)
98 names = xstrndup (record, dash - record);
99 else if (!gl_list_size (descs))
100 /* Some pages have a NAME section with just the page
101 * name and no whatis. We might as well include
102 * this.
103 */
104 names = xstrdup (record);
105 else
106 /* Once at least one record has been seen, further
107 * cases where there is no whatis usually amount to
108 * garbage following the useful records, and can
109 * cause problems due to false WHATIS_MAN entries in
110 * the database. On the whole it seems best to
111 * ignore these.
112 */
113 goto next;
114
115 for (token = strtok (names, ","); token;
116 token = strtok (NULL, ",")) {
117 char *name = trim_spaces (token);
118 struct page_description *desc;
119
120 /* Skip name tokens containing whitespace. They are
121 * almost never useful as manual page names.
122 */
123 if (strpbrk (name, " \t") != NULL) {
124 free (name);
125 continue;
126 }
127
128 /* Allocate new description node. */
129 desc = xmalloc (sizeof *desc);
130 desc->name = name; /* steal memory */
131 desc->whatis = dash ? trim_spaces (dash + 3) : NULL;
132 gl_list_add_last (descs, desc);
133
134 if (base && STREQ (base, desc->name))
135 seen_base = true;
136 }
137
138 free (names);
139 next:
140 free (record);
141
142 sep = nextsep;
143 }
144
145 /* If it isn't there already, add the base name onto the returned
146 * list.
147 */
148 if (base && !seen_base) {
149 struct page_description *desc = xmalloc (sizeof *desc);
150
151 desc->name = xstrdup (base);
152 desc->whatis = NULL;
153 if (gl_list_size (descs)) {
154 const struct page_description *first =
155 gl_list_get_at (descs, 0);
156 if (first->whatis)
157 desc->whatis = xstrdup (first->whatis);
158 }
159 gl_list_add_last (descs, desc);
160 }
161
162 return descs;
163 }