Magellan Linux

Annotation of /tags/mkinitrd-6_1_11/busybox/miscutils/man.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 928 - (hide annotations) (download)
Wed Oct 28 13:31:19 2009 UTC (14 years, 7 months ago) by niro
File MIME type: text/plain
File size: 6880 byte(s)
tagged 'mkinitrd-6_1_11'
1 niro 816 /* mini man implementation for busybox
2     * Copyright (C) 2008 Denys Vlasenko <vda.linux@googlemail.com>
3     * Licensed under GPLv2, see file LICENSE in this tarball for details.
4     */
5    
6     #include "libbb.h"
7    
8     enum {
9     OPT_a = 1, /* all */
10     OPT_w = 2, /* print path */
11     };
12    
13     /* This is what I see on my desktop system being executed:
14    
15     (
16     echo ".ll 12.4i"
17     echo ".nr LL 12.4i"
18     echo ".pl 1100i"
19     gunzip -c '/usr/man/man1/bzip2.1.gz'
20     echo ".\\\""
21     echo ".pl \n(nlu+10"
22     ) | gtbl | nroff -Tlatin1 -mandoc | less
23    
24     */
25    
26     #if ENABLE_FEATURE_SEAMLESS_LZMA
27     #define Z_SUFFIX ".lzma"
28     #elif ENABLE_FEATURE_SEAMLESS_BZ2
29     #define Z_SUFFIX ".bz2"
30     #elif ENABLE_FEATURE_SEAMLESS_GZ
31     #define Z_SUFFIX ".gz"
32     #else
33     #define Z_SUFFIX ""
34     #endif
35    
36     static int show_manpage(const char *pager, char *man_filename, int man, int level);
37    
38     static int run_pipe(const char *pager, char *man_filename, int man, int level)
39     {
40     char *cmd;
41    
42     /* Prevent man page link loops */
43     if (level > 10)
44     return 0;
45    
46     if (access(man_filename, R_OK) != 0)
47     return 0;
48    
49     if (option_mask32 & OPT_w) {
50     puts(man_filename);
51     return 1;
52     }
53    
54     if (man) { /* man page, not cat page */
55     /* Is this a link to another manpage? */
56     /* The link has the following on the first line: */
57     /* ".so another_man_page" */
58    
59     struct stat sb;
60     char *line;
61     char *linkname, *p;
62    
63     /* On my system:
64     * man1/genhostid.1.gz: 203 bytes - smallest real manpage
65     * man2/path_resolution.2.gz: 114 bytes - largest link
66     */
67     xstat(man_filename, &sb);
68     if (sb.st_size > 300) /* err on the safe side */
69     goto ordinary_manpage;
70    
71     line = xmalloc_open_zipped_read_close(man_filename, NULL);
72     if (!line || strncmp(line, ".so ", 4) != 0) {
73     free(line);
74     goto ordinary_manpage;
75     }
76     /* Example: man2/path_resolution.2.gz contains
77     * ".so man7/path_resolution.7\n<junk>"
78     */
79     *strchrnul(line, '\n') = '\0';
80     linkname = skip_whitespace(&line[4]);
81    
82     /* If link has no slashes, we just replace man page name.
83     * If link has slashes (however many), we go back *once*.
84     * ".so zzz/ggg/page.3" does NOT go back two levels. */
85     p = strrchr(man_filename, '/');
86     if (!p)
87     goto ordinary_manpage;
88     *p = '\0';
89     if (strchr(linkname, '/')) {
90     p = strrchr(man_filename, '/');
91     if (!p)
92     goto ordinary_manpage;
93     *p = '\0';
94     }
95    
96     /* Links do not have .gz extensions, even if manpage
97     * is compressed */
98     man_filename = xasprintf("%s/%s" Z_SUFFIX, man_filename, linkname);
99     free(line);
100     /* Note: we leak "new" man_filename string as well... */
101     if (show_manpage(pager, man_filename, man, level + 1))
102     return 1;
103     /* else: show the link, it's better than nothing */
104     }
105    
106     ordinary_manpage:
107     close(STDIN_FILENO);
108     open_zipped(man_filename); /* guaranteed to use fd 0 (STDIN_FILENO) */
109     /* "2>&1" is added so that nroff errors are shown in pager too.
110     * Otherwise it may show just empty screen */
111     cmd = xasprintf(
112     man ? "gtbl | nroff -Tlatin1 -mandoc 2>&1 | %s"
113     : "%s",
114     pager);
115     system(cmd);
116     free(cmd);
117     return 1;
118     }
119    
120     /* man_filename is of the form "/dir/dir/dir/name.s" Z_SUFFIX */
121     static int show_manpage(const char *pager, char *man_filename, int man, int level)
122     {
123     #if ENABLE_FEATURE_SEAMLESS_LZMA
124     if (run_pipe(pager, man_filename, man, level))
125     return 1;
126     #endif
127    
128     #if ENABLE_FEATURE_SEAMLESS_BZ2
129     #if ENABLE_FEATURE_SEAMLESS_LZMA
130     strcpy(strrchr(man_filename, '.') + 1, "bz2");
131     #endif
132     if (run_pipe(pager, man_filename, man, level))
133     return 1;
134     #endif
135    
136     #if ENABLE_FEATURE_SEAMLESS_GZ
137     #if ENABLE_FEATURE_SEAMLESS_LZMA || ENABLE_FEATURE_SEAMLESS_BZ2
138     strcpy(strrchr(man_filename, '.') + 1, "gz");
139     #endif
140     if (run_pipe(pager, man_filename, man, level))
141     return 1;
142     #endif
143    
144     #if ENABLE_FEATURE_SEAMLESS_LZMA || ENABLE_FEATURE_SEAMLESS_BZ2 || ENABLE_FEATURE_SEAMLESS_GZ
145     *strrchr(man_filename, '.') = '\0';
146     #endif
147     if (run_pipe(pager, man_filename, man, level))
148     return 1;
149    
150     return 0;
151     }
152    
153     int man_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
154     int man_main(int argc UNUSED_PARAM, char **argv)
155     {
156     parser_t *parser;
157     const char *pager;
158     char **man_path_list;
159     char *sec_list;
160     char *cur_path, *cur_sect;
161     int count_mp, cur_mp;
162     int opt, not_found;
163     char *token[2];
164    
165     opt_complementary = "-1"; /* at least one argument */
166     opt = getopt32(argv, "+aw");
167     argv += optind;
168    
169     sec_list = xstrdup("1:2:3:4:5:6:7:8:9");
170     /* Last valid man_path_list[] is [0x10] */
171     count_mp = 0;
172     man_path_list = xzalloc(0x11 * sizeof(man_path_list[0]));
173     man_path_list[0] = getenv("MANPATH");
174     if (!man_path_list[0]) /* default, may be overridden by /etc/man.conf */
175     man_path_list[0] = (char*)"/usr/man";
176     else
177     count_mp++;
178     pager = getenv("MANPAGER");
179     if (!pager) {
180     pager = getenv("PAGER");
181     if (!pager)
182     pager = "more";
183     }
184    
185     /* Parse man.conf */
186     parser = config_open2("/etc/man.conf", fopen_for_read);
187     while (config_read(parser, token, 2, 0, "# \t", PARSE_NORMAL)) {
188     if (!token[1])
189     continue;
190     if (strcmp("MANPATH", token[0]) == 0) {
191     /* Do we already have it? */
192     char **path_element = man_path_list;
193     while (*path_element) {
194     if (strcmp(*path_element, token[1]) == 0)
195     goto skip;
196     path_element++;
197     }
198     man_path_list = xrealloc_vector(man_path_list, 4, count_mp);
199     man_path_list[count_mp] = xstrdup(token[1]);
200     count_mp++;
201     /* man_path_list is NULL terminated */
202     /*man_path_list[count_mp] = NULL; - xrealloc_vector did it */
203     }
204     if (strcmp("MANSECT", token[0]) == 0) {
205     free(sec_list);
206     sec_list = xstrdup(token[1]);
207     }
208     skip: ;
209     }
210     config_close(parser);
211    
212     not_found = 0;
213     do { /* for each argv[] */
214     int found = 0;
215     cur_mp = 0;
216    
217     if (strchr(*argv, '/')) {
218     found = show_manpage(pager, *argv, /*man:*/ 1, 0);
219     goto check_found;
220     }
221     while ((cur_path = man_path_list[cur_mp++]) != NULL) {
222     /* for each MANPATH */
223     do { /* for each MANPATH item */
224     char *next_path = strchrnul(cur_path, ':');
225     int path_len = next_path - cur_path;
226     cur_sect = sec_list;
227     do { /* for each section */
228     char *next_sect = strchrnul(cur_sect, ':');
229     int sect_len = next_sect - cur_sect;
230     char *man_filename;
231     int cat0man1 = 0;
232    
233     /* Search for cat, then man page */
234     while (cat0man1 < 2) {
235     int found_here;
236     man_filename = xasprintf("%.*s/%s%.*s/%s.%.*s" Z_SUFFIX,
237     path_len, cur_path,
238     "cat\0man" + (cat0man1 * 4),
239     sect_len, cur_sect,
240     *argv,
241     sect_len, cur_sect);
242     found_here = show_manpage(pager, man_filename, cat0man1, 0);
243     found |= found_here;
244     cat0man1 += found_here + 1;
245     free(man_filename);
246     }
247    
248     if (found && !(opt & OPT_a))
249     goto next_arg;
250     cur_sect = next_sect;
251     while (*cur_sect == ':')
252     cur_sect++;
253     } while (*cur_sect);
254     cur_path = next_path;
255     while (*cur_path == ':')
256     cur_path++;
257     } while (*cur_path);
258     }
259     check_found:
260     if (!found) {
261     bb_error_msg("no manual entry for '%s'", *argv);
262     not_found = 1;
263     }
264     next_arg:
265     argv++;
266     } while (*argv);
267    
268     return not_found;
269     }