1From bryanh@giraffe.giraffe.netgate.net Sat Nov 16 09:32:59 1996
2Received: from giraffe.giraffe.netgate.net by hera.cwi.nl with SMTP
3	id <AA24735@cwi.nl>; Sat, 16 Nov 1996 09:32:53 +0100
4Received: (from bryanh@localhost) by giraffe.giraffe.netgate.net (8.6.11/8.6.9) id AAA00639; Sat, 16 Nov 1996 00:32:46 -0800
5Date: Sat, 16 Nov 1996 00:32:46 -0800
6Message-Id: <199611160832.AAA00639@giraffe.giraffe.netgate.net>
7From: bryanh@giraffe.netgate.net (Bryan Henderson)
8To: Andries.Brouwer@cwi.nl
9In-Reply-To: <9611151043.AA01606=aeb@zeus.cwi.nl> (Andries.Brouwer@cwi.nl)
10Subject: Re: cross references for Linux man page package
11Status: RO
12
13>I hope a shell script or perl script?
14
15Well, no.  Shell scripts are too hard and I don't know perl.  So it's in
16tortured C.  It also needs the shhopt package (from sunsite), which
17effortlessly parses a command line, but not many people know about it.
18So maybe this isn't up to distributions standards.
19
20
21Here it is anyway.  You invoke it just like this:
22
23  preformat ls.1
24
25Or for a whole directory,
26
27  preformat *
28
29Or if you keep preformatted man pages elsewhere than /usr/man/preformat/catN,
30
31  preformat --mandir=/usr/local/doc/package_xyz/man *
32
33It makes the target directories where necessary and groffs and gzips the
34man pages into them.  If it finds a man page that looks like ".so whatever",
35it just does a symbolic link to the base file instead.
36
37--------------------------------------------------------------------------
38#include <string.h>
39#include <stdio.h>
40#include <stdlib.h>
41#include <unistd.h>
42#include <sys/types.h>
43#include <sys/stat.h>
44#include <shhopt.h>
45
46#define TRUE 1
47#define FALSE 0
48
49
50
51void
52compute_mkdir_command(const char *installed_path,
53                      char *mkdir_cmd, const int mkdir_cmd_l) {
54/*----------------------------------------------------------------------------
55  Figure out what, if any, mkdir command we need to create the directories
56  in which to put the file whose full pathname is <installed_path>.
57----------------------------------------------------------------------------*/
58  char *slash_p;  /* pointer to last slash in installed_path. */
59  char need_dir[strlen(installed_path)+1];
60  /* pathname of directory which must exist so we can install the man
61     page into it.  If we're just defaulting to the current directory,
62     then this is a null string.
63     */
64
65  slash_p = strrchr(installed_path, '/');
66  if (slash_p == NULL) need_dir[0] = '\0';
67  else {
68    int need_dir_l;   /* length for need_dir */
69    need_dir_l = slash_p - installed_path + 1;  /* includes slash */
70    strncpy(need_dir, installed_path, need_dir_l);
71    need_dir[need_dir_l] = '\0';   /* need that string terminator */
72  }
73
74  if (need_dir[0] == '\0')
75    mkdir_cmd[0] = '\0';
76  else {
77    struct stat stat_buf;  /* results of a stat system call */
78    int rc;  /* return code from stat() */
79
80    rc = stat(need_dir, &stat_buf);
81    if (rc == 0)
82      mkdir_cmd[0] = '\0';
83    else
84      sprintf(mkdir_cmd, "umask 002;mkdir --parents %s; ", need_dir);
85  }
86}
87
88
89
90void
91extract_dot_so_stmt(const char *man_page_source_path,
92                    char *dot_so_stmt, const int dot_so_stmt_l) {
93
94  FILE *source_file;
95
96  source_file = fopen(man_page_source_path, "r");
97  if (source_file != NULL) {
98    char buffer[200];  /* First line of file */
99
100    fgets(buffer, sizeof(buffer), source_file);
101    fclose(source_file);
102
103    if (strncmp(buffer, ".so ", 4) == 0)
104      snprintf(dot_so_stmt, dot_so_stmt_l, "%s", buffer);
105    else dot_so_stmt[0] = '\0';
106  } else dot_so_stmt[0] = '\0';
107}
108
109
110
111void
112format_page(const char *installed_path, const char *man_page_source_path,
113            const char *mkdir_cmd, int *rc_p) {
114/*----------------------------------------------------------------------------
115  Format and compress the groff source in file <man_page_source_path>
116  and put the output in <installed_path>.  Execute the possible mkdir
117  command <mkdir_cmd> too.
118-----------------------------------------------------------------------------*/
119  char shell_command[100+strlen(installed_path)
120                     + strlen(man_page_source_path)
121                     + strlen(mkdir_cmd)];
122    /* A pipeline we have the shell execute */
123  int rc; /* local return code */
124
125  snprintf(shell_command, sizeof(shell_command),
126           "%sgroff -Tlatin1 -mandoc %s | gzip >%s",
127           mkdir_cmd, man_page_source_path, installed_path);
128
129  printf("%s\n", shell_command);
130  rc = system(shell_command);
131  if (rc != 0) {
132    fprintf(stderr, "groff pipeline failed, rc = %d\n", rc);
133    *rc_p = 10;
134  } else {
135    *rc_p = 0;
136    chmod(installed_path,
137          S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH
138          );
139  }
140}
141
142
143
144void
145create_symlink(const char *installed_path, const char *dot_so_stmt,
146               const char *mkdir_cmd, int *rc_p) {
147/*----------------------------------------------------------------------------
148  Create a symlink from <installed_path> to the installed name of the man
149  page identified by <dot_so_stmt>.
150
151  We make some large assumptions about the .so statement, so this may return
152  gibberish.
153
154  Execute the possible mkdir command <mkdir_cmd> too.
155-----------------------------------------------------------------------------*/
156  char shell_command[100+strlen(mkdir_cmd) +
157                     strlen(installed_path) +
158                     strlen(dot_so_stmt)];
159    /* A pipeline we have the shell execute */
160  int rc; /* local return code */
161  char *slash_p;  /* pointer to last slash in .so statement, or NULL */
162
163  slash_p = strrchr(dot_so_stmt, '/');
164  if (slash_p == NULL) {
165    fprintf(stderr, "Cannot find the base filename "
166            "in the .so statement '%s'.  There is no slash ('/').\n",
167            dot_so_stmt);
168    *rc_p = 15;
169  } else if (*(slash_p+1) == '\0') {
170    fprintf(stderr, "Cannot find the base filename "
171            "in the .so statement '%s'.  There is nothing after the "
172            "last slash ('/').",
173            dot_so_stmt);
174    *rc_p = 13;
175  } else {
176    char link_contents[200];
177
178    strncpy(link_contents, slash_p+1, sizeof(link_contents)-10);
179    if (link_contents[strlen(link_contents)-1] == '\n')
180      link_contents[strlen(link_contents)-1] = '\0';
181    strcat(link_contents, ".gz");
182
183    sprintf(shell_command, "%sln --symbolic %s %s",
184            mkdir_cmd, link_contents, installed_path);
185
186    printf("%s\n", shell_command);
187    rc = system(shell_command);
188    if (rc != 0) {
189      fprintf(stderr, "ln pipeline failed, rc = %d\n", rc);
190      *rc_p = 10;
191    } else *rc_p = 0;
192  }
193}
194
195
196
197void
198install_it(char *installed_path, char *man_page_source_path, int *rc_p){
199/*----------------------------------------------------------------------------
200  Take the man page groff source in file <man_page_source_path>, format
201  it, compress it, and place it in file <installed_path>.
202
203  Special case:  If the file appears to be just a groff .so statement,
204  don't format it; instead, create a symbolic link that will do the same
205  thing as formatting the .so.  A .so statement looks like:
206
207     .so man3/basepage.3
208
209  and means to include all the groff source from the file man3/basepage.3.
210  So we just create a symbolic link to cat3/basepage.3.gz and save some
211  redundancy.
212
213
214  Make any directories necessary to create file <installed_path>.
215
216-----------------------------------------------------------------------------*/
217  char mkdir_cmd[30 + strlen(installed_path)];
218    /* A mkdir shell command to create the necessary directories.  Null
219       string if no directory needs creating.
220       */
221  char dot_so_stmt[200];
222    /* The .so statement from the man page source, if the man page appears
223       to be one that consists solely of a .so statement.  If it doesn't
224       appear so, this is an empty string.
225       */
226
227  /* We have to remove the file first, because it may be a symbolic link
228     for the purposes of having the same man page come up for multiple
229     commands.  If we just overwrite, we will be replacing the base file,
230     which we don't want to do.
231     */
232  unlink(installed_path);
233
234  compute_mkdir_command(installed_path, mkdir_cmd, sizeof(mkdir_cmd));
235
236  extract_dot_so_stmt(man_page_source_path, dot_so_stmt, sizeof(dot_so_stmt));
237
238  if (*dot_so_stmt != '\0')
239    create_symlink(installed_path, dot_so_stmt, mkdir_cmd, rc_p);
240  else
241    format_page(installed_path, man_page_source_path, mkdir_cmd, rc_p);
242}
243
244
245
246char *
247just_filename(const char *full_path) {
248/*----------------------------------------------------------------------------
249  Return pointer into <full_path> of start of filename part.
250  Return NULL if pathname ends with a slash (i.e. it's a directory).
251-----------------------------------------------------------------------------*/
252  char *slash_p;  /* Pointer to last slash in <full_path> */
253  char *filename; /* Our eventual result */
254
255  slash_p = strrchr(full_path, '/');
256  if (slash_p == NULL) filename = (char *) full_path;
257  else if (*(slash_p+1) == '\0') {
258    filename = NULL;
259  } else filename = slash_p+1;
260  return(filename);
261}
262
263
264
265int main(int argc, char *argv[]) {
266  char *mandir;
267  /* The directory in which the formatted man pages are to go.  This is
268     the parent directory of the cat1, cat2, etc. directories.
269     */
270  char default_mandir[] = "/usr/man/preformat";
271    /* default value for mandir, if user doesn't give --mandir option */
272  int error;   /* boolean: we've encountered an error */
273  int i; /* local for loop index */
274
275  const optStruct option_def[] = {
276    { 0, (char *) "mandir", OPT_STRING, &mandir, 0},
277    { 0, 0, OPT_END, 0, 0}
278  };
279  int argc_parse;       /* argc, except we modify it as we parse */
280  char **argv_parse;    /* argv, except we modify it as we parse */
281
282  mandir = default_mandir;  /* initial assumption - default */
283  argc_parse = argc; argv_parse = argv;
284  optParseOptions(&argc_parse, argv_parse, option_def, 0);
285  /* uses and sets argc_parse, argv_parse.  */
286  /* sets mandir (via option_def) */
287
288  error = FALSE;   /* no error yet */
289
290  for (i=1;i <= argc_parse-1 && !error; i++) {
291    /* Do one of the man pages specified in the program arguments */
292    char *man_page_source_path;
293      /* string: pathname of man page source file we're supposed to install
294       */
295    char *man_page_source_fn;   /* pointer within pathname to filename */
296    char *dot_p;   /* pointer within filename to last dot */
297
298    char man_section; /* man section number to which this page belongs */
299    char installed_path[100]; /* full pathname for installed man page file */
300
301    man_page_source_path = argv_parse[i];
302
303    man_page_source_fn = just_filename(man_page_source_path);
304    if (man_page_source_fn == NULL)
305      fprintf(stderr, "Need filename at the end of pathname: %s\n",
306              man_page_source_path);
307    else {
308      dot_p = strrchr(man_page_source_fn, '.');
309      if (dot_p == NULL) {
310        fprintf(stderr, "Invalid source file -- contains no period: %s\n",
311                man_page_source_fn);
312      } else if (*(dot_p+1) == '\0') {
313        fprintf(stderr, "Invalid source file -- need at least one character "
314                "after the last period: %s\n", man_page_source_fn);
315      } else {
316        int rc; /* local return code */
317        /* Filename has a dot with at least one character after it.
318           Manual section number is the character right after that dot.
319           */
320        man_section = *(dot_p+1);
321
322        sprintf(installed_path, "%s/cat%c/%s.gz",
323                mandir, man_section, man_page_source_fn);
324
325        install_it(installed_path, man_page_source_path, &rc);
326        if (rc != 0) error = TRUE;
327      }
328    }
329  }
330  return(error);
331}
332
333