[fa.info-vax] UNIX more for VAX/VMS

info-vax@ucbvax.ARPA (08/22/85)

From: MANAGER%UMDHEP.BITNET@WISCVM.ARPA

/*I got a lot of requests for the code, so here it is. Delete it immediately
if you don't have VAX C2.0 on VMS4.1 or higher. I stress that I am working
on it currently. Any fixes would be appreciated.--TSA*/

/*=============explode on the dotted line with your favorite nuke=========*/
/*==============I know you don't need to. It's for your own good==========*/
/*
        MORE emulates the function of the MORE utility of UNIX. It types text
much like the TYPE command, but it pauses after each screen of data. This
version is designed to work with 'CURSES'.
Author:
                Thomas Bodoh
                U.S.G.S. / EROS data center
                Mundt Federal Building
                Sioux Falls, SD  57198
                (605) 594-2271 (may change soon, try 594-6581)
Hacker:
                Todd Aven
                Softwear Sweatshop
                High Energy Physics (University of Maryland)
                College Park, MD 20742
                (301)454-3508
                MANAGER%UMDHEP.BITNET@WISCVM.ARPA or
                MANAGER@UMDHEP (on bitnet).

        MORE pauses every n lines, where n is the terminal page length.
When paused, the following commands can be typed in (commands increasing
as UNIX capability is approached):

                <space>         Advances 1 page
                <return>        Advances 1 line
                d,D             Advances half a page
                q,Q,^Z          Quits the program

        MORE does not currently handle wildcarding, but may be changed at some
future release. Other handy things which could be added are reverse and search
functions.
Hacker's note: my next project is to add the search and seek(direct
access) functions. I am working from the man page of 4.2BSD for
implementation. I doubt that I will add the editing, but I will add
the multiple/wildcarded file specifications (using scanargs). I
also plan to add the help command. Stay tuned to fa.info-vax for
future releases.--Todd Aven
*/
/*

                ===== KNOWN BUGS ======
1. Curses pads UNKNOWN type terminals with a screen of blanks at the
        end of the file. I have gotten in touch with Colorado Springs
        about this problem. It turns out that SMG$CONTROL_MODE should
        be able to prevent this, but I repeat SHOULD. (It doesn't work
        at present). I have another call in to CS.
2. Curses clears the screen at the beginning (initscr is the culprit).
        I personally would like for it not to do this. I may have to
        scrap curses for the time being and use vanilla SMG$ stuff.
3. The percentage read info is not exact. It counts the number of
        characters read and divides by the file-size obtained by
        stat(), but I don't know how stat() obtains its information.
4. The --More-- is supposed to come out in reverse-video on terminals
        that support it, but the setattr() function is not working.
        Another call is in to those vidiots at CS.

*/
#include stdio
#include descrip
#include iodef
#include curses
#include stat

#define BUFSZ 256
int Pause;
int LineLength;                 /* length of (tab expanded) line */
main(argc,argv)
        int argc;
        char *argv[];
        {
        struct stat *statbuf;   /* gets the filesize in bytes */
        int inp;                /* input file */
        char buf[BUFSZ];        /* input buffer */
        int sz;                 /* size of line read */
        int nolines;            /* number of lines left on the screen*/
        int tlines;             /* number of terminal lines this line needs */
        short response;         /* what the user typed */
        double chars_read;      /* number of characters read so far */
        double file_size;       /* size of file in bytes (includes header) */
        float percent_read;     /* percentage of file read */
        float float_percent;    /* intermediate result for percentage calc */
        short ttchan;           /* channel id for terminal */
        short getchartt();      /* gets a single character from the terminal */
        short ttopen();         /* opens TT: for getchartt */
        unsigned long int scrmod,status;
        initscr();
        if(COLS<5)COLS=72;
            nolines=LINES; /* def'd by initscr() to term page length*/
        chars_read = 0;
        if (argc > 1)
        {
          stat(argv[1],statbuf);
          file_size=statbuf->st_size;
          inp = open(argv[1],0);
        }
        else
        {
          file_size=0.0;
          inp = dup(0);
          dup2(open("/dev/tty",0),0);
        }
        if (inp == -1)
          perror(argv[1]);
        else
                {
                ttchan = ttopen();
                while ((sz = getline(inp,buf)) != 0)
                        {
                        chars_read += sz;
/* COLS is defined by initscr() */
                        tlines = (LineLength + COLS-2) /COLS ;
                        if (tlines == 0) tlines = 1;
                        nolines -= tlines;
                        if (nolines <= 0)
                          {
ask:
                            setattr(_REVERSE);
                            if(file_size>0.0)
                              {
                                float_percent = chars_read / file_size;
                                percent_read = float_percent * 100;
                                if (percent_read > 99.0)
                                  percent_read = 99.0;
                                printf("--More--(%2.0f%%)",percent_read);
                              }
                            else
                              printf("--More--");
                            clrattr(_REVERSE);
                            response = getchartt(ttchan);
                            write(1,"\r                     \r",24);
                            if (response == ' ')
                              nolines = LINES - tlines;
                            else if (response == '\n')
                              nolines = tlines;
                            else if ((response & 0x5f) == 'D')
                              nolines = (LINES-tlines)/2;
                            else if ((response & 0x5f) == 'Q')
                              exit(1);
                            else if (response == '\032')/* ^Z */
                              exit(1);
                            else
                            {
                              printf("\007");
                              goto ask;
                            }
                        }
                        write(1,buf,sz);
                        if (Pause)
                                {
                                nolines = 0;    /* pause after this line */
                                Pause = 0;
                                }
                        }
                }
        }
getline(fd,buf)
        int fd;
        char *buf;
        {
        register int linesz=0;
        register int c;
        LineLength = 1;
        while ((c=getcharacter(fd)) != -1 && c != '\n')
                {
                buf[linesz++] = c;
                if (c == '\t')
                        LineLength = (LineLength + 8) & (~7);
                else if (c == '\f')
                        {
                        Pause = 1;
                        buf[linesz-1] = '^';
                        buf[linesz++] = 'L';
                        }
                else
                        LineLength++;
                }
        if (c != -1)
                buf[linesz++] = '\n';
        return(linesz);
        }
getcharacter(fd)
        int fd;
        {
        static char buf[512];
        static int offset = 512;
        static int bufsz = 512;
        if (offset >= bufsz)
                {
                if ((bufsz=read(fd,buf,512)) == 0)
                        return(-1);
                offset = 0;
                }
        return(buf[offset++]);
        }

short getchartt(chan)
        short chan;
        {
        int status;
        short iosb[4];
        char buf[1];
        status = sys$qiow(0,chan,IO$_READLBLK | IO$M_NOECHO,
                iosb,0,0,buf,1,32767,0,0,0,0);
        if (status != 1)
                LIB$STOP(status);
        if (iosb[0] != 1)
                LIB$STOP(iosb[0]);
        if (buf[0] == '\r')
                buf[0] = '\n';  /* emulate UNIX */
        return(buf[0]);
        }

short ttopen()
        {
        short chan;
        register int status;
        struct dsc$descriptor dev_name;

        dev_name.dsc$w_length = 4;
        dev_name.dsc$a_pointer = "TT:";
        dev_name.dsc$b_class = DSC$K_CLASS_S;
        dev_name.dsc$b_dtype = DSC$K_DTYPE_T;
        status = sys$assign(&dev_name,&chan,0,0,0);
        if (status != 1)
                LIB$STOP(status);
        return(chan);
        }