Logo Search packages:      
Sourcecode: vh version File versions  Download package

browse.c

/*****************************************************************************

NAME
   browse.c -- curses(3)-using display front end for vh database browser

SYNOPSIS
   main(argc, argv)                   --- browser main sequence
   int argc; char *argv[][];

DESCRIPTION
   This module defines the main and browse loop for the vh browser;
all user-interface decisions are made here.  Screen, keyboard and
mouse handling support may be found in screen.c.

AUTHORS
   Written by Eric S. Raymond <eric@snark.thyrsus.com> for a UNIX
port of the 1.1 version of Raymond Gardner's MS-DOS browser, October
1991.  Please see the source distribution's READ.ME for license
terms.

*****************************************************************************/
/*LINTLIBRARY*/
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <ctype.h>
#include <assert.h>
#ifdef ATT
#include <termio.h>
#endif /* ATT */

#include "screen.h"
#include "vh.h"

#ifndef CONST
#define     const
#endif /* CONST */

#ifdef MSDOS
/* emulate Borland-style scrollbar */
#define SBARCH          ACS_CKBOARD /* scrollbar character */
#define SBARTOPCH 30          /* scrollbar up arrow */
#define SBARBOTCH 31          /* scrollbar down arrow */
#else
/* approximate X-style scrollbar */
#define SBARCH          ACS_VLINE   /* scrollbar character */
#define SBARTOPCH ACS_TTEE    /* scrollbar up arrow */
#define SBARBOTCH ACS_BTEE    /* scrollbar down arrow */
#endif
#define SBARTHUMBCH     ACS_BULLET  /* scrollbar thumb character */

#ifdef A_COLOR
#define NORMPAIR  1
#define HILITEPAIR      2
#define SELPAIR         3
#define PROMPTPAIR      4
#define FKEYPAIR  5
#define SBARPAIR  6
#define SBARENDPAIR     7
#define SBTHUMBPAIR     8
#endif /* A_COLOR */

#define     SUPPRESS    0
#define OPTIONAL  1
#define FORCED          2

#ifndef UNIX
#define erasechar()     BS
#endif /* UNIX */

#define CUTFILE   "%s.cut"

/*
 * When paging forward or back, it helps the reader to page by a few less
 * lines than the page depth, so a few of the old ones show to give context.
 * This controls how many old lines will show.
 */
#define OVERLAP   4

/* dialogue/message window parameters */
#define DTOP      10
#define DLEFT     10
#define DHEIGHT   7
#define DWIDTH    42

/*******************************************************************
 *
 * All message texts declared here for internationalization purposes
 *
 ******************************************************************/

#ifdef MSDOS
#define USAGE     "usage: jargon [-cigms] [-b key] [srcname] [indexname]\n\
      -c --- check textfile/index pair for consistency\n\
      -i --- enable incremental-lookup and search\n\
      -g --- generate new index from textfile\n\
      -m --- force monochrome operation\n\
      -s --- compensate for snowy EGA monitor\n\
      -b --- write single entry specified by following key to stdout\n\
      -r --- display random entry\n"
#define TBANNER   "TAB-Index  F1-help  F10-exit"
#define IBANNER   "TAB-Text   F1-help  F10-exit"
#else
#define USAGE     "usage: jargon [-cigmr] [-b key] [srcname] [indexname]\n"
#define TBANNER   "TAB-Index  ^Q-help   ^C-exit"
#define IBANNER   "TAB-Text   ^Q-help   ^C-exit"
#endif
typedef struct
{
    int     left; /* left boundary of pushbutton */
    int right;    /* right boundary of pushbutton */
    char cmd;     /* command character associated with pushbutton */
}
strip;

#ifdef MOUSE
static strip panel[] = {{0, 8, TAB}, {11, 17, CTL('Q')}, {21, 27, CTL('C')}};
#endif

/*
 * Note: I had to descrement this by one to get around a wraparound bug in
 * ncurses.  Doesn't seems to have had any visible effect on the SVr4
 * version, but...
 */
#define PANELSTART      (COLS - 1 - strlen((cf==&vhi) ? IBANNER : TBANNER))

#define NOREFS    "No references on this page"
#define NOSRCH    "No previous search"
#define CUTNOP    "Can't open cut file"
#define CUTNOW    "Cutting to jargon.cut..."
#define CUTDNE    "Cutting to jargon.cut...Done"

#define BRSMES    "Browsing %s...\n"
#define IDXMES    "Generating index for %s...\n"
#define CHKMES    "Performing consistency check for %s...\n"

/*
 * These have to be declared static, not macros; we use the address of the
 * current prompt as a mode indicator.
 */
const static char NOMESG[] =  "                   ";
#ifndef POPUP
/* we want these fixed-length so the entry line looks good */
const static char SEARCH[] =  "String search for: ";
const static char INSRCH[] =  "Search proceeding: ";
const static char NOTFND[] =  "Search failed:     ";
const static char LOOKUP[] =  "Looking up entry:  ";
const static char LOOKING[] = "Lookup in progress:";
#else
/* the popup windows will get sized to these */
const static char SEARCH[] =  "String search for";
const static char INSRCH[] =  "Search proceeding";
const static char NOTFND[] =  "Search failed";
const static char LOOKUP[] =  "Looking up entry";
const static char LOOKING[] = "Lookup in progress";
const static char BDLOOK[] =  "Lookup failed";
#endif /* POPUP */

