Files
calc/help.c
Landon Curt Noll db77e29a23 convert ASCII TABs to ASCII SPACEs
Converted all ASCII tabs to ASCII spaces using a 8 character
tab stop, for all files, except for all Makefiles (plus rpm.mk).
The `git diff -w` reports no changes.
2024-07-11 22:03:52 -07:00

331 lines
9.6 KiB
C

/*
* help - display help for calc
*
* Copyright (C) 1999-2007,2014,2018,2021 Landon Curt Noll
*
* Calc is open software; you can redistribute it and/or modify it under
* the terms of the version 2.1 of the GNU Lesser General Public License
* as published by the Free Software Foundation.
*
* Calc is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
* Public License for more details.
*
* A copy of version 2.1 of the GNU Lesser General Public License is
* distributed with calc under the filename COPYING-LGPL. You should have
* received a copy with calc; if not, write to Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Under source code control: 1997/09/14 10:58:30
* File existed as early as: 1997
*
* chongo <was here> /\oo/\ http://www.isthe.com/chongo/
* Share and enjoy! :-) http://www.isthe.com/chongo/tech/comp/calc/
*/
#include <stdio.h>
#include <ctype.h>
#include <sys/types.h>
#include <signal.h>
#include <sys/errno.h>
#include "calc.h"
#include "conf.h"
#include "lib_calc.h"
#include "have_unistd.h"
#if defined(HAVE_UNISTD_H)
#include <unistd.h>
#endif
#if defined(_WIN32) || defined(_WIN64)
# define popen _popen
# define pclose _pclose
#endif
#include "banned.h" /* include after system header <> includes */
/*
* some help topics are symbols, so we alias them to nice filenames
*/
STATIC struct help_alias {
char *topic;
char *filename;
} halias[] = {
/* We sort the list below via: sort -d -u */
{"=", "address"},
{"->", "arrow"},
{"=", "assign"},
{"*", "dereference"},
{"%", "mod"},
{".", "oldvalue"},
{"#", "pound"},
{"//", "quo"},
{"COPYING_LGPL", "COPYING-LGPL"},
{"COPYLEFT", "copyright"},
{"COPYRIGHT", "copyright"},
{"Copyleft", "copyright"},
{"Copyright", "copyright"},
{"abort", "command"},
{"cd", "command"},
{"copy", "blkcpy"},
{"copying", "COPYING"},
{"copying-lgpl", "COPYING-LGPL"},
{"define", "command"},
{"dm2d", "dms2d"},
{"exit", "command"},
{"gm2g", "gms2g"},
{"hm2d", "hms2d"},
{"man", "calc"},
{"question", "questions"},
{"quit", "command"},
{"read", "command"},
{"release", "releases"},
{"show", "command"},
{"stdlib", "resource"},
{"usage", "calc"},
{"write", "command"},
/* MUST BE LAST! */
{NULL, NULL}
};
/*
* external values
*/
EXTERN char *pager; /* $PAGER or default */
/*
* page_file - output an open file thru a pager
*
* The popen() below is fairly safe. The $PAGER environment variable
* (supplied by the user) is the command we need to run without args.
* True, the user could set $PAGER is a bogus prog ... but if -m is
* 5 or 7 (read and exec allowed), then the calc is given ``permission''
* to open the help file for reading as well as to exec the pager!
*
* given:
* stream open file stream of the file to send to the pager
*/
S_FUNC void
page_file(FILE *stream)
{
FILE *cmd; /* pager command */
char buf[BUFSIZ+1]; /* I/O buffer */
char *fgets_ret; /* fgets() return value */
/*
* flush any pending I/O
*/
fflush(stderr);
fflush(stdout);
fflush(stdin);
/*
* form a write pipe to a pager
*/
if (pager == NULL || pager[0] == '\0') {
cmd = stdout;
} else {
cmd = popen(pager, "w");
}
if (cmd == NULL) {
fprintf(stderr, "unable form pipe to pager: %s", pager);
return;
}
/*
* read the help file and send non-## lines to the pager
* until EOF or error
*/
buf[BUFSIZ] = '\0';
do {
/*
* read the next line that does not begin with ##
*/
buf[0] = '\0';
while ((fgets_ret = fgets(buf, BUFSIZ, stream)) != NULL &&
buf[0] == '#' && buf[1] == '#') {
}
/*
* stop reading when we reach EOF (or error) on help file
*/
if (fgets_ret == NULL) {
fflush(cmd);
break;
}
/*
* write the line to pager, if possible
*
* We have to use fprintf() instead of fputs() because
* on NetBSD, fputs() function returns 0 on success and
# EOF on error. *sigh*
*/
} while (fprintf(cmd, "%s", buf) > 0);
/*
* all done, EOF or error, so just clean up
*/
(void) pclose(cmd);
fflush(stderr);
fflush(stdout);
fflush(stdin);
return;
}
/*
* givehelp - display a help file
*
* given:
* type the type of help to give, NULL => index
*/
void
givehelp(char *type)
{
struct help_alias *p; /* help alias being considered */
FILE *stream; /* help file stream */
char *helppath; /* path to the help file */
char *c;
size_t snprintf_len; /* malloced snprintf buffer length */
/*
* check permissions to see if we are allowed to help
*/
if (!allow_exec || !allow_read) {
fprintf(stderr,
"sorry, help is only allowed with -m mode 5 or 7\n");
return;
}
/* catch the case where we just print the index */
if (type == NULL) {
type = DEFAULTCALCHELP; /* the help index file */
}
/* alias the type of help, if needed */
for (p=halias; p->topic; ++p) {
if (strcmp(type, p->topic) == 0) {
type = p->filename;
break;
}
}
/*
* sanity check on name
*/
/* look for /. or a leading . */
if (strstr(type, "/.") != NULL || type[0] == '.') {
fprintf(stderr, "bad help name\n");
return;
}
/* look for chars that could be shell meta chars */
for (c = type; *c; ++c) {
switch ((int)*c) {
case '+':
case ',':
case '-':
case '.':
case '/':
case '_':
break;
default:
if (!isascii((int)*c) || !isalnum((int)*c)) {
fprintf(stderr, "bogus char in help name\n");
return;
}
break;
}
}
/*
* special case for Copyright and Copyleft
*/
if (strcmp(type, "copyright") == 0) {
fputs(Copyright, stdout);
fflush(stdout);
return;
}
/*
* open the helpfile (looking in HELPDIR first)
*/
snprintf_len = strlen(calc_helpdir)+1+strlen(type) + 1;
helppath = (char *)malloc(snprintf_len+1);
if (helppath == NULL) {
fprintf(stderr, "malloc failure in givehelp #0\n");
return;
}
snprintf(helppath, snprintf_len, "%s/%s", calc_helpdir, type);
helppath[snprintf_len] = '\0'; /* paranoia */
errno = 0;
stream = fopen(helppath, "r");
if (stream != NULL) {
/*
* we have the help file open, now display it
*/
page_file(stream);
(void) fclose(stream);
#if defined(CUSTOM)
/*
* open the helpfile (looking in CUSTOMHELPDIR last)
*/
} else {
char *cust_helppath; /* path to the custom help file */
size_t cust_snprintf_len; /* malloced custom snprintf buf len */
cust_snprintf_len =
strlen(calc_customhelpdir)+1+strlen(type) + 1;
cust_helppath = (char *)malloc(cust_snprintf_len+1);
if (cust_helppath == NULL) {
fprintf(stderr, "malloc failure for givehelp #1\n");
return;
}
snprintf(cust_helppath, cust_snprintf_len,
"%s/%s", calc_customhelpdir, type);
cust_helppath[cust_snprintf_len] = '\0'; /* paranoia */
errno = 0;
stream = fopen(cust_helppath, "r");
if (stream != NULL) {
/*
* we have the help file open, now display it
*/
page_file(stream);
(void) fclose(stream);
/* unable to open help file */
} else {
fprintf(stderr, "unable to open help file: %s - %s\n",
type, strerror(errno));
}
free(cust_helppath);
cust_helppath = NULL;
#else /* CUSTOM */
/* unable to open help file */
} else {
fprintf(stderr, "unable to open help file: %s - %s\n",
type, strerror(errno));
#endif /* CUSTOM */
}
/*
* cleanup
*/
free(helppath);
helppath = NULL;
return;
}