1 1.38 christos /* $NetBSD: catman.c,v 1.38 2019/10/12 17:26:26 christos Exp $ */ 2 1.10 dante 3 1.1 jtc /* 4 1.10 dante * Copyright (c) 1998 The NetBSD Foundation, Inc. 5 1.1 jtc * All rights reserved. 6 1.1 jtc * 7 1.10 dante * Author: Baldassare Dante Profeta <dante (at) mclink.it> 8 1.10 dante * 9 1.1 jtc * Redistribution and use in source and binary forms, with or without 10 1.1 jtc * modification, are permitted provided that the following conditions 11 1.1 jtc * are met: 12 1.1 jtc * 1. Redistributions of source code must retain the above copyright 13 1.1 jtc * notice, this list of conditions and the following disclaimer. 14 1.1 jtc * 2. Redistributions in binary form must reproduce the above copyright 15 1.1 jtc * notice, this list of conditions and the following disclaimer in the 16 1.1 jtc * documentation and/or other materials provided with the distribution. 17 1.1 jtc * 18 1.10 dante * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 19 1.10 dante * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 20 1.10 dante * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 21 1.10 dante * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 22 1.10 dante * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 1.10 dante * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 1.10 dante * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 1.10 dante * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 1.10 dante * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 1.10 dante * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 1.10 dante * POSSIBILITY OF SUCH DAMAGE. 29 1.1 jtc */ 30 1.2 mycroft 31 1.36 dholland #include <sys/cdefs.h> 32 1.36 dholland #ifndef lint 33 1.38 christos __RCSID("$NetBSD: catman.c,v 1.38 2019/10/12 17:26:26 christos Exp $"); 34 1.36 dholland #endif /* not lint */ 35 1.36 dholland 36 1.10 dante #include <sys/types.h> 37 1.10 dante #include <sys/queue.h> 38 1.10 dante #include <sys/param.h> 39 1.4 chopps #include <sys/stat.h> 40 1.4 chopps #include <sys/wait.h> 41 1.14 tsutsui #include <sys/utsname.h> 42 1.10 dante #include <ctype.h> 43 1.6 cgd #include <dirent.h> 44 1.6 cgd #include <err.h> 45 1.6 cgd #include <errno.h> 46 1.10 dante #include <fnmatch.h> 47 1.6 cgd #include <limits.h> 48 1.10 dante #include <libgen.h> 49 1.1 jtc #include <stdio.h> 50 1.1 jtc #include <stdlib.h> 51 1.1 jtc #include <string.h> 52 1.1 jtc #include <unistd.h> 53 1.10 dante #include <glob.h> 54 1.1 jtc 55 1.18 mycroft #include "manconf.h" 56 1.6 cgd #include "pathnames.h" 57 1.4 chopps 58 1.10 dante int f_nowhatis = 0; 59 1.10 dante int f_noaction = 0; 60 1.10 dante int f_noformat = 0; 61 1.10 dante int f_ignerr = 0; 62 1.10 dante int f_noprint = 0; 63 1.10 dante int dowhatis = 0; 64 1.10 dante 65 1.15 jdolecek TAG *defp; /* pointer to _default list */ 66 1.15 jdolecek 67 1.26 chuck static void setdefentries(char *, char *, const char *); 68 1.26 chuck static void uniquepath(void); 69 1.26 chuck static void catman(void); 70 1.26 chuck static void scanmandir(const char *, const char *); 71 1.26 chuck static int splitentry(char *, char *, size_t, char *, size_t); 72 1.26 chuck static void setcatsuffix(char *, const char *, const char *); 73 1.26 chuck static void makecat(const char *, const char *, const char *, const char *); 74 1.26 chuck static void makewhatis(void); 75 1.26 chuck static void dosystem(const char *); 76 1.30 joerg __dead static void usage(void); 77 1.10 dante 78 1.1 jtc 79 1.1 jtc int 80 1.26 chuck main(int argc, char * const *argv) 81 1.1 jtc { 82 1.10 dante char *m_path = NULL; 83 1.10 dante char *m_add = NULL; 84 1.4 chopps int c; 85 1.1 jtc 86 1.10 dante while ((c = getopt(argc, argv, "km:M:npsw")) != -1) { 87 1.1 jtc switch (c) { 88 1.4 chopps case 'k': 89 1.4 chopps f_ignerr = 1; 90 1.4 chopps break; 91 1.1 jtc case 'n': 92 1.4 chopps f_nowhatis = 1; 93 1.1 jtc break; 94 1.1 jtc case 'p': 95 1.4 chopps f_noaction = 1; 96 1.4 chopps break; 97 1.4 chopps case 's': 98 1.4 chopps f_noprint = 1; 99 1.1 jtc break; 100 1.1 jtc case 'w': 101 1.4 chopps f_noformat = 1; 102 1.1 jtc break; 103 1.10 dante case 'm': 104 1.10 dante m_add = optarg; 105 1.10 dante break; 106 1.1 jtc case 'M': 107 1.10 dante m_path = optarg; 108 1.1 jtc break; 109 1.1 jtc default: 110 1.1 jtc usage(); 111 1.1 jtc } 112 1.1 jtc } 113 1.1 jtc 114 1.1 jtc argc -= optind; 115 1.1 jtc argv += optind; 116 1.1 jtc 117 1.4 chopps if (f_noprint && f_noaction) 118 1.4 chopps f_noprint = 0; 119 1.4 chopps 120 1.6 cgd if (argc > 1) 121 1.6 cgd usage(); 122 1.10 dante 123 1.10 dante config(_PATH_MANCONF); 124 1.15 jdolecek setdefentries(m_path, m_add, (argc == 0) ? NULL : argv[argc-1]); 125 1.10 dante uniquepath(); 126 1.1 jtc 127 1.5 chopps if (f_noformat == 0 || f_nowhatis == 0) 128 1.10 dante catman(); 129 1.4 chopps if (f_nowhatis == 0 && dowhatis) 130 1.10 dante makewhatis(); 131 1.1 jtc 132 1.10 dante return(0); 133 1.1 jtc } 134 1.1 jtc 135 1.10 dante static void 136 1.26 chuck setdefentries(char *m_path, char *m_add, const char *sections) 137 1.10 dante { 138 1.15 jdolecek TAG *defnewp, *sectnewp, *subp; 139 1.15 jdolecek ENTRY *e_defp, *e_subp; 140 1.28 xtraeme const char *p, *slashp; 141 1.28 xtraeme char *machine; 142 1.10 dante char buf[MAXPATHLEN * 2]; 143 1.10 dante int i; 144 1.10 dante 145 1.10 dante /* Get the machine type. */ 146 1.14 tsutsui if ((machine = getenv("MACHINE")) == NULL) { 147 1.14 tsutsui struct utsname utsname; 148 1.14 tsutsui 149 1.14 tsutsui if (uname(&utsname) == -1) { 150 1.14 tsutsui perror("uname"); 151 1.14 tsutsui exit(1); 152 1.14 tsutsui } 153 1.14 tsutsui machine = utsname.machine; 154 1.14 tsutsui } 155 1.10 dante 156 1.10 dante /* If there's no _default list, create an empty one. */ 157 1.26 chuck defp = gettag("_default", 1); 158 1.26 chuck subp = gettag("_subdir", 1); 159 1.26 chuck if (defp == NULL || subp == NULL) 160 1.26 chuck err(1, "malloc"); 161 1.15 jdolecek 162 1.10 dante /* 163 1.10 dante * 0: If one or more sections was specified, rewrite _subdir list. 164 1.10 dante */ 165 1.10 dante if (sections != NULL) { 166 1.26 chuck if ((sectnewp = gettag("_section_new", 1)) == NULL) 167 1.26 chuck err(1, "malloc"); 168 1.21 itojun for (p = sections; *p;) { 169 1.15 jdolecek i = snprintf(buf, sizeof(buf), "man%c", *p++); 170 1.29 lukem for (; *p && !isdigit((unsigned char)*p) && i < (int)sizeof(buf) - 1; i++) 171 1.15 jdolecek buf[i] = *p++; 172 1.15 jdolecek buf[i] = '\0'; 173 1.26 chuck if (addentry(sectnewp, buf, 0) < 0) 174 1.26 chuck err(1, "malloc"); 175 1.10 dante } 176 1.15 jdolecek subp = sectnewp; 177 1.10 dante } 178 1.10 dante 179 1.10 dante /* 180 1.10 dante * 1: If the user specified a MANPATH variable, or set the -M 181 1.10 dante * option, we replace the _default list with the user's list, 182 1.10 dante * appending the entries in the _subdir list and the machine. 183 1.10 dante */ 184 1.10 dante if (m_path == NULL) 185 1.10 dante m_path = getenv("MANPATH"); 186 1.10 dante if (m_path != NULL) { 187 1.26 chuck while ((e_defp = TAILQ_FIRST(&defp->entrylist)) != NULL) { 188 1.10 dante free(e_defp->s); 189 1.26 chuck TAILQ_REMOVE(&defp->entrylist, e_defp, q); 190 1.10 dante } 191 1.10 dante for (p = strtok(m_path, ":"); 192 1.10 dante p != NULL; p = strtok(NULL, ":")) { 193 1.10 dante slashp = p[strlen(p) - 1] == '/' ? "" : "/"; 194 1.26 chuck TAILQ_FOREACH(e_subp, &subp->entrylist, q) { 195 1.21 itojun if (!strncmp(e_subp->s, "cat", 3)) 196 1.10 dante continue; 197 1.10 dante (void)snprintf(buf, sizeof(buf), "%s%s%s{/%s,}", 198 1.10 dante p, slashp, e_subp->s, machine); 199 1.26 chuck if (addentry(defp, buf, 0) < 0) 200 1.26 chuck err(1, "malloc"); 201 1.10 dante } 202 1.10 dante } 203 1.10 dante } 204 1.10 dante 205 1.10 dante /* 206 1.10 dante * 2: If the user did not specify MANPATH, -M or a section, rewrite 207 1.10 dante * the _default list to include the _subdir list and the machine. 208 1.10 dante */ 209 1.10 dante if (m_path == NULL) { 210 1.26 chuck defp = gettag("_default", 1); 211 1.26 chuck defnewp = gettag("_default_new1", 1); 212 1.26 chuck if (defp == NULL || defnewp == NULL) 213 1.26 chuck err(1, "malloc"); 214 1.17 lukem 215 1.26 chuck TAILQ_FOREACH(e_defp, &defp->entrylist, q) { 216 1.10 dante slashp = 217 1.10 dante e_defp->s[strlen(e_defp->s) - 1] == '/' ? "" : "/"; 218 1.26 chuck TAILQ_FOREACH(e_subp, &subp->entrylist, q) { 219 1.21 itojun if (!strncmp(e_subp->s, "cat", 3)) 220 1.10 dante continue; 221 1.10 dante (void)snprintf(buf, sizeof(buf), "%s%s%s{/%s,}", 222 1.15 jdolecek e_defp->s, slashp, e_subp->s, machine); 223 1.26 chuck if (addentry(defnewp, buf, 0) < 0) 224 1.26 chuck err(1, "malloc"); 225 1.10 dante } 226 1.10 dante } 227 1.15 jdolecek defp = defnewp; 228 1.10 dante } 229 1.10 dante 230 1.10 dante /* 231 1.10 dante * 3: If the user set the -m option, insert the user's list before 232 1.10 dante * whatever list we have, again appending the _subdir list and 233 1.10 dante * the machine. 234 1.10 dante */ 235 1.10 dante if (m_add != NULL) 236 1.10 dante for (p = strtok(m_add, ":"); p != NULL; p = strtok(NULL, ":")) { 237 1.10 dante slashp = p[strlen(p) - 1] == '/' ? "" : "/"; 238 1.26 chuck TAILQ_FOREACH(e_subp, &subp->entrylist, q) { 239 1.21 itojun if (!strncmp(e_subp->s, "cat", 3)) 240 1.10 dante continue; 241 1.10 dante (void)snprintf(buf, sizeof(buf), "%s%s%s{/%s,}", 242 1.10 dante p, slashp, e_subp->s, machine); 243 1.26 chuck if (addentry(defp, buf, 1) < 0) 244 1.26 chuck err(1, "malloc"); 245 1.10 dante } 246 1.10 dante } 247 1.10 dante } 248 1.1 jtc 249 1.10 dante /* 250 1.10 dante * Remove entries (directory) which are symbolic links to other entries. 251 1.10 dante * Some examples are showed below: 252 1.10 dante * 1) if /usr/X11 -> /usr/X11R6 then remove all /usr/X11/man entries. 253 1.10 dante * 2) if /usr/local/man -> /usr/share/man then remove all /usr/local/man 254 1.10 dante * entries 255 1.10 dante */ 256 1.10 dante static void 257 1.10 dante uniquepath(void) 258 1.1 jtc { 259 1.15 jdolecek TAG *defnewp; 260 1.10 dante ENTRY *e_defp; 261 1.10 dante glob_t manpaths; 262 1.10 dante struct stat st1; 263 1.10 dante struct stat st2; 264 1.10 dante struct stat st3; 265 1.25 christos int len, lnk, gflags; 266 1.25 christos size_t i, j; 267 1.10 dante char path[PATH_MAX], *p; 268 1.10 dante 269 1.17 lukem gflags = 0; 270 1.26 chuck TAILQ_FOREACH(e_defp, &defp->entrylist, q) { 271 1.17 lukem glob(e_defp->s, GLOB_BRACE | GLOB_NOSORT | gflags, NULL, 272 1.17 lukem &manpaths); 273 1.17 lukem gflags = GLOB_APPEND; 274 1.10 dante } 275 1.10 dante 276 1.26 chuck if ((defnewp = gettag("_default_new2", 1)) == NULL) 277 1.26 chuck err(1, "malloc"); 278 1.10 dante 279 1.21 itojun for (i = 0; i < manpaths.gl_pathc; i++) { 280 1.10 dante lnk = 0; 281 1.10 dante lstat(manpaths.gl_pathv[i], &st1); 282 1.21 itojun for (j = 0; j < manpaths.gl_pathc; j++) { 283 1.15 jdolecek if (i != j) { 284 1.10 dante lstat(manpaths.gl_pathv[j], &st2); 285 1.15 jdolecek if (st1.st_ino == st2.st_ino) { 286 1.22 itojun strlcpy(path, manpaths.gl_pathv[i], 287 1.22 itojun sizeof(path)); 288 1.21 itojun for (p = path; *(p+1) != '\0';) { 289 1.10 dante p = dirname(p); 290 1.10 dante lstat(p, &st3); 291 1.15 jdolecek if (S_ISLNK(st3.st_mode)) { 292 1.10 dante lnk = 1; 293 1.10 dante break; 294 1.10 dante } 295 1.10 dante } 296 1.10 dante } else { 297 1.10 dante len = readlink(manpaths.gl_pathv[i], 298 1.21 itojun path, sizeof(path) - 1); 299 1.15 jdolecek if (len == -1) 300 1.10 dante continue; 301 1.20 itojun path[len] = '\0'; 302 1.15 jdolecek if (!strcmp(path, manpaths.gl_pathv[j])) 303 1.10 dante lnk = 1; 304 1.10 dante } 305 1.15 jdolecek if (lnk) 306 1.10 dante break; 307 1.10 dante } 308 1.10 dante } 309 1.15 jdolecek 310 1.26 chuck if (!lnk) { 311 1.26 chuck if (addentry(defnewp, manpaths.gl_pathv[i], 0) < 0) 312 1.26 chuck err(1, "malloc"); 313 1.26 chuck } 314 1.10 dante } 315 1.10 dante 316 1.10 dante globfree(&manpaths); 317 1.10 dante 318 1.15 jdolecek defp = defnewp; 319 1.10 dante } 320 1.10 dante 321 1.10 dante static void 322 1.10 dante catman(void) 323 1.10 dante { 324 1.10 dante ENTRY *e_path; 325 1.15 jdolecek const char *mandir; 326 1.10 dante char catdir[PATH_MAX], *cp; 327 1.10 dante 328 1.26 chuck TAILQ_FOREACH(e_path, &defp->entrylist, q) { 329 1.10 dante mandir = e_path->s; 330 1.22 itojun strlcpy(catdir, mandir, sizeof(catdir)); 331 1.21 itojun if (!(cp = strstr(catdir, "man/man"))) 332 1.10 dante continue; 333 1.21 itojun cp += 4; *cp++ = 'c'; *cp++ = 'a'; *cp = 't'; 334 1.10 dante scanmandir(catdir, mandir); 335 1.10 dante } 336 1.10 dante } 337 1.10 dante 338 1.10 dante static void 339 1.26 chuck scanmandir(const char *catdir, const char *mandir) 340 1.10 dante { 341 1.10 dante TAG *buildp, *crunchp; 342 1.10 dante ENTRY *e_build, *e_crunch; 343 1.38 christos char manpage[2 * PATH_MAX]; 344 1.38 christos char catpage[2 * PATH_MAX]; 345 1.10 dante char linkname[PATH_MAX]; 346 1.10 dante char buffer[PATH_MAX], *bp; 347 1.38 christos char tmp[2 * PATH_MAX]; 348 1.10 dante char buildsuff[256], buildcmd[256]; 349 1.10 dante char crunchsuff[256], crunchcmd[256]; 350 1.38 christos char match[2 * 256]; 351 1.4 chopps struct stat manstat; 352 1.4 chopps struct stat catstat; 353 1.10 dante struct stat lnkstat; 354 1.4 chopps struct dirent *dp; 355 1.4 chopps DIR *dirp; 356 1.10 dante int len, error; 357 1.10 dante 358 1.10 dante if ((dirp = opendir(mandir)) == 0) { 359 1.10 dante warn("can't open %s", mandir); 360 1.10 dante return; 361 1.10 dante } 362 1.4 chopps 363 1.10 dante if (stat(catdir, &catstat) < 0) { 364 1.10 dante if (errno != ENOENT) { 365 1.10 dante warn("can't stat %s", catdir); 366 1.10 dante closedir(dirp); 367 1.10 dante return; 368 1.10 dante } 369 1.10 dante if (f_noprint == 0) 370 1.10 dante printf("mkdir %s\n", catdir); 371 1.21 itojun if (f_noaction == 0 && mkdir(catdir, 0755) < 0) { 372 1.10 dante warn("can't create %s", catdir); 373 1.10 dante closedir(dirp); 374 1.10 dante return; 375 1.4 chopps } 376 1.10 dante } 377 1.10 dante 378 1.10 dante while ((dp = readdir(dirp)) != NULL) { 379 1.10 dante if (strcmp(dp->d_name, ".") == 0 || 380 1.10 dante strcmp(dp->d_name, "..") == 0) 381 1.10 dante continue; 382 1.10 dante 383 1.10 dante snprintf(manpage, sizeof(manpage), "%s/%s", mandir, dp->d_name); 384 1.10 dante snprintf(catpage, sizeof(catpage), "%s/%s", catdir, dp->d_name); 385 1.4 chopps 386 1.13 mycroft e_build = NULL; 387 1.26 chuck if ((buildp = gettag("_build", 1)) == NULL) 388 1.26 chuck err(1, "malloc"); 389 1.26 chuck TAILQ_FOREACH(e_build, &buildp->entrylist, q) { 390 1.22 itojun splitentry(e_build->s, buildsuff, sizeof(buildsuff), 391 1.22 itojun buildcmd, sizeof(buildcmd)); 392 1.17 lukem snprintf(match, sizeof(match), "*%s", 393 1.17 lukem buildsuff); 394 1.21 itojun if (!fnmatch(match, manpage, 0)) 395 1.17 lukem break; 396 1.10 dante } 397 1.4 chopps 398 1.21 itojun if (e_build == NULL) 399 1.4 chopps continue; 400 1.13 mycroft 401 1.13 mycroft e_crunch = NULL; 402 1.26 chuck if ((crunchp = gettag("_crunch", 1)) == NULL) 403 1.26 chuck err(1, "malloc"); 404 1.26 chuck TAILQ_FOREACH(e_crunch, &crunchp->entrylist, q) { 405 1.22 itojun splitentry(e_crunch->s, crunchsuff, sizeof(crunchsuff), 406 1.22 itojun crunchcmd, sizeof(crunchcmd)); 407 1.17 lukem snprintf(match, sizeof(match), "*%s", crunchsuff); 408 1.21 itojun if (!fnmatch(match, manpage, 0)) 409 1.17 lukem break; 410 1.4 chopps } 411 1.4 chopps 412 1.10 dante if (lstat(manpage, &manstat) <0) { 413 1.10 dante warn("can't stat %s", manpage); 414 1.10 dante continue; 415 1.10 dante } else { 416 1.21 itojun if (S_ISLNK(manstat.st_mode)) { 417 1.22 itojun strlcpy(buffer, catpage, sizeof(buffer)); 418 1.22 itojun strlcpy(linkname, basename(buffer), 419 1.22 itojun sizeof(linkname)); 420 1.20 itojun len = readlink(manpage, buffer, 421 1.20 itojun sizeof(buffer) - 1); 422 1.20 itojun if (len == -1) { 423 1.10 dante warn("can't stat read symbolic link %s", 424 1.22 itojun manpage); 425 1.10 dante continue; 426 1.10 dante } 427 1.10 dante buffer[len] = '\0'; 428 1.35 soren 429 1.35 soren if (strcmp(buffer, basename(buffer)) == 0) { 430 1.35 soren bp = basename(buffer); 431 1.35 soren strlcpy(tmp, manpage, sizeof(tmp)); 432 1.35 soren snprintf(manpage, sizeof(manpage), 433 1.35 soren "%s/%s", dirname(tmp), bp); 434 1.35 soren strlcpy(tmp, catpage, sizeof(tmp)); 435 1.35 soren snprintf(catpage, sizeof(catpage), 436 1.35 soren "%s/%s", dirname(tmp), buffer); 437 1.35 soren } else { 438 1.35 soren *linkname = '\0'; 439 1.35 soren } 440 1.35 soren 441 1.1 jtc } 442 1.10 dante else 443 1.21 itojun *linkname = '\0'; 444 1.10 dante } 445 1.10 dante 446 1.21 itojun if (!e_crunch) { 447 1.10 dante *crunchsuff = *crunchcmd = '\0'; 448 1.10 dante } 449 1.10 dante setcatsuffix(catpage, buildsuff, crunchsuff); 450 1.21 itojun if (*linkname != '\0') 451 1.10 dante setcatsuffix(linkname, buildsuff, crunchsuff); 452 1.1 jtc 453 1.10 dante if (stat(manpage, &manstat) < 0) { 454 1.10 dante warn("can't stat %s", manpage); 455 1.10 dante continue; 456 1.1 jtc } 457 1.1 jtc 458 1.10 dante if (!S_ISREG(manstat.st_mode)) { 459 1.21 itojun warnx("not a regular file %s", manpage); 460 1.10 dante continue; 461 1.10 dante } 462 1.4 chopps 463 1.10 dante if ((error = stat(catpage, &catstat)) && 464 1.10 dante errno != ENOENT) { 465 1.10 dante warn("can't stat %s", catpage); 466 1.10 dante continue; 467 1.10 dante } 468 1.4 chopps 469 1.10 dante if ((error && errno == ENOENT) || 470 1.11 mycroft manstat.st_mtime > catstat.st_mtime) { 471 1.10 dante if (f_noformat) { 472 1.10 dante dowhatis = 1; 473 1.10 dante } else { 474 1.10 dante /* 475 1.10 dante * reformat out of date manpage 476 1.10 dante */ 477 1.10 dante makecat(manpage, catpage, buildcmd, crunchcmd); 478 1.10 dante dowhatis = 1; 479 1.4 chopps } 480 1.10 dante } 481 1.4 chopps 482 1.21 itojun if (*linkname != '\0') { 483 1.22 itojun strlcpy(tmp, catpage, sizeof(tmp)); 484 1.10 dante snprintf(tmp, sizeof(tmp), "%s/%s", dirname(tmp), 485 1.10 dante linkname); 486 1.10 dante if ((error = lstat(tmp, &lnkstat)) && 487 1.5 chopps errno != ENOENT) { 488 1.10 dante warn("can't stat %s", tmp); 489 1.4 chopps continue; 490 1.4 chopps } 491 1.4 chopps 492 1.10 dante if (error && errno == ENOENT) { 493 1.10 dante if (f_noformat) { 494 1.5 chopps dowhatis = 1; 495 1.10 dante } else { 496 1.5 chopps /* 497 1.10 dante * create symbolic link 498 1.5 chopps */ 499 1.5 chopps if (f_noprint == 0) 500 1.10 dante printf("ln -s %s %s\n", catpage, 501 1.21 itojun linkname); 502 1.10 dante if (f_noaction == 0) { 503 1.22 itojun strlcpy(tmp, catpage, 504 1.22 itojun sizeof(tmp)); 505 1.21 itojun if (chdir(dirname(tmp)) == -1) { 506 1.10 dante warn("can't chdir"); 507 1.10 dante continue; 508 1.10 dante } 509 1.10 dante 510 1.15 jdolecek if (symlink(catpage, linkname) 511 1.10 dante == -1) { 512 1.10 dante warn("can't create" 513 1.10 dante " symbolic" 514 1.10 dante " link %s", 515 1.10 dante linkname); 516 1.10 dante continue; 517 1.10 dante } 518 1.10 dante } 519 1.5 chopps dowhatis = 1; 520 1.5 chopps } 521 1.4 chopps } 522 1.4 chopps } 523 1.1 jtc } 524 1.10 dante closedir(dirp); 525 1.1 jtc } 526 1.1 jtc 527 1.10 dante static int 528 1.26 chuck splitentry(char *s, char *first, size_t firstlen, char *second, 529 1.26 chuck size_t secondlen) 530 1.1 jtc { 531 1.10 dante char *c; 532 1.10 dante 533 1.24 dsl for (c = s; *c != '\0' && !isspace((unsigned char)*c); ++c) 534 1.21 itojun ; 535 1.21 itojun if (*c == '\0') 536 1.10 dante return(0); 537 1.29 lukem if ((size_t)(c - s + 1) > firstlen) 538 1.22 itojun return(0); 539 1.10 dante strncpy(first, s, c-s); 540 1.10 dante first[c-s] = '\0'; 541 1.24 dsl for (; *c != '\0' && isspace((unsigned char)*c); ++c) 542 1.21 itojun ; 543 1.22 itojun if (strlcpy(second, c, secondlen) >= secondlen) 544 1.22 itojun return(0); 545 1.22 itojun return(1); 546 1.10 dante } 547 1.10 dante 548 1.10 dante static void 549 1.26 chuck setcatsuffix(char *catpage, const char *suffix, const char *crunchsuff) 550 1.10 dante { 551 1.10 dante TAG *tp; 552 1.10 dante char *p; 553 1.10 dante 554 1.21 itojun for (p = catpage + strlen(catpage); p != catpage; p--) 555 1.21 itojun if (!fnmatch(suffix, p, 0)) { 556 1.26 chuck if ((tp = gettag("_suffix", 1)) == NULL) 557 1.26 chuck err(1, "malloc"); 558 1.26 chuck if (! TAILQ_EMPTY(&tp->entrylist)) { 559 1.17 lukem sprintf(p, "%s%s", 560 1.26 chuck TAILQ_FIRST(&tp->entrylist)->s, crunchsuff); 561 1.10 dante } else { 562 1.10 dante sprintf(p, ".0%s", crunchsuff); 563 1.10 dante } 564 1.10 dante break; 565 1.10 dante } 566 1.10 dante } 567 1.10 dante 568 1.10 dante static void 569 1.26 chuck makecat(const char *manpage, const char *catpage, const char *buildcmd, 570 1.31 christos const char *crunchcmd) 571 1.10 dante { 572 1.10 dante char crunchbuf[1024]; 573 1.38 christos char sysbuf[2048 + 128]; 574 1.34 christos size_t len; 575 1.10 dante 576 1.31 christos len = snprintf(sysbuf, sizeof(sysbuf), buildcmd, manpage); 577 1.34 christos if (len > sizeof(sysbuf)) 578 1.33 christos errx(1, "snprintf"); 579 1.10 dante 580 1.21 itojun if (*crunchcmd != '\0') { 581 1.10 dante snprintf(crunchbuf, sizeof(crunchbuf), crunchcmd, catpage); 582 1.32 christos snprintf(sysbuf + len, sizeof(sysbuf) - len, " | %s", 583 1.31 christos crunchbuf); 584 1.10 dante } else { 585 1.32 christos snprintf(sysbuf + len, sizeof(sysbuf) - len, " > %s", catpage); 586 1.10 dante } 587 1.1 jtc 588 1.4 chopps if (f_noprint == 0) 589 1.1 jtc printf("%s\n", sysbuf); 590 1.4 chopps if (f_noaction == 0) 591 1.4 chopps dosystem(sysbuf); 592 1.1 jtc } 593 1.1 jtc 594 1.10 dante static void 595 1.10 dante makewhatis(void) 596 1.10 dante { 597 1.10 dante TAG *whatdbp; 598 1.10 dante ENTRY *e_whatdb; 599 1.10 dante char sysbuf[1024]; 600 1.10 dante 601 1.26 chuck if ((whatdbp = gettag("_whatdb", 1)) == NULL) 602 1.26 chuck err(1, "malloc"); 603 1.26 chuck TAILQ_FOREACH(e_whatdb, &whatdbp->entrylist, q) { 604 1.10 dante snprintf(sysbuf, sizeof(sysbuf), "%s %s", 605 1.37 dholland _PATH_MAKEWHATIS, dirname(e_whatdb->s)); 606 1.10 dante if (f_noprint == 0) 607 1.10 dante printf("%s\n", sysbuf); 608 1.10 dante if (f_noaction == 0) 609 1.10 dante dosystem(sysbuf); 610 1.10 dante } 611 1.10 dante } 612 1.10 dante 613 1.10 dante static void 614 1.26 chuck dosystem(const char *cmd) 615 1.4 chopps { 616 1.4 chopps int status; 617 1.4 chopps 618 1.4 chopps if ((status = system(cmd)) == 0) 619 1.4 chopps return; 620 1.4 chopps 621 1.4 chopps if (status == -1) 622 1.4 chopps err(1, "cannot execute action"); 623 1.4 chopps if (WIFSIGNALED(status)) 624 1.4 chopps errx(1, "child was signaled to quit. aborting"); 625 1.4 chopps if (WIFSTOPPED(status)) 626 1.4 chopps errx(1, "child was stopped. aborting"); 627 1.4 chopps if (f_ignerr == 0) 628 1.7 lukem errx(1, "*** Exited %d", status); 629 1.7 lukem warnx("*** Exited %d (continuing)", status); 630 1.4 chopps } 631 1.1 jtc 632 1.10 dante static void 633 1.10 dante usage(void) 634 1.1 jtc { 635 1.6 cgd (void)fprintf(stderr, 636 1.10 dante "usage: catman [-knpsw] [-m manpath] [sections]\n"); 637 1.10 dante (void)fprintf(stderr, 638 1.10 dante " catman [-knpsw] [-M manpath] [sections]\n"); 639 1.1 jtc exit(1); 640 1.1 jtc } 641