const static char *help_screen[] =
{
"                          Jargon File Browser v. 1.6",
"           Copyright 1991 by Raymond D. Gardner & Eric S. Raymond",
"             All Rights Reserved; Free Redistribution Encouraged",
"",
"      ^L             redraw screen                      (Refresh)",
"",
"      space          page forward                       (PgDn)",
"      ^U             page back                          (PgUp)",
"      ^N / ^P        scroll forward / back              (Down/Up Arrow)",
"      ^A / ^O        go to top / end of file            (Home/End)",
"",
"      ^J or ^M       chase highlighted reference        (Reference)",
"      ^F / ^B        next/previous reference            (Right / Left Arrow)",
"",
"      ^E             lookup entry by name               (Select)",
"      ^S             search for string                  (Find)",
"      ^R             repeat previous search             (Shift-Find)",
#ifdef UNIX /* keypad() seems to disable recognition of ESC */
"      ^H             undo last search or lookup         (Undo)",
#else
"      ^H or ESC      undo last search or lookup         (Undo)",
#endif
"",
"      ^I             toggle between word list and text  (Tab)",
"      ^Y             append selected entry to cut file  (Print)",
"",
"      Typing an entry name followed by Enter works like an ^E command.",
"                  Press any key to return to the browser.",
NULL,
};

/*******************************************************************
 *
 * Shared global data
 *
 ******************************************************************/

static char *execname;  /* name under which program was invoked */
static FILEINFO   *cf;  /* pointer to current fileinfo block */

/* highlights */
static chtype normattr, hiliteattr, selattr, promptattr, fkeyattr, sbarattr;
static chtype sbarendattr, sbthumbattr;

static char lasthit[LNSZ + 1];      /* last key searched for */
static char linbuf[LNSZ + 1]; /* line input scratch buffer */
#ifdef POPUP
static WINDOW *dwin;          /* dialogue & message window */
#endif /* POPUP */
static bool incrsearch;       /* do incremental search? */

/*******************************************************************
 *
 * Message and dialogue code
 *
 ******************************************************************/

static void waitforit(requeue)
/* wait for keystroke or mouse click */
bool  requeue;
{
    int     c;
#ifdef MOUSE
    int     hsy, hsx;

    /* wait for keypress or button click */
    while ((c = egetch()) == KEY_MOUSE)
      if (A_BUTTON_CHANGED & mouse_status(&hsy, &hsx))
          break;
#else
    c = egetch();
#endif /* MOUSE */
    if (requeue)
      ungetch(c);
}

#if defined(POPUP) && defined(CURSES)
static void draw_box(win, ty,tx,  by,bx)
/* draw a box on the screen using best possible forms chars */
WINDOW *win;
int ty, tx, by, bx;
{
    int     j;

    mvwaddch(win, ty, tx, ACS_ULCORNER);
    mvwaddch(win, by, bx, ACS_LRCORNER);
    mvwaddch(win, by, tx, ACS_LLCORNER);
    mvwaddch(win, ty, bx, ACS_URCORNER);
    wmove(win, ty, tx+1);
    for (j = tx + 1; j <= bx - 1; j++)
      waddch(win, ACS_HLINE);
    wmove(win, by, tx+1);
    for (j = tx + 1; j <= bx - 1; j++)
      waddch(win, ACS_HLINE);
    for (j = ty + 1; j <= by - 1; j++)
    { 
      wmove(win, j, tx);
      waddch(win, ACS_VLINE);
    }
    for (j = ty + 1; j <= by - 1; j++)
    { 
      wmove(win, j, bx);
      waddch(win, ACS_VLINE);
    }
    wrefresh(win);
}

static char *get_dialogue(prompt)
/* get string from user, with prompts */
char  *prompt;
{
    static char buf[MAXCOLS + 1];

    if (prompt == (char *)NULL)
    {
      delwin(dwin);
      dwin = (WINDOW *)NULL;
    }
    else
    {
      /* create a dialogue window */
      if (dwin == (WINDOW *)NULL)
          dwin = newwin(DHEIGHT, DWIDTH, DTOP, DLEFT);

      wclear(dwin);

      draw_box(dwin, 0, 0, DHEIGHT-1, DWIDTH-1);
      draw_box(dwin, 2, 1, DHEIGHT-3, DWIDTH-2);

      wattron(dwin, A_BOLD);
      mvwaddstr(dwin, 1, 10, prompt);
      wattroff(dwin, A_BOLD);

      echo();
      mvwgetstr(dwin, 3, 3, buf);
      noecho();

      return(buf);
    }
}
#endif /* defined(POPUP) && defined(CURSES) */

static void message(s)
/* write message to prompt line */
char  *s;
{
#ifndef POPUP
    attrset(promptattr);
    mvaddstr(LINES-1, 0, s);
    attrset(normattr);
    refresh();
#else
    WINDOW  *mwin;

    if (dwin)
    {
      mvwaddstr(dwin, 5, 10, NOMESG);
      wattron(dwin, A_BOLD);
      mvwaddstr(dwin, 5, 10, s);
      wattroff(dwin, A_BOLD);
      wrefresh(dwin);
    }
    else
    {
      mwin = newwin(3, strlen(s) + 2, DTOP, DLEFT);
      wclear(mwin);
      draw_box(mwin, 0, 0, 2, strlen(s) + 1);

      wattron(mwin, A_BOLD);
      mvwaddstr(mwin, 1, 1, s);
      wattroff(mwin, A_BOLD);
      wrefresh(mwin);

      waitforit(TRUE);

      overwrite(stdscr, mwin);
      delwin(mwin);
      touchline(DTOP, stdscr);
      wrefresh(stdscr);
    }
#endif /* POPUP */
}

