misc.c revision 8dd211ca
1/* $XConsortium: misc.c,v 1.31 94/12/16 21:36:53 gildea Exp $ */ 2/* $XdotOrg: xc/programs/xman/misc.c,v 1.6 2004/09/02 08:40:33 kem Exp $ */ 3/* 4 5Copyright (c) 1987, 1988 X Consortium 6 7Permission is hereby granted, free of charge, to any person obtaining 8a copy of this software and associated documentation files (the 9"Software"), to deal in the Software without restriction, including 10without limitation the rights to use, copy, modify, merge, publish, 11distribute, sublicense, and/or sell copies of the Software, and to 12permit persons to whom the Software is furnished to do so, subject to 13the following conditions: 14 15The above copyright notice and this permission notice shall be included 16in all copies or substantial portions of the Software. 17 18THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 19OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21IN NO EVENT SHALL THE X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR 22OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 23ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 24OTHER DEALINGS IN THE SOFTWARE. 25 26Except as contained in this notice, the name of the X Consortium shall 27not be used in advertising or otherwise to promote the sale, use or 28other dealings in this Software without prior written authorization 29from the X Consortium. 30 31*/ 32/* $XFree86: xc/programs/xman/misc.c,v 1.10 2003/08/02 17:35:48 herrb Exp $ */ 33 34/* 35 * xman - X window system manual page display program. 36 * Author: Chris D. Peterson, MIT Project Athena 37 * Created: October 27, 1987 38 */ 39 40#ifdef HAVE_CONFIG_H 41# include "config.h" 42#endif 43 44#include "globals.h" 45#include "vendor.h" 46#include <X11/Xos.h> /* sys/types.h and unistd.h included in here */ 47#include <sys/stat.h> 48#include <errno.h> 49#include <X11/Xaw/Dialog.h> 50#include <X11/Shell.h> 51 52static FILE * Uncompress(ManpageGlobals * man_globals, char * filename); 53#ifndef HAS_MKSTEMP 54static Boolean UncompressNamed(ManpageGlobals * man_globals, char * filename, 55 char * output); 56static Boolean UncompressUnformatted(ManpageGlobals * man_globals, 57 char * entry, char * filename); 58#else 59static Boolean UncompressNamed(ManpageGlobals * man_globals, char * filename, 60 char * output, FILE ** output_fd); 61static Boolean UncompressUnformatted(ManpageGlobals * man_globals, 62 char * entry, char * filename, 63 FILE **file); 64#endif 65#ifdef HANDLE_ROFFSEQ 66static Boolean ConstructCommand(char * cmdbuf, char * path, char * filename, char * tempfile); 67#endif 68 69#if defined(ISC) || defined(__SCO__) || defined(__UNIXWARE__) 70static char *uncompress_format = NULL; 71static char *uncompress_formats[] = 72 { UNCOMPRESS_FORMAT_1, 73 UNCOMPRESS_FORMAT_2, 74 UNCOMPRESS_FORMAT_3 75 }; 76#endif 77 78/* Function Name: PopupWarning 79 * Description: This function pops upa warning message. 80 * Arguments: string - the specific warning string. 81 * Returns: none 82 */ 83 84extern Widget top; 85static Widget warnShell, warnDialog; 86 87static void 88PopdownWarning(Widget w, XtPointer client, XtPointer call) 89{ 90 XtPopdown((Widget)client); 91} 92 93void 94PopupWarning(ManpageGlobals * man_globals, char * string) 95{ 96 int n; 97 Arg wargs[3]; 98 Dimension topX, topY; 99 char buffer[BUFSIZ]; 100 Boolean hasPosition; 101 102 snprintf( buffer, sizeof(buffer), "Xman Warning: %s", string); 103 hasPosition = FALSE; 104 if (top) 105 { 106 n=0; 107 XtSetArg(wargs[n], XtNx, &topX); n++; 108 XtSetArg(wargs[n], XtNy, &topY); n++; 109 XtGetValues(top, wargs, n); 110 hasPosition = TRUE; 111 } 112 113 if (man_globals != NULL) 114 ChangeLabel(man_globals->label, buffer); 115 if (man_globals->label == NULL) { 116 n=0; 117 if (hasPosition) 118 { 119 XtSetArg(wargs[n], XtNx, topX); n++; 120 XtSetArg(wargs[n], XtNy, topY); n++; 121 } 122 XtSetArg(wargs[n], XtNtransientFor, top); n++; 123 warnShell = XtCreatePopupShell("warnShell", transientShellWidgetClass, 124 initial_widget, wargs, n); 125 XtSetArg(wargs[0], XtNlabel, buffer); 126 warnDialog = XtCreateManagedWidget("warnDialog", dialogWidgetClass, 127 warnShell, wargs, 1); 128 XawDialogAddButton(warnDialog, "dismiss", PopdownWarning, 129 (XtPointer)warnShell); 130 XtRealizeWidget(warnShell); 131 Popup(warnShell, XtGrabNone); 132 } 133} 134 135/* Function Name: PrintError 136 * Description: This Function prints an error message and exits. 137 * Arguments: string - the specific message. 138 * Returns: none. - exits tho. 139 */ 140 141void 142PrintError(char * string) 143{ 144 fprintf(stderr,"Xman Error: %s\n",string); 145 exit(EXIT_FAILURE); 146} 147 148/* Function Name: OpenFile 149 * Description: Assignes a file to the manpage. 150 * Arguments: man_globals - global structure. 151 * file - the file pointer. 152 * Returns: none 153 */ 154 155void 156OpenFile(ManpageGlobals * man_globals, FILE * file) 157{ 158 Arg arglist[1]; 159 Cardinal num_args = 0; 160 161 if (man_globals->curr_file) { 162#if 0 /* Ownership rules need to be fixed first */ 163 fclose(man_globals->curr_file); 164#endif 165 } 166 man_globals->curr_file = file; 167 168 XtSetArg(arglist[num_args], XtNfile, man_globals->curr_file); num_args++; 169 XtSetValues(man_globals->manpagewidgets.manpage, arglist, num_args); 170} 171 172 173/* Function Name: FindManualFile 174 * Description: Opens the manual page file given the entry information. 175 * Arguments: man_globals - the globals info for this manpage. 176 * section_num - section number of the man page. 177 * entry_num - entry number of the man page. 178 * Returns: fp - the file pointer 179 * 180 * NOTES: 181 * 182 * If there is a uncompressed section it will look there for uncompresed 183 * manual pages first and then for individually compressed file in the 184 * uncompressed section. 185 * 186 * If there is a compressed directory then it will also look there for 187 * the manual pages. 188 * 189 * If both of these fail then it will attempt to format the manual page. 190 */ 191 192FILE * 193FindManualFile(ManpageGlobals * man_globals, int section_num, int entry_num) 194{ 195 FILE * file; 196 char path[BUFSIZ], page[BUFSIZ], section[BUFSIZ], *temp; 197 char filename[BUFSIZ]; 198 char * entry = manual[section_num].entries[entry_num]; 199 int len_cat = strlen(CAT); 200#if defined(ISC) || defined(__SCO__) || defined(__UNIXWARE__) 201 int i; 202#endif 203 204 temp = CreateManpageName(entry, 0, 0); 205 snprintf(man_globals->manpage_title, sizeof(man_globals->manpage_title), 206 "The current manual page is: %s.", temp); 207 XtFree(temp); 208 209 ParseEntry(entry, path, section, page); 210 211/* 212 * Look for uncompressed files first. 213 */ 214#if defined(__OpenBSD__) || defined(__NetBSD__) 215 /* look in machine subdir first */ 216 snprintf(filename, sizeof(filename), "%s/%s%s/%s/%s", path, CAT, 217 section + len_cat, MACHINE, page); 218 if ( (file = fopen(filename,"r")) != NULL) 219 return(file); 220#endif 221 222 snprintf(filename, sizeof(filename), "%s/%s%s/%s", 223 path, CAT, section + len_cat, page); 224 if ( (file = fopen(filename,"r")) != NULL) 225 return(file); 226 227/* 228 * Then for compressed files in an uncompressed directory. 229 */ 230 231#if !defined(ISC) && !defined(__UNIXWARE__) 232#if defined(__OpenBSD__) || defined(__NetBSD__) 233 /* look in machine subdir first */ 234 snprintf(filename, sizeof(filename), "%s/%s%s/%s/%s.%s", path, CAT, 235 section + len_cat, MACHINE, page, COMPRESSION_EXTENSION); 236 if ( (file = Uncompress(man_globals, filename)) != NULL) 237 return(file); 238#endif 239 snprintf(filename, sizeof(filename), "%s/%s%s/%s.%s", path, CAT, 240 section + len_cat, page, COMPRESSION_EXTENSION); 241 if ( (file = Uncompress(man_globals, filename)) != NULL) 242 return(file); 243#ifdef GZIP_EXTENSION 244 else { 245#if defined(__OpenBSD__) || defined(__NetBSD__) 246 /* look in machine subdir first */ 247 snprintf(filename, sizeof(filename), "%s/%s%s/%s/%s.%s", path, CAT, 248 section + len_cat, MACHINE, page, GZIP_EXTENSION); 249 if ( (file = Uncompress(man_globals, filename)) != NULL) 250 return(file); 251#endif 252 snprintf(filename, sizeof(filename), "%s/%s%s/%s.%s", path, CAT, 253 section + len_cat, page, GZIP_EXTENSION); 254 if ( (file = Uncompress(man_globals, filename)) != NULL) 255 return(file); 256 } 257#endif 258#else 259 for(i = 0; i < strlen(COMPRESSION_EXTENSIONS); i++) { 260 snprintf(filename, sizeof(filename), "%s/%s%s/%s.%c", path, CAT, 261 section + len_cat, page, COMPRESSION_EXTENSIONS[i]); 262 uncompress_format = uncompress_formats[i]; 263#ifdef DEBUG 264 printf("Trying .%c ...\n", COMPRESSION_EXTENSIONS[i]); 265#endif 266 if ( (file = Uncompress(man_globals, filename)) != NULL) 267 return(file); 268 } 269#endif 270 271/* 272 * And lastly files in a compressed directory. 273 * 274 * The directory is not actually compressed it is just named man#.Z 275 * and all files in it are compressed without the .Z extension. 276 * HP does it this way (really :-). 277 */ 278 279 snprintf(filename, sizeof(filename), "%s/%s%s.%s/%s", path, CAT, 280 section + len_cat, COMPRESSION_EXTENSION, page); 281 if ( (file = Uncompress(man_globals, filename)) != NULL) 282 return(file); 283/* 284 * We did not find any preformatted manual pages, try to format it. 285 */ 286 287 return(Format(man_globals, entry)); 288} 289 290/* Function Namecompress 291 * Description: This function will attempt to find a compressed man 292 * page and uncompress it. 293 * Arguments: man_globals - the psuedo global info. 294 * filename - name of file to uncompress. 295 * Returns:; a pointer to the file or NULL. 296 */ 297 298static FILE * 299Uncompress(ManpageGlobals * man_globals, char * filename) 300{ 301 char tmp_file[BUFSIZ], error_buf[BUFSIZ]; 302 FILE * file; 303 304#ifndef HAS_MKSTEMP 305 if ( !UncompressNamed(man_globals, filename, tmp_file) ) 306 return(NULL); 307 308 else if ((file = fopen(tmp_file, "r")) == NULL) { 309 sprintf(error_buf, "Something went wrong in retrieving the %s", 310 "uncompressed manual page try cleaning up /tmp."); 311 PopupWarning(man_globals, error_buf); 312 } 313#else 314 if (!UncompressNamed(man_globals, filename, tmp_file, &file)) { 315 sprintf(error_buf, "Something went wrong in retrieving the %s", 316 "uncompressed manual page try cleaning up /tmp."); 317 PopupWarning(man_globals, error_buf); 318 return(NULL); 319 } 320#endif 321 322 unlink(tmp_file); /* remove name in tree, it will remain 323 until we close the fd, however. */ 324 return(file); 325} 326 327/* Function Name: UncompressNamed 328 * Description: This function will attempt to find a compressed man 329 * page and uncompress it. 330 * Arguments: man_globals - the psuedo global info. 331 * filename - name of file to uncompress. 332 * RETURNED output - the file name output (must be an allocated string). 333 * Returns:; TRUE if the file was found. 334 */ 335 336#ifndef HAS_MKSTEMP 337static Boolean 338UncompressNamed(ManpageGlobals * man_globals, char * filename, char * output) 339#else 340static Boolean 341UncompressNamed(ManpageGlobals * man_globals, char * filename, char * output, 342 FILE ** output_fd) 343#endif 344{ 345 char tmp[BUFSIZ], cmdbuf[BUFSIZ], error_buf[BUFSIZ]; 346 struct stat junk; 347#ifdef HAS_MKSTEMP 348 int fd; 349#endif 350 351 if (stat(filename, &junk) != 0) { /* Check for existance of the file. */ 352 if (errno != ENOENT) { 353 snprintf(error_buf, sizeof(error_buf), 354 "Error while stating file %s, errno = %d", filename, errno); 355 PopupWarning(man_globals, error_buf); 356 } 357 return(FALSE); 358 } 359 360/* 361 * Using stdin is necessary to fool zcat since we cannot guarentee 362 * the .Z extension. 363 */ 364 365 strcpy(tmp, MANTEMP); /* get a temp file. */ 366#ifndef HAS_MKSTEMP 367 (void) mktemp(tmp); 368#else 369 fd = mkstemp(tmp); 370 if (fd < 0) { 371 PopupWarning(man_globals, "Error creating a temp file"); 372 return FALSE; 373 } 374 *output_fd = fdopen(fd, "r"); 375#endif 376 strcpy(output, tmp); 377 378#ifdef GZIP_EXTENSION 379 if (streq(filename + strlen(filename) - strlen(GZIP_EXTENSION), 380 GZIP_EXTENSION)) 381 snprintf(cmdbuf, sizeof(cmdbuf), GUNZIP_FORMAT, filename, output); 382 else 383#endif 384 snprintf(cmdbuf, sizeof(cmdbuf), UNCOMPRESS_FORMAT, filename, output); 385 if(system(cmdbuf) == 0) /* execute search. */ 386 return(TRUE); 387 388 snprintf(error_buf, sizeof(error_buf), 389 "Error while uncompressing, command was: %s", cmdbuf); 390 PopupWarning(man_globals, error_buf); 391 return(FALSE); 392} 393 394#if defined(SMAN) && defined(SFORMAT) 395/* Function Name: SgmlToRoffNamed 396 * Description: This function will attempt to find an SGML man 397 * page and convert it to roff format. 398 * Arguments: man_globals - the psuedo global info. 399 * filename - name of file to uncompress. 400 * RETURNED output - the file name output (must be an allocated string). 401 * Returns:; TRUE if the file was found. 402 */ 403 404#ifndef HAS_MKSTEMP 405static Boolean 406SgmlToRoffNamed(ManpageGlobals * man_globals, char * filename, char * output) 407#else 408static Boolean 409SgmlToRoffNamed(ManpageGlobals * man_globals, char * filename, char * output, 410 FILE ** output_fd) 411#endif 412{ 413 char tmp[BUFSIZ], cmdbuf[BUFSIZ], error_buf[BUFSIZ]; 414 struct stat junk; 415#ifdef HAS_MKSTEMP 416 int fd; 417#endif 418 419 if (stat(filename, &junk) != 0) { /* Check for existance of the file. */ 420 if (errno != ENOENT) { 421 snprintf(error_buf, sizeof(error_buf), 422 "Error while stating file %s, errno = %d", filename, errno); 423 PopupWarning(man_globals, error_buf); 424 } 425 return(FALSE); 426 } 427 428 strcpy(tmp, MANTEMP); /* get a temp file. */ 429#ifndef HAS_MKSTEMP 430 (void) mktemp(tmp); 431#else 432 fd = mkstemp(tmp); 433 if (fd < 0) { 434 PopupWarning(man_globals, "Error creating a temp file"); 435 return FALSE; 436 } 437 *output_fd = fdopen(fd, "r"); 438#endif 439 strcpy(output, tmp); 440 441 snprintf(cmdbuf, sizeof(cmdbuf), "%s %s > %s", SFORMAT, filename, output); 442 if(system(cmdbuf) == 0) /* execute search. */ 443 return(TRUE); 444 445 snprintf(error_buf, sizeof(error_buf), 446 "Error while converting from sgml, command was: %s", cmdbuf); 447 PopupWarning(man_globals, error_buf); 448 return(FALSE); 449} 450#endif /* defined (SMAN) && defined(SFORMAT) */ 451 452/* Function Name: Format 453 * Description: This funtion formats the manual pages and interfaces 454 * with the user. 455 * Arguments: man_globals - the psuedo globals 456 * file - the file pointer to use and return 457 * entry - the current entry struct. 458 * current_box - The current directory being displayed. 459 * Returns: none. 460 */ 461 462/* ARGSUSED */ 463 464FILE * 465Format(ManpageGlobals * man_globals, char * entry) 466{ 467 FILE * file = NULL; 468#ifdef HAS_MKSTEMP 469 int fd; 470#endif 471 Widget manpage = man_globals->manpagewidgets.manpage; 472 char cmdbuf[BUFSIZ], tmp[BUFSIZ], filename[BUFSIZ], error_buf[BUFSIZ]; 473 char path[BUFSIZ], sect[BUFSIZ]; 474 XEvent event; 475 Position x,y; /* location to pop up the 476 "would you like to save" widget. */ 477 478#ifndef HAS_MKSTEMP 479 if ( !UncompressUnformatted(man_globals, entry, filename) ) { 480#else 481 if ( !UncompressUnformatted(man_globals, entry, filename, &file) ) { 482#endif 483 /* We Really could not find it, this should never happen, yea right. */ 484 snprintf(error_buf, sizeof(error_buf), 485 "Could not open manual page, %s", entry); 486 PopupWarning(man_globals, error_buf); 487 XtPopdown( XtParent(man_globals->standby) ); 488 return(NULL); 489 } 490 491#ifndef HAS_MKSTEMP 492 if ((file = fopen(filename, "r")) != NULL) { 493#else 494 if (file != NULL) { 495#endif 496 char line[BUFSIZ]; 497 498 if (fgets(line, sizeof(line), file) != NULL) { 499 if (strncmp(line, ".so ", 4) == 0) { 500 line[strlen(line) - 1] = '\0'; 501 fclose(file); 502 unlink(filename); 503 if (line[4] != '/') { 504 char *ptr = NULL; 505 506 strcpy(tmp, entry); 507 if ((ptr = rindex(tmp, '/')) != NULL) { 508 *ptr = '\0'; 509 if ((ptr = rindex(tmp, '/')) != NULL) 510 ptr[1] = '\0'; 511 } 512 } 513 else 514 *tmp = '\0'; 515 snprintf(filename, sizeof(filename), "%s%s", tmp, line + 4); 516 517 return (Format(man_globals, filename)); 518 } 519 } 520 fclose(file); 521 } 522 523 Popup(XtParent(man_globals->standby), XtGrabExclusive); 524 while ( !XCheckTypedWindowEvent(XtDisplay(man_globals->standby), 525 XtWindow(man_globals->standby), 526 Expose, &event) ); 527 XtDispatchEvent( &event ); 528 XFlush(XtDisplay(man_globals->standby)); 529 530 strcpy(tmp,MANTEMP); /* Get a temp file. */ 531#ifndef HAS_MKSTEMP 532 (void) mktemp(tmp); 533#else 534 fd = mkstemp(tmp); 535 file = fdopen(fd, "r"); 536#endif 537 strcpy(man_globals->tempfile, tmp); 538 539 ParseEntry(entry, path, sect, NULL); 540 541#ifndef HANDLE_ROFFSEQ 542#ifndef HAS_MKSTEMP 543 snprintf(cmdbuf, sizeof(cmdbuf), "cd %s ; %s %s %s > %s %s", path, TBL, 544 filename, FORMAT, man_globals->tempfile, "2> /dev/null"); 545#else 546 snprintf(cmdbuf, sizeof(cmdbuf), "cd %s ; %s %s %s >> %s %s", path, TBL, 547 filename, FORMAT, man_globals->tempfile, "2> /dev/null"); 548#endif 549#else 550 /* Handle more flexible way of specifying the formatting pipeline */ 551 if (! ConstructCommand(cmdbuf, path, filename, man_globals->tempfile)) { 552 sprintf(error_buf, "Constructed command was too long!"); 553 PopupWarning(man_globals, error_buf); 554 file = NULL; 555 } 556 else 557#endif /* HANDLE_ROFFSEQ */ 558 559 if(system(cmdbuf) != 0) { /* execute search. */ 560 snprintf(error_buf, sizeof(error_buf), 561 "Something went wrong trying to run the command: %s", cmdbuf); 562 PopupWarning(man_globals, error_buf); 563 file = NULL; 564 } 565 else { 566#ifndef HAS_MKSTEMP 567 if ((file = fopen(man_globals->tempfile,"r")) == NULL) { 568 sprintf(error_buf, "Something went wrong in retrieving the %s", 569 "temp file, try cleaning up /tmp"); 570 PopupWarning(man_globals, error_buf); 571 } 572 else { 573#endif 574 575 XtPopdown( XtParent(man_globals->standby) ); 576 577 if ( (man_globals->save == NULL) || 578 (man_globals->manpagewidgets.manpage == NULL) ) 579 unlink(man_globals->tempfile); 580 else { 581 char * ptr, catdir[BUFSIZ]; 582 583 /* 584 * If the catdir is writeable then ask the user if he/she wants to 585 * write the man page to it. 586 */ 587 588 strcpy(catdir, man_globals->save_file); 589 if ( (ptr = rindex(catdir, '/')) != NULL) { 590 *ptr = '\0'; 591 592 if ( access(catdir, W_OK) != 0 ) 593 unlink(man_globals->tempfile); 594 else { 595 x = (Position) Width(man_globals->manpagewidgets.manpage)/2; 596 y = (Position) Height(man_globals->manpagewidgets.manpage)/2; 597 XtTranslateCoords(manpage, x, y, &x, &y); 598 PositionCenter( man_globals->save, (int) x, (int) y, 0, 0, 0, 0); 599 XtPopup( man_globals->save, XtGrabExclusive); 600 } 601 } 602 else 603 unlink(man_globals->tempfile); 604 } 605#ifndef HAS_MKSTEMP 606 } 607#endif 608 } 609 610 /* 611 * If the original was compressed or in another format, delete temporary file. 612 */ 613 if (man_globals->deletetempfile) 614 unlink(filename); 615 616 return(file); 617} 618 619#ifdef HANDLE_ROFFSEQ 620/* Function Name: ConstructCommand 621 * Description: Constructs the pipeline of commands necessary to format 622 * a manual page. 623 * Arguments: cmdbuf - the buffer into which to write the command 624 * path - the directory in which the original man page resides 625 * filename - the (uncompressed) manpage source file 626 * tempfile - the name of a temporary file to direct the final 627 * output of the pipeline into 628 * Returns: TRUE if the command fit into the buffer, FALSE if it would 629 * be too long (more than BUFSIZ characters) 630 */ 631static Boolean 632ConstructCommand(cmdbuf, path, filename, tempfile) 633 char *cmdbuf, *path, *filename, *tempfile; 634{ 635 /* The original code did the following to produce a command line: 636 * sprintf(cmdbuf,"cd %s ; %s %s %s > %s %s", path, TBL, 637 * filename, FORMAT, man_globals->tempfile, "2> /dev/null"); 638 * We are more flexible and follow more or less the algorithm used 639 * by the Linux man command: 640 * + Obtain a string of letters from the following sources in order 641 * of preference: 642 * + a command line option (not implemented in xman; it's probably not 643 * useful) 644 * + the first line of the manpage source, if it is of the form: 645 * '\" <string> 646 * + the MANROFFSEQ environment variable 647 * + a default string; this is "". 648 * + Interpret the string as a pipeline of filters: 649 * + e = eqn g = grap p = pic t = tbl v = vgrind r = refer 650 * + zsoelim is always run as the first preprocessor in any case. 651 * 652 * Strictly speaking we should save a catpage iff the string comes 653 * from the file or is the default. 654 * 655 * You'll notice that we format a man page into ASCII text output and then 656 * attempt to interpret things like L^HL as bold and so forth. This 657 * is so obviously the Wrong Thing it's untrue. 658 */ 659 char *c = cmdbuf; /* current posn in buffer */ 660 int left = BUFSIZ; /* space left in buffer */ 661 int used; 662 char *fmt; 663 FILE *file; 664 char fmtbuf[128]; 665 int gotfmt = 0; /* set to 1 if we got a directive from source */ 666 char *fname = NULL; 667#ifdef __UNIXOS2__ 668 int i; 669#endif 670 671 fmt = NULL; 672 /* If you have a command line option that gives a setting for fmt, 673 set it here. */ 674 675 if (!fmt) { 676 /* This is the tricky bit: extract a format string from the source file 677 * Annoyingly, filename might be relative or absolute. We cheat and 678 * use system to get the thing to a known absoute filename. 679 */ 680 if (filename[0] == '/') { 681 fname = filename; 682 } else { 683 fname = malloc(strlen(path) + 1 + strlen(filename) + 1); 684 if (!fname) 685 return FALSE; 686 sprintf(fname, "%s/%s", path, filename); 687 } 688 if ((file = fopen(fname, "r")) && 689 (fgets(fmtbuf, sizeof(fmtbuf), file)) && 690 (!memcmp(fmtbuf, "'\\\" ", 4))) { 691 /* that's squote-backslash-dquote-space */ 692 int len; 693 fmt = fmtbuf + 3; 694 len = strlen(fmt); 695 if (len && (fmt[len-1] == '\n')) { 696 fmt[len-1] = 0; 697 gotfmt++; 698 } 699 } 700 if (fname && fname != filename) 701 free(fname); 702 if (!gotfmt) /* not there or some error */ 703 { 704 fmt = getenv("MANROFFSEQ"); 705 } 706 } 707 708 if (!fmt) 709 { 710 fmt = DEFAULT_MANROFFSEQ; 711 } 712 713 714 /* Start with the first fixed part of the command line */ 715#ifdef __UNIXOS2__ 716 for (i = 0; i < strlen(path); i++) { 717 if (path[i] == '/') 718 path[i] = '\\'; 719 } 720 used = snprintf(c, left, "cd %s & %s %s ", path, ZSOELIM, filename); 721#else 722 used = snprintf(c, left, "cd %s; %s %s ", path, ZSOELIM, filename); 723#endif 724 left -= used; 725 c += used; 726 if (left <= 1) 727 return (FALSE); 728 729 /* Now add preprocessors of the form '| processor' */ 730 for ( ; *fmt; fmt++) 731 { 732 char *filter; 733 switch (*fmt) 734 { 735 case 'e': 736 filter = EQN; 737 break; 738 case 'g': 739 filter = GRAP; 740 break; 741 case 'p': 742 filter = XMAN_PIC; 743 break; 744 case 't': 745 filter = TBL; 746 break; 747 case 'v': 748 filter = VGRIND; 749 break; 750 case 'r': 751 filter = REFER; 752 break; 753 default: 754 filter = NULL; 755 break; 756 } 757 if (filter) 758 { 759 used = snprintf(c, left, " | %s ", filter); 760 left -= used; 761 c += used; 762 if (left <= 1) 763 return (FALSE); 764 } 765 } 766 767 /* Now add the fixed trailing part 'formatprog > tempfile 2> /dev/null' */ 768#ifdef __UNIXOS2__ 769 used = snprintf(c, left, " | %s > %s 2>NUL", FORMAT, tempfile); 770#else 771#ifndef HAS_MKSTEMP 772 used = snprintf(c, left, " | %s > %s 2>/dev/null", FORMAT, tempfile); 773#else 774 used = snprintf(c, left, " | %s >> %s 2>/dev/null", FORMAT, tempfile); 775#endif 776#endif /* __UNIXOS2__ */ 777 left -= used; 778 if (left <= 1) 779 return (FALSE); 780 781 return (TRUE); 782} 783#endif /* HANDLE_ROFFSEQ */ 784 785/* Function Name: UncompressUnformatted 786 * Description: Finds an uncompressed unformatted manual page. 787 * Arguments: man_globals - the psuedo global structure. 788 * entry - the manual page entry. 789 * RETURNED filename - location to put the name of the file. 790 * Returns: TRUE if the file was found. 791 */ 792 793static Boolean 794#ifndef HAS_MKSTEMP 795UncompressUnformatted(ManpageGlobals * man_globals, char * entry, 796 char * filename) 797#else 798UncompressUnformatted(ManpageGlobals * man_globals, char * entry, 799 char * filename, FILE **file) 800#endif 801{ 802 char path[BUFSIZ], page[BUFSIZ], section[BUFSIZ], input[BUFSIZ]; 803 int len_cat = strlen(CAT), len_man = strlen(MAN); 804#if defined(SMAN) && defined(SFORMAT) 805 int len_sman = strlen(SMAN); 806#endif 807 808 ParseEntry(entry, path, section, page); 809 810#if defined(__OpenBSD__) || defined(__NetBSD__) 811 /* 812 * look for uncompressed file in machine subdir first 813 */ 814 snprintf(filename, BUFSIZ, "%s/%s%s/%s/%s", path, MAN, 815 section + len_cat, MACHINE, page); 816 if ( access( filename, R_OK ) == 0 ) { 817 man_globals->compress = FALSE; 818 man_globals->gzip = FALSE; 819 man_globals->deletetempfile = FALSE; 820 snprintf(man_globals->save_file, sizeof(man_globals->save_file), 821 "%s/%s%s/%s/%s", path, CAT, section + len_cat, MACHINE, page); 822 return(TRUE); 823 } 824 /* 825 * Then for compressed files in an uncompressed directory. 826 */ 827 snprintf(input, sizeof(input), "%s.%s", filename, COMPRESSION_EXTENSION); 828#ifndef HAS_MKSTEMP 829 if ( UncompressNamed(man_globals, input, filename) ) { 830#else 831 if ( UncompressNamed(man_globals, input, filename, file) ) { 832#endif 833 man_globals->compress = TRUE; 834 man_globals->deletetempfile = TRUE; 835 snprintf(man_globals->save_file, sizeof(man_globals->save_file), 836 "%s/%s%s/%s.%s", path, CAT, section + len_cat, page, 837 COMPRESSION_EXTENSION); 838 return(TRUE); 839 } 840#ifdef GZIP_EXTENSION 841 else { 842 snprintf(input, sizeof(input), "%s.%s", filename, GZIP_EXTENSION); 843#ifndef HAS_MKSTEMP 844 if ( UncompressNamed(man_globals, input, filename) ) { 845#else 846 if ( UncompressNamed(man_globals, input, filename, file) ) { 847#endif 848 man_globals->compress = TRUE; 849 man_globals->gzip = TRUE; 850 man_globals->deletetempfile = TRUE; 851 snprintf(man_globals->save_file, sizeof(man_globals->save_file), 852 "%s/%s%s/%s.%s", path, CAT, section + len_cat, page, 853 GZIP_EXTENSION); 854 return(TRUE); 855 } 856 } 857#endif /* GZIP_EXTENSION */ 858#endif /* __OpenBSD__ || __NetBSD__ */ 859/* 860 * Look for uncompressed file first. 861 */ 862 863 snprintf(filename, BUFSIZ, "%s/%s%s/%s", path, MAN, section + len_man, page); 864 if ( access( filename, R_OK ) == 0 ) { 865 man_globals->compress = FALSE; 866 man_globals->gzip = FALSE; 867 man_globals->deletetempfile = FALSE; 868 snprintf(man_globals->save_file, sizeof(man_globals->save_file), 869 "%s/%s%s/%s", path, CAT, section + len_cat, page); 870 return(TRUE); 871 } 872 873#if defined(SMAN) && defined(SFORMAT) 874 /* 875 * Look for uncompressed sgml file next. 876 */ 877 878 snprintf(input, BUFSIZ, "%s/%s%s/%s", path, SMAN, section + len_sman, page); 879#ifndef HAS_MKSTEMP 880 if ( SgmlToRoffNamed(man_globals, input, filename) ) { 881#else 882 if ( SgmlToRoffNamed(man_globals, input, filename, file) ) { 883#endif 884 man_globals->compress = FALSE; 885 man_globals->gzip = FALSE; 886 man_globals->deletetempfile = TRUE; 887 snprintf(man_globals->save_file, sizeof(man_globals->save_file), 888 "%s/%s%s/%s", path, CAT, section + len_cat, page); 889 return(TRUE); 890 } 891#endif 892 893/* 894 * Then for compressed files in an uncompressed directory. 895 */ 896 897 snprintf(input, sizeof(input), "%s.%s", filename, COMPRESSION_EXTENSION); 898#ifndef HAS_MKSTEMP 899 if ( UncompressNamed(man_globals, input, filename) ) { 900#else 901 if ( UncompressNamed(man_globals, input, filename, file) ) { 902#endif 903 man_globals->compress = TRUE; 904 man_globals->deletetempfile = TRUE; 905 snprintf(man_globals->save_file, sizeof(man_globals->save_file), 906 "%s/%s%s/%s.%s", path, CAT, section + len_cat, page, 907 COMPRESSION_EXTENSION); 908 return(TRUE); 909 } 910#ifdef GZIP_EXTENSION 911 else { 912 snprintf(input, sizeof(input), "%s.%s", filename, GZIP_EXTENSION); 913#ifndef HAS_MKSTEMP 914 if ( UncompressNamed(man_globals, input, filename) ) { 915#else 916 if ( UncompressNamed(man_globals, input, filename, file) ) { 917#endif 918 man_globals->compress = TRUE; 919 man_globals->gzip = TRUE; 920 man_globals->deletetempfile = TRUE; 921 snprintf(man_globals->save_file, sizeof(man_globals->save_file), 922 "%s/%s%s/%s.%s", path, CAT, section + len_cat, page, 923 GZIP_EXTENSION); 924 return(TRUE); 925 } 926 } 927#endif 928/* 929 * And lastly files in a compressed directory. 930 */ 931 932 snprintf(input, sizeof(input), "%s/%s%s.%s/%s", path, 933 MAN, section + len_man, COMPRESSION_EXTENSION, page); 934#ifndef HAS_MKSTEMP 935 if ( UncompressNamed(man_globals, input, filename) ) { 936#else 937 if ( UncompressNamed(man_globals, input, filename, file) ) { 938#endif 939 man_globals->compress = TRUE; 940 man_globals->deletetempfile = TRUE; 941 snprintf(man_globals->save_file, sizeof(man_globals->save_file), 942 "%s/%s%s.%s/%s", path, CAT, section + len_cat, 943 COMPRESSION_EXTENSION, page); 944 return(TRUE); 945 } 946 return(FALSE); 947} 948 949/* Function Name: AddCursor 950 * Description: This function adds the cursor to the window. 951 * Arguments: w - the widget to add the cursor to. 952 * cursor - the cursor to add to this widget. 953 * Returns: none 954 */ 955 956void 957AddCursor(Widget w, Cursor cursor) 958{ 959 XColor colors[2]; 960 Arg args[10]; 961 Cardinal num_args = 0; 962 Colormap c_map; 963 964 if (!XtIsRealized(w)) { 965 PopupWarning(NULL, "Widget is not realized, no cursor added.\n"); 966 return; 967 } 968 969 XtSetArg( args[num_args], XtNcolormap, &c_map); num_args++; 970 XtGetValues( w, args, num_args); 971 972 colors[0].pixel = resources.cursors.fg_color; 973 colors[1].pixel = resources.cursors.bg_color; 974 975 XQueryColors (XtDisplay(w), c_map, colors, 2); 976 XRecolorCursor(XtDisplay(w), cursor, colors, colors+1); 977 XDefineCursor(XtDisplay(w),XtWindow(w),cursor); 978} 979 980/* Function Name: ChangeLabel 981 * Description: This function changes the label field of the 982 * given widget to the string in str. 983 * Arguments: w - the widget. 984 * str - the string to change the label to. 985 * Returns: none 986 */ 987 988void 989ChangeLabel(Widget w, char * str) 990{ 991 Arg arglist[3]; /* An argument list. */ 992 993 if (w == NULL) return; 994 995 XtSetArg(arglist[0], XtNlabel, str); 996 997/* shouldn't really have to do this. */ 998 XtSetArg(arglist[1], XtNwidth, 0); 999 XtSetArg(arglist[2], XtNheight, 0); 1000 1001 XtSetValues(w, arglist, (Cardinal) 1); 1002} 1003 1004/* 1005 * In an ideal world this would be part of the XToolkit, and I would not 1006 * have to do it, but such is life sometimes. Perhaps in X11R3. 1007 */ 1008 1009/* Function Name: PositionCenter 1010 * Description: This function positions the given widgets center 1011 * in the following location. 1012 * Arguments: widget - the widget widget to postion 1013 * x,y - The location for the center of the widget 1014 * above - number of pixels above center to locate this widget 1015 * left - number of pixels left of center to locate this widget 1016 * h_space, v_space - how close to get to the edges of the 1017 * parent window. 1018 * Returns: none 1019 * Note: This should only be used with a popup widget that has override 1020 * redirect set. 1021 */ 1022 1023void 1024PositionCenter(Widget widget, int x, int y, int above, int left, int v_space, int h_space) 1025{ 1026 Arg wargs[2]; 1027 int x_temp,y_temp; /* location of the new window. */ 1028 int parent_height,parent_width; /* Height and width of the parent widget or 1029 the root window if it has no parent. */ 1030 1031 x_temp = x - left - Width(widget) / 2 + BorderWidth(widget); 1032 y_temp = y - above - Height(widget) / 2 + BorderWidth(widget); 1033 1034 parent_height = HeightOfScreen(XtScreen(widget)); 1035 parent_width = WidthOfScreen(XtScreen(widget)); 1036 1037/* 1038 * Check to make sure that all edges are within the viewable part of the 1039 * root window, and if not then force them to be. 1040 */ 1041 1042 if (x_temp < h_space) 1043 x_temp = v_space; 1044 if (y_temp < v_space) 1045 (y_temp = 2); 1046 1047 if ( y_temp + Height(widget) + v_space > parent_height ) 1048 y_temp = parent_height - Height(widget) - v_space; 1049 1050 if ( x_temp + Width(widget) + h_space > parent_width ) 1051 x_temp = parent_width - Width(widget) - h_space; 1052 1053 XtSetArg(wargs[0], XtNx, x_temp); 1054 XtSetArg(wargs[1], XtNy, y_temp); 1055 XtSetValues(widget, wargs, 2); 1056} 1057 1058/* Function Name: ParseEntry(entry, path, sect, page) 1059 * Description: Parses the manual pages entry filenames. 1060 * Arguments: str - the full path name. 1061 * path - the path name. RETURNED 1062 * sect - the section name. RETURNED 1063 * page - the page name. RETURNED 1064 * Returns: none. 1065 */ 1066 1067void 1068ParseEntry(char *entry, char *path, char *sect, char *page) 1069{ 1070 char *c, temp[BUFSIZ]; 1071 1072 strcpy(temp, entry); 1073 1074 c = rindex(temp, '/'); 1075 if (c == NULL) 1076 PrintError("index failure in ParseEntry."); 1077 *c++ = '\0'; 1078 if (page != NULL) 1079 strcpy(page, c); 1080 1081 c = rindex(temp, '/'); 1082 if (c == NULL) 1083 PrintError("index failure in ParseEntry."); 1084 *c++ = '\0'; 1085#if defined(SFORMAT) && defined(SMAN) 1086 /* sgmltoroff sometimes puts an extra ./ in the path to .so entries */ 1087 if (strcmp(c, ".") == 0) { 1088 c = rindex(temp, '/'); 1089 if (c == NULL) 1090 PrintError("index failure in ParseEntry."); 1091 *c++ = '\0'; 1092 } 1093#endif 1094#if defined(__OpenBSD__) || defined(__NetBSD__) 1095 /* Skip machine subdirectory if present */ 1096 if (strcmp(c, MACHINE) == 0) { 1097 c = rindex(temp, '/'); 1098 if (c == NULL) 1099 PrintError("index failure in ParseEntry."); 1100 *c++ = '\0'; 1101 } 1102#endif 1103 if (sect != NULL) 1104 strcpy(sect, c); 1105 1106 if (path != NULL) 1107 strcpy(path, temp); 1108} 1109 1110/* Function Name: GetGlobals 1111 * Description: Gets the psuedo globals associated with the 1112 * manpage associated with this widget. 1113 * Arguments: w - a widget in the manpage. 1114 * Returns: the psuedo globals. 1115 * Notes: initial_widget is a globals variable. 1116 * manglobals_context is a global variable. 1117 */ 1118 1119ManpageGlobals * 1120GetGlobals(Widget w) 1121{ 1122 Widget temp; 1123 caddr_t data; 1124 1125 while ( (temp = XtParent(w)) != initial_widget && (temp != NULL)) 1126 w = temp; 1127 1128 if (temp == NULL) 1129 XtAppError(XtWidgetToApplicationContext(w), 1130 "Xman: Could not locate widget in tree, exiting"); 1131 1132 if (XFindContext(XtDisplay(w), XtWindow(w), 1133 manglobals_context, &data) != XCSUCCESS) 1134 XtAppError(XtWidgetToApplicationContext(w), 1135 "Xman: Could not find global data, exiting"); 1136 1137 return( (ManpageGlobals *) data); 1138} 1139 1140/* Function Name: SaveGlobals 1141 * Description: Saves the psuedo globals on the widget passed 1142 * to this function, although GetGlobals assumes that 1143 * the data is associated with the popup child of topBox. 1144 * Arguments: w - the widget to associate the data with. 1145 * globals - data to associate with this widget. 1146 * Returns: none. 1147 * Notes: WIDGET MUST BE REALIZED. 1148 * manglobals_context is a global variable. 1149 */ 1150 1151void 1152SaveGlobals(Widget w, ManpageGlobals * globals) 1153{ 1154 if (XSaveContext(XtDisplay(w), XtWindow(w), manglobals_context, 1155 (caddr_t) globals) != XCSUCCESS) 1156 XtAppError(XtWidgetToApplicationContext(w), 1157 "Xman: Could not save global data, are you out of memory?"); 1158} 1159 1160/* Function Name: RemoveGlobals 1161 * Description: Removes the psuedo globals from the widget passed 1162 * to this function. 1163 * Arguments: w - the widget to remove the data from. 1164 * Returns: none. 1165 * Notes: WIDGET MUST BE REALIZED. 1166 * manglobals_context is a global variable. 1167 */ 1168 1169void 1170RemoveGlobals(Widget w) 1171{ 1172 if (XDeleteContext(XtDisplay(w), XtWindow(w), 1173 manglobals_context) != XCSUCCESS) 1174 XtAppError(XtWidgetToApplicationContext(w), 1175 "Xman: Could not remove global data?"); 1176} 1177