/********************************************************************
* $Author: jgoerzen $
* $Revision: 1.5 $
* $Date: 2002/03/19 20:22:42 $
* $Source: /home/jgoerzen/tmp/gopher-umn/gopher/head/gopher/pager.c,v $
* $State: Exp $
*
* Paul Lindner, University of Minnesota CIS.
*
* Copyright 1991, 92, 93, 94 by the Regents of the University of Minnesota
* see the file "Copyright" in the distribution for conditions of use.
*********************************************************************
* MODULE: pager.c
* neato pager..
*********************************************************************
* Revision History:
* $Log: pager.c,v $
* Revision 1.5 2002/03/19 20:22:42 jgoerzen
* Logged regex changes.
* Include STRstring.h and strcasestr proto.
*
* Revision 1.4 2002/02/12 21:20:14 jgoerzen
* Made files using strcasecmp() include util.h
*
* Revision 1.3 2001/01/17 21:48:05 jgoerzen
* Many fixes and tune-ups. Now compiles cleanly with -Wall -Werror!
*
* Revision 1.2 2001/01/03 22:31:38 s2mdalle
* Inclusion of new header file
*
* Revision 1.1.1.1 2000/08/19 00:28:56 jgoerzen
* Import from UMN Gopher 2.3.1 after GPLization
*
* Revision 3.34 1995/11/03 21:18:22 lindner
* ANSIfication
*
* Revision 3.33 1995/05/01 05:41:30 lindner
* compatibility fixes
*
* Revision 3.32 1995/05/01 03:41:49 lindner
* Fix return value
*
* Revision 3.31 1995/04/15 07:08:03 lindner
* Fix
*
* Revision 3.30 1995/02/23 21:53:53 lindner
* Correct cast..
*
* Revision 3.29 1995/01/03 19:53:25 lindner
* Dynamic screen position code, long title fix from Allan L. Bazinet
*
* Revision 3.28 1994/12/03 01:52:19 lindner
* Don't auto exit paging at the end of the file
*
* Revision 3.27 1994/10/21 04:41:08 lindner
* Add ANSI attached printer code..
*
* Revision 3.26 1994/06/29 07:14:39 lindner
* Check for A_CHARTEXT before trying to use it
*
* Revision 3.25 1994/06/29 05:13:55 lindner
* Change to Stat.h
*
* Revision 3.24 1994/06/09 22:13:40 lindner
* More language conversions
*
* Revision 3.23 1994/06/09 16:35:57 lindner
* Fix typos
*
* Revision 3.22 1994/06/09 04:37:06 lindner
* (F.Macrides) Fixed PagerNextPage() and PagerSearch() to not add extra
* blank lines following lines that are exactly equal to the screen width
* on VMS. Added code to disallow 'm'ail if SecureMode or NoShellMode
* and the NOMAIL symbol was defined on compilation.
*
* Revision 3.21 1994/06/03 06:12:19 lindner
* Fix for redisplay after using help
*
* Revision 3.20 1994/05/19 14:08:05 lindner
* use fast malloc on VMS VAXC
*
* Revision 3.19 1994/05/18 03:59:41 lindner
* Change to FIOsystem() for VMS
*
* Revision 3.18 1994/05/17 05:48:06 lindner
* Massive internationalization change
*
* Revision 3.17 1994/05/14 04:13:47 lindner
* Internationalization...
*
*
*********************************************************************/
/** Note, uses Global "Searchstring" **/
#include "gopher.h"
#include "CURcurses.h"
#include "STRstring.h"
#include "fileio.h"
#include "Malloc.h"
#include "pager.h"
#include "util.h"
#ifdef VMS
#include <stat.h>
#else
#include <sys/stat.h>
#endif
#define PosIncrement 10 /* room for 10 screens per malloc */
/** Argv/argc for searched words.. **/
static char *words[50];
static int wordcount = 0;
/** Optional search string **/
static char slashstring[128];
/** bytecount positions for each page.. (non-dynamic.. yet...) **/
/* static int positions[4000]; */ /** Bad programmer **/
static int *positions = NULL; /* not-so-bad programmer */
static int currentpage;
static int pos_max;
/*
* This is the beginning of a built in pager..
* it is very pretty..
*
*/
void
PagerHelp(CursesObj *cur)
{
static char **helpmenu = NULL;
if (helpmenu == NULL) {
helpmenu = (char**) malloc(sizeof(char*) * 9);
helpmenu[0] = Gtxt("u, ^G, left : Return to menu",19);
helpmenu[1] = Gtxt("space, down : Move to the next page",20);
helpmenu[2] = Gtxt("b, up : Move to the previous page",21);
helpmenu[3] = Gtxt("/ : Search for text",22);
helpmenu[4] = Gtxt("m : mail current document",23);
helpmenu[5] = Gtxt("s : save current document",24);
helpmenu[6] = Gtxt("p : print current document",25);
helpmenu[7] = Gtxt("D : download current document",26);
helpmenu[8] = NULL;
}
CURDialog(cur, Gtxt("Pager Help",18), helpmenu);
}
void
PagerInitGlobals(void)
{
currentpage = 0;
wordcount = 0;
slashstring[0] = '\0';
}
void
PagerTitles(CursesObj *cur, GopherObj *gs, int totalbytes)
{
int i, fluff_len, k_bytes;
char *cp;
k_bytes = totalbytes/1024;
/* Okay, first draw the top... ***/
move(0,0);
wboldout(stdscr);
fluff_len = 11 + (k_bytes/10); /* 11 = strlen("_(0k)_100%_"); */
if((strlen(GSgetTitle(gs))) <= (COLS-fluff_len)) {
waddstr(stdscr, GSgetTitle(gs));
} else {
cp = GSgetTitle(gs);
for(i = 0; i < (COLS-(fluff_len+3)); i++) {
waddch(stdscr, *cp++);
}
waddstr(stdscr, "...");
}
wboldend(stdscr);
wprintw(stdscr, " (%dk)", k_bytes);
wmove(stdscr, 1,0);
waddch(stdscr, CURgetBox_ul(cur));
for (i=0; i<COLS-2; i++)
waddch(stdscr, CURgetBox_hline(cur));
waddch(stdscr, CURgetBox_ur(cur));
/* And now the bottom **/
wmove(stdscr, LINES-2, 0);
waddch(stdscr, CURgetBox_ll(cur));
for (i=0; i<COLS-2; i++)
waddch(stdscr, CURgetBox_hline(cur));
waddch(stdscr, CURgetBox_lr(cur));
/****/
wmove(stdscr, LINES-1,0);
CURbutton(cur, stdscr, Gtxt("PageDown: <SPACE>",115), FALSE);
waddch(stdscr, ' ');
CURbutton(cur, stdscr, Gtxt("Help: ?",96), FALSE);
waddch(stdscr, ' ');
CURbutton(cur, stdscr, Gtxt("Return to Menu: u",134), FALSE);
}
int PagePosAlloc(void)
{
size_t MallocSize;
MallocSize = sizeof(int) * PosIncrement;
positions = (int *) malloc(MallocSize);
if (!positions)
return(FALSE);
else {
pos_max = PosIncrement;
return(TRUE);
}
}
int PagePosRealloc(void)
{
int *temp = NULL; /* in case realloc fails */
size_t MallocSize;
MallocSize = sizeof(int) * (pos_max + PosIncrement);
temp = positions;
positions = (int *) realloc(positions, MallocSize);
if(!positions) {
positions = temp;
return(FALSE);
} else {
pos_max += PosIncrement;
return(TRUE);
}
}
void PagePosFree(void)
{
if(positions) free(positions);
}
void
PagerPercent(int bytes, int totalbytes)
{
int per = (totalbytes? ((100 * bytes)/totalbytes) : 0);
wmove(stdscr, 0, COLS-5);
if (totalbytes != 0)
wprintw(stdscr, "%d%%", per);
if (per < 100)
waddch(stdscr, ' ');
if (per < 10)
waddch(stdscr, ' ');
}
/*
* Turn the search terms into a argv style thing..
*/
void
PagerParseSearchstring(void)
{
char *MungeSearchstr = Searchstring;
int numchars;
char theline[256];
wordcount = 0;
if (Searchstring == NULL)
return;
while (isspace(*MungeSearchstr)) /** Strip off spaces **/
MungeSearchstr++;
for (wordcount=0; wordcount<40; wordcount++) {
while (isspace(*MungeSearchstr)) /** Strip off spaces **/
MungeSearchstr++;
numchars = sreadword(MungeSearchstr, theline, 40);
MungeSearchstr += numchars;
if (numchars == 0)
break;
if (strcasecmp(theline, "and")==0 ||
strcasecmp(theline, "or")==0 ||
strcasecmp(theline, "not")==0) {
wordcount--;
} else {
if (words[wordcount] != NULL)
free(words[wordcount]);
words[wordcount] = strdup(theline);
}
}
}
void
PagerPrintLine(char *inputline)
{
int lowwordnum = -1, i;
char *cp, *lowword;
int wlen;
/** Just print it if no search terms **/
if ((wordcount == 0 && slashstring[0] == '\0') ||
RCsearchBolding(GlobalRC) == FALSE) {
waddstr(stdscr, inputline);
return;
}
/** Find the first word in the line **/
while (*inputline!='\0') {
lowword = NULL;
if (slashstring[0] != '\0')
cp = strcasestr(inputline, slashstring);
else
cp = NULL;
if (cp != NULL) {
lowword = cp;
lowwordnum = -1;
}
for (i=0; i< wordcount; i++) {
cp = strcasestr(inputline, words[i]);
if (cp != NULL)
if (cp < lowword || lowword == NULL) {
lowword = cp;
lowwordnum = i;
}
}
if (lowword == NULL) {
/** No search terms, spit it out **/
waddstr(stdscr, inputline);
return;
}
else {
/** add non bolded stuff **/
for (cp = inputline; cp < lowword; cp++)
#if defined(GINTERNATIONAL) && defined(A_CHARTEXT)
waddch(stdscr, *cp & A_CHARTEXT);
#else
waddch(stdscr, *cp);
#endif
inputline = lowword;
/*** Bolded stuff ***/
standout();
if (lowwordnum == -1)
wlen = strlen(slashstring);
else
wlen = strlen(words[lowwordnum]);
for (cp = inputline; cp < inputline + wlen; cp++)
waddch(stdscr, *cp);
inputline += wlen;
standend();
}
}
}
void
PagerSeekPage(FILE *thefile, int gotopage, int *bytecount)
{
if (gotopage < currentpage) {
fseek(thefile, positions[gotopage], 0);
*bytecount = positions[gotopage];
currentpage = gotopage;
}
}
void
PagerNextPage(CursesObj *cur, FILE *thefile, char *theline, int *bytecount, int totalbytes)
{
int i;
char *cp = NULL;
#ifdef VMS
boolean check_next_line = FALSE;
#endif
/** Save current bytecount position **/
positions[currentpage++] = *bytecount;
if(currentpage == pos_max)
if(!PagePosRealloc()) {
CursesErrorMsg(Gtxt("Sorry, can't display this file",148));
return;
}
wmove(stdscr, 2, 0);
/* Display a screen of text */
for (i=0; i<LINES-4; i++) {
cp = fgets(theline, COLS+1, thefile);
if (cp == NULL)
break;
#if defined(VMS) && defined(VMSRecords)
*bytecount = ftell(thefile);
if (check_next_line == TRUE) {
check_next_line = FALSE;
if (strlen(cp) == 1 && cp[0] == '\n')
goto read_again;
}
if (strlen(cp) == COLS && cp[COLS-1] != '\n')
check_next_line = TRUE;
#else
*bytecount += strlen(theline);
#endif
ZapCRLF(theline);
/** Bold the terms... **/
wmove(stdscr, i+2, 0);
wclrtoeol(stdscr);
PagerPrintLine(theline);
}
while (i<LINES-4) {
wmove(stdscr, i+2, 0);
wclrtoeol(stdscr);
i++;
}
/*** Display buttons ***/
wmove(stdscr, LINES - 1, 0);
wclrtoeol(stdscr);
CURbutton(cur, stdscr, Gtxt("Help: ?",96), FALSE);
waddstr(stdscr, " ");
CURbutton(cur, stdscr, Gtxt("Exit: u",86), FALSE);
waddstr(stdscr, " ");
if (*bytecount != totalbytes) {
CURbutton(cur, stdscr, Gtxt("PageDown: Space",116), FALSE);
waddstr(stdscr, " ");
}
if (currentpage > 1)
CURbutton(cur, stdscr, Gtxt("PageUp: b",117), FALSE);
}
/*
* Seek to the page with the specific search term...
*/
boolean
PagerSearch(FILE *thefile, char *theline, int *bytecount, char *search)
{
int i;
char *cp = NULL;
#ifdef VMS
boolean check_next_line = FALSE;
#endif
do {
/** Save current bytecount position **/
positions[currentpage++] = *bytecount;
if(currentpage == pos_max)
if(!PagePosRealloc()) {
CursesErrorMsg(Gtxt("Sorry, can't display this file",148));
return(FALSE);
}
for (i=0; i<LINES-4; i++) {
cp = fgets(theline, COLS+1, thefile);
if (cp == NULL)
break;
#if defined(VMS) && defined(VMSRecords)
*bytecount = ftell(thefile);
if (check_next_line == TRUE) {
check_next_line = FALSE;
if (strlen(cp) == 1 && cp[0] == '\n')
goto search_again;
}
if (strlen(cp) == COLS && cp[COLS-1] != '\n')
check_next_line = TRUE;
#else
*bytecount += strlen(theline);
#endif
if (strcasestr(cp, search) != NULL)
return(TRUE);
}
} while (cp != NULL);
return(FALSE);
}
void
PagerBuiltin(CursesObj *cur, GopherObj *gs)
{
int ch;
FILE *thefile;
char *theline;
char *Dialogmess[3];
char command[MAXSTR];
boolean done = FALSE;
int bytecount = 0;
int totalbytes = 0;
int savedpagenum;
struct stat buf;
PagerInitGlobals();
if(!PagePosAlloc()) {
CursesErrorMsg(Gtxt("Sorry, can't display this file",148));
return;
}
/*** Initialize an array for the screen ... ***/
theline = (char *) malloc(sizeof(char)*(COLS+1));
*theline = '\0';
stat(GSgetLocalFile(gs), &buf);
totalbytes = buf.st_size;
PagerParseSearchstring();
clear();
PagerTitles(cur, gs, totalbytes);
refresh();
thefile = fopen(GSgetLocalFile(gs), "r");
if (thefile == NULL) {
CursesErrorMsg(Gtxt("Cannot open requested file..",71));
if(theline) free(theline);
return;
}
PagerNextPage(cur, thefile, theline, &bytecount, totalbytes);
refresh();
/* Get a keystroke */
while (done==FALSE) {
PagerPercent(bytecount, totalbytes);
refresh();
ch = CURgetch(cur);
switch (ch) {
#ifdef VMS
case '\032': /* ^Z */
#endif
case KEY_LEFT:
case 'u':
case 'q':
case '\007':
done = TRUE;
break;
case '\006':
case '\n':
case ' ':
case KEY_DOWN:
case KEY_NPAGE:
if (bytecount < totalbytes)
PagerNextPage(cur, thefile, theline, &bytecount,
totalbytes);
break;
case '\002':
case KEY_UP:
case KEY_PPAGE:
case 'b':
if (currentpage > 1) {
PagerSeekPage(thefile, currentpage-2, &bytecount);
PagerNextPage(cur, thefile, theline, &bytecount,
totalbytes);
}
break;
case '^':
if (currentpage > 1) {
PagerSeekPage(thefile, 0, &bytecount);
PagerNextPage(cur, thefile, theline, &bytecount,
totalbytes);
}
break;
case '$':
if (bytecount < totalbytes)
do
PagerNextPage(cur, thefile, theline, &bytecount,
totalbytes);
while (bytecount < totalbytes);
break;
case '\037':
case KEY_HELP:
case 'h':
case '?':
PagerHelp(cur);
touchwin(stdscr);
break;
case 'p':
if (SecureMode || NoShellMode) {
CursesErrorMsg(Gtxt("Sorry, you are not allowed to print files",161));
touchwin(stdscr);
break;
}
if (RCuseANSIprinter(GlobalRC) == TRUE) {
FILE *printit;
char *cp;
char printtmpstr[512];
printit = fopen(GSgetLocalFile(gs), "r");
if (printit == NULL) {
CursesErrorMsg(Gtxt("Cannot Open requested file..",71));
break;
}
CURexit(CursesScreen);
printf("Now printing on ANSI attached printer\n");
printf("\033[5i");
while (1) {
cp = fgets(printtmpstr, sizeof(printtmpstr), printit);
if (cp == NULL)
break;
fputs(cp, stdout);
}
fclose(printit);
printf("\f\033[4i");
printf(Gtxt("Press <RETURN> to continue",121));
getchar();
CURenter(CursesScreen);
touchwin(stdscr);
clearok(curscr, TRUE);
break;
}
if (!RCprintCommand(GlobalRC, GSgetLocalView(gs),
GSgetLocalFile(gs), command) ||
!strncasecmp(command, "- none -", 8) ||
strlen(command) == 0) {
CursesErrorMsg(Gtxt("Sorry, no method to print this document",156));
touchwin(stdscr);
break;
}
Dialogmess[0] = strdup(Gtxt("The filename is:",174));
Dialogmess[1] = strdup(GSgetLocalFile(gs));
Dialogmess[2] = NULL;
if (CURDialog(cur, Gtxt("Print current document",122), Dialogmess) != -1) {
if (FIOsystem(command))
CursesErrorMsg(Gtxt("Encountered printing problem, sorry...",84));
}
free(Dialogmess[0]);
free(Dialogmess[1]);
touchwin(stdscr);
clearok(curscr, TRUE);
break;
case 's':
if (!(SecureMode || NoShellMode))
Save_file(gs, NULL, GSgetLocalView(gs));
else
CursesErrorMsg(Gtxt("Sorry, you are not allowed to save files",162));
touchwin(stdscr);
break;
case 'D':
Download_file(gs);
wclear(stdscr);
PagerTitles(cur, gs, totalbytes);
PagerSeekPage(thefile, currentpage-1, &bytecount);
PagerNextPage(cur, thefile, theline, &bytecount, totalbytes);
break;
case 'm':
#ifdef NOMAIL
if (SecureMode || NoShellMode) {
CursesErrorMsg(Gtxt("Sorry, you are not allowed to do this", 64));
touchwin(stdscr);
break;
}
#endif
GSmail(gs);
wclear(stdscr);
PagerTitles(cur, gs, totalbytes);
PagerSeekPage(thefile, currentpage-1, &bytecount);
PagerNextPage(cur, thefile, theline, &bytecount, totalbytes);
break;
case '/':
/** Search ... ***/
if ((CURGetOneOption(CursesScreen, "",
Gtxt("Search text for:",141),
slashstring) < 0) ||
(slashstring[0] == '\0')) {
touchwin(stdscr);
break;
}
case 'n':
/** Next occurrence, and fall through from above.. **/
if (slashstring[0] == '\0') {
CursesErrorMsg(Gtxt("Use '/' to define search a first...",178));
touchwin(stdscr);
break;
}
savedpagenum = currentpage;
if (ch == '/')
PagerSeekPage(thefile, currentpage-1, &bytecount);
if (!PagerSearch(thefile, theline, &bytecount, slashstring)) {
CursesErrorMsg(Gtxt("Couldn't find text",82));
touchwin(stdscr);
PagerSeekPage(thefile, savedpagenum-1, &bytecount);
PagerNextPage(cur, thefile, theline, &bytecount,
totalbytes);
touchwin(stdscr);
break;
}
/* We found it... */
PagerSeekPage(thefile, currentpage-1, &bytecount);
PagerNextPage(cur, thefile, theline, &bytecount, totalbytes);
touchwin(stdscr);
break;
default:
CURBeep(cur);
break;
}
}
/*** Clean up.. ***/
fclose(thefile);
free(theline);
PagePosFree();
}