source: MondoRescue/branches/3.3/mindi-busybox/miscutils/man.c@ 3647

Last change on this file since 3647 was 3621, checked in by Bruno Cornec, 10 years ago

New 3?3 banch for incorporation of latest busybox 1.25. Changing minor version to handle potential incompatibilities.

  • Property svn:eol-style set to native
File size: 7.8 KB
Line 
1/* mini man implementation for busybox
2 * Copyright (C) 2008 Denys Vlasenko <vda.linux@googlemail.com>
3 * Licensed under GPLv2, see file LICENSE in this source tree.
4 */
5
6//usage:#define man_trivial_usage
7//usage: "[-aw] [MANPAGE]..."
8//usage:#define man_full_usage "\n\n"
9//usage: "Format and display manual page\n"
10//usage: "\n -a Display all pages"
11//usage: "\n -w Show page locations"
12
13#include "libbb.h"
14
15enum {
16 OPT_a = 1, /* all */
17 OPT_w = 2, /* print path */
18};
19
20/* This is what I see on my desktop system being executed:
21
22(
23echo ".ll 12.4i"
24echo ".nr LL 12.4i"
25echo ".pl 1100i"
26gunzip -c '/usr/man/man1/bzip2.1.gz'
27echo ".\\\""
28echo ".pl \n(nlu+10"
29) | gtbl | nroff -Tlatin1 -mandoc | less
30
31*/
32
33static int show_manpage(const char *pager, char *man_filename, int man, int level);
34
35static int run_pipe(const char *pager, char *man_filename, int man, int level)
36{
37 char *cmd;
38
39 /* Prevent man page link loops */
40 if (level > 10)
41 return 0;
42
43 if (access(man_filename, R_OK) != 0)
44 return 0;
45
46 if (option_mask32 & OPT_w) {
47 puts(man_filename);
48 return 1;
49 }
50
51 if (man) { /* man page, not cat page */
52 /* Is this a link to another manpage? */
53 /* The link has the following on the first line: */
54 /* ".so another_man_page" */
55
56 struct stat sb;
57 char *line;
58 char *linkname, *p;
59
60 /* On my system:
61 * man1/genhostid.1.gz: 203 bytes - smallest real manpage
62 * man2/path_resolution.2.gz: 114 bytes - largest link
63 */
64 xstat(man_filename, &sb);
65 if (sb.st_size > 300) /* err on the safe side */
66 goto ordinary_manpage;
67
68 line = xmalloc_open_zipped_read_close(man_filename, NULL);
69 if (!line || !is_prefixed_with(line, ".so ")) {
70 free(line);
71 goto ordinary_manpage;
72 }
73 /* Example: man2/path_resolution.2.gz contains
74 * ".so man7/path_resolution.7\n<junk>"
75 */
76 *strchrnul(line, '\n') = '\0';
77 linkname = skip_whitespace(&line[4]);
78
79 /* If link has no slashes, we just replace man page name.
80 * If link has slashes (however many), we go back *once*.
81 * ".so zzz/ggg/page.3" does NOT go back two levels. */
82 p = strrchr(man_filename, '/');
83 if (!p)
84 goto ordinary_manpage;
85 *p = '\0';
86 if (strchr(linkname, '/')) {
87 p = strrchr(man_filename, '/');
88 if (!p)
89 goto ordinary_manpage;
90 *p = '\0';
91 }
92
93 /* Links do not have .gz extensions, even if manpage
94 * is compressed */
95 man_filename = xasprintf("%s/%s", man_filename, linkname);
96 free(line);
97 /* Note: we leak "new" man_filename string as well... */
98 if (show_manpage(pager, man_filename, man, level + 1))
99 return 1;
100 /* else: show the link, it's better than nothing */
101 }
102
103 ordinary_manpage:
104 close(STDIN_FILENO);
105 open_zipped(man_filename, /*fail_if_not_compressed:*/ 0); /* guaranteed to use fd 0 (STDIN_FILENO) */
106 /* "2>&1" is added so that nroff errors are shown in pager too.
107 * Otherwise it may show just empty screen */
108 cmd = xasprintf(
109 /* replaced -Tlatin1 with -Tascii for non-UTF8 displays */
110 man ? "gtbl | nroff -Tascii -mandoc 2>&1 | %s"
111 : "%s",
112 pager);
113 system(cmd);
114 free(cmd);
115 return 1;
116}
117
118/* man_filename is of the form "/dir/dir/dir/name.s" */
119static int show_manpage(const char *pager, char *man_filename, int man, int level)
120{
121#if SEAMLESS_COMPRESSION
122 /* We leak this allocation... */
123 char *filename_with_zext = xasprintf("%s.lzma", man_filename);
124 char *ext = strrchr(filename_with_zext, '.') + 1;
125#endif
126
127#if ENABLE_FEATURE_SEAMLESS_LZMA
128 if (run_pipe(pager, filename_with_zext, man, level))
129 return 1;
130#endif
131#if ENABLE_FEATURE_SEAMLESS_XZ
132 strcpy(ext, "xz");
133 if (run_pipe(pager, filename_with_zext, man, level))
134 return 1;
135#endif
136#if ENABLE_FEATURE_SEAMLESS_BZ2
137 strcpy(ext, "bz2");
138 if (run_pipe(pager, filename_with_zext, man, level))
139 return 1;
140#endif
141#if ENABLE_FEATURE_SEAMLESS_GZ
142 strcpy(ext, "gz");
143 if (run_pipe(pager, filename_with_zext, man, level))
144 return 1;
145#endif
146
147 return run_pipe(pager, man_filename, man, level);
148}
149
150static char **add_MANPATH(char **man_path_list, int *count_mp, char *path)
151{
152 if (path) while (*path) {
153 char *next_path;
154 char **path_element;
155
156 next_path = strchr(path, ':');
157 if (next_path) {
158 if (next_path == path) /* "::"? */
159 goto next;
160 *next_path = '\0';
161 }
162 /* Do we already have path? */
163 path_element = man_path_list;
164 if (path_element) while (*path_element) {
165 if (strcmp(*path_element, path) == 0)
166 goto skip;
167 path_element++;
168 }
169 man_path_list = xrealloc_vector(man_path_list, 4, *count_mp);
170 man_path_list[*count_mp] = xstrdup(path);
171 (*count_mp)++;
172 /* man_path_list is NULL terminated */
173 /* man_path_list[*count_mp] = NULL; - xrealloc_vector did it */
174 skip:
175 if (!next_path)
176 break;
177 /* "path" may be a result of getenv(), be nice and don't mangle it */
178 *next_path = ':';
179 next:
180 path = next_path + 1;
181 }
182 return man_path_list;
183}
184
185int man_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
186int man_main(int argc UNUSED_PARAM, char **argv)
187{
188 parser_t *parser;
189 const char *pager = ENABLE_LESS ? "less" : "more";
190 char *sec_list;
191 char *cur_path, *cur_sect;
192 char **man_path_list;
193 int count_mp;
194 int cur_mp;
195 int opt, not_found;
196 char *token[2];
197
198 opt_complementary = "-1"; /* at least one argument */
199 opt = getopt32(argv, "+aw");
200 argv += optind;
201
202 sec_list = xstrdup("0p:1:1p:2:3:3p:4:5:6:7:8:9");
203
204 count_mp = 0;
205 man_path_list = add_MANPATH(NULL, &count_mp,
206 getenv("MANDATORY_MANPATH"+10) /* "MANPATH" */
207 );
208 if (!man_path_list) {
209 /* default, may be overridden by /etc/man.conf */
210 man_path_list = xzalloc(2 * sizeof(man_path_list[0]));
211 man_path_list[0] = (char*)"/usr/man";
212 /* count_mp stays 0.
213 * Thus, man.conf will overwrite man_path_list[0]
214 * if a path is defined there.
215 */
216 }
217
218 /* Parse man.conf[ig] or man_db.conf */
219 /* man version 1.6f uses man.config */
220 /* man-db implementation of man uses man_db.conf */
221 parser = config_open2("/etc/man.config", fopen_for_read);
222 if (!parser)
223 parser = config_open2("/etc/man.conf", fopen_for_read);
224 if (!parser)
225 parser = config_open2("/etc/man_db.conf", fopen_for_read);
226
227 while (config_read(parser, token, 2, 0, "# \t", PARSE_NORMAL)) {
228 if (!token[1])
229 continue;
230 if (strcmp("DEFINE", token[0]) == 0) {
231 if (is_prefixed_with("pager", token[1])) {
232 pager = xstrdup(skip_whitespace(token[1]) + 5);
233 }
234 } else
235 if (strcmp("MANDATORY_MANPATH"+10, token[0]) == 0 /* "MANPATH"? */
236 || strcmp("MANDATORY_MANPATH", token[0]) == 0
237 ) {
238 man_path_list = add_MANPATH(man_path_list, &count_mp, token[1]);
239 }
240 if (strcmp("MANSECT", token[0]) == 0) {
241 free(sec_list);
242 sec_list = xstrdup(token[1]);
243 }
244 }
245 config_close(parser);
246
247 {
248 /* environment overrides setting from man.config */
249 char *env_pager = getenv("MANPAGER");
250 if (!env_pager)
251 env_pager = getenv("PAGER");
252 if (env_pager)
253 pager = env_pager;
254 }
255
256 not_found = 0;
257 do { /* for each argv[] */
258 int found = 0;
259 cur_mp = 0;
260
261 if (strchr(*argv, '/')) {
262 found = show_manpage(pager, *argv, /*man:*/ 1, 0);
263 goto check_found;
264 }
265 while ((cur_path = man_path_list[cur_mp++]) != NULL) {
266 /* for each MANPATH */
267 cur_sect = sec_list;
268 do { /* for each section */
269 char *next_sect = strchrnul(cur_sect, ':');
270 int sect_len = next_sect - cur_sect;
271 char *man_filename;
272 int cat0man1 = 0;
273
274 /* Search for cat, then man page */
275 while (cat0man1 < 2) {
276 int found_here;
277 man_filename = xasprintf("%s/%s%.*s/%s.%.*s",
278 cur_path,
279 "cat\0man" + (cat0man1 * 4),
280 sect_len, cur_sect,
281 *argv,
282 sect_len, cur_sect);
283 found_here = show_manpage(pager, man_filename, cat0man1, 0);
284 found |= found_here;
285 cat0man1 += found_here + 1;
286 free(man_filename);
287 }
288
289 if (found && !(opt & OPT_a))
290 goto next_arg;
291 cur_sect = next_sect;
292 while (*cur_sect == ':')
293 cur_sect++;
294 } while (*cur_sect);
295 }
296 check_found:
297 if (!found) {
298 bb_error_msg("no manual entry for '%s'", *argv);
299 not_found = 1;
300 }
301 next_arg:
302 argv++;
303 } while (*argv);
304
305 return not_found;
306}
Note: See TracBrowser for help on using the repository browser.