2006-11-25 13:07:29 -07:00
|
|
|
/*
|
|
|
|
|
|
|
|
Copyright (c) 1987, 1988 X Consortium
|
|
|
|
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining
|
|
|
|
a copy of this software and associated documentation files (the
|
|
|
|
"Software"), to deal in the Software without restriction, including
|
|
|
|
without limitation the rights to use, copy, modify, merge, publish,
|
|
|
|
distribute, sublicense, and/or sell copies of the Software, and to
|
|
|
|
permit persons to whom the Software is furnished to do so, subject to
|
|
|
|
the following conditions:
|
|
|
|
|
|
|
|
The above copyright notice and this permission notice shall be included
|
|
|
|
in all copies or substantial portions of the Software.
|
|
|
|
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
|
|
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
|
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
|
|
IN NO EVENT SHALL THE X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
|
|
|
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
|
|
|
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
|
|
OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
|
|
|
|
Except as contained in this notice, the name of the X Consortium shall
|
|
|
|
not be used in advertising or otherwise to promote the sale, use or
|
|
|
|
other dealings in this Software without prior written authorization
|
|
|
|
from the X Consortium.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* xman - X window system manual page display program.
|
|
|
|
* Author: Chris D. Peterson, MIT Project Athena
|
|
|
|
* Created: October 27, 1987
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
# include "config.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "globals.h"
|
|
|
|
#include "vendor.h"
|
2013-09-28 10:22:59 -06:00
|
|
|
#include <X11/Xos.h> /* sys/types.h and unistd.h included in here */
|
2006-11-25 13:07:29 -07:00
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <X11/Xaw/Dialog.h>
|
|
|
|
#include <X11/Shell.h>
|
|
|
|
|
2013-09-28 10:22:59 -06:00
|
|
|
static FILE *Uncompress(ManpageGlobals * man_globals, const char *filename);
|
|
|
|
|
|
|
|
static Boolean UncompressNamed(ManpageGlobals * man_globals,
|
|
|
|
const char *filename, char *output,
|
|
|
|
FILE ** output_file);
|
2006-11-25 13:07:29 -07:00
|
|
|
static Boolean UncompressUnformatted(ManpageGlobals * man_globals,
|
2013-09-28 10:22:59 -06:00
|
|
|
const char *entry, char *filename,
|
|
|
|
FILE ** file);
|
|
|
|
|
2006-11-25 13:07:29 -07:00
|
|
|
#ifdef HANDLE_ROFFSEQ
|
2013-09-28 10:22:59 -06:00
|
|
|
static Boolean ConstructCommand(char *cmdbuf, const char *path,
|
|
|
|
const char *filename, const char *tempfile);
|
2006-11-25 13:07:29 -07:00
|
|
|
#endif
|
|
|
|
|
|
|
|
#if defined(ISC) || defined(__SCO__) || defined(__UNIXWARE__)
|
|
|
|
static char *uncompress_format = NULL;
|
2013-09-28 10:22:59 -06:00
|
|
|
|
|
|
|
static char *uncompress_formats[] = {
|
|
|
|
UNCOMPRESS_FORMAT_1,
|
|
|
|
UNCOMPRESS_FORMAT_2,
|
|
|
|
UNCOMPRESS_FORMAT_3
|
|
|
|
};
|
2006-11-25 13:07:29 -07:00
|
|
|
#endif
|
|
|
|
|
|
|
|
/* Function Name: PopupWarning
|
2013-09-28 10:22:59 -06:00
|
|
|
* Description: This function pops up a warning message.
|
2006-11-25 13:07:29 -07:00
|
|
|
* Arguments: string - the specific warning string.
|
|
|
|
* Returns: none
|
|
|
|
*/
|
|
|
|
|
|
|
|
static Widget warnShell, warnDialog;
|
|
|
|
|
|
|
|
static void
|
|
|
|
PopdownWarning(Widget w, XtPointer client, XtPointer call)
|
|
|
|
{
|
2013-09-28 10:22:59 -06:00
|
|
|
XtPopdown((Widget) client);
|
2006-11-25 13:07:29 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2013-09-28 10:22:59 -06:00
|
|
|
PopupWarning(ManpageGlobals * man_globals, const char *string)
|
2006-11-25 13:07:29 -07:00
|
|
|
{
|
2013-09-28 10:22:59 -06:00
|
|
|
int n;
|
|
|
|
Arg wargs[3];
|
|
|
|
Dimension topX, topY;
|
|
|
|
char buffer[BUFSIZ];
|
|
|
|
Boolean hasPosition;
|
|
|
|
|
|
|
|
snprintf(buffer, sizeof(buffer), "Xman Warning: %s", string);
|
|
|
|
hasPosition = FALSE;
|
|
|
|
if (top) {
|
|
|
|
n = 0;
|
|
|
|
XtSetArg(wargs[n], XtNx, &topX);
|
|
|
|
n++;
|
|
|
|
XtSetArg(wargs[n], XtNy, &topY);
|
|
|
|
n++;
|
|
|
|
XtGetValues(top, wargs, n);
|
|
|
|
hasPosition = TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (man_globals != NULL)
|
|
|
|
ChangeLabel(man_globals->label, buffer);
|
|
|
|
if (man_globals->label == NULL) {
|
|
|
|
n = 0;
|
|
|
|
if (hasPosition) {
|
|
|
|
XtSetArg(wargs[n], XtNx, topX);
|
|
|
|
n++;
|
|
|
|
XtSetArg(wargs[n], XtNy, topY);
|
|
|
|
n++;
|
|
|
|
}
|
|
|
|
XtSetArg(wargs[n], XtNtransientFor, top);
|
|
|
|
n++;
|
|
|
|
warnShell = XtCreatePopupShell("warnShell", transientShellWidgetClass,
|
|
|
|
initial_widget, wargs, n);
|
|
|
|
XtSetArg(wargs[0], XtNlabel, buffer);
|
|
|
|
warnDialog = XtCreateManagedWidget("warnDialog", dialogWidgetClass,
|
|
|
|
warnShell, wargs, 1);
|
|
|
|
XawDialogAddButton(warnDialog, "dismiss", PopdownWarning,
|
|
|
|
(XtPointer) warnShell);
|
|
|
|
XtRealizeWidget(warnShell);
|
|
|
|
Popup(warnShell, XtGrabNone);
|
2006-11-25 13:07:29 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Function Name: PrintError
|
|
|
|
* Description: This Function prints an error message and exits.
|
|
|
|
* Arguments: string - the specific message.
|
2013-09-28 10:22:59 -06:00
|
|
|
* Returns: none. - exits though.
|
2006-11-25 13:07:29 -07:00
|
|
|
*/
|
|
|
|
|
|
|
|
void
|
2013-09-28 10:22:59 -06:00
|
|
|
PrintError(const char *string)
|
2006-11-25 13:07:29 -07:00
|
|
|
{
|
2013-09-28 10:22:59 -06:00
|
|
|
fprintf(stderr, "Xman Error: %s\n", string);
|
|
|
|
exit(EXIT_FAILURE);
|
2006-11-25 13:07:29 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Function Name: OpenFile
|
2013-09-28 10:22:59 -06:00
|
|
|
* Description: Assigns a file to the manpage.
|
2006-11-25 13:07:29 -07:00
|
|
|
* Arguments: man_globals - global structure.
|
|
|
|
* file - the file pointer.
|
|
|
|
* Returns: none
|
|
|
|
*/
|
|
|
|
|
|
|
|
void
|
|
|
|
OpenFile(ManpageGlobals * man_globals, FILE * file)
|
|
|
|
{
|
2013-09-28 10:22:59 -06:00
|
|
|
Arg arglist[1];
|
|
|
|
Cardinal num_args = 0;
|
|
|
|
|
|
|
|
if (man_globals->curr_file) {
|
|
|
|
#if 0 /* Ownership rules need to be fixed first */
|
|
|
|
fclose(man_globals->curr_file);
|
2006-11-25 13:07:29 -07:00
|
|
|
#endif
|
2013-09-28 10:22:59 -06:00
|
|
|
}
|
|
|
|
man_globals->curr_file = file;
|
2006-11-25 13:07:29 -07:00
|
|
|
|
2013-09-28 10:22:59 -06:00
|
|
|
XtSetArg(arglist[num_args], XtNfile, man_globals->curr_file);
|
|
|
|
num_args++;
|
|
|
|
XtSetValues(man_globals->manpagewidgets.manpage, arglist, num_args);
|
2006-11-25 13:07:29 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Function Name: FindManualFile
|
|
|
|
* Description: Opens the manual page file given the entry information.
|
|
|
|
* Arguments: man_globals - the globals info for this manpage.
|
|
|
|
* section_num - section number of the man page.
|
|
|
|
* entry_num - entry number of the man page.
|
|
|
|
* Returns: fp - the file pointer
|
|
|
|
*
|
|
|
|
* NOTES:
|
|
|
|
*
|
2013-09-28 10:22:59 -06:00
|
|
|
* If there is a uncompressed section it will look there for uncompressed
|
2006-11-25 13:07:29 -07:00
|
|
|
* manual pages first and then for individually compressed file in the
|
|
|
|
* uncompressed section.
|
|
|
|
*
|
|
|
|
* If there is a compressed directory then it will also look there for
|
|
|
|
* the manual pages.
|
|
|
|
*
|
|
|
|
* If both of these fail then it will attempt to format the manual page.
|
|
|
|
*/
|
|
|
|
|
|
|
|
FILE *
|
|
|
|
FindManualFile(ManpageGlobals * man_globals, int section_num, int entry_num)
|
|
|
|
{
|
2013-09-28 10:22:59 -06:00
|
|
|
FILE *file;
|
|
|
|
char path[BUFSIZ], page[BUFSIZ], section[BUFSIZ], *temp;
|
|
|
|
char filename[BUFSIZ];
|
|
|
|
const char *entry = manual[section_num].entries[entry_num];
|
|
|
|
int len_cat = strlen(CAT);
|
|
|
|
|
2006-11-25 13:07:29 -07:00
|
|
|
#if defined(ISC) || defined(__SCO__) || defined(__UNIXWARE__)
|
2013-09-28 10:22:59 -06:00
|
|
|
int i;
|
2006-11-25 13:07:29 -07:00
|
|
|
#endif
|
|
|
|
|
2013-09-28 10:22:59 -06:00
|
|
|
temp = CreateManpageName(entry, 0, 0);
|
|
|
|
snprintf(man_globals->manpage_title, sizeof(man_globals->manpage_title),
|
|
|
|
"The current manual page is: %s.", temp);
|
|
|
|
XtFree(temp);
|
2006-11-25 13:07:29 -07:00
|
|
|
|
2013-09-28 10:22:59 -06:00
|
|
|
ParseEntry(entry, path, section, page);
|
2006-11-25 13:07:29 -07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Look for uncompressed files first.
|
|
|
|
*/
|
|
|
|
#if defined(__OpenBSD__) || defined(__NetBSD__)
|
2013-09-28 10:22:59 -06:00
|
|
|
/* look in machine subdir first */
|
|
|
|
snprintf(filename, sizeof(filename), "%s/%s%s/%s/%s", path, CAT,
|
|
|
|
section + len_cat, MACHINE, page);
|
|
|
|
if ((file = fopen(filename, "r")) != NULL)
|
|
|
|
return (file);
|
2006-11-25 13:07:29 -07:00
|
|
|
#endif
|
|
|
|
|
2013-09-28 10:22:59 -06:00
|
|
|
snprintf(filename, sizeof(filename), "%s/%s%s/%s",
|
|
|
|
path, CAT, section + len_cat, page);
|
|
|
|
if ((file = fopen(filename, "r")) != NULL)
|
|
|
|
return (file);
|
2006-11-25 13:07:29 -07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Then for compressed files in an uncompressed directory.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#if !defined(ISC) && !defined(__UNIXWARE__)
|
|
|
|
#if defined(__OpenBSD__) || defined(__NetBSD__)
|
2013-09-28 10:22:59 -06:00
|
|
|
/* look in machine subdir first */
|
|
|
|
snprintf(filename, sizeof(filename), "%s/%s%s/%s/%s.%s", path, CAT,
|
|
|
|
section + len_cat, MACHINE, page, COMPRESSION_EXTENSION);
|
|
|
|
if ((file = Uncompress(man_globals, filename)) != NULL)
|
|
|
|
return (file);
|
2006-11-25 13:07:29 -07:00
|
|
|
#endif
|
2013-09-28 10:22:59 -06:00
|
|
|
snprintf(filename, sizeof(filename), "%s/%s%s/%s.%s", path, CAT,
|
|
|
|
section + len_cat, page, COMPRESSION_EXTENSION);
|
|
|
|
if ((file = Uncompress(man_globals, filename)) != NULL)
|
|
|
|
return (file);
|
2006-11-25 13:07:29 -07:00
|
|
|
#ifdef GZIP_EXTENSION
|
2013-09-28 10:22:59 -06:00
|
|
|
else {
|
2006-11-25 13:07:29 -07:00
|
|
|
#if defined(__OpenBSD__) || defined(__NetBSD__)
|
2013-09-28 10:22:59 -06:00
|
|
|
/* look in machine subdir first */
|
|
|
|
snprintf(filename, sizeof(filename), "%s/%s%s/%s/%s.%s", path, CAT,
|
|
|
|
section + len_cat, MACHINE, page, GZIP_EXTENSION);
|
|
|
|
if ((file = Uncompress(man_globals, filename)) != NULL)
|
|
|
|
return (file);
|
2006-11-25 13:07:29 -07:00
|
|
|
#endif
|
2013-09-28 10:22:59 -06:00
|
|
|
snprintf(filename, sizeof(filename), "%s/%s%s/%s.%s", path, CAT,
|
|
|
|
section + len_cat, page, GZIP_EXTENSION);
|
|
|
|
if ((file = Uncompress(man_globals, filename)) != NULL)
|
|
|
|
return (file);
|
|
|
|
}
|
2006-11-25 13:07:29 -07:00
|
|
|
#endif
|
2009-10-24 09:00:51 -06:00
|
|
|
#ifdef BZIP2_EXTENSION
|
|
|
|
#if defined(__OpenBSD__) || defined(__NetBSD__)
|
2013-09-28 10:22:59 -06:00
|
|
|
/* look in machine subdir first */
|
|
|
|
snprintf(filename, sizeof(filename), "%s/%s%s/%s/%s.%s", path, CAT,
|
|
|
|
section + len_cat, MACHINE, page, BZIP2_EXTENSION);
|
|
|
|
if ((file = Uncompress(man_globals, filename)) != NULL)
|
|
|
|
return (file);
|
2009-10-24 09:00:51 -06:00
|
|
|
#endif
|
2013-09-28 10:22:59 -06:00
|
|
|
{
|
|
|
|
snprintf(filename, sizeof(filename), "%s/%s%s/%s.%s", path, CAT,
|
|
|
|
section + len_cat, page, BZIP2_EXTENSION);
|
|
|
|
if ((file = Uncompress(man_globals, filename)) != NULL)
|
|
|
|
return (file);
|
|
|
|
}
|
2009-10-24 09:00:51 -06:00
|
|
|
#endif
|
|
|
|
#ifdef LZMA_EXTENSION
|
2013-09-28 10:22:59 -06:00
|
|
|
{
|
|
|
|
snprintf(filename, sizeof(filename), "%s/%s%s/%s.%s", path, CAT,
|
|
|
|
section + len_cat, page, LZMA_EXTENSION);
|
|
|
|
if ((file = Uncompress(man_globals, filename)) != NULL)
|
|
|
|
return (file);
|
|
|
|
}
|
2009-10-24 09:00:51 -06:00
|
|
|
#endif
|
2006-11-25 13:07:29 -07:00
|
|
|
#else
|
2013-09-28 10:22:59 -06:00
|
|
|
for (i = 0; i < strlen(COMPRESSION_EXTENSIONS); i++) {
|
|
|
|
snprintf(filename, sizeof(filename), "%s/%s%s/%s.%c", path, CAT,
|
|
|
|
section + len_cat, page, COMPRESSION_EXTENSIONS[i]);
|
|
|
|
uncompress_format = uncompress_formats[i];
|
2006-11-25 13:07:29 -07:00
|
|
|
#ifdef DEBUG
|
2013-09-28 10:22:59 -06:00
|
|
|
printf("Trying .%c ...\n", COMPRESSION_EXTENSIONS[i]);
|
2006-11-25 13:07:29 -07:00
|
|
|
#endif
|
2013-09-28 10:22:59 -06:00
|
|
|
if ((file = Uncompress(man_globals, filename)) != NULL)
|
|
|
|
return (file);
|
|
|
|
}
|
2006-11-25 13:07:29 -07:00
|
|
|
#endif
|
|
|
|
|
|
|
|
/*
|
|
|
|
* And lastly files in a compressed directory.
|
|
|
|
*
|
|
|
|
* The directory is not actually compressed it is just named man#.Z
|
|
|
|
* and all files in it are compressed without the .Z extension.
|
|
|
|
* HP does it this way (really :-).
|
|
|
|
*/
|
|
|
|
|
2013-09-28 10:22:59 -06:00
|
|
|
snprintf(filename, sizeof(filename), "%s/%s%s.%s/%s", path, CAT,
|
|
|
|
section + len_cat, COMPRESSION_EXTENSION, page);
|
|
|
|
if ((file = Uncompress(man_globals, filename)) != NULL)
|
|
|
|
return (file);
|
2006-11-25 13:07:29 -07:00
|
|
|
/*
|
|
|
|
* We did not find any preformatted manual pages, try to format it.
|
|
|
|
*/
|
|
|
|
|
2013-09-28 10:22:59 -06:00
|
|
|
return (Format(man_globals, entry));
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifndef HAVE_MKSTEMP
|
|
|
|
/* Emulate mkstemp to allow use of a common API in the many calls below */
|
|
|
|
_X_HIDDEN int
|
|
|
|
Xmkstemp (char *template)
|
|
|
|
{
|
|
|
|
int fd = 0;
|
|
|
|
char tmp[PATH_MAX];
|
|
|
|
|
|
|
|
if (strlen(template) >= sizeof(tmp))
|
|
|
|
return -1;
|
|
|
|
/* save copy of unmodified template in case we have to try again */
|
|
|
|
strcpy(tmp, template);
|
|
|
|
|
|
|
|
do {
|
|
|
|
if (fd == -1)
|
|
|
|
strcpy(template, tmp);
|
|
|
|
if ((mktemp(template) == NULL) || (template[0] == '\0'))
|
|
|
|
return -1;
|
|
|
|
fd = open(template, O_RDWR | O_CREAT | O_EXCL, 0600);
|
|
|
|
} while ((fd == -1) && (errno == EEXIST || errno == EINTR));
|
|
|
|
|
|
|
|
return fd;
|
2006-11-25 13:07:29 -07:00
|
|
|
}
|
2013-09-28 10:22:59 -06:00
|
|
|
#endif
|
2006-11-25 13:07:29 -07:00
|
|
|
|
|
|
|
/* Function Namecompress
|
|
|
|
* Description: This function will attempt to find a compressed man
|
|
|
|
* page and uncompress it.
|
2013-09-28 10:22:59 -06:00
|
|
|
* Arguments: man_globals - the pseudo global info.
|
2006-11-25 13:07:29 -07:00
|
|
|
* filename - name of file to uncompress.
|
|
|
|
* Returns:; a pointer to the file or NULL.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static FILE *
|
2013-09-28 10:22:59 -06:00
|
|
|
Uncompress(ManpageGlobals * man_globals, const char *filename)
|
2006-11-25 13:07:29 -07:00
|
|
|
{
|
2013-09-28 10:22:59 -06:00
|
|
|
char tmp_file[BUFSIZ];
|
|
|
|
FILE *file;
|
2006-11-25 13:07:29 -07:00
|
|
|
|
2013-09-28 10:22:59 -06:00
|
|
|
if (!UncompressNamed(man_globals, filename, tmp_file, &file)) {
|
|
|
|
PopupWarning(man_globals, "Something went wrong in retrieving the "
|
|
|
|
"uncompressed manual page try cleaning up /tmp.");
|
|
|
|
return (NULL);
|
|
|
|
}
|
2006-11-25 13:07:29 -07:00
|
|
|
|
2013-09-28 10:22:59 -06:00
|
|
|
remove(tmp_file); /* remove name in tree, it will remain
|
|
|
|
until we close the fd, however. */
|
|
|
|
return (file);
|
2006-11-25 13:07:29 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Function Name: UncompressNamed
|
|
|
|
* Description: This function will attempt to find a compressed man
|
|
|
|
* page and uncompress it.
|
2013-09-28 10:22:59 -06:00
|
|
|
* Arguments: man_globals - the pseudo global info.
|
2006-11-25 13:07:29 -07:00
|
|
|
* filename - name of file to uncompress.
|
|
|
|
* RETURNED output - the file name output (must be an allocated string).
|
|
|
|
* Returns:; TRUE if the file was found.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static Boolean
|
2013-09-28 10:22:59 -06:00
|
|
|
UncompressNamed(ManpageGlobals * man_globals, const char *filename,
|
|
|
|
char *output, FILE ** output_file)
|
2006-11-25 13:07:29 -07:00
|
|
|
{
|
2013-09-28 10:22:59 -06:00
|
|
|
char tmp[BUFSIZ], cmdbuf[BUFSIZ], error_buf[BUFSIZ];
|
|
|
|
struct stat junk;
|
|
|
|
int fd;
|
|
|
|
|
|
|
|
if (stat(filename, &junk) != 0) { /* Check for existence of the file. */
|
|
|
|
if (errno != ENOENT) {
|
|
|
|
snprintf(error_buf, sizeof(error_buf),
|
|
|
|
"Error while stating file %s, errno = %d", filename,
|
|
|
|
errno);
|
|
|
|
PopupWarning(man_globals, error_buf);
|
|
|
|
}
|
|
|
|
return (FALSE);
|
2006-11-25 13:07:29 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2013-09-28 10:22:59 -06:00
|
|
|
* Using stdin is necessary to fool zcat since we cannot guarantee
|
2006-11-25 13:07:29 -07:00
|
|
|
* the .Z extension.
|
|
|
|
*/
|
|
|
|
|
2013-09-28 10:22:59 -06:00
|
|
|
strcpy(tmp, MANTEMP); /* get a temp file. */
|
|
|
|
fd = mkstemp(tmp);
|
|
|
|
if (fd < 0) {
|
|
|
|
PopupWarning(man_globals, "Error creating a temp file");
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
*output_file = fdopen(fd, "r");
|
|
|
|
if (*output_file == NULL) {
|
|
|
|
remove(tmp);
|
|
|
|
close(fd);
|
|
|
|
PopupWarning(man_globals, "Error opening temp file");
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
strcpy(output, tmp);
|
2006-11-25 13:07:29 -07:00
|
|
|
|
|
|
|
#ifdef GZIP_EXTENSION
|
2013-09-28 10:22:59 -06:00
|
|
|
if (streq(filename + strlen(filename) - strlen(GZIP_EXTENSION),
|
|
|
|
GZIP_EXTENSION))
|
|
|
|
snprintf(cmdbuf, sizeof(cmdbuf), GUNZIP_FORMAT, filename, output);
|
|
|
|
else
|
2009-10-24 09:00:51 -06:00
|
|
|
#endif
|
|
|
|
#ifdef BZIP2_EXTENSION
|
2013-09-28 10:22:59 -06:00
|
|
|
if (streq(filename + strlen(filename) - strlen(BZIP2_EXTENSION),
|
|
|
|
BZIP2_EXTENSION))
|
|
|
|
snprintf(cmdbuf, sizeof(cmdbuf), BUNZIP2_FORMAT, filename, output);
|
|
|
|
else
|
2009-10-24 09:00:51 -06:00
|
|
|
#endif
|
|
|
|
#ifdef LZMA_EXTENSION
|
2013-09-28 10:22:59 -06:00
|
|
|
if (streq(filename + strlen(filename) - strlen(LZMA_EXTENSION),
|
|
|
|
LZMA_EXTENSION))
|
|
|
|
snprintf(cmdbuf, sizeof(cmdbuf), UNLZMA_FORMAT, filename, output);
|
|
|
|
else
|
2006-11-25 13:07:29 -07:00
|
|
|
#endif
|
2013-09-28 10:22:59 -06:00
|
|
|
snprintf(cmdbuf, sizeof(cmdbuf), UNCOMPRESS_FORMAT, filename, output);
|
|
|
|
if (system(cmdbuf) == 0) /* execute search. */
|
|
|
|
return (TRUE);
|
|
|
|
|
|
|
|
snprintf(error_buf, sizeof(error_buf),
|
|
|
|
"Error while uncompressing, command was: %s", cmdbuf);
|
|
|
|
PopupWarning(man_globals, error_buf);
|
|
|
|
return (FALSE);
|
2006-11-25 13:07:29 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#if defined(SMAN) && defined(SFORMAT)
|
|
|
|
/* Function Name: SgmlToRoffNamed
|
|
|
|
* Description: This function will attempt to find an SGML man
|
|
|
|
* page and convert it to roff format.
|
2013-09-28 10:22:59 -06:00
|
|
|
* Arguments: man_globals - the pseudo global info.
|
2006-11-25 13:07:29 -07:00
|
|
|
* filename - name of file to uncompress.
|
|
|
|
* RETURNED output - the file name output (must be an allocated string).
|
|
|
|
* Returns:; TRUE if the file was found.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static Boolean
|
2013-09-28 10:22:59 -06:00
|
|
|
SgmlToRoffNamed(ManpageGlobals * man_globals, char *filename, char *output,
|
|
|
|
FILE ** output_file)
|
2006-11-25 13:07:29 -07:00
|
|
|
{
|
2013-09-28 10:22:59 -06:00
|
|
|
char tmp[BUFSIZ], cmdbuf[BUFSIZ], error_buf[BUFSIZ];
|
|
|
|
struct stat junk;
|
|
|
|
int fd;
|
|
|
|
|
|
|
|
if (stat(filename, &junk) != 0) { /* Check for existence of the file. */
|
|
|
|
if (errno != ENOENT) {
|
|
|
|
snprintf(error_buf, sizeof(error_buf),
|
|
|
|
"Error while stating file %s, errno = %d", filename,
|
|
|
|
errno);
|
|
|
|
PopupWarning(man_globals, error_buf);
|
|
|
|
}
|
|
|
|
return (FALSE);
|
2006-11-25 13:07:29 -07:00
|
|
|
}
|
|
|
|
|
2013-09-28 10:22:59 -06:00
|
|
|
strcpy(tmp, MANTEMP); /* get a temp file. */
|
|
|
|
fd = mkstemp(tmp);
|
|
|
|
if (fd < 0) {
|
|
|
|
PopupWarning(man_globals, "Error creating a temp file");
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
*output_file = fdopen(fd, "r");
|
|
|
|
if (*output_file == NULL) {
|
|
|
|
remove(tmp);
|
|
|
|
close(fd);
|
|
|
|
PopupWarning(man_globals, "Error opening temp file");
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
strcpy(output, tmp);
|
2006-11-25 13:07:29 -07:00
|
|
|
|
2013-09-28 10:22:59 -06:00
|
|
|
snprintf(cmdbuf, sizeof(cmdbuf), "%s %s >> %s", SFORMAT, filename, output);
|
|
|
|
if (system(cmdbuf) == 0) /* execute search. */
|
|
|
|
return (TRUE);
|
2006-11-25 13:07:29 -07:00
|
|
|
|
2013-09-28 10:22:59 -06:00
|
|
|
snprintf(error_buf, sizeof(error_buf),
|
|
|
|
"Error while converting from sgml, command was: %s", cmdbuf);
|
|
|
|
PopupWarning(man_globals, error_buf);
|
|
|
|
return (FALSE);
|
2006-11-25 13:07:29 -07:00
|
|
|
}
|
2013-09-28 10:22:59 -06:00
|
|
|
#endif /* defined (SMAN) && defined(SFORMAT) */
|
2006-11-25 13:07:29 -07:00
|
|
|
|
|
|
|
/* Function Name: Format
|
2013-09-28 10:22:59 -06:00
|
|
|
* Description: This function formats the manual pages and interfaces
|
2006-11-25 13:07:29 -07:00
|
|
|
* with the user.
|
2013-09-28 10:22:59 -06:00
|
|
|
* Arguments: man_globals - the pseudo globals
|
2006-11-25 13:07:29 -07:00
|
|
|
* file - the file pointer to use and return
|
|
|
|
* entry - the current entry struct.
|
|
|
|
* current_box - The current directory being displayed.
|
|
|
|
* Returns: none.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* ARGSUSED */
|
|
|
|
FILE *
|
2013-09-28 10:22:59 -06:00
|
|
|
Format(ManpageGlobals * man_globals, const char *entry)
|
2006-11-25 13:07:29 -07:00
|
|
|
{
|
2013-09-28 10:22:59 -06:00
|
|
|
FILE *file = NULL;
|
|
|
|
int fd;
|
|
|
|
|
|
|
|
Widget manpage = man_globals->manpagewidgets.manpage;
|
|
|
|
char cmdbuf[BUFSIZ], tmp[BUFSIZ], filename[BUFSIZ], error_buf[BUFSIZ];
|
|
|
|
char path[BUFSIZ], sect[BUFSIZ];
|
|
|
|
XEvent event;
|
|
|
|
Position x, y; /* location to pop up the
|
|
|
|
"would you like to save" widget. */
|
|
|
|
|
|
|
|
if (!UncompressUnformatted(man_globals, entry, filename, &file)) {
|
|
|
|
/* We Really could not find it, this should never happen, yea right. */
|
|
|
|
snprintf(error_buf, sizeof(error_buf),
|
|
|
|
"Could not open manual page, %s", entry);
|
|
|
|
PopupWarning(man_globals, error_buf);
|
|
|
|
XtPopdown(XtParent(man_globals->standby));
|
|
|
|
return (NULL);
|
|
|
|
}
|
2006-11-25 13:07:29 -07:00
|
|
|
|
2013-09-28 10:22:59 -06:00
|
|
|
if (file != NULL) {
|
|
|
|
char line[BUFSIZ];
|
|
|
|
|
|
|
|
if (fgets(line, sizeof(line), file) != NULL) {
|
|
|
|
if (strncmp(line, ".so ", 4) == 0) {
|
2015-05-10 04:21:18 -06:00
|
|
|
size_t len = strlen(line); /* must be >= 4 to pass strncmp */
|
|
|
|
if (line[len - 1] == '\n')
|
|
|
|
line[len - 1] = '\0';
|
2013-09-28 10:22:59 -06:00
|
|
|
fclose(file);
|
|
|
|
remove(filename);
|
|
|
|
if (line[4] != '/') {
|
|
|
|
char *ptr = NULL;
|
|
|
|
|
|
|
|
strcpy(tmp, entry);
|
|
|
|
if ((ptr = strrchr(tmp, '/')) != NULL) {
|
|
|
|
*ptr = '\0';
|
|
|
|
if ((ptr = strrchr(tmp, '/')) != NULL)
|
|
|
|
ptr[1] = '\0';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
*tmp = '\0';
|
|
|
|
snprintf(filename, sizeof(filename), "%s%s", tmp, line + 4);
|
|
|
|
|
|
|
|
return (Format(man_globals, filename));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fclose(file);
|
2006-11-25 13:07:29 -07:00
|
|
|
}
|
|
|
|
|
2013-09-28 10:22:59 -06:00
|
|
|
Popup(XtParent(man_globals->standby), XtGrabExclusive);
|
|
|
|
while (!XCheckTypedWindowEvent(XtDisplay(man_globals->standby),
|
|
|
|
XtWindow(man_globals->standby),
|
|
|
|
Expose, &event));
|
|
|
|
XtDispatchEvent(&event);
|
|
|
|
XFlush(XtDisplay(man_globals->standby));
|
|
|
|
|
|
|
|
strcpy(tmp, MANTEMP); /* Get a temp file. */
|
|
|
|
fd = mkstemp(tmp);
|
|
|
|
if (fd >= 0) {
|
|
|
|
file = fdopen(fd, "r");
|
|
|
|
if (file == NULL) {
|
|
|
|
remove(tmp);
|
|
|
|
close(fd);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
file = NULL;
|
|
|
|
if (file == NULL) {
|
|
|
|
PopupWarning(man_globals, "Something went wrong in opening the "
|
|
|
|
"temp file, try cleaning up /tmp");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
strcpy(man_globals->tempfile, tmp);
|
|
|
|
|
|
|
|
ParseEntry(entry, path, sect, NULL);
|
2006-11-25 13:07:29 -07:00
|
|
|
|
|
|
|
#ifndef HANDLE_ROFFSEQ
|
2013-09-28 10:22:59 -06:00
|
|
|
snprintf(cmdbuf, sizeof(cmdbuf), "cd %s ; %s %s %s >> %s %s", path, TBL,
|
|
|
|
filename, FORMAT, man_globals->tempfile, "2> /dev/null");
|
2006-11-25 13:07:29 -07:00
|
|
|
#else
|
2013-09-28 10:22:59 -06:00
|
|
|
/* Handle more flexible way of specifying the formatting pipeline */
|
|
|
|
if (!ConstructCommand(cmdbuf, path, filename, man_globals->tempfile)) {
|
|
|
|
PopupWarning(man_globals, "Constructed command was too long!");
|
|
|
|
fclose(file);
|
|
|
|
file = NULL;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
#endif /* HANDLE_ROFFSEQ */
|
|
|
|
|
|
|
|
if (system(cmdbuf) != 0) { /* execute search. */
|
|
|
|
snprintf(error_buf, sizeof(error_buf),
|
|
|
|
"Something went wrong trying to run the command: %s", cmdbuf);
|
|
|
|
PopupWarning(man_globals, error_buf);
|
|
|
|
fclose(file);
|
|
|
|
file = NULL;
|
2006-11-25 13:07:29 -07:00
|
|
|
}
|
|
|
|
else {
|
2013-09-28 10:22:59 -06:00
|
|
|
if (file != NULL) {
|
|
|
|
XtPopdown(XtParent(man_globals->standby));
|
|
|
|
|
|
|
|
if ((man_globals->save == NULL) ||
|
|
|
|
(man_globals->manpagewidgets.manpage == NULL))
|
|
|
|
remove(man_globals->tempfile);
|
|
|
|
else {
|
|
|
|
char *ptr, catdir[BUFSIZ];
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If the catdir is writable then ask the user if he/she wants to
|
|
|
|
* write the man page to it.
|
|
|
|
*/
|
|
|
|
|
|
|
|
strcpy(catdir, man_globals->save_file);
|
|
|
|
if ((ptr = strrchr(catdir, '/')) != NULL) {
|
|
|
|
*ptr = '\0';
|
|
|
|
|
|
|
|
if (access(catdir, W_OK) != 0)
|
|
|
|
remove(man_globals->tempfile);
|
|
|
|
else {
|
|
|
|
x = (Position) Width(man_globals->manpagewidgets.
|
|
|
|
manpage) / 2;
|
|
|
|
y = (Position) Height(man_globals->manpagewidgets.
|
|
|
|
manpage) / 2;
|
|
|
|
XtTranslateCoords(manpage, x, y, &x, &y);
|
|
|
|
PositionCenter(man_globals->save, (int) x, (int) y, 0,
|
|
|
|
0, 0, 0);
|
|
|
|
XtPopup(man_globals->save, XtGrabExclusive);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
remove(man_globals->tempfile);
|
|
|
|
}
|
|
|
|
}
|
2006-11-25 13:07:29 -07:00
|
|
|
}
|
|
|
|
|
2013-09-28 10:22:59 -06:00
|
|
|
/*
|
|
|
|
* If the original was compressed or in another format, delete temporary file.
|
|
|
|
*/
|
|
|
|
if (man_globals->deletetempfile)
|
|
|
|
remove(filename);
|
2006-11-25 13:07:29 -07:00
|
|
|
|
2013-09-28 10:22:59 -06:00
|
|
|
return (file);
|
2006-11-25 13:07:29 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef HANDLE_ROFFSEQ
|
|
|
|
/* Function Name: ConstructCommand
|
|
|
|
* Description: Constructs the pipeline of commands necessary to format
|
|
|
|
* a manual page.
|
|
|
|
* Arguments: cmdbuf - the buffer into which to write the command
|
|
|
|
* path - the directory in which the original man page resides
|
|
|
|
* filename - the (uncompressed) manpage source file
|
|
|
|
* tempfile - the name of a temporary file to direct the final
|
|
|
|
* output of the pipeline into
|
|
|
|
* Returns: TRUE if the command fit into the buffer, FALSE if it would
|
|
|
|
* be too long (more than BUFSIZ characters)
|
|
|
|
*/
|
|
|
|
static Boolean
|
2013-09-28 10:22:59 -06:00
|
|
|
ConstructCommand(char *cmdbuf, const char *path,
|
|
|
|
const char *filename, const char *tempfile)
|
2006-11-25 13:07:29 -07:00
|
|
|
{
|
2013-09-28 10:22:59 -06:00
|
|
|
/* The original code did the following to produce a command line:
|
|
|
|
* sprintf(cmdbuf,"cd %s ; %s %s %s > %s %s", path, TBL,
|
|
|
|
* filename, FORMAT, man_globals->tempfile, "2> /dev/null");
|
|
|
|
* We are more flexible and follow more or less the algorithm used
|
|
|
|
* by the Linux man command:
|
|
|
|
* + Obtain a string of letters from the following sources in order
|
|
|
|
* of preference:
|
|
|
|
* + a command line option (not implemented in xman; it's probably not
|
|
|
|
* useful)
|
|
|
|
* + the first line of the manpage source, if it is of the form:
|
|
|
|
* '\" <string>
|
|
|
|
* + the MANROFFSEQ environment variable
|
|
|
|
* + a default string; this is "".
|
|
|
|
* + Interpret the string as a pipeline of filters:
|
|
|
|
* + e = eqn g = grap p = pic t = tbl v = vgrind r = refer
|
|
|
|
* + zsoelim is always run as the first preprocessor in any case.
|
|
|
|
*
|
|
|
|
* Strictly speaking we should save a catpage iff the string comes
|
|
|
|
* from the file or is the default.
|
|
|
|
*
|
|
|
|
* You'll notice that we format a man page into ASCII text output and then
|
|
|
|
* attempt to interpret things like L^HL as bold and so forth. This
|
|
|
|
* is so obviously the Wrong Thing it's untrue.
|
|
|
|
*/
|
|
|
|
char *c = cmdbuf; /* current posn in buffer */
|
|
|
|
int left = BUFSIZ; /* space left in buffer */
|
|
|
|
int used;
|
|
|
|
const char *fmt;
|
|
|
|
char fmtbuf[128];
|
|
|
|
|
|
|
|
fmt = NULL;
|
|
|
|
/* If you have a command line option that gives a setting for fmt,
|
|
|
|
set it here. */
|
|
|
|
|
|
|
|
if (!fmt) {
|
|
|
|
/* This is the tricky bit: extract a format string from the source file
|
|
|
|
* Annoyingly, filename might be relative or absolute. We cheat and
|
|
|
|
* use system to get the thing to a known absolute filename.
|
|
|
|
*/
|
|
|
|
FILE *file;
|
|
|
|
int gotfmt = 0; /* set to 1 if we got a directive from source */
|
|
|
|
char fname[PATH_MAX];
|
|
|
|
|
|
|
|
if (filename[0] == '/') {
|
|
|
|
snprintf(fname, sizeof(fname), "%s", filename);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
snprintf(fname, sizeof(fname), "%s/%s", path, filename);
|
|
|
|
}
|
|
|
|
if ((file = fopen(fname, "r")) != NULL) {
|
|
|
|
if ((fgets(fmtbuf, sizeof(fmtbuf), file)) &&
|
|
|
|
(!memcmp(fmtbuf, "'\\\" ", 4))) {
|
|
|
|
/* that's squote-backslash-dquote-space */
|
|
|
|
int len = strlen(fmtbuf);
|
|
|
|
|
|
|
|
if (len && (fmtbuf[len - 1] == '\n')) {
|
|
|
|
fmtbuf[len - 1] = 0;
|
|
|
|
fmt = fmtbuf + 3;
|
|
|
|
gotfmt++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fclose(file);
|
|
|
|
}
|
|
|
|
if (!gotfmt) { /* not there or some error */
|
|
|
|
fmt = getenv("MANROFFSEQ");
|
|
|
|
}
|
|
|
|
}
|
2006-11-25 13:07:29 -07:00
|
|
|
|
2013-09-28 10:22:59 -06:00
|
|
|
if (!fmt) {
|
|
|
|
fmt = DEFAULT_MANROFFSEQ;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Start with the first fixed part of the command line */
|
|
|
|
used = snprintf(c, left, "cd %s; %s %s ", path, ZSOELIM, filename);
|
|
|
|
left -= used;
|
|
|
|
c += used;
|
|
|
|
if (left <= 1)
|
|
|
|
return (FALSE);
|
|
|
|
|
|
|
|
/* Now add preprocessors of the form '| processor' */
|
|
|
|
for (; *fmt; fmt++) {
|
|
|
|
const char *filter;
|
|
|
|
|
|
|
|
switch (*fmt) {
|
|
|
|
case 'e':
|
2006-11-25 13:07:29 -07:00
|
|
|
filter = EQN;
|
|
|
|
break;
|
2013-09-28 10:22:59 -06:00
|
|
|
case 'g':
|
2006-11-25 13:07:29 -07:00
|
|
|
filter = GRAP;
|
|
|
|
break;
|
2013-09-28 10:22:59 -06:00
|
|
|
case 'p':
|
|
|
|
filter = ROFF_PIC;
|
2006-11-25 13:07:29 -07:00
|
|
|
break;
|
2013-09-28 10:22:59 -06:00
|
|
|
case 't':
|
2006-11-25 13:07:29 -07:00
|
|
|
filter = TBL;
|
|
|
|
break;
|
2013-09-28 10:22:59 -06:00
|
|
|
case 'v':
|
2006-11-25 13:07:29 -07:00
|
|
|
filter = VGRIND;
|
|
|
|
break;
|
2013-09-28 10:22:59 -06:00
|
|
|
case 'r':
|
2006-11-25 13:07:29 -07:00
|
|
|
filter = REFER;
|
|
|
|
break;
|
2013-09-28 10:22:59 -06:00
|
|
|
default:
|
2006-11-25 13:07:29 -07:00
|
|
|
filter = NULL;
|
|
|
|
break;
|
2013-09-28 10:22:59 -06:00
|
|
|
}
|
|
|
|
if (filter) {
|
|
|
|
used = snprintf(c, left, " | %s ", filter);
|
|
|
|
left -= used;
|
|
|
|
c += used;
|
|
|
|
if (left <= 1)
|
|
|
|
return (FALSE);
|
|
|
|
}
|
|
|
|
}
|
2006-11-25 13:07:29 -07:00
|
|
|
|
2013-09-28 10:22:59 -06:00
|
|
|
/* Now add the fixed trailing part 'formatprog > tempfile 2> /dev/null' */
|
|
|
|
used = snprintf(c, left, " | %s >> %s 2>/dev/null", FORMAT, tempfile);
|
|
|
|
left -= used;
|
|
|
|
if (left <= 1)
|
|
|
|
return (FALSE);
|
|
|
|
|
|
|
|
return (TRUE);
|
2006-11-25 13:07:29 -07:00
|
|
|
}
|
2013-09-28 10:22:59 -06:00
|
|
|
#endif /* HANDLE_ROFFSEQ */
|
2006-11-25 13:07:29 -07:00
|
|
|
|
|
|
|
/* Function Name: UncompressUnformatted
|
|
|
|
* Description: Finds an uncompressed unformatted manual page.
|
2013-09-28 10:22:59 -06:00
|
|
|
* Arguments: man_globals - the pseudo global structure.
|
2006-11-25 13:07:29 -07:00
|
|
|
* entry - the manual page entry.
|
|
|
|
* RETURNED filename - location to put the name of the file.
|
|
|
|
* Returns: TRUE if the file was found.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static Boolean
|
2013-09-28 10:22:59 -06:00
|
|
|
UncompressUnformatted(ManpageGlobals * man_globals, const char *entry,
|
|
|
|
char *filename, FILE ** file)
|
2006-11-25 13:07:29 -07:00
|
|
|
{
|
2013-09-28 10:22:59 -06:00
|
|
|
char path[BUFSIZ], page[BUFSIZ], section[BUFSIZ], input[BUFSIZ];
|
|
|
|
int len_cat = strlen(CAT), len_man = strlen(MAN);
|
|
|
|
|
2006-11-25 13:07:29 -07:00
|
|
|
#if defined(SMAN) && defined(SFORMAT)
|
2013-09-28 10:22:59 -06:00
|
|
|
int len_sman = strlen(SMAN);
|
2006-11-25 13:07:29 -07:00
|
|
|
#endif
|
|
|
|
|
2013-09-28 10:22:59 -06:00
|
|
|
ParseEntry(entry, path, section, page);
|
2006-11-25 13:07:29 -07:00
|
|
|
|
2013-09-28 10:22:59 -06:00
|
|
|
man_globals->bzip2 = FALSE;
|
|
|
|
man_globals->lzma = FALSE;
|
2009-10-24 09:00:51 -06:00
|
|
|
|
2006-11-25 13:07:29 -07:00
|
|
|
#if defined(__OpenBSD__) || defined(__NetBSD__)
|
2013-09-28 10:22:59 -06:00
|
|
|
/*
|
|
|
|
* look for uncompressed file in machine subdir first
|
|
|
|
*/
|
|
|
|
snprintf(filename, BUFSIZ, "%s/%s%s/%s/%s", path, MAN,
|
|
|
|
section + len_cat, MACHINE, page);
|
|
|
|
if (access(filename, R_OK) == 0) {
|
|
|
|
man_globals->compress = FALSE;
|
|
|
|
man_globals->gzip = FALSE;
|
|
|
|
man_globals->deletetempfile = FALSE;
|
|
|
|
snprintf(man_globals->save_file, sizeof(man_globals->save_file),
|
|
|
|
"%s/%s%s/%s/%s", path, CAT, section + len_cat, MACHINE, page);
|
|
|
|
return (TRUE);
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Then for compressed files in an uncompressed directory.
|
|
|
|
*/
|
|
|
|
snprintf(input, sizeof(input), "%s.%s", filename, COMPRESSION_EXTENSION);
|
|
|
|
if (UncompressNamed(man_globals, input, filename, file)) {
|
|
|
|
man_globals->compress = TRUE;
|
|
|
|
man_globals->deletetempfile = TRUE;
|
|
|
|
snprintf(man_globals->save_file, sizeof(man_globals->save_file),
|
|
|
|
"%s/%s%s/%s.%s", path, CAT, section + len_cat, page,
|
|
|
|
COMPRESSION_EXTENSION);
|
|
|
|
return (TRUE);
|
|
|
|
}
|
2006-11-25 13:07:29 -07:00
|
|
|
#ifdef GZIP_EXTENSION
|
2013-09-28 10:22:59 -06:00
|
|
|
else {
|
|
|
|
snprintf(input, sizeof(input), "%s.%s", filename, GZIP_EXTENSION);
|
|
|
|
if (UncompressNamed(man_globals, input, filename, file)) {
|
|
|
|
man_globals->compress = TRUE;
|
|
|
|
man_globals->gzip = TRUE;
|
|
|
|
man_globals->deletetempfile = TRUE;
|
|
|
|
snprintf(man_globals->save_file, sizeof(man_globals->save_file),
|
|
|
|
"%s/%s%s/%s.%s", path, CAT, section + len_cat, page,
|
|
|
|
GZIP_EXTENSION);
|
|
|
|
return (TRUE);
|
|
|
|
}
|
2006-11-25 13:07:29 -07:00
|
|
|
}
|
2013-09-28 10:22:59 -06:00
|
|
|
#endif /* GZIP_EXTENSION */
|
|
|
|
#endif /* __OpenBSD__ || __NetBSD__ */
|
2009-10-24 09:00:51 -06:00
|
|
|
|
|
|
|
#ifdef BZIP2_EXTENSION
|
2013-09-28 10:22:59 -06:00
|
|
|
{
|
|
|
|
snprintf(input, sizeof(input), "%s.%s", filename, BZIP2_EXTENSION);
|
|
|
|
if (UncompressNamed(man_globals, input, filename, file)) {
|
|
|
|
man_globals->compress = TRUE;
|
|
|
|
man_globals->gzip = FALSE;
|
|
|
|
man_globals->bzip2 = TRUE;
|
|
|
|
snprintf(man_globals->save_file, sizeof(man_globals->save_file),
|
|
|
|
"%s/%s%s/%s.%s", path, CAT, section + len_cat, page,
|
|
|
|
BZIP2_EXTENSION);
|
|
|
|
return (TRUE);
|
|
|
|
}
|
2009-10-24 09:00:51 -06:00
|
|
|
}
|
2013-09-28 10:22:59 -06:00
|
|
|
#endif /* BZIP2_EXTENSION */
|
2009-10-24 09:00:51 -06:00
|
|
|
|
|
|
|
#ifdef LZMA_EXTENSION
|
2013-09-28 10:22:59 -06:00
|
|
|
{
|
|
|
|
snprintf(input, sizeof(input), "%s.%s", filename, LZMA_EXTENSION);
|
|
|
|
if (UncompressNamed(man_globals, input, filename, file)) {
|
|
|
|
man_globals->compress = TRUE;
|
|
|
|
man_globals->gzip = FALSE;
|
|
|
|
man_globals->lzma = TRUE;
|
|
|
|
snprintf(man_globals->save_file, sizeof(man_globals->save_file),
|
|
|
|
"%s/%s%s/%s.%s", path, CAT, section + len_cat, page,
|
|
|
|
LZMA_EXTENSION);
|
|
|
|
return (TRUE);
|
|
|
|
}
|
2009-10-24 09:00:51 -06:00
|
|
|
}
|
2013-09-28 10:22:59 -06:00
|
|
|
#endif /* LZMA_EXTENSION */
|
2009-10-24 09:00:51 -06:00
|
|
|
|
2006-11-25 13:07:29 -07:00
|
|
|
/*
|
|
|
|
* Look for uncompressed file first.
|
|
|
|
*/
|
|
|
|
|
2013-09-28 10:22:59 -06:00
|
|
|
snprintf(filename, BUFSIZ, "%s/%s%s/%s", path, MAN, section + len_man,
|
|
|
|
page);
|
|
|
|
if (access(filename, R_OK) == 0) {
|
|
|
|
man_globals->compress = FALSE;
|
|
|
|
man_globals->gzip = FALSE;
|
|
|
|
man_globals->deletetempfile = FALSE;
|
|
|
|
snprintf(man_globals->save_file, sizeof(man_globals->save_file),
|
|
|
|
"%s/%s%s/%s", path, CAT, section + len_cat, page);
|
|
|
|
return (TRUE);
|
|
|
|
}
|
2006-11-25 13:07:29 -07:00
|
|
|
|
|
|
|
#if defined(SMAN) && defined(SFORMAT)
|
2013-09-28 10:22:59 -06:00
|
|
|
/*
|
|
|
|
* Look for uncompressed sgml file next.
|
|
|
|
*/
|
|
|
|
|
|
|
|
snprintf(input, BUFSIZ, "%s/%s%s/%s", path, SMAN, section + len_sman, page);
|
|
|
|
if (SgmlToRoffNamed(man_globals, input, filename, file)) {
|
|
|
|
man_globals->compress = FALSE;
|
|
|
|
man_globals->gzip = FALSE;
|
|
|
|
man_globals->deletetempfile = TRUE;
|
|
|
|
snprintf(man_globals->save_file, sizeof(man_globals->save_file),
|
|
|
|
"%s/%s%s/%s", path, CAT, section + len_cat, page);
|
|
|
|
return (TRUE);
|
|
|
|
}
|
2006-11-25 13:07:29 -07:00
|
|
|
#endif
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Then for compressed files in an uncompressed directory.
|
|
|
|
*/
|
|
|
|
|
2013-09-28 10:22:59 -06:00
|
|
|
snprintf(input, sizeof(input), "%s.%s", filename, COMPRESSION_EXTENSION);
|
|
|
|
if (UncompressNamed(man_globals, input, filename, file)) {
|
|
|
|
man_globals->compress = TRUE;
|
|
|
|
man_globals->deletetempfile = TRUE;
|
|
|
|
snprintf(man_globals->save_file, sizeof(man_globals->save_file),
|
|
|
|
"%s/%s%s/%s.%s", path, CAT, section + len_cat, page,
|
|
|
|
COMPRESSION_EXTENSION);
|
|
|
|
return (TRUE);
|
|
|
|
}
|
2006-11-25 13:07:29 -07:00
|
|
|
#ifdef GZIP_EXTENSION
|
2013-09-28 10:22:59 -06:00
|
|
|
else {
|
|
|
|
snprintf(input, sizeof(input), "%s.%s", filename, GZIP_EXTENSION);
|
|
|
|
if (UncompressNamed(man_globals, input, filename, file)) {
|
|
|
|
man_globals->compress = TRUE;
|
|
|
|
man_globals->gzip = TRUE;
|
|
|
|
man_globals->deletetempfile = TRUE;
|
|
|
|
snprintf(man_globals->save_file, sizeof(man_globals->save_file),
|
|
|
|
"%s/%s%s/%s.%s", path, CAT, section + len_cat, page,
|
|
|
|
GZIP_EXTENSION);
|
|
|
|
return (TRUE);
|
|
|
|
}
|
2006-11-25 13:07:29 -07:00
|
|
|
}
|
|
|
|
#endif
|
2009-10-24 09:00:51 -06:00
|
|
|
|
|
|
|
#ifdef BZIP2_EXTENSION
|
2013-09-28 10:22:59 -06:00
|
|
|
{
|
|
|
|
snprintf(input, sizeof(input), "%s.%s", filename, BZIP2_EXTENSION);
|
|
|
|
if (UncompressNamed(man_globals, input, filename, file)) {
|
|
|
|
man_globals->compress = TRUE;
|
|
|
|
man_globals->gzip = TRUE;
|
|
|
|
snprintf(man_globals->save_file, sizeof(man_globals->save_file),
|
|
|
|
"%s/%s%s/%s.%s", path, CAT, section + len_cat, page,
|
|
|
|
BZIP2_EXTENSION);
|
|
|
|
return (TRUE);
|
|
|
|
}
|
2009-10-24 09:00:51 -06:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef LZMA_EXTENSION
|
2013-09-28 10:22:59 -06:00
|
|
|
{
|
|
|
|
snprintf(input, sizeof(input), "%s.%s", filename, LZMA_EXTENSION);
|
|
|
|
if (UncompressNamed(man_globals, input, filename, file)) {
|
|
|
|
man_globals->compress = TRUE;
|
|
|
|
man_globals->lzma = TRUE;
|
|
|
|
snprintf(man_globals->save_file, sizeof(man_globals->save_file),
|
|
|
|
"%s/%s%s/%s.%s", path, CAT, section + len_cat, page,
|
|
|
|
LZMA_EXTENSION);
|
|
|
|
return (TRUE);
|
|
|
|
}
|
2009-10-24 09:00:51 -06:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2006-11-25 13:07:29 -07:00
|
|
|
/*
|
|
|
|
* And lastly files in a compressed directory.
|
|
|
|
*/
|
|
|
|
|
2013-09-28 10:22:59 -06:00
|
|
|
snprintf(input, sizeof(input), "%s/%s%s.%s/%s", path,
|
|
|
|
MAN, section + len_man, COMPRESSION_EXTENSION, page);
|
|
|
|
if (UncompressNamed(man_globals, input, filename, file)) {
|
|
|
|
man_globals->compress = TRUE;
|
|
|
|
man_globals->deletetempfile = TRUE;
|
|
|
|
snprintf(man_globals->save_file, sizeof(man_globals->save_file),
|
|
|
|
"%s/%s%s.%s/%s", path, CAT, section + len_cat,
|
|
|
|
COMPRESSION_EXTENSION, page);
|
|
|
|
return (TRUE);
|
|
|
|
}
|
|
|
|
return (FALSE);
|
2006-11-25 13:07:29 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Function Name: AddCursor
|
|
|
|
* Description: This function adds the cursor to the window.
|
|
|
|
* Arguments: w - the widget to add the cursor to.
|
|
|
|
* cursor - the cursor to add to this widget.
|
|
|
|
* Returns: none
|
|
|
|
*/
|
|
|
|
|
|
|
|
void
|
|
|
|
AddCursor(Widget w, Cursor cursor)
|
|
|
|
{
|
2013-09-28 10:22:59 -06:00
|
|
|
XColor colors[2];
|
|
|
|
Arg args[10];
|
|
|
|
Cardinal num_args = 0;
|
|
|
|
Colormap c_map;
|
|
|
|
|
|
|
|
if (!XtIsRealized(w)) {
|
|
|
|
PopupWarning(NULL, "Widget is not realized, no cursor added.\n");
|
|
|
|
return;
|
|
|
|
}
|
2006-11-25 13:07:29 -07:00
|
|
|
|
2013-09-28 10:22:59 -06:00
|
|
|
XtSetArg(args[num_args], XtNcolormap, &c_map);
|
|
|
|
num_args++;
|
|
|
|
XtGetValues(w, args, num_args);
|
2006-11-25 13:07:29 -07:00
|
|
|
|
2013-09-28 10:22:59 -06:00
|
|
|
colors[0].pixel = resources.cursors.fg_color;
|
|
|
|
colors[1].pixel = resources.cursors.bg_color;
|
2006-11-25 13:07:29 -07:00
|
|
|
|
2013-09-28 10:22:59 -06:00
|
|
|
XQueryColors(XtDisplay(w), c_map, colors, 2);
|
|
|
|
XRecolorCursor(XtDisplay(w), cursor, colors, colors + 1);
|
|
|
|
XDefineCursor(XtDisplay(w), XtWindow(w), cursor);
|
2006-11-25 13:07:29 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Function Name: ChangeLabel
|
|
|
|
* Description: This function changes the label field of the
|
|
|
|
* given widget to the string in str.
|
|
|
|
* Arguments: w - the widget.
|
|
|
|
* str - the string to change the label to.
|
|
|
|
* Returns: none
|
|
|
|
*/
|
|
|
|
|
|
|
|
void
|
2013-09-28 10:22:59 -06:00
|
|
|
ChangeLabel(Widget w, const char *str)
|
2006-11-25 13:07:29 -07:00
|
|
|
{
|
2013-09-28 10:22:59 -06:00
|
|
|
Arg arglist[3]; /* An argument list. */
|
2006-11-25 13:07:29 -07:00
|
|
|
|
2013-09-28 10:22:59 -06:00
|
|
|
if (w == NULL)
|
|
|
|
return;
|
2006-11-25 13:07:29 -07:00
|
|
|
|
2013-09-28 10:22:59 -06:00
|
|
|
XtSetArg(arglist[0], XtNlabel, str);
|
2006-11-25 13:07:29 -07:00
|
|
|
|
|
|
|
/* shouldn't really have to do this. */
|
2013-09-28 10:22:59 -06:00
|
|
|
XtSetArg(arglist[1], XtNwidth, 0);
|
|
|
|
XtSetArg(arglist[2], XtNheight, 0);
|
2006-11-25 13:07:29 -07:00
|
|
|
|
2013-09-28 10:22:59 -06:00
|
|
|
XtSetValues(w, arglist, (Cardinal) 1);
|
2006-11-25 13:07:29 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* In an ideal world this would be part of the XToolkit, and I would not
|
|
|
|
* have to do it, but such is life sometimes. Perhaps in X11R3.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* Function Name: PositionCenter
|
|
|
|
* Description: This function positions the given widgets center
|
|
|
|
* in the following location.
|
2013-09-28 10:22:59 -06:00
|
|
|
* Arguments: widget - the widget widget to position
|
2006-11-25 13:07:29 -07:00
|
|
|
* x,y - The location for the center of the widget
|
|
|
|
* above - number of pixels above center to locate this widget
|
|
|
|
* left - number of pixels left of center to locate this widget
|
|
|
|
* h_space, v_space - how close to get to the edges of the
|
|
|
|
* parent window.
|
|
|
|
* Returns: none
|
|
|
|
* Note: This should only be used with a popup widget that has override
|
|
|
|
* redirect set.
|
|
|
|
*/
|
|
|
|
|
|
|
|
void
|
2013-09-28 10:22:59 -06:00
|
|
|
PositionCenter(Widget widget, int x, int y, int above, int left, int v_space,
|
|
|
|
int h_space)
|
2006-11-25 13:07:29 -07:00
|
|
|
{
|
2013-09-28 10:22:59 -06:00
|
|
|
Arg wargs[2];
|
|
|
|
int x_temp, y_temp; /* location of the new window. */
|
|
|
|
int parent_height, parent_width; /* Height and width of the parent widget or
|
|
|
|
the root window if it has no parent. */
|
2006-11-25 13:07:29 -07:00
|
|
|
|
2013-09-28 10:22:59 -06:00
|
|
|
x_temp = x - left - Width(widget) / 2 + BorderWidth(widget);
|
|
|
|
y_temp = y - above - Height(widget) / 2 + BorderWidth(widget);
|
2006-11-25 13:07:29 -07:00
|
|
|
|
2013-09-28 10:22:59 -06:00
|
|
|
parent_height = HeightOfScreen(XtScreen(widget));
|
|
|
|
parent_width = WidthOfScreen(XtScreen(widget));
|
2006-11-25 13:07:29 -07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Check to make sure that all edges are within the viewable part of the
|
|
|
|
* root window, and if not then force them to be.
|
|
|
|
*/
|
|
|
|
|
2013-09-28 10:22:59 -06:00
|
|
|
if (x_temp < h_space)
|
|
|
|
x_temp = v_space;
|
|
|
|
if (y_temp < v_space)
|
|
|
|
(y_temp = 2);
|
2006-11-25 13:07:29 -07:00
|
|
|
|
2013-09-28 10:22:59 -06:00
|
|
|
if (y_temp + Height(widget) + v_space > parent_height)
|
|
|
|
y_temp = parent_height - Height(widget) - v_space;
|
2006-11-25 13:07:29 -07:00
|
|
|
|
2013-09-28 10:22:59 -06:00
|
|
|
if (x_temp + Width(widget) + h_space > parent_width)
|
|
|
|
x_temp = parent_width - Width(widget) - h_space;
|
2006-11-25 13:07:29 -07:00
|
|
|
|
2013-09-28 10:22:59 -06:00
|
|
|
XtSetArg(wargs[0], XtNx, x_temp);
|
|
|
|
XtSetArg(wargs[1], XtNy, y_temp);
|
|
|
|
XtSetValues(widget, wargs, 2);
|
2006-11-25 13:07:29 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Function Name: ParseEntry(entry, path, sect, page)
|
|
|
|
* Description: Parses the manual pages entry filenames.
|
|
|
|
* Arguments: str - the full path name.
|
|
|
|
* path - the path name. RETURNED
|
|
|
|
* sect - the section name. RETURNED
|
|
|
|
* page - the page name. RETURNED
|
|
|
|
* Returns: none.
|
|
|
|
*/
|
|
|
|
|
|
|
|
void
|
2013-09-28 10:22:59 -06:00
|
|
|
ParseEntry(const char *entry, char *path, char *sect, char *page)
|
2006-11-25 13:07:29 -07:00
|
|
|
{
|
2013-09-28 10:22:59 -06:00
|
|
|
char *c, temp[BUFSIZ];
|
2006-11-25 13:07:29 -07:00
|
|
|
|
2013-09-28 10:22:59 -06:00
|
|
|
strcpy(temp, entry);
|
2006-11-25 13:07:29 -07:00
|
|
|
|
2013-09-28 10:22:59 -06:00
|
|
|
c = strrchr(temp, '/');
|
|
|
|
if (c == NULL)
|
|
|
|
PrintError("Failed to find / in ParseEntry.");
|
|
|
|
*c++ = '\0';
|
|
|
|
if (page != NULL)
|
|
|
|
strcpy(page, c);
|
2006-11-25 13:07:29 -07:00
|
|
|
|
2013-09-28 10:22:59 -06:00
|
|
|
c = strrchr(temp, '/');
|
|
|
|
if (c == NULL)
|
|
|
|
PrintError("Failed to find / in ParseEntry.");
|
|
|
|
*c++ = '\0';
|
2006-11-25 13:07:29 -07:00
|
|
|
#if defined(SFORMAT) && defined(SMAN)
|
2013-09-28 10:22:59 -06:00
|
|
|
/* sgmltoroff sometimes puts an extra ./ in the path to .so entries */
|
|
|
|
if (strcmp(c, ".") == 0) {
|
|
|
|
c = strrchr(temp, '/');
|
|
|
|
if (c == NULL)
|
|
|
|
PrintError("Failed to find / in ParseEntry.");
|
|
|
|
*c++ = '\0';
|
|
|
|
}
|
|
|
|
#endif
|
2006-11-25 13:07:29 -07:00
|
|
|
#if defined(__OpenBSD__) || defined(__NetBSD__)
|
2013-09-28 10:22:59 -06:00
|
|
|
/* Skip machine subdirectory if present */
|
|
|
|
if (strcmp(c, MACHINE) == 0) {
|
|
|
|
c = strrchr(temp, '/');
|
|
|
|
if (c == NULL)
|
|
|
|
PrintError("Failed to find / in ParseEntry.");
|
|
|
|
*c++ = '\0';
|
|
|
|
}
|
2006-11-25 13:07:29 -07:00
|
|
|
#endif
|
2013-09-28 10:22:59 -06:00
|
|
|
if (sect != NULL)
|
|
|
|
strcpy(sect, c);
|
2006-11-25 13:07:29 -07:00
|
|
|
|
2013-09-28 10:22:59 -06:00
|
|
|
if (path != NULL)
|
|
|
|
strcpy(path, temp);
|
2006-11-25 13:07:29 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Function Name: GetGlobals
|
2013-09-28 10:22:59 -06:00
|
|
|
* Description: Gets the pseudo globals associated with the
|
2006-11-25 13:07:29 -07:00
|
|
|
* manpage associated with this widget.
|
|
|
|
* Arguments: w - a widget in the manpage.
|
2013-09-28 10:22:59 -06:00
|
|
|
* Returns: the pseudo globals.
|
2006-11-25 13:07:29 -07:00
|
|
|
* Notes: initial_widget is a globals variable.
|
|
|
|
* manglobals_context is a global variable.
|
|
|
|
*/
|
|
|
|
|
|
|
|
ManpageGlobals *
|
|
|
|
GetGlobals(Widget w)
|
|
|
|
{
|
2013-09-28 10:22:59 -06:00
|
|
|
Widget temp;
|
|
|
|
caddr_t data;
|
2006-11-25 13:07:29 -07:00
|
|
|
|
2013-09-28 10:22:59 -06:00
|
|
|
while ((temp = XtParent(w)) != initial_widget && (temp != NULL))
|
|
|
|
w = temp;
|
2006-11-25 13:07:29 -07:00
|
|
|
|
2013-09-28 10:22:59 -06:00
|
|
|
if (temp == NULL)
|
|
|
|
XtAppError(XtWidgetToApplicationContext(w),
|
|
|
|
"Xman: Could not locate widget in tree, exiting");
|
2006-11-25 13:07:29 -07:00
|
|
|
|
2013-09-28 10:22:59 -06:00
|
|
|
if (XFindContext(XtDisplay(w), XtWindow(w),
|
|
|
|
manglobals_context, &data) != XCSUCCESS)
|
|
|
|
XtAppError(XtWidgetToApplicationContext(w),
|
|
|
|
"Xman: Could not find global data, exiting");
|
2006-11-25 13:07:29 -07:00
|
|
|
|
2013-09-28 10:22:59 -06:00
|
|
|
return ((ManpageGlobals *) data);
|
2006-11-25 13:07:29 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Function Name: SaveGlobals
|
2013-09-28 10:22:59 -06:00
|
|
|
* Description: Saves the pseudo globals on the widget passed
|
2006-11-25 13:07:29 -07:00
|
|
|
* to this function, although GetGlobals assumes that
|
|
|
|
* the data is associated with the popup child of topBox.
|
|
|
|
* Arguments: w - the widget to associate the data with.
|
|
|
|
* globals - data to associate with this widget.
|
|
|
|
* Returns: none.
|
|
|
|
* Notes: WIDGET MUST BE REALIZED.
|
|
|
|
* manglobals_context is a global variable.
|
|
|
|
*/
|
|
|
|
|
|
|
|
void
|
|
|
|
SaveGlobals(Widget w, ManpageGlobals * globals)
|
|
|
|
{
|
2013-09-28 10:22:59 -06:00
|
|
|
if (XSaveContext(XtDisplay(w), XtWindow(w), manglobals_context,
|
|
|
|
(caddr_t) globals) != XCSUCCESS)
|
|
|
|
XtAppError(XtWidgetToApplicationContext(w),
|
|
|
|
"Xman: Could not save global data, are you out of memory?");
|
2006-11-25 13:07:29 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Function Name: RemoveGlobals
|
2013-09-28 10:22:59 -06:00
|
|
|
* Description: Removes the pseudo globals from the widget passed
|
2006-11-25 13:07:29 -07:00
|
|
|
* to this function.
|
|
|
|
* Arguments: w - the widget to remove the data from.
|
|
|
|
* Returns: none.
|
|
|
|
* Notes: WIDGET MUST BE REALIZED.
|
|
|
|
* manglobals_context is a global variable.
|
|
|
|
*/
|
|
|
|
|
|
|
|
void
|
|
|
|
RemoveGlobals(Widget w)
|
|
|
|
{
|
2013-09-28 10:22:59 -06:00
|
|
|
if (XDeleteContext(XtDisplay(w), XtWindow(w),
|
|
|
|
manglobals_context) != XCSUCCESS)
|
|
|
|
XtAppError(XtWidgetToApplicationContext(w),
|
|
|
|
"Xman: Could not remove global data?");
|
2006-11-25 13:07:29 -07:00
|
|
|
}
|