/*******************************************************************
 *
 * The browser itself
 *
 ******************************************************************/

static void uninitbrowse(doclear)
/* end the browse, either normally or due to signal */
bool  doclear;
{
    mouse_hide();
    if (doclear)
    {
      clear();
      refresh();
    }
#ifdef CURSES
    /* weird sex with the tty driver */
    (void)resetterm();
    (void)echo();
#ifndef OLDCURSES
    (void)flushinp();
#endif /* OLDCURSES */
#endif /* CURSES */
    (void)endwin();
    exit(0);
}

static void paintselect(rp, attr)
/* turn highlight on a selection on or off */
region      *rp;  /* which to highlight */
chtype      attr; /* attribute to use */
{
    int     x, y;

    attrset(attr);
    if (rp->yl == rp->yr)
    {
      move(rp->yl, rp->xl);
      for (x = rp->xl; x <= rp->xr; x++)
          addch(inch() & A_CHARTEXT);
    }
    else
    {
      int /*c,*/ lastnsp;

      /* don't highlight trailing spaces on line-wrapped references */
      for (lastnsp = COLS - 1; lastnsp > 0; lastnsp--)
      {
          move(rp->yl, lastnsp);
          if ((inch() & A_CHARTEXT) != ' ')
            break;
      }

      /* write trailing-line part of highlight */
      move(rp->yl, rp->xl);
      for (x = rp->xl; x <= lastnsp; x++)
          addch(inch() & A_CHARTEXT);

      /* highlight everything *between* 1st and last lines in region */
      for (y = rp->yl + 1; y < rp->yr; y++)
      {
          move(y, x);
          for (x = 0; x < COLS - 1; x++)
            addch(inch() & A_CHARTEXT);
      }

      /* don't highlight leading spaces on line-wrapped references */
      for (x = 0; x <= COLS - 1; x++)
      {
          move(rp->yr, x);
          if ((inch() & A_CHARTEXT) != ' ')
            break;
      }

      /* write leading-line part of highlight */
      move(rp->yr, x);
      for (; x <= rp->xr; x++)
          addch(inch() & A_CHARTEXT);
    }
    attrset(normattr);
}

static bool chase(x, y, isindex)
/* try to chase a link at this location */
int   x, y;
bool  isindex;
{
    long pos;     /* find target */
    int dummy;

    enqueue(cf);  /* make sure we can un-chase it */

    pos = iflink(x, y, &dummy, &dummy, isindex);
    if (pos <= 0)
    {
      dequeue(cf);
      return(FALSE);
    }
    else
    {
      if (isindex)      /* if index, switch to text */
      {
          cf = vht;
          cf->dsptoppos = cf->dspnextpos = NOWHERE;
      }

      cf->toppos = pos; /* set for new display */
      return(TRUE);
    }
}


static bool search(str, c)
/* search for given string in file */
char  *str;
{
    int     i;

    message(INSRCH);
    enqueue(cf);
    if (c)
      cf->hitpos = ifind(cf->fp, cf->dspnextpos, c);
    else
      cf->hitpos = ffind(cf->fp, cf->dspnextpos, str);
    if (cf->hitpos == NOWHERE)
    {
      message(NOTFND);
      dequeue(cf);
      return(FALSE);
    }
    else
    {
      /* else display hit on fourth line */
      cf->toppos = cf->hitpos;
      for (i = 0; i < OVERLAP; i++)
          cf->toppos = getprevln(cf->fp, cf->toppos, linbuf);
      if (str != lasthit)
          (void) strcpy(lasthit, str);
      return(TRUE);
    }
}

static bool lookup(str, c)
/* search for given entry in file */
char  *str, c;
{
    long pos;                 /* find target */

    enqueue(cf);

    if (c)
      pos = ilocate(c);
    else
    {
      message(LOOKING);
      pos = xlocate(str);
    }

    if (cf == &vhi)           /* if index, switch to text */
    {
      cf = vht;
      cf->dsptoppos = cf->dspnextpos = cf->endpos;
    }

    if (pos < 0)
    {
      cf->toppos = -pos;            /* set for new display */
      return(FALSE);
    }
    else
    {
      cf->toppos = pos;       /* set for new display */
      return(TRUE);
    }
}

static daddr_t byname(prompt, str, c)
/* search or lookup given entry in file, depending on prompt value */
char  *prompt, *str, c;
{
    if (prompt == SEARCH)
      return(search(str, c));
    else
      return(lookup(str, c));
}

/* entry buffer for lookup and search commands */
static char entrybuf[LNSZ + 1];
static int  entrycnt = 0;

static void enterchar(c, prompt, row, col, maxcol)
/* accept a character into the entry buffer */
char  c;
char  *prompt;
int   row, col;
int   maxcol;
{
    int     es;

    if (c == '\0')
    {
      entrybuf[entrycnt = 0] = '\0';
      return;
    }

    es = col + strlen(prompt);
    attrset(promptattr);
    if (c == erasechar())
    {
      if (entrycnt > 0)
      {
          mvaddch(row, es + --entrycnt, ' ');
          entrybuf[entrycnt] = '\0';
          move(row, es + entrycnt);
          ilocate(BS);
          if (incrsearch)
            dequeue(cf);
      }
    }
#ifdef CURSES
    else if (c == killchar())
    {
      /* delete everything back to the end of the prompt */
      while (entrycnt > 0)
      {
          mvaddch(row, es + --entrycnt, ' ');
          entrybuf[entrycnt] = '\0';
          move(row, es + entrycnt);
          if (incrsearch)
            dequeue(cf);
      }
      ilocate(DEL);
    }
#endif /* CURSES */
    else if (isprint(c))
    {
      mvaddstr(row, col, prompt);
      if (es + entrycnt < maxcol
          && (!incrsearch || byname(prompt, (char *)NULL, c) != NOWHERE))
      {
          mvaddch(row, es + entrycnt, c);
          entrybuf[entrycnt++] = c;
          entrybuf[entrycnt] = '\0';
      }
      else if (c == '\0')
          entrybuf[entrycnt = 0] = '\0';
      else
          beep();
    }
    attrset(normattr);

    refresh();
}

