man.c 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  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 tarball for details.
  4. */
  5. #include "libbb.h"
  6. enum {
  7. OPT_a = 1, /* all */
  8. OPT_w = 2, /* print path */
  9. };
  10. /* This is what I see on my desktop system being executed:
  11. (
  12. echo ".ll 12.4i"
  13. echo ".nr LL 12.4i"
  14. echo ".pl 1100i"
  15. gunzip -c '/usr/man/man1/bzip2.1.gz'
  16. echo ".\\\""
  17. echo ".pl \n(nlu+10"
  18. ) | gtbl | nroff -Tlatin1 -mandoc | less
  19. */
  20. /* Trick gcc to reuse "cat" string. */
  21. #define STR_catNULmanNUL "cat\0man"
  22. #define STR_cat "cat\0man"
  23. static int run_pipe(const char *unpacker, const char *pager, char *man_filename, int man)
  24. {
  25. char *cmd;
  26. if (access(man_filename, R_OK) != 0)
  27. return 0;
  28. if (option_mask32 & OPT_w) {
  29. puts(man_filename);
  30. return 1;
  31. }
  32. /* "2>&1" is added so that nroff errors are shown in pager too.
  33. * Otherwise it may show just empty screen */
  34. cmd = xasprintf(
  35. man ? "%s '%s' | gtbl | nroff -Tlatin1 -mandoc 2>&1 | %s"
  36. : "%s '%s' | %s",
  37. unpacker, man_filename, pager);
  38. system(cmd);
  39. free(cmd);
  40. return 1;
  41. }
  42. /* man_filename is of the form "/dir/dir/dir/name.s.bz2" */
  43. static int show_manpage(const char *pager, char *man_filename, int man)
  44. {
  45. int len;
  46. if (run_pipe("bunzip2 -c", pager, man_filename, man))
  47. return 1;
  48. len = strlen(man_filename) - 1;
  49. man_filename[len] = '\0'; /* ".bz2" -> ".gz" */
  50. man_filename[len - 2] = 'g';
  51. if (run_pipe("gunzip -c", pager, man_filename, man))
  52. return 1;
  53. man_filename[len - 3] = '\0'; /* ".gz" -> "" */
  54. if (run_pipe(STR_cat, pager, man_filename, man))
  55. return 1;
  56. return 0;
  57. }
  58. int man_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
  59. int man_main(int argc UNUSED_PARAM, char **argv)
  60. {
  61. parser_t *parser;
  62. const char *pager;
  63. char **man_path_list;
  64. char *sec_list;
  65. char *cur_path, *cur_sect;
  66. int count_mp, cur_mp;
  67. int opt, not_found;
  68. char *token[2];
  69. opt_complementary = "-1"; /* at least one argument */
  70. opt = getopt32(argv, "+aw");
  71. argv += optind;
  72. sec_list = xstrdup("1:2:3:4:5:6:7:8:9");
  73. /* Last valid man_path_list[] is [0x10] */
  74. man_path_list = xzalloc(0x11 * sizeof(man_path_list[0]));
  75. count_mp = 0;
  76. man_path_list[0] = xstrdup(getenv("MANPATH"));
  77. if (man_path_list[0])
  78. count_mp++;
  79. pager = getenv("MANPAGER");
  80. if (!pager) {
  81. pager = getenv("PAGER");
  82. if (!pager)
  83. pager = "more";
  84. }
  85. /* Parse man.conf */
  86. parser = config_open("/etc/man.conf");
  87. while (config_read(parser, token, 2, 0, "# \t", PARSE_NORMAL)) {
  88. if (!token[1])
  89. continue;
  90. if (strcmp("MANPATH", token[0]) == 0) {
  91. man_path_list = xrealloc_vector(man_path_list, 4, count_mp);
  92. man_path_list[count_mp] = xstrdup(token[1]);
  93. count_mp++;
  94. /* man_path_list is NULL terminated */
  95. /*man_path_list[count_mp] = NULL; - xrealloc_vector did it */
  96. }
  97. if (strcmp("MANSECT", token[0]) == 0) {
  98. free(sec_list);
  99. sec_list = xstrdup(token[1]);
  100. }
  101. }
  102. config_close(parser);
  103. // TODO: my man3/getpwuid.3.gz contains just one line:
  104. // .so man3/getpwnam.3
  105. // (and I _dont_ have man3/getpwnam.3, I have man3/getpwnam.3.gz)
  106. // need to support this...
  107. not_found = 0;
  108. do { /* for each argv[] */
  109. int found = 0;
  110. cur_mp = 0;
  111. while ((cur_path = man_path_list[cur_mp++]) != NULL) {
  112. /* for each MANPATH */
  113. do { /* for each MANPATH item */
  114. char *next_path = strchrnul(cur_path, ':');
  115. int path_len = next_path - cur_path;
  116. cur_sect = sec_list;
  117. do { /* for each section */
  118. char *next_sect = strchrnul(cur_sect, ':');
  119. int sect_len = next_sect - cur_sect;
  120. char *man_filename;
  121. int cat0man1 = 0;
  122. /* Search for cat, then man page */
  123. while (cat0man1 < 2) {
  124. int found_here;
  125. man_filename = xasprintf("%.*s/%s%.*s/%s.%.*s" ".bz2",
  126. path_len, cur_path,
  127. STR_catNULmanNUL + cat0man1 * 4,
  128. sect_len, cur_sect,
  129. *argv,
  130. sect_len, cur_sect);
  131. found_here = show_manpage(pager, man_filename, cat0man1);
  132. found |= found_here;
  133. cat0man1 += found_here + 1;
  134. free(man_filename);
  135. }
  136. if (found && !(opt & OPT_a))
  137. goto next_arg;
  138. cur_sect = next_sect;
  139. while (*cur_sect == ':')
  140. cur_sect++;
  141. } while (*cur_sect);
  142. cur_path = next_path;
  143. while (*cur_path == ':')
  144. cur_path++;
  145. } while (*cur_path);
  146. }
  147. if (!found) {
  148. bb_error_msg("no manual entry for '%s'", *argv);
  149. not_found = 1;
  150. }
  151. next_arg:
  152. argv++;
  153. } while (*argv);
  154. return not_found;
  155. }