mirror of
https://github.com/openbsd/src.git
synced 2026-06-18 15:23:33 +02:00
df69c215c7
value < 0. errno is only updated in this case. Change all (most?) callers of syscalls to follow this better, and let's see if this strictness helps us in the future.
473 lines
11 KiB
C
473 lines
11 KiB
C
/* $OpenBSD: hack.pager.c,v 1.25 2019/06/28 13:32:52 deraadt Exp $ */
|
|
|
|
/*
|
|
* Copyright (c) 1985, Stichting Centrum voor Wiskunde en Informatica,
|
|
* Amsterdam
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are
|
|
* met:
|
|
*
|
|
* - Redistributions of source code must retain the above copyright notice,
|
|
* this list of conditions and the following disclaimer.
|
|
*
|
|
* - Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* - Neither the name of the Stichting Centrum voor Wiskunde en
|
|
* Informatica, nor the names of its contributors may be used to endorse or
|
|
* promote products derived from this software without specific prior
|
|
* written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
|
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
|
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
|
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
/*
|
|
* Copyright (c) 1982 Jay Fenlason <hack@gnu.org>
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. The name of the author may not be used to endorse or promote products
|
|
* derived from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
|
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
|
* THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
|
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
|
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
|
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
/* This file contains the command routine dowhatis() and a pager. */
|
|
/* Also readmail() and doshell(), and generally the things that
|
|
contact the outside world. */
|
|
|
|
#include <libgen.h>
|
|
#include <signal.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
|
|
#include "hack.h"
|
|
|
|
extern int CO, LI; /* usually COLNO and ROWNO+2 */
|
|
extern char *CD;
|
|
extern char quitchars[];
|
|
|
|
static void page_more(FILE *, int);
|
|
|
|
int
|
|
dowhatis(void)
|
|
{
|
|
FILE *fp;
|
|
char bufr[BUFSZ+6];
|
|
char *buf = &bufr[6], q;
|
|
size_t len;
|
|
extern char readchar();
|
|
|
|
if (!(fp = fopen(DATAFILE, "r")))
|
|
pline("Cannot open data file!");
|
|
else {
|
|
pline("Specify what? ");
|
|
q = readchar();
|
|
if (q != '\t')
|
|
while (fgets(buf,BUFSZ,fp))
|
|
if (*buf == q) {
|
|
len = strcspn(buf, "\n");
|
|
/* bad data file */
|
|
if (len == 0)
|
|
continue;
|
|
buf[len] = '\0';
|
|
/* Expand tab 'by hand' */
|
|
if (buf[1] == '\t'){
|
|
buf = bufr;
|
|
buf[0] = q;
|
|
(void) strncpy(buf+1, " ", 7);
|
|
len = strlen(buf);
|
|
}
|
|
pline("%s", buf);
|
|
if (buf[len - 1] == ';') {
|
|
pline("More info? ");
|
|
if (readchar() == 'y') {
|
|
page_more(fp,1); /* does fclose() */
|
|
return(0);
|
|
}
|
|
}
|
|
(void) fclose(fp); /* kopper@psuvax1 */
|
|
return(0);
|
|
}
|
|
pline("I've never heard of such things.");
|
|
(void) fclose(fp);
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
/* make the paging of a file interruptible */
|
|
static int got_intrup;
|
|
|
|
void
|
|
intruph(int notused)
|
|
{
|
|
got_intrup++;
|
|
}
|
|
|
|
/* simple pager, also used from dohelp() */
|
|
/* strip: nr of chars to be stripped from each line (0 or 1) */
|
|
static void
|
|
page_more(FILE *fp, int strip)
|
|
{
|
|
char *bufr;
|
|
sig_t prevsig = signal(SIGINT, intruph);
|
|
|
|
set_pager(0);
|
|
bufr = (char *) alloc((unsigned) CO);
|
|
while (fgets(bufr, CO, fp) && (!strip || *bufr == '\t') &&
|
|
!got_intrup) {
|
|
bufr[strcspn(bufr, "\n")] = '\0';
|
|
if (page_line(bufr+strip)) {
|
|
set_pager(2);
|
|
goto ret;
|
|
}
|
|
}
|
|
set_pager(1);
|
|
ret:
|
|
free(bufr);
|
|
(void) fclose(fp);
|
|
(void) signal(SIGINT, prevsig);
|
|
got_intrup = 0;
|
|
}
|
|
|
|
static boolean whole_screen = TRUE;
|
|
#define PAGMIN 12 /* minimum # of lines for page below level map */
|
|
|
|
void
|
|
set_whole_screen(void)
|
|
{ /* called in termcap as soon as LI is known */
|
|
whole_screen = (LI-ROWNO-2 <= PAGMIN || !CD);
|
|
}
|
|
|
|
#ifdef NEWS
|
|
int
|
|
readnews(void)
|
|
{
|
|
int ret;
|
|
|
|
whole_screen = TRUE; /* force a docrt(), our first */
|
|
ret = page_file(NEWS, TRUE);
|
|
set_whole_screen();
|
|
return(ret); /* report whether we did docrt() */
|
|
}
|
|
#endif /* NEWS */
|
|
|
|
/* 0: open 1: wait+close 2: close */
|
|
void
|
|
set_pager(int mode)
|
|
{
|
|
static boolean so;
|
|
if(mode == 0) {
|
|
if(!whole_screen) {
|
|
/* clear topline */
|
|
clrlin();
|
|
/* use part of screen below level map */
|
|
curs(1, ROWNO+4);
|
|
} else {
|
|
cls();
|
|
}
|
|
so = flags.standout;
|
|
flags.standout = 1;
|
|
} else {
|
|
if(mode == 1) {
|
|
curs(1, LI);
|
|
more();
|
|
}
|
|
flags.standout = so;
|
|
if(whole_screen)
|
|
docrt();
|
|
else {
|
|
curs(1, ROWNO+4);
|
|
cl_eos();
|
|
}
|
|
}
|
|
}
|
|
|
|
int
|
|
page_line(char *s) /* returns 1 if we should quit */
|
|
{
|
|
extern char morc;
|
|
|
|
if(cury == LI-1) {
|
|
if(!*s)
|
|
return(0); /* suppress blank lines at top */
|
|
putchar('\n');
|
|
cury++;
|
|
cmore("q\033");
|
|
if(morc) {
|
|
morc = 0;
|
|
return(1);
|
|
}
|
|
if(whole_screen)
|
|
cls();
|
|
else {
|
|
curs(1, ROWNO+4);
|
|
cl_eos();
|
|
}
|
|
}
|
|
puts(s);
|
|
cury++;
|
|
return(0);
|
|
}
|
|
|
|
/*
|
|
* Flexible pager: feed it with a number of lines and it will decide
|
|
* whether these should be fed to the pager above, or displayed in a
|
|
* corner.
|
|
* Call:
|
|
* cornline(0, title or 0) : initialize
|
|
* cornline(1, text) : add text to the chain of texts
|
|
* cornline(2, morcs) : output everything and cleanup
|
|
* cornline(3, 0) : cleanup
|
|
*/
|
|
void
|
|
cornline(int mode, char *text)
|
|
{
|
|
static struct line {
|
|
struct line *next_line;
|
|
char *line_text;
|
|
} *texthead, *texttail;
|
|
static int maxlen;
|
|
static int linect;
|
|
struct line *tl;
|
|
|
|
if(mode == 0) {
|
|
texthead = 0;
|
|
maxlen = 0;
|
|
linect = 0;
|
|
if(text) {
|
|
cornline(1, text); /* title */
|
|
cornline(1, ""); /* blank line */
|
|
}
|
|
return;
|
|
}
|
|
|
|
if(mode == 1) {
|
|
int len;
|
|
|
|
if(!text) return; /* superfluous, just to be sure */
|
|
linect++;
|
|
len = strlen(text);
|
|
if(len > maxlen)
|
|
maxlen = len;
|
|
tl = (struct line *)
|
|
alloc((unsigned)(len + sizeof(struct line) + 1));
|
|
tl->next_line = 0;
|
|
tl->line_text = (char *)(tl + 1);
|
|
(void) strlcpy(tl->line_text, text, len + 1);
|
|
if(!texthead)
|
|
texthead = tl;
|
|
else
|
|
texttail->next_line = tl;
|
|
texttail = tl;
|
|
return;
|
|
}
|
|
|
|
/* --- now we really do it --- */
|
|
if(mode == 2 && linect == 1) /* topline only */
|
|
pline("%s", texthead->line_text);
|
|
else
|
|
if(mode == 2) {
|
|
int curline, lth;
|
|
|
|
if(flags.toplin == 1) more(); /* ab@unido */
|
|
remember_topl();
|
|
|
|
lth = CO - maxlen - 2; /* Use full screen width */
|
|
if (linect < LI && lth >= 10) { /* in a corner */
|
|
home();
|
|
cl_end();
|
|
flags.toplin = 0;
|
|
curline = 1;
|
|
for (tl = texthead; tl; tl = tl->next_line) {
|
|
curs(lth, curline);
|
|
if(curline > 1)
|
|
cl_end();
|
|
putsym(' ');
|
|
putstr (tl->line_text);
|
|
curline++;
|
|
}
|
|
curs(lth, curline);
|
|
cl_end();
|
|
cmore(text);
|
|
home();
|
|
cl_end();
|
|
docorner(lth, curline-1);
|
|
} else { /* feed to pager */
|
|
set_pager(0);
|
|
for (tl = texthead; tl; tl = tl->next_line) {
|
|
if (page_line (tl->line_text)) {
|
|
set_pager(2);
|
|
goto cleanup;
|
|
}
|
|
}
|
|
if(text) {
|
|
cgetret(text);
|
|
set_pager(2);
|
|
} else
|
|
set_pager(1);
|
|
}
|
|
}
|
|
|
|
cleanup:
|
|
while ((tl = texthead)) {
|
|
texthead = tl->next_line;
|
|
free(tl);
|
|
}
|
|
}
|
|
|
|
int
|
|
dohelp(void)
|
|
{
|
|
char c;
|
|
|
|
pline ("Long or short help? ");
|
|
while (((c = readchar ()) != 'l') && (c != 's') && !strchr(quitchars,c))
|
|
hackbell ();
|
|
if (!strchr(quitchars, c))
|
|
(void) page_file((c == 'l') ? HELP : SHELP, FALSE);
|
|
return(0);
|
|
}
|
|
|
|
/* return: 0 - cannot open fnam; 1 - otherwise */
|
|
int
|
|
page_file(char *fnam, boolean silent)
|
|
{
|
|
#ifdef DEF_PAGER /* this implies that UNIX is defined */
|
|
{
|
|
/* use external pager; this may give security problems */
|
|
|
|
int fd = open(fnam, O_RDONLY);
|
|
|
|
if(fd == -1) {
|
|
if(!silent) pline("Cannot open %s.", fnam);
|
|
return(0);
|
|
}
|
|
if(child(1)){
|
|
extern char *catmore;
|
|
|
|
/* Now that child() does a setuid(getuid()) and a chdir(),
|
|
we may not be able to open file fnam anymore, so make
|
|
it stdin. */
|
|
(void) close(0);
|
|
if(dup(fd)) {
|
|
if(!silent) printf("Cannot open %s as stdin.\n", fnam);
|
|
} else {
|
|
execlp(catmore, basename(catmore), (char *)NULL);
|
|
if(!silent) printf("Cannot exec %s.\n", catmore);
|
|
}
|
|
exit(1);
|
|
}
|
|
(void) close(fd);
|
|
}
|
|
#else /* DEF_PAGER */
|
|
{
|
|
FILE *f; /* free after Robert Viduya */
|
|
|
|
if ((f = fopen (fnam, "r")) == (FILE *) 0) {
|
|
if(!silent) {
|
|
home(); perror (fnam); flags.toplin = 1;
|
|
pline ("Cannot open %s.", fnam);
|
|
}
|
|
return(0);
|
|
}
|
|
page_more(f, 0);
|
|
}
|
|
#endif /* DEF_PAGER */
|
|
|
|
return(1);
|
|
}
|
|
|
|
#ifdef UNIX
|
|
#ifdef SHELL
|
|
int
|
|
dosh(void)
|
|
{
|
|
char *str;
|
|
|
|
if(child(0)) {
|
|
if ((str = getenv("SHELL")))
|
|
execlp(str, str, (char *)NULL);
|
|
else
|
|
execl("/bin/sh", "sh", (char *)NULL);
|
|
pline("sh: cannot execute.");
|
|
exit(1);
|
|
}
|
|
return(0);
|
|
}
|
|
#endif /* SHELL */
|
|
|
|
#include <sys/wait.h>
|
|
|
|
int
|
|
child(int wt)
|
|
{
|
|
int status;
|
|
int f;
|
|
char *home;
|
|
gid_t gid;
|
|
|
|
f = fork();
|
|
if(f == 0){ /* child */
|
|
settty(NULL); /* also calls end_screen() */
|
|
/* revoke privs */
|
|
gid = getgid();
|
|
setresgid(gid, gid, gid);
|
|
#ifdef CHDIR
|
|
home = getenv("HOME");
|
|
if (home == NULL || *home == '\0')
|
|
home = "/";
|
|
(void) chdir(home);
|
|
#endif /* CHDIR */
|
|
return(1);
|
|
}
|
|
if(f == -1) { /* cannot fork */
|
|
pline("Fork failed. Try again.");
|
|
return(0);
|
|
}
|
|
/* fork succeeded; wait for child to exit */
|
|
(void) signal(SIGINT,SIG_IGN);
|
|
(void) signal(SIGQUIT,SIG_IGN);
|
|
(void) wait(&status);
|
|
gettty();
|
|
setftty();
|
|
(void) signal(SIGINT,done1);
|
|
#ifdef WIZARD
|
|
if(wizard) (void) signal(SIGQUIT,SIG_DFL);
|
|
#endif /* WIZARD */
|
|
if(wt) getret();
|
|
docrt();
|
|
return(0);
|
|
}
|
|
#endif /* UNIX */
|