static void cutentry(pos, fp)
/* send the text of the entry starting at pos to the gin file pointer */
daddr_t pos;
FILE *fp;
{
    (void) fseek(vht->fp, pos, SEEK_SET);

    /* always want first line */
    (void) fgets(linbuf, LNSZ, vht->fp);
    (void) fputs(linbuf, fp);

    /* copy succeeding lines till we get to next entry */
    while (fgets(linbuf, LNSZ, vht->fp) != (char *)NULL)
      if (linbuf[0] == '=' || headword(linbuf))
          break;
      else
          (void) fputs(linbuf, fp);
}

static char *nblanks(n)
/* blank-string return for fast fills; return up to 132 = MAXCOLS */
int   n;
{
    /* MAXCOLS+1 blanks */
    const static char blanks[] = "                                                                                                                                     ";
    return(&blanks[sizeof(blanks) - 1 - n]);
}

static void browse(name, cok)
/* interactive browser loop */
char  *name;
bool  cok;
{
    int           i, c;
    char    **hp;
    bool    restore_select = FALSE;
    FILE    *cutfp;
    int           lightup = FORCED;
#ifdef MOUSE
    int           hsy, hsx, mstat;  /* mouse hot spot coordinates */
    bool    thumbdrag = FALSE;      /* are we in a thumbdrag? */
#endif /* MOUSE */
#ifndef POPUP
    char    *prompt = NOMESG;
#else
    char    *cp;
#endif /* POPUP */
#ifdef ATT
    struct termio tty;
    int           saveintr;
#endif /* ATT */

    if (!initbrowse(name))
      exit(1);
    cf = vht;

#ifdef UNIX
    (void) signal(SIGINT,uninitbrowse);
    (void) signal(SIGQUIT,uninitbrowse);
    (void) signal(SIGTERM,uninitbrowse);
    (void) signal(SIGIOT,uninitbrowse);         /* for assert(3) */
#ifdef ATT
    (void) ioctl(0, TCGETA, &tty);
    saveintr = tty.c_cc[VINTR];
#endif /* ATT */
#endif /* UNIX */

    (void) initscr();
    setlastpage();

#ifdef A_COLOR
    if ((cok = (cok && has_colors())))
    {
      start_color();

      init_pair(NORMPAIR, COLOR_WHITE, COLOR_BLUE);
      init_pair(HILITEPAIR, COLOR_RED, COLOR_WHITE);
      init_pair(SELPAIR, COLOR_YELLOW, COLOR_BLUE);
      init_pair(PROMPTPAIR, COLOR_BLACK, COLOR_CYAN);
      init_pair(FKEYPAIR, COLOR_RED, COLOR_CYAN);
      init_pair(SBARPAIR, COLOR_WHITE, COLOR_BLACK);
      init_pair(SBARENDPAIR, COLOR_BLUE, COLOR_WHITE);
      init_pair(SBTHUMBPAIR, COLOR_BLUE, COLOR_BLACK);
    }
    mouse_color(cok);

    normattr = cok ? (COLOR_PAIR(NORMPAIR) | A_BOLD) : A_NORMAL;
    hiliteattr = cok ? COLOR_PAIR(HILITEPAIR) : A_REVERSE;
    selattr = cok ? (COLOR_PAIR(SELPAIR) | A_BOLD) : A_BOLD;
    promptattr = cok ? COLOR_PAIR(PROMPTPAIR) : A_REVERSE;
    fkeyattr = cok ? COLOR_PAIR(FKEYPAIR) : A_BOLD;
#ifndef MSDOS
    sbarendattr = sbthumbattr = 
      sbarattr = cok ? COLOR_PAIR(SBARPAIR) : A_NORMAL;
#else
    sbarattr = cok ? COLOR_PAIR(SBARPAIR) : A_REVERSE;
    sbarendattr = cok ? (COLOR_PAIR(SBARENDPAIR) | A_BOLD) : A_REVERSE;
    sbthumbattr = cok ? (COLOR_PAIR(SBTHUMBPAIR) | A_BOLD) : A_NORMAL;
#endif
#else
    normattr = A_NORMAL;
    hiliteattr = A_REVERSE;
    selattr = A_BOLD;
    promptattr = A_REVERSE;
    fkeyattr = A_BOLD;
    sbarendattr = sbthumbattr = 
      sbarattr = A_NORMAL;
#ifdef MSDOS
    sbarattr = A_REVERSE;
    sbarendattr = A_REVERSE;
    sbthumbattr = A_NORMAL;
#endif /* MSDOS */
#endif /* A_COLOR */

    mouse_init();
#ifdef MOUSE
    mouse_move(LINES - 1, COLS - 1);
#endif /* MOUSE */

#ifdef CURSES
    (void)saveterm();
    (void)nonl();
    (void)raw();
    (void)noecho();
#ifndef OLDCURSES
    (void)keypad(stdscr, TRUE);
#endif /* OLDCURSES */
#endif /* CURSES */

#ifdef ATT
    (void) ioctl(0, TCGETA, &tty);
    tty.c_cc[VINTR] = saveintr;
    tty.c_iflag |= BRKINT;
    tty.c_iflag &=~ IGNBRK;
    tty.c_lflag |= ISIG;
    (void) ioctl(0, TCSETA, &tty);
#endif /* ATT */

    lasthit[0] = '\0';

    for (c = KEY_REFRESH; c != CTL('C') && c != KEY_EXIT; c = egetch())
    {
#ifdef DEBUG
      if (isprint(c))
          (void) fprintf(stderr, "command: %c\n", c);
      else if (iscntrl(c))
          (void) fprintf(stderr, "command: ^%c\n", c + '@');
      else if (c >= 0x80 & c <= 0x9f)
          (void) fprintf(stderr, "command: M-^%c\n", (c &~ 0x80) + '@');
      else if (c >= 0x80)
          (void) fprintf(stderr, "command: M-%c\n", c &~ 0x80);
      else
          (void) fprintf(stderr, "command: 0x%02x\n", c);
#endif /* DEBUG */

      switch(c)
      {
#ifdef MSDOS
      case KEY_F(10):
          (void) ungetch(KEY_EXIT);
          break;
#endif /* MSDOS */

#ifndef OLDCURSES
      case KEY_HELP:          /* go to help screen */
#endif /* OLDCURSES */
      case CTL('Q'):
#ifdef MSDOS
      case KEY_F(1):
#endif /* MSDOS */
          restore_select = TRUE;
          i = 0;
          for (hp = help_screen; *hp; hp++)
          {
            mvaddstr(i++, 0, *hp);
            addstr(nblanks(COLS - strlen(*hp)));
          }
          refresh();
          cf->dsptoppos = NOWHERE;
          waitforit(FALSE);
          /* FALL THROUGH */

#ifndef OLDCURSES
      case KEY_REFRESH: /* redraw screen */
#endif /* OLDCURSES */
      case CTL('L'):
          restore_select = TRUE;
#ifdef CURSES
          clearok(stdscr, TRUE);
#endif /* CURSES */
          break;

      case SP:
          restore_select = FALSE;
#ifndef POPUP
          if (prompt != NOMESG)
            goto medialspace;
          /* FALL THROUGH */
#endif /* POPUP */

#ifndef OLDCURSES
      case KEY_NPAGE:         /* page forward */
#endif /* OLDCURSES */
#ifdef MOUSE
      pagefwd:
#endif
          enqueue(cf);
          restore_select = FALSE;
          for (i = 0; i < LINES - OVERLAP; i++)
          {
            if (cf->toppos == cf->lastpagetoppos)
                break;
            cf->toppos = getnextln(cf->fp, cf->toppos, linbuf);
          }
          if (cf->sel.xl != NOPLACE)
          {
            cf->sel.yl -= i; cf->sel.yr -= i;
            if (!(restore_select = (cf->sel.yl >= 0)))
                cf->sel.xl = NOPLACE;
          }
          break;

#ifndef OLDCURSES
      case KEY_PPAGE:   /* page back */
#endif /* OLDCURSES */
      case CTL('U'):
#ifdef MOUSE
      pagebak:
#endif
          enqueue(cf);
          restore_select = FALSE;
          for (i = 0; i < LINES - OVERLAP; i++)
          {
            if (cf->toppos == 0)
                break;
            cf->toppos = getprevln(cf->fp, cf->toppos, linbuf);
          }
          if (cf->sel.xl != NOPLACE)
          {
            cf->sel.yl += i; cf->sel.yr += i;
            if (!(restore_select = (cf->sel.yr < LINES - 1)))
                cf->sel.xl = NOPLACE;
          }
          break;

#ifndef OLDCURSES
      case KEY_UP:
#endif /* OLDCURSES */
      case CTL('N'):
#ifdef MOUSE
linebak:
#endif
          restore_select = FALSE;
          if (cf->toppos)
          {
            cf->toppos = getprevln(cf->fp, cf->toppos, linbuf);
            if (cf->sel.xl != NOPLACE)
            {
                cf->sel.yl++; cf->sel.yr++;
                if (!(restore_select = (cf->sel.yr < LINES - 1)))
                  cf->sel.xl = NOPLACE;
            }
          }
          break;

#ifndef OLDCURSES
      case KEY_DOWN:
#endif /* OLDCURSES */
      case CTL('P'):
#ifdef MOUSE
linefwd:
#endif
          restore_select = FALSE;
          if (cf->toppos < cf->lastpagetoppos)
          {
            cf->toppos = getnextln(cf->fp, cf->toppos, linbuf);
            if (cf->sel.xl != NOPLACE)
            {
                cf->sel.yl--; cf->sel.yr--;
                if (!(restore_select = (cf->sel.yl >= 0)))
                  cf->sel.xl = NOPLACE;
            }
          }
          break;

#ifndef OLDCURSES
      case KEY_ENTER:         /* accept keyboard input */
#endif /* OLDCURSES */
      case CTL('J'):
      case CTL('M'):
          lightup = FORCED;
#ifndef POPUP
          restore_select = FALSE;
          if (prompt == SEARCH || prompt == LOOKUP)
          {
            if (!incrsearch && byname(prompt, entrybuf, '\0') == NOWHERE)
                beep();
            enterchar('\0', LOOKUP, LINES - 1, 0, PANELSTART);
            prompt = NOMESG;
            break;
          }
          /* nothing in prompt buffer, no search active: FALL THROUGH */
#endif /* POPUP */

#ifndef OLDCURSES
      case KEY_REFERENCE:     /* chase reference */
#endif /* OLDCURSES */
          restore_select = FALSE;
          if (cf->sel.xl == NOPLACE)      /* any link? */
          {
            message(NOREFS);
            continue;
          }
          else
            (void) chase(cf->sel.xl, cf->sel.yl, cf == &vhi);
          break;

#ifndef OLDCURSES
      case KEY_RIGHT:         /* next reference */
#endif /* OLDCURSES */
      case CTL('F'):
          restore_select = FALSE;
          if (cf->sel.xl == NOPLACE)
            message(NOREFS);
          else
          {
            paintselect(&cf->sel, (cf == &vhi) ? normattr : selattr);
            cf->sel = findnextsel(cf->sel.xl, cf->sel.yl, cf == &vhi);
            paintselect(&cf->sel, hiliteattr);
          }
          refresh();
          continue;

#ifndef OLDCURSES
      case KEY_LEFT:          /* previous reference */
#endif /* OLDCURSES */
      case CTL('B'):
          restore_select = FALSE;
          if (cf->sel.xl == NOPLACE)
            message(NOREFS);
          else
          {
            paintselect(&cf->sel, (cf == &vhi) ? normattr : selattr);
            /*
             * The -1 offset on xsel is important.  It insures that
             * when findprevsel() starts looking for a previous
             * tag, it doesn't find the current one again.
             */
            cf->sel = findprevsel(cf->sel.xl-1, cf->sel.yl, cf == &vhi);
            paintselect(&cf->sel, hiliteattr);
          }
          refresh();
          continue;

#ifndef OLDCURSES
      case KEY_FIND:          /* search for string */
#endif /* OLDCURSES */
      case CTL('S'):
          restore_select = FALSE;
#ifndef POPUP
          prompt = SEARCH;
#else
          if (cp = get_dialogue(SEARCH))
            if (search(cp, '\0') != NOWHERE)
            {
                get_dialogue((char *)NULL);
                break;
            }
            else
                continue;
#endif /* POPUP */
          break;

#ifndef OLDCURSES
      case KEY_SFIND:         /* search for last string again */
#endif /* OLDCURSES */
        case CTL('R'):
          restore_select = FALSE;
          if (lasthit[0] == '\0')
          {
            message(NOSRCH);
            continue;
          }
          else
#ifdef POPUP
            (void) search(lasthit, '\0');
#else
            {
                prompt = SEARCH;
                (void) strcpy(entrybuf, lasthit);
                entrycnt = strlen(lasthit);
                (void) ungetch(CTL('J'));
            }
#endif /* POPUP */
          break;

#ifndef OLDCURSES
      case KEY_SELECT:  /* search for entry */
#endif /* OLDCURSES */
      case CTL('E'):
      lookfor:
          restore_select = FALSE;
#ifndef POPUP
          prompt = LOOKUP;
#else
          if (cp = get_dialogue(LOOKUP))
          {
            if (lookup(cp, '\0') == NOWHERE)
                beep();
            get_dialogue((char *)NULL);
            break;
          }
#endif /* POPUP */
          break;

#ifndef OLDCURSES
      case KEY_HOME:          /* go to beginning of file */
      case KEY_BEG:
#endif /* OLDCURSES */
      case CTL('A'):
          enqueue(cf);
          restore_select = FALSE;
          cf->toppos = 0;
          break;

#ifndef OLDCURSES
      case KEY_END:           /* go to end of file */
#endif /* OLDCURSES */
      case CTL('O'):
          enqueue(cf);
          restore_select = FALSE;
          cf->toppos = cf->lastpagetoppos;
          break;

      case BS:          /* this is context-sensitive */
#ifndef POPUP
          if (prompt != NOMESG)
            goto medialspace;
          /* FALL THROUGH */
#endif /* POPUP */

#ifndef OLDCURSES
      case KEY_UNDO:          /* backtrack into the location stack */
#endif /* OLDCURSES */
      case ESC:
          restore_select = TRUE;
          dequeue(cf);
          break;

      case TAB:         /* toggle between text and index */
          restore_select = TRUE;
          vhi.dsptoppos = vht->dsptoppos = NOWHERE;
          if (cf == vht)
            cf = &vhi;
          else
            cf = vht;
          break;

#ifndef OLDCURSES
      case KEY_PRINT:         /* append selected entry to file */
#endif /* OLDCURSES */
      case CTL('Y'):
          restore_select = FALSE;
          (void) sprintf(linbuf, CUTFILE, execname);
          if (cf->sel.xl == NOPLACE)
          {
            message(NOREFS);
            continue;
          }
          else if ((cutfp = fopen(linbuf, "a")) == (FILE *)NULL)
          {
            message(CUTNOP);
            continue;
          }
          else
          {
            long pos;   /* find target */
            int dummy;

            message(CUTNOW);

            pos = iflink(cf->sel.xl, cf->sel.yl, &dummy,&dummy, cf == &vhi);
            if (pos >= 0)     /* how can this fail? */
                cutentry(pos, cutfp);
            (void) fclose(cutfp);

            message(CUTDNE);
            continue;
          }
          break;

#ifdef MOUSE
      /*
       * Note: we treat a single-button mouse as having the left
       * button only.
       *
       * The mouse_status() function should return the key-status defines
       * implied below when appropriate.  It should return the zero-origin
       * mouse hotspot coordinates at character-cell resolution, with
       * CMDLINE and THUMBCOL as special values designating the command
       * line and thumb column respectively (on character displays these
       * would be LINES - 1 and COLS - 1 respectively).
       */
      case KEY_MOUSE:   /* mouse gesture has occurred */
          mstat = mouse_status(&hsy, &hsx);
#ifndef POPUP
          hsy = (hsy == LINES - 1) ? CMDLINE : hsy;
#endif /* POPUP */
          switch(mstat)
          {
          case BUTTON1_PRESSED:     /* left button down; chase link, else page */
            restore_select = FALSE;
            if (hsy == CMDLINE)           /* clicked on entry line */
            {
                hsx -= PANELSTART;
                for (i = 0; i < sizeof(panel) / sizeof(strip); i++)
                  if (hsx >= panel[i].left && hsx <= panel[i].right)
                      (void) ungetch(panel[i].cmd);
                continue;
            }
            else if (hsx == THUMBCOL)     /* click in thumb column */
              {
                if (hsy == 0)       /* clicked top of bar */
                  goto linebak;
                else if (hsy == LASTLINE) /* clicked bottom of bar */
                  goto linefwd;
                else if (hsy < cf->ythumb)      /* clicked above thumb */
                  goto pagebak;
                else if (hsy > cf->ythumb)      /* clicked below thumb */
                  goto pagefwd;
                else                /* clicked on the thumb */
                  thumbdrag = TRUE;
              }
            else if (!chase(hsx, hsy, cf == &vhi))
                goto pagefwd; /* click in text somewhere */
            break;

          case BUTTON1_RELEASED:    /* left button up */
            restore_select = FALSE;
            if (thumbdrag && 0 < hsy && hsy < LASTLINE)
            {
                cf->toppos =        /* compute new position */
                  cf->lastpagetoppos *
                  (hsy - 1) / (LASTLINE - 2);

                assert(0 <= cf->toppos &&
                     cf->toppos <= cf->lastpagetoppos);

                /* ensure on line boundary */
                if (0 < cf->toppos && cf->toppos < cf->lastpagetoppos)
                  cf->toppos = getnextln(cf->fp, cf->toppos, linbuf);
            }
            thumbdrag = FALSE;                
            break;

          case BUTTON3_PRESSED:     /* right button down; backtrack */
            restore_select = TRUE;
            dequeue(cf);
            break;

          case BUTTON3_RELEASED:    /* right button up */
            /* reserved for future expansion */
            break;
          }
          break;
#endif /* MOUSE */

      default:
#ifdef POPUP
          if (isprint(c))
          {
            (void) ungetch(c);
            goto lookfor;
          }           
#else
      medialspace:
          restore_select = FALSE;
          if (prompt == NOMESG)
          {
            ungetch(c);
            goto lookfor;
          }           
          enterchar(c, prompt, LINES - 1, 0, PANELSTART);
#endif /* POPUP */
          lightup = SUPPRESS;
      }

#ifndef POPUP
      /* regenerate prompt */
      attrset(promptattr);
      move(LINES - 1, 0);
      addstr(prompt);
      addstr(entrybuf);
      addstr(nblanks(PANELSTART - strlen(prompt) - strlen(entrybuf)));
      mvaddstr(LINES - 1, PANELSTART, (cf == &vhi) ? IBANNER : TBANNER);
      attrset(normattr);
#endif /* POPUP */

      if (cf->toppos != cf->dsptoppos)
      {
          int i;
          long newpos = cf->toppos;

#ifdef DEBUG
          (void) fprintf(stderr,
                     "screen update triggered, toppos = %ld\n",
                     cf->toppos);
#endif /* DEBUG */

          attrset(normattr);
          for (i = 0; i <= LASTLINE; i++)
            if ((newpos = getnextln(cf->fp, newpos, linbuf)) != NOWHERE)
            {
                mvaddstr(i, 0, linbuf);
                /*
                 * This is actually faster, and creates less
                 * flicker, than clearing the line.
                 */
                addstr(nblanks(COLS - strlen(linbuf)));
            }
          cf->dspnextpos = ftell(cf->fp);

#ifdef SCROLLBAR
          /*
           * Generate the scroll bar.  Yes, we really do want the denominator
           * to be lastpagetoppos and not endpos.  This means the thumb only
           * bottoms out if we're really at the end.
           */
          cf->ythumb = 1 + cf->toppos * (LASTLINE - 2) / cf->lastpagetoppos;
          assert(0 < cf->ythumb && cf->ythumb < LASTLINE);
          mvaddch(0, COLS, SBARTOPCH | sbarendattr);
          for (i = 1; i < LASTLINE; i++)   /* set up scrollbar */
            mvaddch(i, COLS, SBARCH | sbarattr);
          mvaddch(LASTLINE, COLS, SBARBOTCH | sbarendattr);
          mvaddch(cf->ythumb, COLS, SBARTHUMBCH | sbthumbattr);
#endif /* SCROLLBAR */
      }

      /*
       * This has to be done whether or not lightup is going to happen.
       * Otherwise, a following command may enqueue a bogus selection
       * box as part of state.
       */
      cf->sel = findnextsel(0, -1, cf == &vhi);

      if (lightup == SUPPRESS)
          lightup = OPTIONAL;
      else 
      {
          if (incrsearch)
            (void) byname(prompt, (char *)NULL, DEL);

          if (lightup == FORCED || cf->toppos != cf->dsptoppos)
          {
            lightup = OPTIONAL;

            /* highlight the current select, if any */
            if (restore_select && cf->sel.xl != NOPLACE)
                paintselect(&cf->sel, hiliteattr); /* restore existing select */
            else
            {
                /* highlight the first selection, if there is one */
                if (cf->sel.xl != NOPLACE)
                  paintselect(&cf->sel, hiliteattr);
            }

            /* if in text, boldface all potential selects */
            if (!(cf == &vhi) && cf->sel.xl != NOPLACE)
            {
                region  hibox;

#ifdef DEBUG
                (void) fprintf(stderr,
                           "current: x = %2d, y = %2d, pos = %ld.\n",
                           cf->sel.xl, cf->sel.yl, cf->toppos);
#endif /* DEBUG */
                hibox.xl = cf->sel.xl;
                hibox.yl = cf->sel.yl;
                for (;;)
                {
                  hibox = findnextsel(hibox.xl, hibox.yl, cf == &vhi);
#ifdef DEBUG
                  (void) fprintf(stderr,
                               "hibox: x = %2d, y = %2d.\n",
                               hibox.xl, hibox.yl);
#endif /* DEBUG */
                  if (hibox.xl == cf->sel.xl && hibox.yl == cf->sel.yl)
                      break;
                  paintselect(&hibox, selattr);
                }
            }
          }
      }

      move(LINES - 1, (entrycnt != 0) * strlen(prompt) + entrycnt);

      cf->dsptoppos = cf->toppos;

      refresh();
    }

    uninitbrowse(TRUE);
}

