aboutsummaryrefslogtreecommitdiff
path: root/toys/pending/man.c
blob: 912be8b5ec45bd8a2eaf116ebb84286e2d7c00fc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
/* man.c - Read system documentation
 *
 * Copyright 2019 makepost <makepost@firemail.cc>
 *
 * See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/man.html

USE_MAN(NEWTOY(man, "<1>1", TOYFLAG_USR|TOYFLAG_BIN))

config MAN
  bool "man"
  default n
  help
    usage: man COMMAND

    Read manual for system command.
*/

#define FOR_man
#include <toys.h>
#include <glob.h>

GLOBALS(
  char any, cell, *f, *line;
)

static void newln()
{
  if (TT.any) putchar('\n');
  if (TT.any && TT.cell != 2) putchar('\n'); // gawk alias
  TT.any = TT.cell = 0;
}
static void put(char *x)
{
  while (*x && *x != '\n') TT.any = putchar(*x++);
}

// Substitute with same length or shorter.
static void s(char *x, char *y)
{
  int i = strlen(x), j = strlen(y), k, l;

  for (k = 0; TT.line[k]; k++) if (!strncmp(x, &TT.line[k], i)) {
    memmove(&TT.line[k], y, j);
    for (l = k += j; TT.line[l]; l++) TT.line[l] = TT.line[l + i - j];
  }
}

static char start(char *x)
{
  return !strncmp(x, TT.line, strlen(x));
}

static void trim(char *x)
{
  if (start(x)) while (*x++) TT.line++;
}

static void do_man(FILE *fp)
{
  size_t len = 0;
  char *line = 0;
  while (getline(&line, &len, fp) > 0) {
    TT.line = line;
    s("\\fB", ""), s("\\fI", ""), s("\\fP", ""), s("\\fR", ""); // bash bold,ita
    s("\\(aq", "'"), s("\\(cq", "'"), s("\\(dq", "\""); // bash,rsync quote
    s("\\*(lq", "\""), s("\\*(rq", "\""); // gawk quote
    s("\\(bu", "*"), s("\\(bv", "|"); // bash symbol
    s("\\&", ""), s("\\f(CW", ""); // gawk,rsync fancy
    s("\\-", "-"), s("\\(", ""), s("\\^", ""), s("\\e", "\\"); // bash escape
    s("\\*(", "#"); // gawk var
    if (start(".BR")) trim(".BR "), s(" ", ""); // bash boldpunct
    if (start(".IP")) newln(), trim(".IP "); // bash list
    if (start(".IR")) trim(".IR "), s(" ", ""); // bash itapunct
    trim(".B "); // bash bold
    trim(".BI "); // gawk boldita
    trim(".FN "); // bash filename
    trim(".I "); // bash ita
    trim(".if n "); // bash nroff
    if (start(".PP")) newln(); // bash paragraph
    else if (start(".SM")); // bash small
    else if (start(".S")) newln(), put(TT.line + 4), newln(); // bash section
    else if (start(".so")) put("See "), put(basename(TT.line + 4)); // lastb
    else if (start(".TH")) s("\"", " "), put(TT.line + 4); // gawk,git head
    else if (start(".TP")) newln(), TT.cell = 1; // bash table
    else if (start(".") || start("\'")); // bash,git garbage
    else if (!*TT.line); // emerge
    else ((TT.cell != 0) && TT.cell++), put(" "), put(TT.line);
  }
  newln();
  free(line);
  fclose(fp);
}

static FILE *bzcat()
{
  char cmd[FILENAME_MAX];
  snprintf(cmd, sizeof(cmd), "bzcat %s", TT.f);
  return popen(cmd, "r");
}

static char *find(char *path, int suf)
{
  glob_t g;
  int i;
  size_t len = strlen(*toys.optargs);
  char *name;
  glob(path, 0, 0, &g);
  for (i = 0; !TT.f && i < g.gl_pathc; i++) {
    name = basename(g.gl_pathv[i]);
    if (strlen(name) == len + suf && !strncmp(name, *toys.optargs, len))
      TT.f = strdup(g.gl_pathv[i]);
  }
  globfree(&g);
  return TT.f;
}

void man_main(void)
{
  chdir("/usr/share/man");
  if (find("man?/*.?.bz2", 6)) do_man(bzcat()); // curl_strequal
  else if (find("man?/*.bz2", 4)) do_man(bzcat()); // curl_strequal.3
  else if (find("man?/*.?", 2)) do_man(fopen(TT.f, "r")); // curl_strnequal
  else if (find("man?/*", 0)) do_man(fopen(TT.f, "r")); // curl_strnequal.3
  if (TT.f) free(TT.f);
}