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