1 /*
2 * descriptions_store.c: store man page descriptions in database
3 *
4 * Copyright (C) 2002, 2003, 2006, 2007, 2008, 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 <assert.h>
28 #include <stdbool.h>
29 #include <stdio.h>
30 #include <string.h>
31 #include <stdlib.h>
32 #include <sys/stat.h>
33 #include <sys/types.h>
34 #include <time.h>
35 #include <unistd.h>
36
37 #include "gettext.h"
38 #define _(String) gettext (String)
39
40 #include "error.h"
41 #include "gl_array_list.h"
42 #include "gl_hash_map.h"
43 #include "gl_xlist.h"
44 #include "gl_xmap.h"
45 #include "stat-time.h"
46 #include "xalloc.h"
47
48 #include "manconfig.h"
49
50 #include "debug.h"
51 #include "filenames.h"
52 #include "glcontainers.h"
53
54 #include "db_storage.h"
55
56 #include "ult_src.h"
57 #include "descriptions.h"
58
59 static void gripe_bad_store (const char *name, const char *ext)
60 {
61 if (quiet < 2)
62 error (0, 0, _("warning: failed to store entry for %s(%s)"),
63 name, ext);
64 }
65
66 /* Is PARENT a prefix of CHILD, such that CHILD is in the manual hierarchy
67 * PARENT? This requires that the part of CHILD following PARENT start with
68 * "/man".
69 */
70 static int is_prefix (const char *parent, const char *child)
71 {
72 return (STRNEQ (child, parent, strlen (parent)) &&
73 STRNEQ (child + strlen (parent), "/man", 4));
74 }
75
76 /* Take a list of descriptions returned by parse_descriptions() and store
77 * it into the database.
78 */
79 void store_descriptions (MYDBM_FILE dbf, gl_list_t descs, struct mandata *info,
80 const char *path, const char *base, gl_list_t trace)
81 {
82 const struct page_description *desc;
83 const char *trace_name;
84 gl_map_t trace_infos;
85 gl_list_t whatis_infos;
86 struct mandata *whatis_info;
87 const struct mandata *pointer_info;
88
89 assert (trace);
90 assert (gl_list_size (trace) > 0);
91
92 if (gl_list_size (descs)) {
93 GL_LIST_FOREACH (trace, trace_name)
94 debug ("trace: '%s'\n", trace_name);
95 }
96
97 trace_infos = new_string_map (GL_HASH_MAP,
98 (gl_mapvalue_dispose_fn)
99 free_mandata_struct);
100 whatis_infos = gl_list_create_empty (GL_ARRAY_LIST, NULL, NULL,
101 (gl_listelement_dispose_fn)
102 free_mandata_struct,
103 true);
104
105 GL_LIST_FOREACH (trace, trace_name)
106 gl_map_put (trace_infos, xstrdup (trace_name),
107 filename_info (trace_name, quiet < 2));
108
109 GL_LIST_FOREACH (descs, desc) {
110 /* Either it's the real thing or merely a reference. Get the
111 * id and pointer right in either case.
112 */
113 bool found_real_page = false;
114 bool found_external = false;
115
116 whatis_info = XZALLOC (struct mandata);
117 whatis_info->ext = xstrdup (info->ext);
118 whatis_info->sec = xstrdup (info->sec);
119 whatis_info->id = info->id;
120 if (info->comp)
121 whatis_info->comp = xstrdup (info->comp);
122 if (info->filter)
123 whatis_info->filter = xstrdup (info->filter);
124 if (desc->whatis)
125 whatis_info->whatis = xstrdup (desc->whatis);
126 whatis_info->mtime = info->mtime;
127
128 if (STREQ (base, desc->name))
129 found_real_page = true;
130 else {
131 GL_LIST_FOREACH (trace, trace_name) {
132 const struct mandata *trace_info;
133 struct stat st;
134
135 trace_info = gl_map_get (trace_infos,
136 trace_name);
137 if (!trace_info ||
138 !STREQ (trace_info->name, desc->name))
139 continue;
140
141 if (path && !is_prefix (path, trace_name)) {
142 /* Link outside this manual
143 * hierarchy; skip this description.
144 */
145 found_external = true;
146 break;
147 }
148 free (whatis_info->ext);
149 whatis_info->ext = xstrdup (trace_info->ext);
150 free (whatis_info->sec);
151 whatis_info->sec = xstrdup (trace_info->sec);
152 if (!gl_list_next_node (trace, trace_node)) {
153 if (info->id == SO_MAN)
154 whatis_info->id = ULT_MAN;
155 } else {
156 if (info->id == ULT_MAN)
157 whatis_info->id = SO_MAN;
158 }
159 free (whatis_info->comp);
160 if (trace_info->comp)
161 whatis_info->comp = xstrdup
162 (trace_info->comp);
163 else
164 whatis_info->comp = NULL;
165 if (lstat (trace_name, &st) == 0)
166 whatis_info->mtime = get_stat_mtime
167 (&st);
168 else
169 whatis_info->mtime = info->mtime;
170 found_real_page = true;
171 }
172 }
173
174 if (found_external) {
175 debug ("skipping '%s'; link outside manual "
176 "hierarchy\n", desc->name);
177 free_mandata_struct (whatis_info);
178 continue;
179 }
180
181 if (!found_real_page) {
182 whatis_info->name = xstrdup (desc->name);
183 if (info->id < STRAY_CAT)
184 whatis_info->id = WHATIS_MAN;
185 else
186 whatis_info->id = WHATIS_CAT;
187 /* Don't waste space storing the whatis in the db
188 * more than once.
189 */
190 free (whatis_info->whatis);
191 whatis_info->whatis = NULL;
192 gl_list_add_last (whatis_infos, whatis_info);
193 continue;
194 }
195
196 debug ("name = '%s', ext = '%s', id = %c\n",
197 desc->name, whatis_info->ext, whatis_info->id);
198 if (dbstore (dbf, whatis_info, desc->name) > 0) {
199 gripe_bad_store (base, whatis_info->ext);
200 free_mandata_struct (whatis_info);
201 goto out;
202 }
203
204 free_mandata_struct (whatis_info);
205 }
206
207 /* The pointer for a WHATIS_MAN or WHATIS_CAT entry should be the
208 * last entry in the trace that has the same section and extension
209 * as the starting page (which is always the first entry in the
210 * trace). If we were to add WHATIS_* entries for different
211 * extensions, then try_db -> add_candidate -> make_filename in
212 * man(1) would end up constructing a path that doesn't exist and is
213 * thus unusable.
214 */
215 pointer_info = NULL;
216 GL_LIST_FOREACH (trace, trace_name) {
217 const struct mandata *trace_info;
218
219 trace_info = gl_map_get (trace_infos, trace_name);
220 if (trace_info &&
221 STREQ (trace_info->sec, info->sec) &&
222 STREQ (trace_info->ext, info->ext))
223 pointer_info = trace_info;
224 }
225 assert (pointer_info);
226
227 GL_LIST_FOREACH (whatis_infos, whatis_info) {
228 char *name;
229
230 name = whatis_info->name;
231 whatis_info->name = NULL;
232
233 whatis_info->pointer = xstrdup (pointer_info->name);
234
235 debug ("name = '%s', ext = '%s', id = %c, pointer = '%s'\n",
236 name, whatis_info->ext, whatis_info->id,
237 whatis_info->pointer);
238 if (dbstore (dbf, whatis_info, name) > 0) {
239 gripe_bad_store (base, whatis_info->ext);
240 free (name);
241 goto out;
242 }
243
244 free (name);
245 }
246
247 out:
248 gl_list_free (whatis_infos);
249 gl_map_free (trace_infos);
250 }