#ifdef MSDOS
static char *basename(s)
/* isolate base name of fully qualified filename (modifies the name passed!) */
char  *s;
{
    char *p;
    p = strrchr(s, '.');      /* find rightmost dot */
    if ( strchr(p, '\\') )    /* if a \ comes after it, find string end */
      p = strchr(p, 0);
    *p = 0;             /* terminate at last dot after a \ (if any)*/
    if ( (p = strrchr(s, '\\')) == NULL ) /* find last \ (if any) */
      p = s;                  /* take whole string if no \ */
    else
      ++p;              /* else take string after \ */
    strlwr(p);
    return p;
}
#else
#define basename(x)     x     /* don't need to strip argv[0] under UNIX */
#endif /* MSDOS */

/*******************************************************************
 *
 * Main sequence
 *
 ******************************************************************/

int
main(argc, argv)
int   argc;
char  **argv;
{
    char    *dbname, *batchkey = NULL;
    int           i;
    bool    doindex = FALSE, docheck = FALSE, dofilter = FALSE;
    bool    forcemono = FALSE, atrandom = FALSE;

    execname = argv[0];

#ifdef UNIX
    /* 
     * Weird!  It seems bash under linux passes in a full pathname, even if
     * argv[0] was relative!  Foil this.
     */
    if ((dbname = strrchr(execname, '/')) != (char *)NULL)
      execname = dbname + 1;
#endif /* UNIX */

    while ((++argv, --argc) && *argv[0] == '-')
      for (i = 1; argv[0][i]; i++)
          switch(argv[0][i])
          {
          case 'b':
            batchkey = argv[1];
            break;

          case 'c':
            docheck = TRUE;
            break;

          case 'f':
            dofilter = TRUE;
            break;

          case 'g':
            doindex = TRUE;
            break;

          case 'i':
            incrsearch = TRUE;
            break;

          case 'm':
            forcemono = TRUE;
            break;

          case 'r':
            atrandom = TRUE;
            break;

#ifdef __TURBOC__
          case 's':
            {   /* if CGA that snows: */
                extern bool has_snowy_CGA;
                has_snowy_CGA = TRUE;
            }
            break;
#endif /* __TURBOC__ */

          default:
            (void) fprintf(stderr, USAGE);
            exit(1);
          }

#ifdef DEBUG
    (void) freopen("ERRLOG", "w", stderr);
#endif /* DEBUG */

    /*
     * User specified a batch-retrieval option.
     * OK, snarf the keyword from the next argument.
     */
    if (batchkey)
      ++argv, --argc;

    dbname = argc ? argv[0] : basename(execname);

    if (!doindex && !docheck && !atrandom && !dofilter
      && batchkey == (char *)NULL)
    {
      (void) printf(BRSMES, dbname);
      browse(dbname, !forcemono);
    }

    if (doindex)
    {
      (void) printf(IDXMES, dbname);
      if (argc)
          mkindex(argc, argv);
      else
          mkindex(1, &dbname);
    }

    if (docheck)
    {
      (void) printf(CHKMES, dbname);
      chkindex(dbname);
    }

    if (batchkey)
    {
      daddr_t     pos;

      if (!initbrowse(dbname))
          exit(1);

      if ((pos = xlocate(batchkey)) >= 0)
          cutentry(pos, stdout);
    }

    if (dofilter)
    {
      daddr_t     pos;
      char  batchkey[81];

      if (!initbrowse(dbname))
          exit(1);

      while (fgets(batchkey, sizeof(batchkey) - 1, stdin) != (char *)NULL)
      {
          char    *ep = &batchkey[strlen(batchkey) - 1];

          if (*ep == '\n')
          {
            *ep = '\0';
            if (*--ep == '\r')
                *ep = '\0';
          }
          if ((pos = xlocate(batchkey)) >= 0)
            cutentry(pos, stdout);
      }
    }

    if (atrandom)
    {
      daddr_t     pos;

      if (!initbrowse(dbname))
          exit(1);

      if ((pos = jrandom()) >= 0)
          cutentry(pos, stdout);
    }

    exit(0);
}

/* browse.c ends here */

Generated by  Doxygen 1.6.0   Back to index