1/* 2 * rarian-man.c 3 * This file is part of Rarian 4 * 5 * Copyright (C) 2007 - Don Scorgie 6 * 7 * Rarian is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU Lesser General Public 9 * License as published by the Free Software Foundation; either 10 * version 2.1 of the License, or (at your option) any later version. 11 * 12 * Rarian is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * Lesser General Public License for more details. 16 * 17 * You should have received a copy of the GNU Lesser General Public 18 * License along with this library; if not, write to the Free Software 19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 20 */ 21 22#include <config.h> 23#include <stdio.h> 24#include <stdlib.h> 25#include <unistd.h> 26#include <dirent.h> 27#include <string.h> 28#include <sys/stat.h> 29#include "rarian-man.h" 30#include "rarian-language.h" 31#include "rarian-utils.h" 32 33/*#ifdef HAVE_LIBGDBM 34#include <gdbm.h> 35static GDBM_FILE *gdbm_handle; 36#endif 37 38#ifdef HAVE_LIBNDBM 39#include <ndbm.h> 40static DBM *ndbm_handle; 41#endif*/ 42 43typedef struct _ManLink ManLink; 44 45struct _ManLink 46{ 47 RrnManEntry *reg; 48 ManLink *next; 49 ManLink *prev; 50}; 51 52static char *keys[43] = {"1", "1p", "1g", "1t", "1x", "1ssl", "1m", 53 "2", 54 "3", "3o", "3t", "3p", "3blt", "3nas", "3form", "3menu", "3tiff", "3ssl", "3readline", 55 "3ncurses", "3curses", "3f", "3pm", "3perl", "3qt", "3x", "3X11", 56 "4", "4x", 57 "5", "5snmp", "5x", "5ssl", 58 "6", "6x", 59 "7", "7gcc", "7x", "7ssl", 60 "8", "8l", "9", "0p" 61}; 62 63static ManLink *manhead[44]; 64static ManLink *mantail[44]; 65 66static char *avail_dirs[] = { 67 "man0p", "man1", "man1p", "man2", "man3", 68 "man3p", "man4", "man5", "man6", "man7", 69 "man8", "man9", "mann", 70 NULL 71 }; 72 73static char **man_paths = NULL; 74 75int initialised = FALSE; 76int mode = -1; 77/* Modes: 0 = default 78 * 1 = gdbm 79 * 2 = ndbm 80 */ 81 82static void 83setup_man_path () 84{ 85 int outfd[2]; 86 int infd[2]; 87 88 int oldstdin, oldstdout; 89 fflush(stdin); 90 fflush(stdout); 91 fflush(stderr); 92 93 pipe(outfd); // Where the parent is going to write to 94 pipe(infd); // From where parent is going to read 95 96 oldstdin = dup(0); // Save current stdin 97 oldstdout = dup(1); // Save stdout 98 99 close(0); 100 close(1); 101 102 dup2(outfd[0], 0); // Make the read end of outfd pipe as stdin 103 dup2(infd[1],1); // Make the write end of infd as stdout 104 105 if(!fork()) { 106 /* Child process */ 107 char *argv[]={"manpath"}; 108 close(outfd[0]); // Not required for the child 109 close(outfd[1]); 110 close(infd[0]); 111 close(infd[1]); 112 execlp("manpath", "manpath", (char *) 0); 113 exit (0); 114 } else { 115 /* Parent process */ 116 char *input = NULL; 117 char *colon = NULL; 118 char *next = NULL; 119 int i, count = 0; 120 input = malloc(sizeof(char) * 256); 121 close(0); // Restore the original std fds of parent 122 close(1); 123 dup2(oldstdin, 0); 124 dup2(oldstdout, 1); 125 126 close(outfd[0]); // These are being used by the child 127 close(infd[1]); 128 memset(input, 0, sizeof(char)*255); 129 130 input[read(infd[0],input,255)] = 0; // Read from child's stdout 131 if (*input != '\0') { 132 int i; 133 i = strlen(input); 134 input[i-1]='\0'; 135 } 136 if (!input || *input == '\0') { 137 char *env = NULL; 138 env = getenv("MANPATH"); 139 if (env) 140 input = strdup(env); 141 } 142 if (!input || *input == '\0') { 143 if (input) 144 free(input); 145 input = strdup ("@DEFAULT_MANPATH@"); 146 } 147 colon = input; 148 149 while (*colon) { 150 if (*colon == ':') 151 count++; 152 colon++; 153 } 154 man_paths = malloc (sizeof(char *) * (count+2)); /* 2 is for final string 155 * + NULL entry */ 156 colon = input; 157 for (i=0; i< count; i++) { 158 next = strchr (colon, ':'); 159 man_paths[i] = rrn_strndup(colon, next-colon); 160 colon = next; 161 colon++; 162 } 163 /* Final 2 entries - straight strdup of final entry and NULL */ 164 man_paths[i] = strdup(colon); 165 i++; 166 man_paths[i] = NULL; 167 free (input); 168 } 169 170} 171#if 0 172#ifdef HAVE_LIBGDBM 173static void 174setup_gdbm (char *name) 175{ 176 gdbm_handle = gdbm_open(name, 0, GDBM_READER, 0666, 0); 177 if (!gdbm_handle) { 178 sprintf(stderr, "ERROR: GDBM index %s could not be opened. Falling back\n", name); 179 setup_default(); 180 } else { 181 initialised = TRUE; 182 mode = 1; 183 } 184} 185#endif 186 187#ifdef HAVE_LIBNDBM 188static void 189setup_ndbm (char *name) 190{ 191 char *trunc; 192 193 trunc = malloc (sizeof(char) * (strlen(name)-3)); 194 trunc = rrn_strndup (name, strlen(name) - 4); 195 ndbm_handle = dbm_open(trunc, O_RDONLY, 0666); 196 if (!ndbm_handle) { 197 sprintf(stderr, "ERROR: NDBM index %s could not be opened. Falling back\n", trunc); 198 setup_default(); 199 } else { 200 initialise = TRUE; 201 mode = 2; 202 } 203 free (trunc); 204} 205#endif 206#endif 207 208static char *strrstr (char *s, char *wanted) 209{ 210 char *scan; 211 char *first; 212 int len; 213 214 first = wanted; 215 len = strlen(wanted); 216 for (scan = s + strlen(s) - len ; scan >= s ; scan--) 217 { 218 if (*scan == *first) 219 { 220 if (!strncmp (scan, wanted, len)) 221 { 222 return (scan); 223 } 224 } 225 } 226 return(NULL); 227} 228 229 230 231static char * 232get_name_for_file (char *filename, char **subsect) 233{ 234 char *suffix; 235 char *cut; 236 char *sect; 237 char *final; 238 239 /* We assume, like reasonable people, that man pages 240 * have one of the forms: 241 * manname.sect.{gz,bz,bz2,lzma} 242 * manname.sect 243 * If it doesn't, things will probably break but we return 244 * our "best guess" (i.e. everything up to the suffix) 245 */ 246 suffix = strrstr(filename, ".gz"); 247 if (!suffix) { 248 suffix = strrstr(filename, ".bz2"); 249 if (!suffix) { 250 suffix = strrstr(filename, ".bz"); 251 } 252 if (!suffix) { 253 suffix = strrstr(filename, ".lzma"); 254 } 255 } 256 if (suffix) 257 cut = rrn_strndup (filename, suffix-filename); 258 else 259 cut = strdup (filename); 260 sect = strrchr (cut, '.'); 261 if (!sect) 262 return cut; 263 264 final = rrn_strndup (cut, sect-cut); 265 sect++; 266 *subsect = strdup (sect); 267 free (cut); 268 return final; 269 270} 271 272static int 273check_for_dup (RrnManEntry *reg, int entry) 274{ 275 ManLink *iter = manhead[entry]; 276 277 while (iter) { 278 if (!strcmp(reg->name, iter->reg->name)) 279 return TRUE; 280 iter = iter->next; 281 282 } 283 return FALSE; 284} 285 286static int 287find_key (char *sect) 288{ 289 int i; 290 for (i=0; i<43; i++){ 291 if (!strcmp (sect, keys[i])) 292 return i; 293 } 294 return i; 295} 296 297static void 298process_dir(char *dir) 299{ 300 char **dir_iter; 301 char *path = NULL; 302 303 DIR * dirp = NULL; 304 struct dirent * dp = NULL; 305 struct stat buf; 306 int current; 307 308 current = -1; 309 path = malloc(sizeof(char) * (strlen(dir) + 8)); 310 311 dir_iter = avail_dirs; 312 while (dir_iter && *dir_iter) { 313 current++; 314 sprintf(path, "%s/%s", dir, *dir_iter); 315 if (access (path, R_OK)) { 316 dir_iter++; 317 continue; 318 } 319 320 dirp = opendir (path); 321 322 if (!dirp) { 323 dir_iter++; 324 continue; 325 } 326 327 while (1) { 328 if ((dp = readdir(dirp)) != NULL) { 329 char *full_name; 330 331 full_name = malloc (sizeof(char) * (strlen(dp->d_name) + strlen (path) + 3)); 332 sprintf (full_name, "%s/%s", path, dp->d_name); 333 334 stat(full_name,&buf); 335 if (S_ISREG(buf.st_mode) || buf.st_mode & S_IFLNK) { 336 char *tmp = NULL; 337 char *suffix = NULL; 338 RrnManEntry *entry; 339 ManLink *link; 340 char *subsect = NULL; 341 342 entry = malloc (sizeof(RrnManEntry)); 343 entry->name = get_name_for_file (dp->d_name, &subsect); 344 entry->path = full_name; 345 if (subsect) { 346 entry->section = subsect; 347 entry->comment = NULL; 348 current = find_key(subsect); 349 if (!check_for_dup (entry, current)) { 350 link = malloc (sizeof(ManLink)); 351 link->reg = entry; 352 353 if (mantail[current]) { 354 mantail[current]->next = link; 355 link->next = NULL; 356 link->prev = mantail[current]; 357 mantail[current] = link; 358 } else { 359 manhead[current] = mantail[current] = link; 360 link->prev = link->next = NULL; 361 } 362 } else { 363 free (entry->name); 364 free (entry->path); 365 free (entry->section); 366 if (entry->comment) 367 free (entry->comment); 368 free (entry); 369 } 370 } 371 } 372 } else { 373 closedir (dirp); 374 break; 375 } 376 } 377 dir_iter++; 378 } 379 free (path); 380} 381 382static void 383setup_default() 384{ 385 char **path_iter; 386 char **langs; 387 char **lang_iter; 388 389 path_iter = man_paths; 390 langs = rrn_language_get_langs(); 391 392 while (path_iter && *path_iter) { 393 if (access (*path_iter, R_OK)) { 394 path_iter++; 395 continue; 396 } 397 398 lang_iter = langs; 399 while (lang_iter && *lang_iter) { 400 char *path; 401 path = malloc (sizeof(char) * (strlen(*path_iter) + 402 strlen(*lang_iter)+2)); 403 sprintf (path, "%s/%s", *path_iter, *lang_iter); 404 if (!access (path, R_OK)) { 405 process_dir(path); 406 } 407 free (path); 408 lang_iter++; 409 } 410 411 412 process_dir(*path_iter); 413 path_iter++; 414 } 415 416 /*lang_iter = langs; 417 while (lang_iter && *lang_iter) { 418 char **next = lang_iter; 419 next++; 420 free (lang_iter); 421 lang_iter = next; 422 }*/ 423 free (langs); 424} 425 426static void 427rrn_man_init (void) 428{ 429 char *default_dirs = "/var/cache/man:/usr/man"; 430 char *split = NULL; 431#if 0 432 char *gdbm_index = "index.db"; 433 char *ndbm_index = "index.dir"; 434#endif 435 int i; 436 int npaths = 0; 437 char *ddirs; 438 439 for (i=0; i<44; i++) { 440 manhead[i] = mantail[i] = NULL; 441 } 442 443 setup_man_path (); 444 445 split = default_dirs; 446 447#if 0 448 while (split) { 449 char *next = strchr(split, ':'); 450 char *dirname = NULL; 451 char *index = NULL; 452 FILE *access = NULL; 453 454 if (next) 455 dirname = rrn_strndup(split, (next-split)); 456 else 457 dirname = strdup (split); 458#ifdef HAVE_LIBGDBM 459 index = malloc (sizeof(char) * (strlen(dirname) + 10 /*Len of gdbm_index + additional chars*/)); 460 sprintf (index, "%s/%s", dirname, gdbm_index); 461 access = fopen(index, "r"); 462 free(dirname); 463 if (access) { 464 fclose(access); 465 setup_gdbm(index); 466 free(index); 467 return; 468 } 469#endif 470#ifdef HAVE_NDBM 471 index = malloc (sizeof(char) * (strlen(dirname) + 11 /*Len of ndbm_index + additional chars*/)); 472 sprintf (index, "%s/%s", dirname, gdbm_index); 473 access = fopen(index, "r"); 474 free(dirname); 475 if (access) { 476 fclose(access); 477 setup_ndbm(index); 478 free(index); 479 return; 480 } 481#endif 482 split = strchr(split, ':'); 483 if (split) 484 split++; 485 } 486#endif /* 0 */ 487 /* If we get here, we have to do our own thang */ 488 setup_default(); 489 initialised = TRUE; 490 491} 492 493void 494rrn_man_for_each (RrnManForeachFunc funct, void * user_data) 495{ 496 ManLink *iter; 497 int i; 498 499 if (!initialised) 500 rrn_man_init(); 501 502 for (i=0; i<44; i++) { 503 iter = manhead[i]; 504 505 while (iter) { 506 int res; 507 res = funct (iter->reg, user_data); 508 if (res == FALSE) 509 break; 510 iter = iter->next; 511 } 512 } 513 return; 514} 515 516 517void 518rrn_man_for_each_in_category (char *category, 519 RrnManForeachFunc funct, 520 void * user_data) 521{ 522 ManLink *iter; 523 int cat; 524 525 if (!initialised) 526 rrn_man_init(); 527 528 cat = find_key(category); 529 iter = manhead[cat]; 530 531 while (iter) { 532 int res; 533 if (!strcmp (iter->reg->section, category)) { 534 res = funct (iter->reg, user_data); 535 if (res == FALSE) 536 break; 537 } 538 iter = iter->next; 539 } 540 return; 541} 542 543RrnManEntry * 544rrn_man_find_from_name (char *name, char *sect) 545{ 546 ManLink *iter; 547 int cat; 548 RrnManEntry *res; 549 550 if (!initialised) 551 rrn_man_init (); 552 553 if (sect) { 554 cat = find_key(sect); 555 } else { 556 int i=0; 557 for (i=0; i<43; i++) { 558 iter = manhead[i]; 559 560 while (iter) { 561 if (!strcmp (iter->reg->name, name)) { 562 return iter->reg; 563 } 564 iter = iter->next; 565 } 566 } 567 return NULL; 568 } 569 iter = manhead[cat]; 570 571 while (iter) { 572 if (!strcmp (iter->reg->name, name)) { 573 return iter->reg; 574 } 575 iter = iter->next; 576 } 577 578 579 return NULL; 580} 581 582char ** 583rrn_man_get_categories (void) 584{ 585 if (!initialised) 586 rrn_man_init(); 587 588 return keys; 589} 590 591 592void 593rrn_man_shutdown () 594{ 595 ManLink *iter; 596 int i; 597 initialised = FALSE; 598 599 for (i=0; i< 44; i++) { 600 iter = manhead[i]; 601 while (iter) { 602 ManLink *tmp = iter->next; 603 free (iter->reg->name); 604 free (iter->reg->path); 605 free (iter->reg->section); 606 if (iter->reg->comment) 607 free (iter->reg->comment); 608 free (iter->reg); 609 free (iter); 610 iter = tmp; 611 } 612 manhead[i] = mantail[i] = NULL; 613 } 614 rrn_language_shutdown (); 615 return; 616} 617