Imported Upstream version 2.8.4
[platform/upstream/man-db.git] / src / ult_src.c
1 /*
2  * ult_src.c: Find the ultimate source of a page
3  *
4  * Copyright (C) 1994, 1995 Graeme W. Wilford. (Wilf.)
5  * Copyright (C) 2001, 2002, 2003, 2004, 2006, 2007, 2008, 2009, 2010, 2011,
6  *               2012 Colin Watson.
7  *
8  * This file is part of man-db.
9  *
10  * man-db is free software; you can redistribute it and/or modify it
11  * under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * man-db is distributed in the hope that it will be useful, but
16  * WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with man-db; if not, write to the Free Software Foundation,
22  * Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
23  *
24  * code to seek out the original (ultimate) source man file for
25  * any specified man file. Soft and hard links and .so inclusions
26  * are traced. Use: reduce amount of cat files to a minimum.
27  *
28  * Mon May  2 11:14:28 BST 1994 Wilf. (G.Wilford@ee.surrey.ac.uk)
29  */
30
31 #ifdef HAVE_CONFIG_H
32 #  include "config.h"
33 #endif /* HAVE_CONFIG_H */
34
35 #include <string.h>
36 #include <stdlib.h>
37 #include <stdio.h>
38 #include <ctype.h>
39 #include <sys/types.h>
40 #include <sys/stat.h>
41 #include <errno.h>
42 #include <assert.h>
43 #include <dirent.h>
44 #include <unistd.h>
45
46 #include "canonicalize.h"
47 #include "dirname.h"
48 #include "error.h"
49 #include "xvasprintf.h"
50
51 #include "gettext.h"
52 #define _(String) gettext (String)
53
54 #include "manconfig.h"
55
56 #include "pipeline.h"
57 #include "decompress.h"
58
59 #include "globbing.h"
60 #include "ult_src.h"
61
62 /* Find minimum value hard link filename for given file and inode.
63  * Returns a newly allocated string.
64  */
65 static char *ult_hardlink (const char *fullpath, ino_t inode)
66 {
67         DIR *mdir;
68         struct dirent *manlist;
69         char *base, *dir, *ret;
70         const char *slash;
71
72         slash = strrchr (fullpath, '/');
73         assert (slash);
74         dir = xstrndup (fullpath, slash - fullpath);
75         base = xstrdup (++slash);
76
77         mdir = opendir (dir);
78         if (mdir == NULL) {
79                 if (quiet < 2)
80                         error (0, errno, _("can't search directory %s"), dir);
81                 free (dir);
82                 free (base);
83                 return NULL;
84         }
85
86         while ((manlist = readdir (mdir))) {
87                 if (manlist->d_ino == inode &&
88                     strcmp (base, manlist->d_name) > 0) {
89                         free (base);
90                         base = xstrdup (manlist->d_name);
91                         debug ("ult_hardlink: (%s)\n", base);
92                 }
93         }
94         closedir (mdir);
95
96         /* If we already are the link with the smallest name value */
97         /* return NULL */
98
99         if (strcmp (base, slash) == 0) {
100                 free (dir);
101                 free (base);
102                 return NULL;
103         }
104
105         ret = xasprintf ("%s/%s", dir, base);
106         free (dir);
107         free (base);
108         return ret;
109 }
110
111 /* Resolve all symbolic links within 'fullpath'.
112  * Returns a newly allocated string.
113  */
114 static char *ult_softlink (const char *fullpath)
115 {
116         char *resolved_path;
117
118         resolved_path = canonicalize_file_name (fullpath);
119         if (!resolved_path) {
120                 /* discard the unresolved path */
121                 if (quiet < 2) {
122                         if (errno == ENOENT)
123                                 error (0, 0,
124                                        _("warning: %s is a dangling symlink"),
125                                        fullpath);
126                         else
127                                 error (0, errno, _("can't resolve %s"),
128                                        fullpath);
129                 }
130                 return NULL;
131         }
132
133         debug ("ult_softlink: (%s)\n", resolved_path);
134
135         return resolved_path;
136 }
137
138 /* Test 'buffer' to see if it contains a .so include. If so and it's not an 
139  * absolute filename, return newly allocated string whose contents are the
140  * include.
141  */
142 static char *test_for_include (const char *buffer)
143 {
144         if (!buffer)
145                 return NULL;
146
147         /* strip out any leading whitespace (if any) */
148         while (CTYPE (isspace, *buffer))
149                 buffer++;
150
151         /* see if the `command' is a .so */
152         if (strncmp (buffer, ".so", 3) == 0) {
153                 buffer += 3;
154
155                 /* strip out any whitespace between the command and 
156                    it's argumant */
157                 while (CTYPE (isspace, *buffer))
158                         buffer++;
159
160                 /* If .so's argument is an absolute filename, it could be
161                  * either (i) a macro inclusion, (ii) a non local manual page
162                  * or (iii) a (somewhat bogus) reference to a local manual 
163                  * page.
164                  * 
165                  * If (i) or (ii), we must not follow the reference. (iii) is
166                  * a problem with the manual page, thus we don't want to 
167                  * follow any absolute inclusions in our quest for the 
168                  * ultimate source file */
169                 if (*buffer != '/') {
170                         const char *end = buffer;
171                         while (*end && !CTYPE (isspace, *end))
172                                 ++end;
173                         return xstrndup (buffer, end - buffer);
174                 }
175         }
176         return NULL;
177 }
178
179 static char *find_include (const char *name, const char *path,
180                            const char *include)
181 {
182         char *ret;
183         char *dirname;
184         char *temp_file;
185
186         /* Restore the original path from before ult_softlink() etc., in
187          * case it went outside the mantree.
188          */
189         ret = xasprintf ("%s/%s", path, include);
190
191         /* If the original path from above doesn't exist, try to create new
192          * path as if the "include" was relative to the current man page.
193          */
194         if (CAN_ACCESS (ret, F_OK))
195                 return ret;
196
197         dirname = dir_name (name);
198         temp_file = xasprintf ("%s/%s", dirname, include);
199         free (dirname);
200
201         if (CAN_ACCESS (temp_file, F_OK)) {
202                 /* Just plain include. */
203                 free (ret);
204                 ret = canonicalize_file_name (temp_file);
205         } else {
206                 /* Try globbing - the file suffix might be missing. */
207                 char *temp_file_asterisk = xasprintf ("%s*", temp_file);
208                 char **candidate_files = expand_path (temp_file_asterisk);
209                 int i;
210
211                 free (temp_file_asterisk);
212                 if (CAN_ACCESS (candidate_files[0], F_OK)) {
213                         free (ret);
214                         ret = canonicalize_file_name (candidate_files[0]);
215                 }
216                 for (i = 0; candidate_files[i]; i++)
217                         free (candidate_files[i]);
218                 free (candidate_files);
219         }
220         free (temp_file);
221
222         return ret;
223 }
224
225 static void ult_trace (struct ult_trace *trace, const char *s)
226 {
227         if (!trace)
228                 return;
229         if (trace->len >= trace->max) {
230                 trace->max *= 2;
231                 trace->names = xnrealloc (trace->names, trace->max,
232                                           sizeof (char *));
233         }
234         trace->names[trace->len++] = xstrdup (s);
235 }
236
237 void free_ult_trace (struct ult_trace *trace)
238 {
239         size_t i;
240         for (i = 0; i < trace->len; ++i)
241                 free (trace->names[i]);
242         free (trace->names);
243 }
244
245 /*
246  * recursive function which finds the ultimate source file by following
247  * any ".so filename" directives in the first line of the man pages.
248  * Also (optionally) traces symlinks and hard links(!).
249  *
250  * name is full pathname, path is the MANPATH directory (/usr/man)
251  * flags is a combination of SO_LINK | SOFT_LINK | HARD_LINK
252  */
253 const char *ult_src (const char *name, const char *path,
254                      struct stat *buf, int flags, struct ult_trace *trace)
255 {
256         static char *base;              /* must be static */
257         static short recurse;           /* must be static */
258
259         /* initialise the function */
260
261         if (trace) {
262                 if (!trace->names) {
263                         trace->len = 0;
264                         trace->max = 16;
265                         trace->names = XNMALLOC (trace->max, char *);
266                 }
267                 ult_trace (trace, name);
268         }
269
270         /* as ult_softlink() & ult_hardlink() do all of their respective
271          * resolving in one call, only need to sort them out once
272          */
273
274         if (recurse == 0) {
275                 struct stat new_buf;
276                 free (base);
277                 base = xstrdup (name);
278
279                 debug ("\nult_src: File %s in mantree %s\n", name, path);
280
281                 /* If we don't have a buf, allocate and assign one */
282                 if (!buf && ((flags & SOFT_LINK) || (flags & HARD_LINK))) {
283                         buf = &new_buf;
284                         if (lstat (base, buf) == -1) {
285                                 if (quiet < 2)
286                                         error (0, errno, _("can't resolve %s"),
287                                                base);
288                                 return NULL;
289                         }
290                 }
291
292                 /* Permit semi local (inter-tree) soft links */
293                 if (flags & SOFT_LINK) {
294                         assert (buf); /* initialised above */
295                         if (S_ISLNK (buf->st_mode)) {
296                                 /* Is a symlink, resolve it. */
297                                 char *softlink = ult_softlink (base);
298                                 if (softlink) {
299                                         free (base);
300                                         base = softlink;
301                                 } else
302                                         return NULL;
303                         }
304                 }
305
306                 /* Only deal with local (inter-dir) HARD links */
307                 if (flags & HARD_LINK) {
308                         assert (buf); /* initialised above */
309                         if (buf->st_nlink > 1) {
310                                 /* Has HARD links, find least value */
311                                 char *hardlink = ult_hardlink (base,
312                                                                buf->st_ino);
313                                 if (hardlink) {
314                                         free (base);
315                                         base = hardlink;
316                                 }
317                         }
318                 }
319         }
320
321         /* keep a check on recursion level */
322         else if (recurse == 10) {
323                 if (quiet < 2)
324                         error (0, 0, _("%s is self referencing"), name);
325                 return NULL;
326         }
327
328         if (flags & SO_LINK) {
329                 const char *buffer;
330                 char *decomp_base;
331                 pipeline *decomp;
332                 char *include;
333 #ifdef COMP_SRC
334                 struct stat st;
335
336                 if (stat (base, &st) < 0) {
337                         struct compression *comp = comp_file (base);
338
339                         if (comp) {
340                                 free (base);
341                                 base = comp->stem;
342                                 comp->stem = NULL; /* steal memory */
343                         } else {
344                                 if (quiet < 2)
345                                         error (0, errno, _("can't open %s"),
346                                                base);
347                                 return NULL;
348                         }
349                 }
350 #endif
351
352                 /* base may change for recursive calls to ult_src, but
353                  * decompress_open doesn't keep its own copy.
354                  */
355                 decomp_base = xstrdup (base);
356                 decomp = decompress_open (decomp_base);
357                 if (!decomp) {
358                         if (quiet < 2)
359                                 error (0, errno, _("can't open %s"), base);
360                         free (decomp_base);
361                         return NULL;
362                 }
363                 pipeline_start (decomp);
364
365                 /* make sure that we skip over any comments */
366                 do {
367                         buffer = pipeline_readline (decomp);
368                 } while (buffer && STRNEQ (buffer, ".\\\"", 3));
369
370                 include = test_for_include (buffer);
371                 if (include) {
372                         char *new_name;
373                         const char *ult;
374
375                         free (base);
376                         base = find_include (name, path, include);
377                         free (include);
378
379                         debug ("ult_src: points to %s\n", base);
380
381                         recurse++;
382                         /* Take a copy; it's unwise to pass base directly to
383                          * a recursive call, as it may be freed.
384                          */
385                         new_name = xstrdup (base);
386                         ult = ult_src (new_name, path, NULL, flags, trace);
387                         free (new_name);
388                         recurse--;
389
390                         pipeline_wait (decomp);
391                         pipeline_free (decomp);
392                         free (decomp_base);
393                         return ult;
394                 }
395
396                 pipeline_wait (decomp);
397                 pipeline_free (decomp);
398                 free (decomp_base);
399         }
400
401         /* We have the ultimate source */
402         if (trace)
403                 ult_trace (trace, base);
404         return base;
405 }