From 784e3de7cdef8499d293025a579b9e64aaede0c9 Mon Sep 17 00:00:00 2001 From: gdr-ftp Date: Mon, 9 Mar 1998 08:30:21 +0000 Subject: [PATCH] Initial checkin of aroff, binprint, center, less, ls, make, makemake, passwd, ps, purge, shutdown, stty, upper, and vi. These sources are for the versions of the utils shipped with GNO v2.0.4. --- bin/aroff/Makefile | 13 + bin/aroff/aroff.c | 160 ++++ bin/aroff/aroff.h | 57 ++ bin/aroff/awgs.c | 160 ++++ bin/aroff/awgs.h | 57 ++ bin/aroff/print.c | 157 ++++ bin/binprint/Makefile | 8 + bin/binprint/binprint.asm | 123 +++ bin/binprint/binprint.c | 107 +++ bin/binprint/doline.asm | 123 +++ bin/center/Makefile | 5 + bin/center/center.c | 133 +++ bin/less/CHANGES | 71 ++ bin/less/Info | 16 + bin/less/Makefile | 104 +++ bin/less/brac.c | 94 ++ bin/less/ch.c | 576 ++++++++++++ bin/less/charset.c | 212 +++++ bin/less/cmd.h | 59 ++ bin/less/cmdbuf.c | 145 +++ bin/less/command.c | 1203 +++++++++++++++++++++++++ bin/less/decode.c | 373 ++++++++ bin/less/defines.h | 134 +++ bin/less/edit.c | 461 ++++++++++ bin/less/filename.c | 369 ++++++++ bin/less/forwback.c | 380 ++++++++ bin/less/gsos.c | 32 + bin/less/help.c | 58 ++ bin/less/ifile.c | 199 ++++ bin/less/input.c | 270 ++++++ bin/less/jump.c | 267 ++++++ bin/less/less.1 | 946 +++++++++++++++++++ bin/less/less.h | 123 +++ bin/less/less.hlp | 104 +++ bin/less/lesskey.c | 361 ++++++++ bin/less/line.c | 540 +++++++++++ bin/less/linenum.c | 452 ++++++++++ bin/less/linkscr | 33 + bin/less/lsystem.c | 323 +++++++ bin/less/main.c | 293 ++++++ bin/less/mark.c | 245 +++++ bin/less/note | 2 + bin/less/optfunc.c | 381 ++++++++ bin/less/option.c | 505 +++++++++++ bin/less/option.h | 38 + bin/less/opttbl.c | 265 ++++++ bin/less/os.c | 143 +++ bin/less/output.c | 440 +++++++++ bin/less/position.c | 212 +++++ bin/less/position.h | 8 + bin/less/prompt.c | 444 +++++++++ bin/less/proto.h | 232 +++++ bin/less/screen.c | 712 +++++++++++++++ bin/less/search.c | 360 ++++++++ bin/less/signal.c | 242 +++++ bin/less/tags.c | 179 ++++ bin/less/termcap | 41 + bin/less/ttyin.c | 86 ++ bin/less/version.c | 320 +++++++ bin/ls/Makefile | 8 + bin/ls/itqsort.c | 156 ++++ bin/ls/ls.c | 586 ++++++++++++ bin/make/build.make | 2 + bin/make/make.c | 492 ++++++++++ bin/makemake/makemake.c | 82 ++ bin/passwd/Makefile | 7 + bin/passwd/passwd.1 | 51 ++ bin/passwd/passwd.c | 209 +++++ bin/ps/ps.1 | 46 + bin/ps/ps.c | 350 +++++++ bin/purge/purge.asm | 206 +++++ bin/purge/purge.mac | 361 ++++++++ bin/stty/Makefile | 6 + bin/stty/stty.c | 302 +++++++ bin/upper/Makefile | 2 + bin/upper/upper.c | 22 + bin/vi/Makefile | 74 ++ bin/vi/README | 72 ++ bin/vi/README.gno | 80 ++ bin/vi/TAGS | 161 ++++ bin/vi/TODO | 38 + bin/vi/alloc.c | 211 +++++ bin/vi/ascii.h | 29 + bin/vi/bug2.c | 1683 ++++++++++++++++++++++++++++++++++ bin/vi/bugstevie.h | 346 +++++++ bin/vi/charset.c | 374 ++++++++ bin/vi/cmdline.c | 829 +++++++++++++++++ bin/vi/dec.c | 31 + bin/vi/edit.c | 412 +++++++++ bin/vi/env.h | 70 ++ bin/vi/fileio.c | 381 ++++++++ bin/vi/format.l.c | 81 ++ bin/vi/gsos.c | 287 ++++++ bin/vi/gsos.h | 16 + bin/vi/help.c | 321 +++++++ bin/vi/inc.c | 32 + bin/vi/keymap.h | 51 ++ bin/vi/linefunc.c | 85 ++ bin/vi/linkscr | 24 + bin/vi/macros.h | 82 ++ bin/vi/main.c | 375 ++++++++ bin/vi/make.tags | 8 + bin/vi/mark.c | 129 +++ bin/vi/misccmds.c | 425 +++++++++ bin/vi/mk.c | 48 + bin/vi/normal.c | 1804 +++++++++++++++++++++++++++++++++++++ bin/vi/param.c | 161 ++++ bin/vi/param.h | 59 ++ bin/vi/regexp.c | 1318 +++++++++++++++++++++++++++ bin/vi/regexp.h | 36 + bin/vi/regmagic.h | 16 + bin/vi/regsub.c | 111 +++ bin/vi/s.io.c | 485 ++++++++++ bin/vi/screen.c | 148 +++ bin/vi/search.c | 1028 +++++++++++++++++++++ bin/vi/source.doc | 114 +++ bin/vi/stevie.doc | 532 +++++++++++ bin/vi/stevie.h | 345 +++++++ bin/vi/term.h | 170 ++++ bin/vi/test | 71 ++ bin/vi/version.c | 186 ++++ 121 files changed, 30383 insertions(+) create mode 100644 bin/aroff/Makefile create mode 100644 bin/aroff/aroff.c create mode 100644 bin/aroff/aroff.h create mode 100644 bin/aroff/awgs.c create mode 100644 bin/aroff/awgs.h create mode 100644 bin/aroff/print.c create mode 100644 bin/binprint/Makefile create mode 100644 bin/binprint/binprint.asm create mode 100644 bin/binprint/binprint.c create mode 100644 bin/binprint/doline.asm create mode 100644 bin/center/Makefile create mode 100644 bin/center/center.c create mode 100644 bin/less/CHANGES create mode 100644 bin/less/Info create mode 100644 bin/less/Makefile create mode 100644 bin/less/brac.c create mode 100644 bin/less/ch.c create mode 100644 bin/less/charset.c create mode 100644 bin/less/cmd.h create mode 100644 bin/less/cmdbuf.c create mode 100644 bin/less/command.c create mode 100644 bin/less/decode.c create mode 100644 bin/less/defines.h create mode 100644 bin/less/edit.c create mode 100644 bin/less/filename.c create mode 100644 bin/less/forwback.c create mode 100644 bin/less/gsos.c create mode 100644 bin/less/help.c create mode 100644 bin/less/ifile.c create mode 100644 bin/less/input.c create mode 100644 bin/less/jump.c create mode 100644 bin/less/less.1 create mode 100644 bin/less/less.h create mode 100644 bin/less/less.hlp create mode 100644 bin/less/lesskey.c create mode 100644 bin/less/line.c create mode 100644 bin/less/linenum.c create mode 100755 bin/less/linkscr create mode 100644 bin/less/lsystem.c create mode 100644 bin/less/main.c create mode 100644 bin/less/mark.c create mode 100644 bin/less/note create mode 100644 bin/less/optfunc.c create mode 100644 bin/less/option.c create mode 100644 bin/less/option.h create mode 100644 bin/less/opttbl.c create mode 100644 bin/less/os.c create mode 100644 bin/less/output.c create mode 100644 bin/less/position.c create mode 100644 bin/less/position.h create mode 100644 bin/less/prompt.c create mode 100644 bin/less/proto.h create mode 100644 bin/less/screen.c create mode 100644 bin/less/search.c create mode 100644 bin/less/signal.c create mode 100644 bin/less/tags.c create mode 100644 bin/less/termcap create mode 100644 bin/less/ttyin.c create mode 100644 bin/less/version.c create mode 100644 bin/ls/Makefile create mode 100644 bin/ls/itqsort.c create mode 100644 bin/ls/ls.c create mode 100755 bin/make/build.make create mode 100644 bin/make/make.c create mode 100644 bin/makemake/makemake.c create mode 100644 bin/passwd/Makefile create mode 100644 bin/passwd/passwd.1 create mode 100644 bin/passwd/passwd.c create mode 100644 bin/ps/ps.1 create mode 100644 bin/ps/ps.c create mode 100644 bin/purge/purge.asm create mode 100644 bin/purge/purge.mac create mode 100644 bin/stty/Makefile create mode 100644 bin/stty/stty.c create mode 100644 bin/upper/Makefile create mode 100644 bin/upper/upper.c create mode 100644 bin/vi/Makefile create mode 100644 bin/vi/README create mode 100644 bin/vi/README.gno create mode 100644 bin/vi/TAGS create mode 100644 bin/vi/TODO create mode 100644 bin/vi/alloc.c create mode 100644 bin/vi/ascii.h create mode 100644 bin/vi/bug2.c create mode 100644 bin/vi/bugstevie.h create mode 100644 bin/vi/charset.c create mode 100644 bin/vi/cmdline.c create mode 100644 bin/vi/dec.c create mode 100644 bin/vi/edit.c create mode 100644 bin/vi/env.h create mode 100644 bin/vi/fileio.c create mode 100644 bin/vi/format.l.c create mode 100644 bin/vi/gsos.c create mode 100644 bin/vi/gsos.h create mode 100644 bin/vi/help.c create mode 100644 bin/vi/inc.c create mode 100644 bin/vi/keymap.h create mode 100644 bin/vi/linefunc.c create mode 100755 bin/vi/linkscr create mode 100644 bin/vi/macros.h create mode 100644 bin/vi/main.c create mode 100755 bin/vi/make.tags create mode 100644 bin/vi/mark.c create mode 100644 bin/vi/misccmds.c create mode 100644 bin/vi/mk.c create mode 100644 bin/vi/normal.c create mode 100644 bin/vi/param.c create mode 100644 bin/vi/param.h create mode 100644 bin/vi/regexp.c create mode 100644 bin/vi/regexp.h create mode 100644 bin/vi/regmagic.h create mode 100644 bin/vi/regsub.c create mode 100644 bin/vi/s.io.c create mode 100644 bin/vi/screen.c create mode 100644 bin/vi/search.c create mode 100644 bin/vi/source.doc create mode 100644 bin/vi/stevie.doc create mode 100644 bin/vi/stevie.h create mode 100644 bin/vi/term.h create mode 100644 bin/vi/test create mode 100644 bin/vi/version.c diff --git a/bin/aroff/Makefile b/bin/aroff/Makefile new file mode 100644 index 0000000..0235a6d --- /dev/null +++ b/bin/aroff/Makefile @@ -0,0 +1,13 @@ +# +# Makefile for aroff +# AWGS WordProc -> Text formatter +# + +awgs.root: awgs.c awgs.h + compile awgs.c keep=awgs + +print.root: print.c awgs.h + compile print.c keep=print + +awgs: awgs.root + link awgs print 2/direct256 keep=aroff diff --git a/bin/aroff/aroff.c b/bin/aroff/aroff.c new file mode 100644 index 0000000..23c21b4 --- /dev/null +++ b/bin/aroff/aroff.c @@ -0,0 +1,160 @@ +/* + + awgs.c + + Main loop driver code and awgs wordproc file read routines + +*/ + +#pragma stacksize 2048 +#include +#include +#include +#include +#include +#include + +#include "awgs.h" + +void fileError(char *s) +{ +int err; +int cl[2]; + + if (err = toolerror()) { + fprintf(stderr,"%s\n",s); + ERROR(&err); + cl[0] = 1; cl[1] = 0; CloseGS(cl); + exit(1); + } +} + +int noboldflag = 0; +saveArray *docSaveArray; +Ruler *docRulers; +textBlock **docTextBlocks; /* an array of textBlockPtrs */ +word docSACount, numRulers, numBlocks; + +void readAWGS(char *file) +{ +int ref,err,z; /* refnum, err temp, and loop variable */ +long recBlockSize; /* size of the upcoming text block */ +int cl[2]; /* pBlock for GS/OS CloseGS call */ +GSString255 f; /* converted cstring(file) -> GSString */ +OpenRecGS o; /* pBlock for GS/OS Open call */ +SetPositionRecGS p; +IORecGS i; + + f.length = strlen(file); + strcpy(f.text,file); + o.pCount = 7; + o.pathname = &f; + o.requestAccess = readEnable; + o.resourceNumber = 0; + OpenGS(&o); + if (err = toolerror()) { + fprintf(stderr,"aroff: could not open AWGS file %s\n",file); + ERROR(&err); + exit(1); + } + ref = o.refNum; + if ((o.fileType != 0x50) || (o.auxType != 0x8010l)) { + cl[0] = 1; cl[1] = ref; CloseGS(cl); + fprintf(stderr,"aroff: file (%s) is not an AWGS file\n",file); + exit(1); + } + p.pCount = 3; p.refNum = ref; p.base = startPlus; p.displacement = 668l; + SetMarkGS(&p); fileError("SetMarkGS"); + + i.pCount = 4; + i.refNum = ref; + i.dataBuffer = (void *) &docSACount; + i.requestCount = 2l; + ReadGS(&i); fileError("ReadGS (docSACount)"); + +#ifdef DEBUG +fprintf(stderr,"Number of SaveArray entries: %d\n",docSACount); +#endif + + docSaveArray = calloc((size_t) docSACount, sizeof(saveArray)); + i.dataBuffer = (void *) docSaveArray; + i.requestCount = sizeof(saveArray) * docSACount; + ReadGS(&i); fileError("ReadGS (docSaveArray)"); + +#ifdef DEBUG +fprintf(stderr," saNum textBlock rulerNum\n"); +fprintf(stderr," ----- --------- --------\n"); +for (z = 0; z < docSACount; z++) { + fprintf(stderr," [%3d] %5d %5d\n", + z+1, docSaveArray[z].textBlock,docSaveArray[z].rulerNum); +} +#endif + + numRulers = numBlocks = 0; + for (z = 0; z < docSACount; z++) { + if (docSaveArray[z].rulerNum+1 > numRulers) + numRulers = docSaveArray[z].rulerNum+1; + if (docSaveArray[z].textBlock+1 > numBlocks) + numBlocks = docSaveArray[z].textBlock+1; + } +#ifdef DEBUG +fprintf(stderr,"Number of Rulers: %d\n",numRulers); +fprintf(stderr,"Number of Blocks: %d\n",numBlocks); +#endif + + docRulers = calloc((size_t) numRulers, sizeof(Ruler)); + i.dataBuffer = (void *) docRulers; + i.requestCount = sizeof(Ruler) * numRulers; + ReadGS(&i); fileError("ReadGS (docRulers)"); + + docTextBlocks = calloc((size_t) numBlocks, sizeof(textBlockPtr)); + for (z = 0; z < numBlocks; z++) { + i.requestCount = 4l; + i.dataBuffer = (void *) &recBlockSize; + ReadGS(&i); fileError("ReadGS (recBlockSize)"); + +#ifdef DEBUG +fprintf(stderr,"block %d size %8ld : ",z,recBlockSize); +#endif + docTextBlocks[z] = malloc(recBlockSize); + i.requestCount = recBlockSize; + i.dataBuffer = (void *) docTextBlocks[z]; + ReadGS(&i); fileError("ReadGS (textBlock)"); + } + + cl[0] = 1; + cl[1] = ref; + CloseGS(cl); +} + +void usage(void) +{ + fprintf(stderr,"aroff [-b] file1 [file ...]\n" + "-b don't do any boldfacing (useful for GNO Ref. Manuals)\n"); + exit(1); +} + +int main(int argc, char *argv[]) +{ +int i,z; +extern void printAWGS(void); +extern int _INITGNOSTDIO(); + + _INITGNOSTDIO(); + + if (argc == 1) usage(); + for (i = 1; i < argc; i++) { + if (*argv[i] == '-') { + if (argv[i][1] == 'b') + { noboldflag = 1; continue; } + else usage(); + } + readAWGS(argv[i]); + printAWGS(); + free(docSaveArray); + free(docRulers); + for (z = 0; z < numBlocks; z++) + free(docTextBlocks[z]); + free(docTextBlocks); + } +} diff --git a/bin/aroff/aroff.h b/bin/aroff/aroff.h new file mode 100644 index 0000000..f3a7d54 --- /dev/null +++ b/bin/aroff/aroff.h @@ -0,0 +1,57 @@ +/* + This file contains the data structures that are + used in AWGS Word Processor files. + + Data structures gleaned from DTS File Type Note TN.50.8010 +*/ + +/* #define DEBUG */ + +typedef struct pgraph { + word firstFont; + byte firstStyle; + byte firstSize; + byte firstColor; + word reserved; +} pgraph, *pgraphPtr; + +typedef struct textBlock { + word blockSize; + word blockUsed; + pgraphPtr pgraphs; +} textBlock, *textBlockPtr; + +typedef struct tabRec { + word tabLocation; + word tabType; +} tabRec, *tabRecPtr; + +#define rsFULL 0x80 +#define rsRIGHT 0x40 +#define rsCENTER 0x20 +#define rsLEFT 0x10 +#define rsNOBREAK 0x08 +#define rsTRIPLE 0x04 +#define rsDOUBLE 0x02 +#define rsSINGLE 0x01 + +typedef struct Ruler { + word numParagraphs; + word statusBits; + word leftMargin; + word indentMargin; + word rightMargin; + word numTabs; + tabRec tabRecs[10]; +} Ruler, *RulerPtr; + +typedef struct SaveArrEntry { + word textBlock; /* Text block number */ + word offset; /* offset + text block = paragraph */ + word attributes; /* 0 = normal text, 1 = page break paragrf */ + word rulerNum; /* #of ruler associated with this paragrf */ + word pixelHeight; /* height of paragraph in pixels */ + word numLines; /* # of lines in this paragraph */ +} saveArray, *saveArrayPtr; + +extern int noboldflag; diff --git a/bin/aroff/awgs.c b/bin/aroff/awgs.c new file mode 100644 index 0000000..23c21b4 --- /dev/null +++ b/bin/aroff/awgs.c @@ -0,0 +1,160 @@ +/* + + awgs.c + + Main loop driver code and awgs wordproc file read routines + +*/ + +#pragma stacksize 2048 +#include +#include +#include +#include +#include +#include + +#include "awgs.h" + +void fileError(char *s) +{ +int err; +int cl[2]; + + if (err = toolerror()) { + fprintf(stderr,"%s\n",s); + ERROR(&err); + cl[0] = 1; cl[1] = 0; CloseGS(cl); + exit(1); + } +} + +int noboldflag = 0; +saveArray *docSaveArray; +Ruler *docRulers; +textBlock **docTextBlocks; /* an array of textBlockPtrs */ +word docSACount, numRulers, numBlocks; + +void readAWGS(char *file) +{ +int ref,err,z; /* refnum, err temp, and loop variable */ +long recBlockSize; /* size of the upcoming text block */ +int cl[2]; /* pBlock for GS/OS CloseGS call */ +GSString255 f; /* converted cstring(file) -> GSString */ +OpenRecGS o; /* pBlock for GS/OS Open call */ +SetPositionRecGS p; +IORecGS i; + + f.length = strlen(file); + strcpy(f.text,file); + o.pCount = 7; + o.pathname = &f; + o.requestAccess = readEnable; + o.resourceNumber = 0; + OpenGS(&o); + if (err = toolerror()) { + fprintf(stderr,"aroff: could not open AWGS file %s\n",file); + ERROR(&err); + exit(1); + } + ref = o.refNum; + if ((o.fileType != 0x50) || (o.auxType != 0x8010l)) { + cl[0] = 1; cl[1] = ref; CloseGS(cl); + fprintf(stderr,"aroff: file (%s) is not an AWGS file\n",file); + exit(1); + } + p.pCount = 3; p.refNum = ref; p.base = startPlus; p.displacement = 668l; + SetMarkGS(&p); fileError("SetMarkGS"); + + i.pCount = 4; + i.refNum = ref; + i.dataBuffer = (void *) &docSACount; + i.requestCount = 2l; + ReadGS(&i); fileError("ReadGS (docSACount)"); + +#ifdef DEBUG +fprintf(stderr,"Number of SaveArray entries: %d\n",docSACount); +#endif + + docSaveArray = calloc((size_t) docSACount, sizeof(saveArray)); + i.dataBuffer = (void *) docSaveArray; + i.requestCount = sizeof(saveArray) * docSACount; + ReadGS(&i); fileError("ReadGS (docSaveArray)"); + +#ifdef DEBUG +fprintf(stderr," saNum textBlock rulerNum\n"); +fprintf(stderr," ----- --------- --------\n"); +for (z = 0; z < docSACount; z++) { + fprintf(stderr," [%3d] %5d %5d\n", + z+1, docSaveArray[z].textBlock,docSaveArray[z].rulerNum); +} +#endif + + numRulers = numBlocks = 0; + for (z = 0; z < docSACount; z++) { + if (docSaveArray[z].rulerNum+1 > numRulers) + numRulers = docSaveArray[z].rulerNum+1; + if (docSaveArray[z].textBlock+1 > numBlocks) + numBlocks = docSaveArray[z].textBlock+1; + } +#ifdef DEBUG +fprintf(stderr,"Number of Rulers: %d\n",numRulers); +fprintf(stderr,"Number of Blocks: %d\n",numBlocks); +#endif + + docRulers = calloc((size_t) numRulers, sizeof(Ruler)); + i.dataBuffer = (void *) docRulers; + i.requestCount = sizeof(Ruler) * numRulers; + ReadGS(&i); fileError("ReadGS (docRulers)"); + + docTextBlocks = calloc((size_t) numBlocks, sizeof(textBlockPtr)); + for (z = 0; z < numBlocks; z++) { + i.requestCount = 4l; + i.dataBuffer = (void *) &recBlockSize; + ReadGS(&i); fileError("ReadGS (recBlockSize)"); + +#ifdef DEBUG +fprintf(stderr,"block %d size %8ld : ",z,recBlockSize); +#endif + docTextBlocks[z] = malloc(recBlockSize); + i.requestCount = recBlockSize; + i.dataBuffer = (void *) docTextBlocks[z]; + ReadGS(&i); fileError("ReadGS (textBlock)"); + } + + cl[0] = 1; + cl[1] = ref; + CloseGS(cl); +} + +void usage(void) +{ + fprintf(stderr,"aroff [-b] file1 [file ...]\n" + "-b don't do any boldfacing (useful for GNO Ref. Manuals)\n"); + exit(1); +} + +int main(int argc, char *argv[]) +{ +int i,z; +extern void printAWGS(void); +extern int _INITGNOSTDIO(); + + _INITGNOSTDIO(); + + if (argc == 1) usage(); + for (i = 1; i < argc; i++) { + if (*argv[i] == '-') { + if (argv[i][1] == 'b') + { noboldflag = 1; continue; } + else usage(); + } + readAWGS(argv[i]); + printAWGS(); + free(docSaveArray); + free(docRulers); + for (z = 0; z < numBlocks; z++) + free(docTextBlocks[z]); + free(docTextBlocks); + } +} diff --git a/bin/aroff/awgs.h b/bin/aroff/awgs.h new file mode 100644 index 0000000..f3a7d54 --- /dev/null +++ b/bin/aroff/awgs.h @@ -0,0 +1,57 @@ +/* + This file contains the data structures that are + used in AWGS Word Processor files. + + Data structures gleaned from DTS File Type Note TN.50.8010 +*/ + +/* #define DEBUG */ + +typedef struct pgraph { + word firstFont; + byte firstStyle; + byte firstSize; + byte firstColor; + word reserved; +} pgraph, *pgraphPtr; + +typedef struct textBlock { + word blockSize; + word blockUsed; + pgraphPtr pgraphs; +} textBlock, *textBlockPtr; + +typedef struct tabRec { + word tabLocation; + word tabType; +} tabRec, *tabRecPtr; + +#define rsFULL 0x80 +#define rsRIGHT 0x40 +#define rsCENTER 0x20 +#define rsLEFT 0x10 +#define rsNOBREAK 0x08 +#define rsTRIPLE 0x04 +#define rsDOUBLE 0x02 +#define rsSINGLE 0x01 + +typedef struct Ruler { + word numParagraphs; + word statusBits; + word leftMargin; + word indentMargin; + word rightMargin; + word numTabs; + tabRec tabRecs[10]; +} Ruler, *RulerPtr; + +typedef struct SaveArrEntry { + word textBlock; /* Text block number */ + word offset; /* offset + text block = paragraph */ + word attributes; /* 0 = normal text, 1 = page break paragrf */ + word rulerNum; /* #of ruler associated with this paragrf */ + word pixelHeight; /* height of paragraph in pixels */ + word numLines; /* # of lines in this paragraph */ +} saveArray, *saveArrayPtr; + +extern int noboldflag; diff --git a/bin/aroff/print.c b/bin/aroff/print.c new file mode 100644 index 0000000..3ca0737 --- /dev/null +++ b/bin/aroff/print.c @@ -0,0 +1,157 @@ +/* + print.c + + the code that formats each individual paragraph is here. + +*/ + +#include +#include +#include +#include +#include + +#include "awgs.h" + +extern saveArray *docSaveArray; +extern Ruler *docRulers; +extern textBlock **docTextBlocks; /* an array of textBlockPtrs */ +extern word docSACount, numRulers, numBlocks; + +/* + 1 2 +01234567890123456 7890 +This is a bloody ^car +z = 16; + +This is a bloody +^car + +col=4 (width - z - 1) +printcol=3 +*/ + +/* 100 lines should be more than enough */ +char paraBuf[100][80]; + + +/* given a paragraph, and a pointer to it's ruler, format the + paragraph according to the ruler. */ +void printPara(RulerPtr ruler, pgraphPtr pptr) +{ +char *txt; +int width,z; +int curLine, col, printcol, numctrl; +int i,left,style; + + curLine = col = printcol = 0; + txt = ((char *)pptr) + sizeof(pgraph); + +calcLine: + /* width determines how long this line is in characters; thus, where + the break for word wrap will occur */ + if (curLine == 0) width = (ruler->rightMargin - ruler->indentMargin)/8; + else width = (ruler->rightMargin - ruler->leftMargin)/8; + + while (*txt != 0x0d) { + switch (*txt) { + case 1: txt+=3; break; + case 2: + style = *(++txt); + if (noboldflag) { ++txt; break; }/* turn off boldfacing */ + if (style & 3) paraBuf[curLine][col++] = 15; + else paraBuf[curLine][col++] = 14; + txt++; + break; + case 3: txt+=2; break; + case 4: txt+=2; break; + case 5: + case 6: + case 7: break; + + default: + if (printcol == width) { + numctrl = 0; + for (z = col - 1; z > 0; z--) { + if (paraBuf[curLine][z] == ' ') { + if (z != col - 1) + memcpy(¶Buf[curLine+1][0],¶Buf[curLine][z+1], + (size_t) (col - z - 1)); + paraBuf[curLine][z] = 0; + curLine++; printcol -= (z + 1 + numctrl); + col -= (z + 1); + goto calcLine; + } + else if (paraBuf[curLine][z] < ' ') numctrl++; + } + curLine++; col = printcol = 0; + /* one big word... don't break line */ + goto calcLine; + } + paraBuf[curLine][col] = *(txt++); + printcol++; col++; + } + } + paraBuf[curLine][col] = 0; + for (z = 0; z <= curLine; z++) { + if (z == 0) { + width = (ruler->rightMargin - ruler->indentMargin)/8; + left = (ruler->indentMargin)/8; + } + else { + width = (ruler->rightMargin - ruler->leftMargin)/8; + left = (ruler->leftMargin)/8; + } + for (i = 0; i < left; i++) putchar(' '); + printf("%s\n",paraBuf[z]); + } +} + +/* this is an obsolete routine that prints a paragraph with no + formatting at all */ +#ifdef NOTDEFINED +void printPara(RulerPtr ruler, pgraphPtr pptr) +{ +char *txt; + + txt = ((char *)pptr) + sizeof(pgraph); + while (*txt != 0x0D) { + switch (*txt) { + case 1: txt+=2; break; + case 2: txt++; break; + case 3: txt++; break; + case 4: txt++; break; + case 5: + case 6: + case 7: break; + + default: putchar(*txt); + } + txt++; + } + putchar('\n'); +} +#endif + + +/* go through each textBlock, sending each paragraph in turn to printPara. */ + +void printAWGS(void) +{ +int z; +pgraphPtr pptr; +char *txt; +char x; + + for (z = 0; z < docSACount; z++) { + pptr = (pgraphPtr) (((byte *)docTextBlocks[docSaveArray[z].textBlock]) + + docSaveArray[z].offset); + +#ifdef DEBUG +fprintf(stderr,"[%d] offset %d paragraph : %08lX",z,docSaveArray[z].offset, + pptr); +fprintf(stderr," textBlock: %08lX\n",docTextBlocks[docSaveArray[z].textBlock]); +#endif + printPara(&docRulers[docSaveArray[z].rulerNum],pptr); + } +} diff --git a/bin/binprint/Makefile b/bin/binprint/Makefile new file mode 100644 index 0000000..4884a1d --- /dev/null +++ b/bin/binprint/Makefile @@ -0,0 +1,8 @@ +binprint.a: binprint.c + compile -p binprint.c keep=binprint + +bprasm.a: binprint.asm + compile -p binprint.asm + +binprint: binprint.a bprasm.a + link -p binprint bprasm keep=binprint diff --git a/bin/binprint/binprint.asm b/bin/binprint/binprint.asm new file mode 100644 index 0000000..0f48c44 --- /dev/null +++ b/bin/binprint/binprint.asm @@ -0,0 +1,123 @@ + case on + + keep bprasm + +doline start + pha + tsc + phd + tcd + phb + phk + plb + +counter equ 1 +RTLs equ 3 +dest equ 6 +source equ 10 +cols equ 14 +actual equ 16 +PREVIOUS equ 18 + + pei actual + stz counter + ldy #0 +loop anop + lda [source],y + jsr puthex + iny + dec cols + dec actual + bne loop + ldy counter +loop2 lda cols + beq done + lda #$2020 + sta [dest],y + iny + sta [dest],y + iny + iny + dec cols + bra loop2 + +done anop + pla + sta actual + tya + clc + inc a + adc actual + sta counter + ldy actual +chrloop dey + cpy #$ffff + beq endloop + lda [source],y + and #$ff + cmp #$7f + bcs noprint + cmp #$20 + bcs printable +noprint lda #'.' +printable sep #$20 + sta [source],y + rep #$20 + bra chrloop +endloop ldy actual + dey + lda [source],y + and #$00ff + ora #$0d00 + sta [source],y + + ldy counter + plb + lda 4 + sta PREVIOUS-2 + lda 3 + sta PREVIOUS-3 + pld + tsc + clc + adc #PREVIOUS-4 + tcs + tya + rtl + end + +puthex private +counter equ 1 +RTLs equ 3 +dest equ 6 +source equ 10 +cols equ 14 + phy + ldy counter + pha + lsr a + lsr a + lsr a + lsr a + and #$f + tax + lda hexdigits,x + sta [ +#include +#include +#include +#include + +extern FILE *fdopen(int,char *); + +unsigned int doline(char *dest, char *source, + unsigned int actual, unsigned int cols); + +unsigned char *buffer2; + +main(argc,argv) +int argc; +char **argv; +{ +int duh; +int a; +int c,errflg = 0,columns = 16; +size_t pos = 0; +unsigned char *buffer; +extern char *optarg; +extern int optind; +extern int getopt(int,char **,char*); + + while ((c = getopt(argc,argv, "Vc:")) != EOF) + switch (c) { + case 'c' : + columns = atoi(optarg); + break; + case 'V' : + fprintf(stdout, "binprint v1.2 for GNO/ME\n"); + exit(0); + default : errflg++; + } + if (errflg) { + fprintf(stderr,"usage: binprint [-c] files...\n"); + exit(2); + } + argv += optind; + if (columns < 8) columns = 8; + if ((buffer2 = (unsigned char *)malloc((size_t)(columns*4)+1)) == NULL) { + fprintf(stderr,"Cannot allocate buffer space\n"); + exit(1); + } + buffer = (unsigned char *)(buffer2+(columns*3)); + if (optind == argc) { + duh = STDIN_FILENO; + goto action; + } + for (;optind +#include +#define VERSION "1.00" + +char input[81]; +char output[81]; + +main(argc, argv) +int argc; +char **argv; +{ + int t, i, x, s, fflag; + FILE *in; + +/* _INITGNOSTDIO(); + setvbuf(stdin,NULL,_IOLBF,256l); */ + if (argc > 3) + usage(argv[0]); + + s = 1; + + if(argc > 1) { + for(x = 0 ; x <= (strlen(argv[1])-1) ; x++) { + s = isdigit(argv[1][x]); + t = atoi(argv[1]); + } + } + + else if (argc == 1) + t = 80; + + fflag = 0; + + if (argc == 3) { + in = fopen(argv[2], "r"); + if(!in) error(argv[1], argv[2]); + fflag = 1; + } + + if(s != 0) { + if(fflag == 1) + center(in, t); + else + center(stdin, t); + } + + else + usage(argv[0]); +} + +/* Function to call on other subroutines to result in a completely + centered line! */ + +center(stream, t) +FILE *stream; +int t; +{ + int x, i; + + while(feof(stream) == 0) { + fgets(input, 80, stream); + + i = (t - strlen(input)) / 2; + + fillit(i, 0); + + for(x = 0 ; x <= strlen(input) ; x++) + output[i+x] = input[x]; + if(feof(stream) == 0) + fputs(output, stdout); + } +} + +/* Function to tell the person that the filename offered + not be opened for reading */ + +error(name, file) +char *name; +char *file; +{ + fprintf(stderr, "%s: cannot open %s\n", name, file); + exit(0); +} + +/* Function to display the usage of the utility */ + +usage(file) +char *file; +{ + fprintf(stderr, "Usage: %s [columns] [file]\n", file); + exit(0); +} + +/* Function to fill in the left side of text with the appropriate + number of spaces */ + +fillit(ntf, start) +int ntf; /* Number To Fill */ +int start; /* Start filling at.. */ +{ + int x; + + for(x = start ; x <= ntf ; x++) { + output[x] = ' '; + } +} diff --git a/bin/less/CHANGES b/bin/less/CHANGES new file mode 100644 index 0000000..97bd380 --- /dev/null +++ b/bin/less/CHANGES @@ -0,0 +1,71 @@ +Well, first off: + +we are: Mike Horwath + James Brookes + Dave Huang + +5/12/92 +Jawaid bitched about some stack overflowing, so, between us both, we searched +diligently for the error. Inside one of the functions, there was a stack +allocated variable over 2K in size. So, the fix was to make it a static +variable, so that it is gloabally allocated from normal memory, instead of the +stack. That error is now fixed. + +Jawaid also has fixed the getenv() library call, and that was the problem with +using a $editor variable in LESS. That is now fixed. + +Less now also works with either files terminated with lf's (unix normal) or +cr's (Apple IIgs normal). Works GREAT. + +Fixed a bug where pipes would cause a problem if you shelled out '!' (alone), +added GS/OS specific code to take care of which stdin to use, either the pipe +or normal stuff... + +5/11/92 +Well, with big help from Jawaid "The God" Bazyar, piping and redirection are +now working correctly. Problems stemmed from file descriptors being set to +numbers that were not correct, meaning that, it was trying to write to the +input of a pipe or stdin. + +JB put assembly into the putchr() and putstr() calls, noticable speed increase +and some savings on disk. + +It prints the control codes by default, so that when you use man, it will do +things like inverse and the like correctly. + +did a #pragma noroot so that it cuts off about 1K of exec file. Kind of nice +of jawaid to tell me this. Nice little savings I must say. + +Reset the stacksize from 4.5K to 1K, as I was wrong about the stack usage. +Thanks go to jawaid for this bit of news also. + +5/5/92 +Well, another change this time to prchr(), so that you can pass terrible +command lines, and have them done right. Problem came from not outputing the +right escape type character for end of line stuff. So, that now works. NOW, I +only wish I had a terminal to test all this on, hope everythign is fine, if not +we are screwed for alot of work here. + +Made the default as -e (let you hit space or return to quit when at eof) and +-m (medium prompt line, shows you the file you are viewing, nothing more). + +5/4/92 +changed most of the open()'s to use the correct modes of the file. solves +errors with coming back from vi and such. + +changed the stacksize from default of 8K to 4.5K, seems to work like a dream. + +put in an optimize -1 into the code. + +Worked on the lsystem() call, as it is really messed in the head. Hopefully, +by the time you have read this, it will be written from scratch, and working +as it should. + +5/3/92-5/4/92 +Dave fixed all the problems with \n and \r's being wrong in places. + +Dave also fixed the gno termcap entry, as sr and sl (am I right?) were not +correct for less to work right. + +5/2/92-5/4/92 +James and Dave did all the prototyping into the sourcecode. diff --git a/bin/less/Info b/bin/less/Info new file mode 100644 index 0000000..6909b48 --- /dev/null +++ b/bin/less/Info @@ -0,0 +1,16 @@ +Okay, less, for GNO: + +if you use the newest version of MAN (v1.6), then set pager=less will let you +use less as your pager. + +copy less.hlp to 31:bin so that less can find its help file. + +copy less to 31:bin and type rehash to get it updated in your path. + +Thats all there is to it, enjoy. + +many thanks go to Jawaid Bazyar for his assembly added to 2 routines for screen +updating, as it was a major speed increase. + +the termcap file included is fixed for less and alot of other utilities. +please, copy this to your 31:etc directory or there will be problems. diff --git a/bin/less/Makefile b/bin/less/Makefile new file mode 100644 index 0000000..1dd6274 --- /dev/null +++ b/bin/less/Makefile @@ -0,0 +1,104 @@ +o/gsos.a: gsos.c + compile gsos.c keep=o/gsos + +o/ttyin.a: ttyin.c less.h defines.h proto.h + compile ttyin.c keep=o/ttyin + +o/edit.a: edit.c less.h defines.h proto.h + compile edit.c keep=o/edit + +o/lsystem.a: lsystem.c less.h defines.h proto.h position.h + compile lsystem.c keep=o/lsystem + +o/line.a: line.c less.h defines.h proto.h + compile line.c keep=o/line + +o/help.a: help.c less.h defines.h proto.h + compile help.c keep=o/help + +o/output.a: output.c less.h defines.h proto.h + compile output.c keep=o/output + +o/position.a: position.c less.h defines.h proto.h position.h + compile position.c keep=o/position + +o/tags.a: tags.c less.h defines.h proto.h + compile tags.c keep=o/tags + +o/input.a: input.c less.h defines.h proto.h + compile input.c keep=o/input + +o/command.a: command.c less.h defines.h proto.h position.h option.h cmd.h + compile command.c keep=o/command + +o/optfunc.a: optfunc.c less.h defines.h proto.h option.h + compile optfunc.c keep=o/optfunc + +o/screen.a: screen.c less.h defines.h proto.h + compile screen.c keep=o/screen + +o/jump.a: jump.c less.h defines.h proto.h position.h + compile jump.c keep=o/jump + +o/ifile.a: ifile.c less.h defines.h proto.h + compile ifile.c keep=o/ifile + +o/charset.a: charset.c less.h defines.h proto.h + compile charset.c keep=o/charset + +o/search.a: search.c less.h defines.h proto.h position.h + compile search.c keep=o/search + +o/mark.a: mark.c less.h defines.h proto.h position.h + compile mark.c keep=o/mark + +o/os.a: os.c less.h defines.h proto.h + compile os.c keep=o/os + +o/main.a: main.c less.h defines.h proto.h position.h + compile main.c keep=o/main + +o/option.a: option.c less.h defines.h proto.h option.h + compile option.c keep=o/option + +o/opttbl.a: opttbl.c less.h defines.h proto.h option.h + compile opttbl.c keep=o/opttbl + +o/forwback.a: forwback.c less.h defines.h proto.h position.h + compile forwback.c keep=o/forwback + +o/version.a: version.c less.h defines.h proto.h + compile version.c keep=o/version + +o/signal.a: signal.c less.h defines.h proto.h + compile signal.c keep=o/signal + +o/cmdbuf.a: cmdbuf.c less.h defines.h proto.h + compile cmdbuf.c keep=o/cmdbuf + +o/linenum.a: linenum.c less.h defines.h proto.h position.h + compile linenum.c keep=o/linenum + +o/ch.a: ch.c less.h defines.h proto.h + compile ch.c keep=o/ch + +o/decode.a: decode.c less.h defines.h proto.h cmd.h + compile decode.c keep=o/decode + +o/filename.a: filename.c less.h defines.h proto.h + compile filename.c keep=o/filename + +o/prompt.a: prompt.c less.h defines.h proto.h position.h + compile prompt.c keep=o/prompt + +o/brac.a: brac.c less.h defines.h proto.h position.h + compile brac.c keep=o/brac + +less: o/BRAC.A o/FILENAME.A o/LINENUM.A o/OS.A o/TAGS.A o/CH.A o/FORWBACK.A \ + o/LSYSTEM.A o/OUTPUT.A o/TTYIN.A o/CHARSET.A o/HELP.A o/MAIN.A \ + o/POSITION.A o/VERSION.A o/CMDBUF.A o/IFILE.A o/MARK.A o/PROMPT.A \ + o/COMMAND.A o/INPUT.A o/OPTFUNC.A o/SCREEN.A o/DECODE.A o/JUMP.A \ + o/OPTION.A o/SEARCH.A o/EDIT.A o/LINE.A o/OPTTBL.A o/SIGNAL.A o/GSOS.A + purge + compile linkscr + purge diff --git a/bin/less/brac.c b/bin/less/brac.c new file mode 100644 index 0000000..e0555e0 --- /dev/null +++ b/bin/less/brac.c @@ -0,0 +1,94 @@ +/* + * Routines to perform bracket matching functions. + */ +#pragma noroot +#include "less.h" +#include "position.h" + +#ifdef _ORCAC_ +segment "LoadSegONE"; +#endif + +/* + * Try to match the n-th open bracket + * which appears in the top displayed line (forwdir), + * or the n-th close bracket + * which appears in the bottom displayed line (!forwdir). + * The characters which serve as "open bracket" and + * "close bracket" are given. + */ +public void match_brac(obrac, cbrac, forwdir, n) + register int obrac; + register int cbrac; + int forwdir; + int n; +{ + register int c; + register int nest; + POSITION pos; + int (*chget)(); + + extern int ch_forw_get(), ch_back_get(); + + /* + * Seek to the line containing the open bracket. + * This is either the top or bottom line on the screen, + * depending on the type of bracket. + */ + pos = position((forwdir) ? TOP : BOTTOM); + if (pos == NULL_POSITION || ch_seek(pos)) + { + if (forwdir) + error("Nothing in top line", NULL_PARG); + else + error("Nothing in bottom line", NULL_PARG); + return; + } + + /* + * Look thru the line to find the open bracket to match. + */ + do + { +/* if ((c = ch_forw_get()) == '\n' || c == EOI) */ + if ((c = ch_forw_get()) == '\r' || c == EOI) + { + if (forwdir) + error("No bracket in top line", NULL_PARG); + else + error("No bracket in bottom line", NULL_PARG); + return; + } + } while (c != obrac || --n > 0); + + /* + * Position the file just "after" the open bracket + * (in the direction in which we will be searching). + * If searching forward, we are already after the bracket. + * If searching backward, skip back over the open bracket. + */ + if (!forwdir) + (void) ch_back_get(); + + /* + * Search the file for the matching bracket. + */ + chget = (forwdir) ? ch_forw_get : ch_back_get; + nest = 0; + while ((c = (*chget)()) != EOI) + { + if (c == obrac) + nest++; + else if (c == cbrac && --nest < 0) + { + /* + * Found the matching bracket. + * If searching backward, put it on the top line. + * If searching forward, put it on the bottom line. + */ + jump_line_loc(ch_tell(), forwdir ? -1 : 1); + return; + } + } + error("No matching bracket", NULL_PARG); +} diff --git a/bin/less/ch.c b/bin/less/ch.c new file mode 100644 index 0000000..e4b2274 --- /dev/null +++ b/bin/less/ch.c @@ -0,0 +1,576 @@ +/* + * Low level character input from the input file. + * We use these special purpose routines which optimize moving + * both forward and backward from the current read pointer. + */ +#pragma noroot +#include "less.h" + +#ifdef _ORCAC_ +segment "LoadSegONE"; +#include +#endif + +public int file = -1; /* File descriptor of the input file */ +public int ignore_eoi; + +/* + * Pool of buffers holding the most recently used blocks of the input file. + */ +#ifdef BUFSIZ +#undef BUFSIZ +#define BUFSIZ 1840 /* was 1024, but 80x23 == 1840 */ +#endif +struct buf { + struct buf *next, *prev; /* Must be first to match struct filestate */ + long block; + unsigned int datasize; + unsigned char data[BUFSIZ]; +}; + +/* + * The buffer pool is kept as a doubly-linked circular list, + * in order from most- to least-recently used. + * The circular list is anchored by the file state "thisfile". + * + * The file state is maintained in a filestate structure. + * There are two such structures, one used when input is a pipe + * and the other when input is an ordinary file. + * This is so that we can leave a pipe, look and other files, + * and return to the pipe without losing buffered data. + * Buffered data can be reconstructed for a non-pipe file by + * simply re-reading the file, but a pipe cannot be re-read. + */ + +struct filestate { + struct buf *next, *prev; /* Must be first to match struct buf */ + POSITION fpos; + int nbufs; + long block; + int offset; + POSITION fsize; +}; + +#define END_OF_CHAIN ((struct buf *)thisfile) +#define buf_head thisfile->next +#define buf_tail thisfile->prev +#define ch_nbufs thisfile->nbufs +#define ch_block thisfile->block +#define ch_offset thisfile->offset +#define ch_fpos thisfile->fpos +#define ch_fsize thisfile->fsize + +static struct filestate pipefile = + { (struct buf *)&pipefile, (struct buf *)&pipefile }; + +static struct filestate nonpipefile = + { (struct buf *)&nonpipefile, (struct buf *)&nonpipefile }; + +static struct filestate *thisfile; + +extern int ispipe; +extern int autobuf; +extern int sigs; +#if LOGFILE +extern int logfile; +extern char *namelogfile; +#endif + +static int fch_get(void); +static int buffered(long block); +static int ch_addbuf(int nnew); + +/* + * Get the character pointed to by the read pointer. + * ch_get() is a macro which is more efficient to call + * than fch_get (the function), in the usual case + * that the block desired is at the head of the chain. + */ +#define ch_get() ((ch_block == buf_head->block && \ + ch_offset < buf_head->datasize) ? \ + buf_head->data[ch_offset] : fch_get()) + static int +fch_get(void) +{ + register struct buf *bp; + register int n; + register int slept; + POSITION pos; + POSITION len; + + slept = 0; + + /* + * Look for a buffer holding the desired block. + */ + for (bp = buf_head; bp != END_OF_CHAIN; bp = bp->next) + if (bp->block == ch_block) + { + if (ch_offset >= bp->datasize) + /* + * Need more data in this buffer. + */ + goto read_more; + goto found; + } + /* + * Block is not in a buffer. + * Take the least recently used buffer + * and read the desired block into it. + * If the LRU buffer has data in it, + * and autobuf is true, and input is a pipe, + * then try to allocate a new buffer first. + */ + if (autobuf && ispipe && buf_tail->block != (long)(-1)) + if (ch_addbuf(1)) + /* + * Allocation failed: turn off autobuf. + */ + autobuf = 0; + bp = buf_tail; + bp->block = ch_block; + bp->datasize = 0; + + read_more: + pos = (ch_block * BUFSIZ) + bp->datasize; + if ((len = ch_length()) != NULL_POSITION && pos >= len) + /* + * At end of file. + */ + return (EOI); + + if (pos != ch_fpos) + { + /* + * Not at the correct position: must seek. + * If input is a pipe, we're in trouble (can't seek on a pipe). + * Some data has been lost: just return "?". + */ + if (ispipe) + return ('?'); + if (lseek(file, (offset_t)pos, 0) == BAD_LSEEK) + { + error("seek error", NULL_PARG); + quit(1); + } + ch_fpos = pos; + } + + /* + * Read the block. + * If we read less than a full block, that's ok. + * We use partial block and pick up the rest next time. + */ + n = iread(file, &bp->data[bp->datasize], + (unsigned int)(BUFSIZ - bp->datasize)); + if (n == READ_INTR) + return (EOI); + if (n < 0) + { + error("read error", NULL_PARG); + quit(1); + } + ch_fpos += n; + +#if LOGFILE + /* + * If we have a log file, write the new data to it. + */ + if (logfile >= 0 && n > 0) + write(logfile, (char *) &bp->data[bp->datasize], n); +#endif + + bp->datasize += n; + + /* + * If we have read to end of file, set ch_fsize to indicate + * the position of the end of file. + */ + if (n == 0) + { + ch_fsize = pos; + if (ignore_eoi) + { + /* + * We are ignoring EOF. + * Wait a while, then try again. + */ + if (!slept) + ierror("Waiting for data", NULL_PARG); + sleep(1); + slept = 1; + } + if (sigs) + return (EOI); + } + + found: + if (buf_head != bp) + { + /* + * Move the buffer to the head of the buffer chain. + * This orders the buffer chain, most- to least-recently used. + */ + bp->next->prev = bp->prev; + bp->prev->next = bp->next; + + bp->next = buf_head; + bp->prev = END_OF_CHAIN; + buf_head->prev = bp; + buf_head = bp; + } + + if (ch_offset >= bp->datasize) + /* + * After all that, we still don't have enough data. + * Go back and try again. + */ + goto read_more; + + return (bp->data[ch_offset]); +} + +#if LOGFILE +/* + * Close the logfile. + * If we haven't read all of standard input into it, do that now. + */ + public void +end_logfile(void) +{ + static int tried = 0; + + if (logfile < 0) + return; + if (!tried && ch_fsize == NULL_POSITION) + { + tried = 1; + ierror("Finishing logfile", NULL_PARG); + while (ch_forw_get() != EOI) + if (sigs) + break; + } + close(logfile); + logfile = -1; + namelogfile = NULL; +} + +/* + * Start a log file AFTER less has already been running. + * Invoked from the - command; see toggle_option(). + * Write all the existing buffered data to the log file. + */ + public void +sync_logfile(void) +{ + register struct buf *bp; + long block; + long last_block; + + last_block = (ch_fpos + BUFSIZ - 1) / BUFSIZ; + for (block = 0; block <= last_block; block++) + for (bp = buf_head; bp != END_OF_CHAIN; bp = bp->next) + if (bp->block == block) + { + write(logfile, (char *) bp->data, bp->datasize); + break; + } +} + +#endif + +/* + * Determine if a specific block is currently in one of the buffers. + */ + static int +buffered(block) + long block; +{ + register struct buf *bp; + + for (bp = buf_head; bp != END_OF_CHAIN; bp = bp->next) + if (bp->block == block) + return (1); + return (0); +} + +/* + * Seek to a specified position in the file. + * Return 0 if successful, non-zero if can't seek there. + */ + public int +ch_seek(pos) + register POSITION pos; +{ + long new_block; + POSITION len; + + len = ch_length(); + if (pos < ch_zero() || (len != NULL_POSITION && pos > len)) + return (1); + + new_block = pos / BUFSIZ; + if (ispipe && pos != ch_fpos && !buffered(new_block)) + return (1); + /* + * Set read pointer. + */ + ch_block = new_block; + ch_offset = pos % BUFSIZ; + return (0); +} + +/* + * Seek to the end of the file. + */ + public int +ch_end_seek(void) +{ + POSITION len; + + if (!ispipe) + ch_fsize = filesize(file); + + len = ch_length(); + if (len != NULL_POSITION) + return (ch_seek(len)); + + /* + * Do it the slow way: read till end of data. + */ + while (ch_forw_get() != EOI) + if (sigs) + return (1); + return (0); +} + +/* + * Seek to the beginning of the file, or as close to it as we can get. + * We may not be able to seek there if input is a pipe and the + * beginning of the pipe is no longer buffered. + */ + public int +ch_beg_seek(void) +{ + register struct buf *bp, *firstbp; + + /* + * Try a plain ch_seek first. + */ + if (ch_seek(ch_zero()) == 0) + return (0); + + /* + * Can't get to position 0. + * Look thru the buffers for the one closest to position 0. + */ + firstbp = bp = buf_head; + if (bp == END_OF_CHAIN) + return (1); + while ((bp = bp->next) != END_OF_CHAIN) + if (bp->block < firstbp->block) + firstbp = bp; + ch_block = firstbp->block; + ch_offset = 0; + return (0); +} + +/* + * Return the length of the file, if known. + */ + public POSITION +ch_length(void) +{ + if (ignore_eoi) + return (NULL_POSITION); + return (ch_fsize); +} + +/* + * Return the current position in the file. + */ +#define tellpos(blk,off) ((POSITION)((((long)(blk)) * BUFSIZ) + (off))) + + public POSITION +ch_tell(void) +{ + return (tellpos(ch_block, ch_offset)); +} + +/* + * Get the current char and post-increment the read pointer. + */ + public int +ch_forw_get(void) +{ + register int c; + + c = ch_get(); + if (c == EOI) + return (EOI); + if (ch_offset < BUFSIZ-1) + ch_offset++; + else + { +#if __ZOFFSET /* NOT WORKING */ + if (ch_fsize != NULL_POSITION && + tellpos(ch_block+1, 0) >= ch_fsize) + return (EOI); +#endif + ch_block ++; + ch_offset = 0; + } + return (c); +} + +/* + * Pre-decrement the read pointer and get the new current char. + */ + public int +ch_back_get(void) +{ + if (ch_offset > 0) + ch_offset --; + else + { +#if __ZOFFSET /* NOT WORKING */ + if (tellpos(ch_block-1, BUFSIZ-1) < ch_zero()) + return (EOI); +#else + if (ch_block <= 0) + return (EOI); +#endif + if (ispipe && !buffered(ch_block-1)) + return (EOI); + ch_block--; + ch_offset = BUFSIZ-1; + } + return (ch_get()); +} + +/* + * Allocate buffers. + * Caller wants us to have a total of at least want_nbufs buffers. + */ + public int +ch_nbuf(want_nbufs) + int want_nbufs; +{ + PARG parg; + + if (ch_nbufs < want_nbufs && ch_addbuf(want_nbufs - ch_nbufs)) + { + /* + * Cannot allocate enough buffers. + * If we don't have ANY, then quit. + * Otherwise, just report the error and return. + */ + parg.p_int = want_nbufs - ch_nbufs; + error("Cannot allocate %d buffers", &parg); + if (ch_nbufs == 0) + quit(1); + } + return (ch_nbufs); +} + +/* + * Flush any saved file state, including buffer contents. + */ + public void +ch_flush(void) +{ + register struct buf *bp; + + if (ispipe) + { + /* + * If input is a pipe, we don't flush buffer contents, + * since the contents can't be recovered. + */ + ch_fsize = NULL_POSITION; + return; + } + + /* + * Initialize all the buffers. + */ + for (bp = buf_head; bp != END_OF_CHAIN; bp = bp->next) + bp->block = (long)(-1); + + /* + * Figure out the size of the file, if we can. + */ + ch_fsize = filesize(file); + + /* + * Seek to a known position: the beginning of the file. + */ + ch_fpos = 0; + ch_block = ch_fpos / BUFSIZ; + ch_offset = ch_fpos % BUFSIZ; + + if (lseek(file, (offset_t)0, 0) == BAD_LSEEK) + { + /* + * Warning only; even if the seek fails for some reason, + * there's a good chance we're at the beginning anyway. + * {{ I think this is bogus reasoning. }} + */ + error("seek error to 0", NULL_PARG); + } +} + +/* + * Allocate some new buffers. + * The buffers are added to the tail of the buffer chain. + */ + static int +ch_addbuf(nnew) + int nnew; +{ + register struct buf *bp; + register struct buf *newbufs; + + /* + * We don't have enough buffers. + * Allocate some new ones. + */ + newbufs = (struct buf *) calloc(nnew, sizeof(struct buf)); + if (newbufs == NULL) + return (1); + + /* + * Initialize the new buffers and link them together. + * Link them all onto the tail of the buffer list. + */ + ch_nbufs += nnew; + for (bp = &newbufs[0]; bp < &newbufs[nnew]; bp++) + { + bp->next = bp + 1; + bp->prev = bp - 1; + bp->block = (long)(-1); + } + newbufs[nnew-1].next = END_OF_CHAIN; + newbufs[0].prev = buf_tail; + buf_tail->next = &newbufs[0]; + buf_tail = &newbufs[nnew-1]; + return (0); +} + +/* + * Use the pipe file state. + */ + public void +ch_pipe(void) +{ + thisfile = &pipefile; +} + +/* + * Use the non-pipe file state. + */ + public void +ch_nonpipe(void) +{ + thisfile = &nonpipefile; +} diff --git a/bin/less/charset.c b/bin/less/charset.c new file mode 100644 index 0000000..12c41c0 --- /dev/null +++ b/bin/less/charset.c @@ -0,0 +1,212 @@ +/* + * Functions to define the character set + * and do things specific to the character set. + */ +#pragma noroot +#include "less.h" + +#ifdef _ORCAC_ +segment "LoadSegONE"; +#endif + +/* + * Predefined character sets, + * selected by the LESSCHARSET environment variable. + */ +struct charset { + char *name; + char *desc; +} charsets[] = { + { "ascii", "8bcccbcc18b95.b" }, + { "latin1", "8bcccbcc18b95.33b." }, + { NULL } +}; + +#define IS_BINARY_CHAR 01 +#define IS_CONTROL_CHAR 02 + +static char chardef[256]; +static char *binfmt = "\\%o"; +public int binattr = BLINK; + +extern char *getenv(char *); + +static void ichardef(char *s); +static int icharset(char *name); + +/* + * Define a charset, given a description string. + * The string consists of 256 letters, + * one for each character in the charset. + * If the string is shorter than 256 letters, missing letters + * are taken to be identical to the last one. + * A decimal number followed by a letter is taken to be a + * repetition of the letter. + * + * Each letter is one of: + * . normal character + * b binary character + * c control character + */ + static void +ichardef(s) + char *s; +{ + register char *cp; + register int n; + register char v; + + n = 0; + cp = chardef; + while (*s != '\0') + { + switch (*s++) + { + case '.': + v = 0; + break; + case 'c': + v = IS_CONTROL_CHAR; + break; + case 'b': + v = IS_BINARY_CHAR|IS_CONTROL_CHAR; + break; + + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + n = (10 * n) + (s[-1] - '0'); + continue; + + default: + error("invalid chardef", NULL_PARG); + quit(1); + /*NOTREACHED*/ + } + + do + { + if (cp >= chardef + sizeof(chardef)) + { + error("chardef longer than 256", NULL_PARG); + quit(1); + /*NOTREACHED*/ + } + *cp++ = v; + } while (--n > 0); + n = 0; + } + + while (cp < chardef + sizeof(chardef)) + *cp++ = v; +} + +/* + * Define a charset, given a charset name. + * The valid charset names are listed in the "charsets" array. + */ + static int +icharset(name) + register char *name; +{ + register struct charset *p; + + if (name == NULL || *name == '\0') + return (0); + + for (p = charsets; p->name != NULL; p++) + { + if (strcmp(name, p->name) == 0) + { + ichardef(p->desc); + return (1); + } + } + + error("invalid charset name", NULL_PARG); + quit(1); + /*NOTREACHED*/ +} + +/* + * Initialize charset data structures. + */ + public void +init_charset(void) +{ + register char *s; + + /* + * Try environment variable LESSCHARSET. + * If LESSCHARSET is not set, try LESSCHARDEF. + * If LESSCHARDEF is not set, default to "ascii" charset. + */ + s = getenv("LESSCHARSET"); + if (icharset(s)) + return; + + s = getenv("LESSCHARDEF"); + if (s != NULL && *s != '\0') + { + ichardef(s); + return; + } + + (void) icharset("ascii"); + + s = getenv("LESSBINFMT"); + if (s != NULL && *s != '\0') + { + if (*s == '*') + { + switch (s[1]) + { + case 'd': binattr = BOLD; break; + case 'k': binattr = BLINK; break; + case 'u': binattr = UNDERLINE; break; + default: binattr = NORMAL; break; + } + s += 2; + } + if (*s != '\0') + binfmt = s; + } +} + +/* + * Is a given character a "binary" character? + */ + public int +binary_char(c) + int c; +{ + return (chardef[c] & IS_BINARY_CHAR); +} + +/* + * Is a given character a "control" character? + */ + public int +control_char(c) + int c; +{ + return (chardef[c] & IS_CONTROL_CHAR); +} + +/* + * Return the printable form of a character. + * For example, in the "ascii" charset '\3' is printed as "^C". + */ + public char * +prchar(c) + int c; +{ + static char buf[8]; + + if (!control_char(c)) + sprintf(buf, "%c", c); + else if (!control_char(c ^ 0100)) + sprintf(buf, "^%c", c ^ 0100); + else + sprintf(buf, binfmt, c); + return (buf); +} diff --git a/bin/less/cmd.h b/bin/less/cmd.h new file mode 100644 index 0000000..b1d94b2 --- /dev/null +++ b/bin/less/cmd.h @@ -0,0 +1,59 @@ +#define MAX_USERCMD 500 +#define MAX_CMDLEN 16 + +#define A_B_LINE 2 +#define A_B_SCREEN 3 +#define A_B_SCROLL 4 +#define A_B_SEARCH 5 +#define A_DIGIT 6 +#define A_DISP_OPTION 7 +#define A_DEBUG 8 +#define A_EXAMINE 9 +#define A_FIRSTCMD 10 +#define A_FREPAINT 11 +#define A_F_LINE 12 +#define A_F_SCREEN 13 +#define A_F_SCROLL 14 +#define A_F_SEARCH 15 +#define A_GOEND 16 +#define A_GOLINE 17 +#define A_GOMARK 18 +#define A_HELP 19 +#define A_NEXT_FILE 20 +#define A_PERCENT 21 +#define A_PREFIX 22 +#define A_PREV_FILE 23 +#define A_QUIT 24 +#define A_REPAINT 25 +#define A_SETMARK 26 +#define A_SHELL 27 +#define A_STAT 28 +#define A_FF_LINE 29 +#define A_BF_LINE 30 +#define A_VERSION 31 +#define A_VISUAL 32 +#define A_F_WINDOW 33 +#define A_B_WINDOW 34 +#define A_F_BRACKET 35 +#define A_B_BRACKET 36 +#define A_PIPE 37 +#define A_INDEX_FILE 38 + + + + +#define A_AGAIN_SEARCH 43 +#define A_T_AGAIN_SEARCH 44 +#define A_REVERSE_SEARCH 45 +#define A_T_REVERSE_SEARCH 46 +#define A_OPT_TOGGLE 47 +#define A_OPT_SET 48 +#define A_OPT_UNSET 49 +#define A_F_FOREVER 50 +#define A_GOPOS 51 + +#define A_INVALID 100 +#define A_NOACTION 101 +#define A_UINVALID 102 + +#define A_EXTRA 0200 diff --git a/bin/less/cmdbuf.c b/bin/less/cmdbuf.c new file mode 100644 index 0000000..2c8e2de --- /dev/null +++ b/bin/less/cmdbuf.c @@ -0,0 +1,145 @@ +/* + * Functions which manipulate the command buffer. + * Used only by command() and related functions. + */ +#pragma noroot +#include "less.h" + +#ifdef _ORCAC_ +segment "LoadSegONE"; +#endif + +extern int erase_char, kill_char; +extern int sc_width; + +static char cmdbuf[120]; /* Buffer for holding a multi-char command */ +static int cmd_col; /* Current column of the multi-char command */ +static char *cp; /* Pointer into cmdbuf */ + +/* + * Reset command buffer (to empty). + */ + public void +cmd_reset(void) +{ + cp = cmdbuf; + *cp = '\0'; + cmd_col = 0; +} + +/* + * How many characters are in the command buffer? + */ + public int +len_cmdbuf(void) +{ + return (cp - cmdbuf); +} + +/* + * Backspace in the command buffer. + */ + public int +cmd_erase(void) +{ + register char *s; + + if (cp == cmdbuf) + /* + * Backspace past beginning of the string: + * this usually means abort the command. + */ + return (1); + + --cp; + if (*cp == ESC) + s = "ESC"; + else + s = prchar(*cp); + while (*s++ != '\0') + { + backspace(); + cmd_col--; + } + *cp = '\0'; + return (0); +} + +/* + * Process a single character of a multi-character command, such as + * a number, or the pattern of a search command. + */ + public int +cmd_char(c) + int c; +{ + char *s; + + if (c == erase_char) + { + if (cmd_erase()) + return (1); + } else if (c == kill_char) + { + /* {{ Could do this faster, but who cares? }} */ + while (cmd_erase() == 0) + ; + } else if (cp >= &cmdbuf[sizeof(cmdbuf)-1]) + { + /* + * No room in the command buffer. + */ + bell(); + } else if (cmd_col >= sc_width-4) + { + /* + * No room on the screen. + * {{ Could get fancy here; maybe shift the displayed + * line and make room for more chars, like ksh. }} + */ + bell(); + } else + { + /* + * Append the character to the string. + */ + *cp++ = c; + *cp = '\0'; + if (c == ESC) + s = "ESC"; + else + s = prchar(c); + putstr(s); + cmd_col += strlen(s); + } + return (0); +} + +/* + * Return the number currently in the command buffer. + */ + public int +cmd_int(void) +{ + return (atoi(cmdbuf)); +} + +/* + * Display a string, usually as a prompt for input into the command buffer. + */ + public void +cmd_putstr(s) + char *s; +{ + putstr(s); + cmd_col += strlen(s); +} + +/* + * Return a pointer to the command buffer. + */ + public char * +get_cmdbuf(void) +{ + return (cmdbuf); +} diff --git a/bin/less/command.c b/bin/less/command.c new file mode 100644 index 0000000..cd0c28d --- /dev/null +++ b/bin/less/command.c @@ -0,0 +1,1203 @@ +/* + * User-level command processor. + */ +#pragma noroot +#include "less.h" +#include "position.h" +#include "option.h" +#include "cmd.h" + +#ifdef _ORCAC_ +segment "LoadSegONE"; +#endif + +#define NO_MCA 0 +#define MCA_DONE 1 +#define MCA_MORE 2 + +extern int erase_char, kill_char; +extern int ispipe; +extern int sigs; +extern int quit_at_eof; +extern int hit_eof; +extern int sc_width; +extern int sc_height; +extern int swindow; +extern int jump_sline; +extern int quitting; +extern int scroll; +extern int nohelp; +extern int ignore_eoi; +extern char *every_first_cmd; +extern char version[]; +extern struct scrpos initial_scrpos; +extern IFILE curr_ifile; +#if EDITOR +extern char *editor; +extern char *editproto; +#endif +extern int screen_trashed; /* The screen has been overwritten */ + +static char ungot[100]; +static char *ungotp = NULL; +#if SHELL_ESCAPE +static char *shellcmd = NULL; /* For holding last shell command for "!!" */ +#endif +static int mca; /* The multicharacter command (action) */ +static int search_type; /* The previous type of search */ +static int number; /* The number typed by the user */ +static char optchar; +static int optflag; +#if PIPEC +static char pipec; +#endif + +static void multi_search(char *pattern, int n); +static void cmd_exec(void); +static void start_mca(int action, char *prompt); +static void mca_search(void); +static void exec_mca(void); +static int mca_char(int c); +static void prompt(void); +static int getcc(void); + +/* + * Move the cursor to lower left before executing a command. + * This looks nicer if the command takes a long time before + * updating the screen. + */ + static void +cmd_exec(void) +{ + lower_left(); + flush(); +} + +/* + * Set up the display to start a new multi-character command. + */ + static void +start_mca(action, prompt) + int action; + char *prompt; +{ + mca = action; + lower_left(); + clear_eol(); + cmd_putstr(prompt); +} + +/* + * Set up the display to start a new search command. + */ + static void +mca_search(void) +{ + switch (SRCH_DIR(search_type)) + { + case SRCH_FORW: + mca = A_F_SEARCH; + break; + case SRCH_BACK: + mca = A_B_SEARCH; + break; + } + + lower_left(); + clear_eol(); + + if (search_type & SRCH_FIRST_FILE) + cmd_putstr("@"); + + if (search_type & SRCH_PAST_EOF) + cmd_putstr("*"); + + if (search_type & SRCH_NOMATCH) + cmd_putstr("!"); + + switch (SRCH_DIR(search_type)) + { + case SRCH_FORW: + cmd_putstr("/"); + break; + case SRCH_BACK: + cmd_putstr("?"); + break; + } +} + +/* + * Execute a multicharacter command. + */ + static void +exec_mca(void) +{ + register char *cbuf; + register char *s; + + cmd_exec(); + cbuf = get_cmdbuf(); + + switch (mca) + { + case A_F_SEARCH: + case A_B_SEARCH: + multi_search(cbuf, number); + break; + case A_FIRSTCMD: + /* + * Skip leading spaces or + signs in the string. + */ + while (*cbuf == '+' || *cbuf == ' ') + cbuf++; + if (every_first_cmd != NULL) + free(every_first_cmd); + if (*cbuf == '\0') + every_first_cmd = NULL; + else + every_first_cmd = save(cbuf); + break; + case A_OPT_TOGGLE: + toggle_option(optchar, cbuf, optflag); + optchar = '\0'; + break; + case A_F_BRACKET: + match_brac(cbuf[0], cbuf[1], 1, number); + break; + case A_B_BRACKET: + match_brac(cbuf[1], cbuf[0], 0, number); + break; + case A_EXAMINE: + /* + * Ignore leading spaces and glob the filename. + */ + cbuf = skipsp(cbuf); + s = glob(cbuf); + if (s != NULL) + { + edit_list(s); + free(s); + } else + edit_list(cbuf); + break; +#if SHELL_ESCAPE + case A_SHELL: + /* + * !! just uses whatever is in shellcmd. + * Otherwise, copy cmdbuf to shellcmd, + * expanding any special characters ("%" or "#"). + */ + if (*cbuf != '!') + { + if (shellcmd != NULL) + free(shellcmd); + shellcmd = fexpand(cbuf); + if (shellcmd == NULL) + break; + } + + if (shellcmd == NULL) + lsystem(""); + else + lsystem(shellcmd); + error("!done", NULL_PARG); + break; +#endif +#if PIPEC + case A_PIPE: + (void) pipe_mark(pipec, cbuf); + error("|done", NULL_PARG); + break; +#endif + } +} + +/* + * Add a character to a multi-character command. + */ + static int +mca_char(c) + int c; +{ + char *p; + int flag; + char buf[3]; + + switch (mca) + { + case 0: + /* + * Not in a multicharacter command. + */ + return (NO_MCA); + + case A_PREFIX: + /* + * In the prefix of a command. + * This not considered a multichar command + * (even tho it uses cmdbuf, etc.). + * It is handled in the commands() switch. + */ + return (NO_MCA); + + case A_DIGIT: + /* + * Entering digits of a number. + * Terminated by a non-digit. + */ + if ((c < '0' || c > '9') && + c != erase_char && c != kill_char) + { + /* + * Not part of the number. + * Treat as a normal command character. + */ + number = cmd_int(); + mca = 0; + return (NO_MCA); + } + break; + + case A_OPT_TOGGLE: + /* + * Special case for the TOGGLE_OPTION command. + * If the option letter which was entered is a + * single-char option, execute the command immediately, + * so user doesn't have to hit RETURN. + * If the first char is + or -, this indicates + * OPT_UNSET or OPT_SET respectively, instead of OPT_TOGGLE. + */ + if (c == erase_char || c == kill_char) + break; + if (optchar != '\0' && optchar != '+' && optchar != '-') + /* + * We already have the option letter. + */ + break; + switch (c) + { + case '+': + optflag = OPT_UNSET; + break; + case '-': + optflag = OPT_SET; + break; + default: + optchar = c; + if (optflag != OPT_TOGGLE || single_char_option(c)) + { + toggle_option(c, "", optflag); + return (MCA_DONE); + } + break; + } + if (optchar == '+' || optchar == '-') + { + optchar = c; + break; + } + /* + * Display a prompt appropriate for the option letter. + */ + if ((p = opt_prompt(c)) == NULL) + { + buf[0] = '-'; + buf[1] = c; + buf[2] = '\0'; + p = buf; + } + start_mca(A_OPT_TOGGLE, p); + return (MCA_MORE); + + case A_F_SEARCH: + case A_B_SEARCH: + /* + * Special case for search commands. + * Certain characters as the first char of + * the pattern have special meaning: + * ! Toggle the NOMATCH flag + * * Toggle the PAST_EOF flag + * @ Toggle the FIRST_FILE flag + */ + if (len_cmdbuf() > 0) + /* + * Only works for the first char of the pattern. + */ + break; + + flag = 0; + switch (c) + { + case '!': + flag = SRCH_NOMATCH; + break; + case '@': + flag = SRCH_FIRST_FILE; + break; + case '*': + flag = SRCH_PAST_EOF; + break; + } + if (flag != 0) + { + search_type ^= flag; + mca_search(); + return (MCA_MORE); + } + break; + } + + /* + * Any other multicharacter command + * is terminated by a newline. + */ + if (c == '\n' || c == '\r') + { + /* + * Execute the command. + */ + exec_mca(); + return (MCA_DONE); + } + /* + * Append the char to the command buffer. + */ + if (cmd_char(c)) + /* + * Abort the multi-char command. + */ + return (MCA_DONE); + + if ((mca == A_F_BRACKET || mca == A_B_BRACKET) && len_cmdbuf() >= 2) + { + /* + * Special case for the bracket-matching commands. + * Execute the command after getting exactly two + * characters from the user. + */ + exec_mca(); + return (MCA_DONE); + } + + /* + * Need another character. + */ + return (MCA_MORE); +} + +/* + * Display the appropriate prompt. + */ + static void +prompt(void) +{ + register char *p; + + if (ungotp != NULL && ungotp > ungot) + { + /* + * No prompt necessary if commands are from + * ungotten chars rather than from the user. + */ + return; + } + + /* + * If nothing is displayed yet, display starting from initial_scrpos. + */ + if (empty_screen()) + { + if (initial_scrpos.pos == NULL_POSITION) + /* + * {{ Maybe this should be: + * jump_loc(ch_zero(), jump_sline); + * but this behavior seems rather unexpected + * on the first screen. }} + */ + jump_loc(ch_zero(), 1); + else + jump_loc(initial_scrpos.pos, initial_scrpos.ln); + } else if (screen_trashed) + repaint(); + + /* + * If the -E flag is set and we've hit EOF on the last file, quit. + */ + if (quit_at_eof == 2 && hit_eof && + next_ifile(curr_ifile) == NULL_IFILE) + quit(0); + + /* + * Select the proper prompt and display it. + */ + lower_left(); + clear_eol(); + p = pr_string(); + if (p == NULL) + putchr(':'); + else + { + so_enter(); + putstr(p); + so_exit(); + } +#if __MSDOS__ + scroll_bar(); +#endif +} + +/* + * Get command character. + * The character normally comes from the keyboard, + * but may come from ungotten characters + * (characters previously given to ungetcc or ungetsc). + */ + static int +getcc(void) +{ + if (ungotp == NULL) + /* + * Normal case: no ungotten chars, so get one from the user. + */ + return (getchr()); + + if (ungotp > ungot) + /* + * Return the next ungotten char. + */ + return (*--ungotp); + + /* + * We have just run out of ungotten chars. + */ + ungotp = NULL; + if (len_cmdbuf() == 0 || !empty_screen()) + return (getchr()); + /* + * Command is incomplete, so try to complete it. + */ + switch (mca) + { + case A_DIGIT: + /* + * We have a number but no command. Treat as #g. + */ + return ('g'); + + case A_F_SEARCH: + case A_B_SEARCH: + /* + * We have "/string" but no newline. Add the \n. + */ + return ('\n'); + + default: + /* + * Some other incomplete command. Let user complete it. + */ + return (getchr()); + } +} + +/* + * "Unget" a command character. + * The next getcc() will return this character. + */ + public void +ungetcc(c) + int c; +{ + if (ungotp == NULL) + ungotp = ungot; + if (ungotp >= ungot + sizeof(ungot)) + { + error("ungetcc overflow", NULL_PARG); + quit(1); + } + *ungotp++ = c; +} + +/* + * Unget a whole string of command characters. + * The next sequence of getcc()'s will return this string. + */ + public void +ungetsc(s) + char *s; +{ + register char *p; + + for (p = s + strlen(s) - 1; p >= s; p--) + ungetcc(*p); +} + +/* + * Search for a pattern, possibly in multiple files. + * If SRCH_FIRST_FILE is set, begin searching at the first file. + * If SRCH_PAST_EOF is set, continue the search thru multiple files. + */ + static void +multi_search(pattern, n) + char *pattern; + int n; +{ + register int nomore; + char *curr_filename; + int changed_file; + + changed_file = 0; + curr_filename = get_filename(curr_ifile); + + if (search_type & SRCH_FIRST_FILE) + { + /* + * Start at the first (or last) file + * in the command line list. + */ + if (SRCH_DIR(search_type) == SRCH_FORW) + nomore = edit_first(); + else + nomore = edit_last(); + if (nomore) + return; + changed_file = 1; + search_type &= ~SRCH_FIRST_FILE; + } + + for (;;) + { + if ((n = search(search_type, pattern, n)) == 0) + /* + * Found it. + */ + return; + + if (n < 0) + /* + * Some kind of error in the search. + * Error message has been printed by search(). + */ + break; + + if ((search_type & SRCH_PAST_EOF) == 0) + /* + * We didn't find a match, but we're + * supposed to search only one file. + */ + break; + /* + * Move on to the next file. + */ + if (SRCH_DIR(search_type) == SRCH_BACK) + nomore = edit_prev(1); + else + nomore = edit_next(1); + if (nomore) + break; + changed_file = 1; + } + + /* + * Didn't find it. + * Print an error message if we haven't already. + */ + if (n > 0) + error("Pattern not found", NULL_PARG); + + if (changed_file) + /* + * Restore the file we were originally viewing. + */ + (void) edit(curr_filename, 0); +} + +/* + * Main command processor. + * Accept and execute commands until a quit command. + */ + public void +commands(void) +{ + register int c; + register int action; + register char *cbuf; + int save_search_type; + char *s; + char tbuf[2]; + PARG parg; + + search_type = SRCH_FORW; + scroll = (sc_height + 1) / 2; + + for (;;) + { + mca = 0; + number = 0; + optchar = '\0'; + + /* + * See if any signals need processing. + */ + if (sigs) + { + psignals(); + if (quitting) + quit(-1); + } + + /* + * Display prompt and accept a character. + */ + cmd_reset(); + prompt(); + if (sigs) + continue; + c = getcc(); + + again: + if (sigs) + continue; + + /* + * If we are in a multicharacter command, call mca_char. + * Otherwise we call cmd_decode to determine the + * action to be performed. + */ + if (mca) + switch (mca_char(c)) + { + case MCA_MORE: + /* + * Need another character. + */ + c = getcc(); + goto again; + case MCA_DONE: + /* + * Command has been handled by mca_char. + * Start clean with a prompt. + */ + continue; + case NO_MCA: + /* + * Not a multi-char command + * (at least, not anymore). + */ + break; + } + + /* + * Decode the command character and decide what to do. + */ + if (mca) + { + /* + * We're in a multichar command. + * Add the character to the command buffer + * and display it on the screen. + * If the user backspaces past the start + * of the line, abort the command. + */ + if (cmd_char(c) || len_cmdbuf() == 0) + continue; + cbuf = get_cmdbuf(); + } else + { + /* + * Don't use cmd_char if we're starting fresh + * at the beginning of a command, because we + * don't want to echo the command until we know + * it is a multichar command. We also don't + * want erase_char/kill_char to be treated + * as line editing characters. + */ + tbuf[0] = c; + tbuf[1] = '\0'; + cbuf = tbuf; + } + s = NULL; + action = cmd_decode(cbuf, &s); + /* + * If an "extra" string was returned, + * process it as a string of command characters. + */ + if (s != NULL) + ungetsc(s); + /* + * Clear the cmdbuf string. + * (But not if we're in the prefix of a command, + * because the partial command string is kept there.) + */ + if (action != A_PREFIX) + cmd_reset(); + + switch (action) + { + case A_DIGIT: + /* + * First digit of a number. + */ + start_mca(A_DIGIT, ":"); + goto again; + + case A_F_WINDOW: + /* + * Forward one window (and set the window size). + */ + if (number > 0) + swindow = number; + /* FALLTHRU */ + case A_F_SCREEN: + /* + * Forward one screen. + */ + if (number <= 0) + number = swindow; + cmd_exec(); + forward(number, 0, 1); + break; + + case A_B_WINDOW: + /* + * Backward one window (and set the window size). + */ + if (number > 0) + swindow = number; + /* FALLTHRU */ + case A_B_SCREEN: + /* + * Backward one screen. + */ + if (number <= 0) + number = swindow; + cmd_exec(); + backward(number, 0, 1); + break; + + case A_F_LINE: + /* + * Forward N (default 1) line. + */ + if (number <= 0) + number = 1; + cmd_exec(); + forward(number, 0, 0); + break; + + case A_B_LINE: + /* + * Backward N (default 1) line. + */ + if (number <= 0) + number = 1; + cmd_exec(); + backward(number, 0, 0); + break; + + case A_FF_LINE: + /* + * Force forward N (default 1) line. + */ + if (number <= 0) + number = 1; + cmd_exec(); + forward(number, 1, 0); + break; + + case A_BF_LINE: + /* + * Force backward N (default 1) line. + */ + if (number <= 0) + number = 1; + cmd_exec(); + backward(number, 1, 0); + break; + + case A_F_FOREVER: + /* + * Forward forever, ignoring EOF. + */ + cmd_exec(); + jump_forw(); + ignore_eoi = 1; + hit_eof = 0; + while (sigs == 0) + forward(1, 0, 0); + ignore_eoi = 0; + break; + + case A_F_SCROLL: + /* + * Forward N lines + * (default same as last 'd' or 'u' command). + */ + if (number > 0) + scroll = number; + cmd_exec(); + forward(scroll, 0, 0); + break; + + case A_B_SCROLL: + /* + * Forward N lines + * (default same as last 'd' or 'u' command). + */ + if (number > 0) + scroll = number; + cmd_exec(); + backward(scroll, 0, 0); + break; + + case A_FREPAINT: + /* + * Flush buffers, then repaint screen. + * Don't flush the buffers on a pipe! + */ + ch_flush(); + if (!ispipe) + clr_linenum(); + /* FALLTHRU */ + case A_REPAINT: + /* + * Repaint screen. + */ + cmd_exec(); + repaint(); + break; + + case A_GOLINE: + /* + * Go to line N, default beginning of file. + */ + if (number <= 0) + number = 1; + cmd_exec(); + jump_back(number); + break; + + case A_PERCENT: + /* + * Go to a specified percentage into the file. + */ + if (number < 0) + number = 0; + if (number > 100) + number = 100; + cmd_exec(); + jump_percent(number); + break; + + case A_GOEND: + /* + * Go to line N, default end of file. + */ + cmd_exec(); + if (number <= 0) + jump_forw(); + else + jump_back(number); + break; + + case A_GOPOS: + /* + * Go to a specified byte position in the file. + */ + cmd_exec(); + if (number < 0) + number = 0; + jump_line_loc((POSITION)number, jump_sline); + break; + + case A_STAT: + /* + * Print file name, etc. + */ + cmd_exec(); + parg.p_string = eq_message(); + error("%s", &parg); + break; + + case A_VERSION: + /* + * Print version number, without the "@(#)". + */ + cmd_exec(); + parg.p_string = version+4; + error("%s", &parg); + break; + + case A_QUIT: + /* + * Exit. + */ + quit(0); + +/* + * Define abbreviation for a commonly used sequence below. + */ +#define DO_SEARCH() if (number <= 0) number = 1; \ + mca_search(); \ + cmd_exec(); \ + multi_search((char *)NULL, number); + + + case A_F_SEARCH: + /* + * Search forward for a pattern. + * Get the first char of the pattern. + */ + search_type = SRCH_FORW; + if (number <= 0) + number = 1; + mca_search(); + c = getcc(); + goto again; + + case A_B_SEARCH: + /* + * Search backward for a pattern. + * Get the first char of the pattern. + */ + search_type = SRCH_BACK; + if (number <= 0) + number = 1; + mca_search(); + c = getcc(); + goto again; + + case A_AGAIN_SEARCH: + /* + * Repeat previous search. + */ + DO_SEARCH(); + break; + + case A_T_AGAIN_SEARCH: + /* + * Repeat previous search, multiple files. + */ + search_type |= SRCH_PAST_EOF; + DO_SEARCH(); + break; + + case A_REVERSE_SEARCH: + /* + * Repeat previous search, in reverse direction. + */ + save_search_type = search_type; + search_type = SRCH_REVERSE(search_type); + DO_SEARCH(); + search_type = save_search_type; + break; + + case A_T_REVERSE_SEARCH: + /* + * Repeat previous search, + * multiple files in reverse direction. + */ + save_search_type = search_type; + search_type = SRCH_REVERSE(search_type); + search_type |= SRCH_PAST_EOF; + DO_SEARCH(); + search_type = save_search_type; + break; + + case A_HELP: + /* + * Help. + */ + if (nohelp) + { + bell(); + break; + } + lower_left(); + clear_eol(); + putstr("help"); + cmd_exec(); + help(); + break; + + case A_EXAMINE: + /* + * Edit a new file. Get the filename. + */ + start_mca(A_EXAMINE, "Examine: "); + c = getcc(); + goto again; + + case A_VISUAL: + /* + * Invoke an editor on the input file. + */ +#if EDITOR + if (strcmp(get_filename(curr_ifile), "-") == 0) + { + error("Cannot edit standard input", NULL_PARG); + break; + } + /* + * Expand the editor prototype string + * and pass it to the system to execute. + */ + cmd_exec(); + lsystem(pr_expand(editproto, 0)); + /* + * Re-edit the file, since data may have changed. + * Some editors even recreate the file, so flushing + * buffers is not sufficient. + */ + (void) edit(get_filename(curr_ifile), 0); + break; +#else + error("Command not available", NULL_PARG); + break; +#endif + + case A_NEXT_FILE: + /* + * Examine next file. + */ + if (number <= 0) + number = 1; + if (edit_next(number)) + { + if (quit_at_eof && hit_eof) + quit(0); + parg.p_string = (number > 1) ? "(N-th) " : ""; + error("No %snext file", &parg); + } + break; + + case A_PREV_FILE: + /* + * Examine previous file. + */ + if (number <= 0) + number = 1; + if (edit_prev(number)) + { + parg.p_string = (number > 1) ? "(N-th) " : ""; + error("No %sprevious file", &parg); + } + break; + + case A_INDEX_FILE: + /* + * Examine a particular file. + */ + if (number <= 0) + number = 1; + if (edit_index(number)) + error("No such file", NULL_PARG); + break; + + case A_OPT_TOGGLE: + start_mca(A_OPT_TOGGLE, "-"); + optflag = OPT_TOGGLE; + c = getcc(); + goto again; + + case A_DISP_OPTION: + /* + * Report a flag setting. + */ + start_mca(A_DISP_OPTION, "_"); + c = getcc(); + if (c == erase_char || c == kill_char) + break; + toggle_option(c, "", OPT_NO_TOGGLE); + break; + + case A_FIRSTCMD: + /* + * Set an initial command for new files. + */ + start_mca(A_FIRSTCMD, "+"); + c = getcc(); + goto again; + + case A_SHELL: + /* + * Shell escape. + */ +#if SHELL_ESCAPE + start_mca(A_SHELL, "!"); + c = getcc(); + goto again; +#else + error("Command not available", NULL_PARG); + break; +#endif + + case A_SETMARK: + /* + * Set a mark. + */ + start_mca(A_SETMARK, "mark: "); + c = getcc(); + if (c == erase_char || c == kill_char || + c == '\n' || c == '\r') + break; + setmark(c); + break; + + case A_GOMARK: + /* + * Go to a mark. + */ + start_mca(A_GOMARK, "goto mark: "); + c = getcc(); + if (c == erase_char || c == kill_char || + c == '\n' || c == '\r') + break; + gomark(c); + break; + +#if PIPEC + case A_PIPE: + start_mca(A_PIPE, "|mark: "); + c = getcc(); + if (c == erase_char || c == kill_char) + break; + if (c == '\n' || c == '\r') + c = '.'; + if (badmark(c)) + break; + pipec = c; + start_mca(A_PIPE, "!"); + c = getcc(); + goto again; +#endif + + case A_B_BRACKET: + case A_F_BRACKET: + start_mca(action, "Brackets: "); + c = getcc(); + goto again; + + case A_PREFIX: + /* + * The command is incomplete (more chars are needed). + * Display the current char, so the user knows + * what's going on, and get another character. + */ + if (mca != A_PREFIX) + { + start_mca(A_PREFIX, " "); + cmd_reset(); + (void) cmd_char(c); + } + c = getcc(); + goto again; + + case A_NOACTION: + break; + + default: + bell(); + break; + } + } +} diff --git a/bin/less/decode.c b/bin/less/decode.c new file mode 100644 index 0000000..332fc2a --- /dev/null +++ b/bin/less/decode.c @@ -0,0 +1,373 @@ +/* + * Routines to decode user commands. + * + * This is all table driven. + * A command table is a sequence of command descriptors. + * Each command descriptor is a sequence of bytes with the following format: + * ...<0> + * The characters c1,c2,...,cN are the command string; that is, + * the characters which the user must type. + * It is terminated by a null <0> byte. + * The byte after the null byte is the action code associated + * with the command string. + * If an action byte is OR-ed with A_EXTRA, this indicates + * that the option byte is followed by an extra string. + * + * There may be many command tables. + * The first (default) table is built-in. + * Other tables are read in from "lesskey" files. + * All the tables are linked together and are searched in order. + */ +#pragma noroot +#include "less.h" +#include "cmd.h" +#if __MSDOS__ +#include +#include +#endif + +#ifdef _ORCAC_ +#include +segment "LoadSegONE"; +#endif + +/* + * Command table is ordered roughly according to expected + * frequency of use, so the common commands are near the beginning. + */ +static char cmdtable[] = +{ +#if __MSDOS__ + /* + * PC function keys. + * Note that '\0' is converted to '\200' on input. + */ + '\200','\120',0, A_F_LINE, /* down arrow */ + '\200','\121',0, A_F_SCREEN, /* page down */ + '\200','\110',0, A_B_LINE, /* up arrow */ + '\200','\111',0, A_B_SCREEN, /* page up */ + '\200','\107',0, A_GOLINE, /* home */ + '\200','\117',0, A_GOEND, /* end */ + '\200','\073',0, A_HELP, /* F1 */ + '\200','\104',0, A_MODIFY_WINDOW, /* F10 */ + '\200','\103',0, A_MODIFY_COLOURS, /* F9 */ +#endif + '\r',0, A_F_LINE, + '\n',0, A_F_LINE, + 'e',0, A_F_LINE, + 'j',0, A_F_LINE, + CONTROL('E'),0, A_F_LINE, + CONTROL('N'),0, A_F_LINE, + 'k',0, A_B_LINE, + 'y',0, A_B_LINE, + CONTROL('Y'),0, A_B_LINE, + CONTROL('K'),0, A_B_LINE, + CONTROL('P'),0, A_B_LINE, + 'J',0, A_FF_LINE, + 'K',0, A_BF_LINE, + 'Y',0, A_BF_LINE, + 'd',0, A_F_SCROLL, + CONTROL('D'),0, A_F_SCROLL, + 'u',0, A_B_SCROLL, + CONTROL('U'),0, A_B_SCROLL, + ' ',0, A_F_SCREEN, + 'f',0, A_F_SCREEN, + CONTROL('F'),0, A_F_SCREEN, + CONTROL('V'),0, A_F_SCREEN, + 'b',0, A_B_SCREEN, + CONTROL('B'),0, A_B_SCREEN, + ESC,'v',0, A_B_SCREEN, + 'z',0, A_F_WINDOW, + 'w',0, A_B_WINDOW, + 'F',0, A_F_FOREVER, + 'R',0, A_FREPAINT, + 'r',0, A_REPAINT, + CONTROL('R'),0, A_REPAINT, + CONTROL('L'),0, A_REPAINT, + 'g',0, A_GOLINE, + '<',0, A_GOLINE, + ESC,'<',0, A_GOLINE, + 'p',0, A_PERCENT, + '%',0, A_PERCENT, + '{',0, A_F_BRACKET|A_EXTRA, '{','}',0, + '}',0, A_B_BRACKET|A_EXTRA, '{','}',0, + '(',0, A_F_BRACKET|A_EXTRA, '(',')',0, + ')',0, A_B_BRACKET|A_EXTRA, '(',')',0, + '[',0, A_F_BRACKET|A_EXTRA, '[',']',0, + ']',0, A_B_BRACKET|A_EXTRA, '[',']',0, + ESC,CONTROL('F'),0, A_F_BRACKET, + ESC,CONTROL('B'),0, A_B_BRACKET, + 'G',0, A_GOEND, + ESC,'>',0, A_GOEND, + '>',0, A_GOEND, + 'P',0, A_GOPOS, + + '0',0, A_DIGIT, + '1',0, A_DIGIT, + '2',0, A_DIGIT, + '3',0, A_DIGIT, + '4',0, A_DIGIT, + '5',0, A_DIGIT, + '6',0, A_DIGIT, + '7',0, A_DIGIT, + '8',0, A_DIGIT, + '9',0, A_DIGIT, + + '=',0, A_STAT, + CONTROL('G'),0, A_STAT, + ':','f',0, A_STAT, + '/',0, A_F_SEARCH, + '?',0, A_B_SEARCH, + ESC,'/',0, A_F_SEARCH|A_EXTRA, '*',0, + ESC,'?',0, A_B_SEARCH|A_EXTRA, '*',0, + 'n',0, A_AGAIN_SEARCH, + ESC,'n',0, A_T_AGAIN_SEARCH, + 'N',0, A_REVERSE_SEARCH, + ESC,'N',0, A_T_REVERSE_SEARCH, + 'm',0, A_SETMARK, + '\'',0, A_GOMARK, + CONTROL('X'),CONTROL('X'),0, A_GOMARK, + 'E',0, A_EXAMINE, + ':','e',0, A_EXAMINE, + CONTROL('X'),CONTROL('V'),0, A_EXAMINE, + ':','n',0, A_NEXT_FILE, + ':','p',0, A_PREV_FILE, + ':','x',0, A_INDEX_FILE, + '-',0, A_OPT_TOGGLE, + ':','t',0, A_OPT_TOGGLE|A_EXTRA, 't',0, + 's',0, A_OPT_TOGGLE|A_EXTRA, 'o',0, + '_',0, A_DISP_OPTION, + '|',0, A_PIPE, + 'v',0, A_VISUAL, + '!',0, A_SHELL, + '+',0, A_FIRSTCMD, + + 'H',0, A_HELP, + 'h',0, A_HELP, + 'V',0, A_VERSION, + 'q',0, A_QUIT, + ':','q',0, A_QUIT, + ':','Q',0, A_QUIT, + 'Z','Z',0, A_QUIT, + ESC,ESC,0, A_QUIT, +}; + +/* + * Structure to support a list of command tables. + */ +struct tablelist +{ + struct tablelist *t_next; + char *t_start; + char *t_end; +}; + +/* + * Structure for the default command table. + */ +static struct tablelist deftable = + { NULL, cmdtable, cmdtable+sizeof(cmdtable) }; + +/* + * List of tables; initially contains only the default table. + */ +static struct tablelist *tables = &deftable; + +static int cmd_search(char *cmd, char *table, char *endtable, char **sp); + +extern int erase_char, kill_char; + +/* + * Decode a command character and return the associated action. + * The "extra" string, if any, is returned in sp. + */ + public int +cmd_decode(cmd, sp) + char *cmd; + char **sp; +{ + register struct tablelist *t; + register int action; + + /* + * Search thru all the command tables. + * Stop when we find an action which is not A_INVALID. + */ + for (t = tables; t != NULL; t = t->t_next) + { + action = cmd_search(cmd, t->t_start, t->t_end, sp); + if (action != A_INVALID) + break; + } + return (action); +} + +/* + * Search a command table for the current command string (in cmd). + */ + static int +cmd_search(cmd, table, endtable, sp) + char *cmd; + char *table; + char *endtable; + char **sp; +{ + register char *p; + register char *q; + register int a; + + for (p = table, q = cmd; p < endtable; p++, q++) + { + if (*p == *q) + { + /* + * Current characters match. + * If we're at the end of the string, we've found it. + * Return the action code, which is the character + * after the null at the end of the string + * in the command table. + */ + if (*p == '\0') + { + a = *++p & 0377; + /* + * Check for an "extra" string. + */ + if (a & A_EXTRA) + { + *sp = ++p; + a &= ~A_EXTRA; + } else + *sp = NULL; + return (a); + } + } else if (*q == '\0') + { + /* + * Hit the end of the user's command, + * but not the end of the string in the command table. + * The user's command is incomplete. + */ + return (A_PREFIX); + } else + { + /* + * Not a match. + * Skip ahead to the next command in the + * command table, and reset the pointer + * to the beginning of the user's command. + */ + while (*p++ != '\0') ; + if (*p & A_EXTRA) + while (*++p != '\0') ; + q = cmd-1; + } + } + /* + * No match found in the entire command table. + */ + return (A_INVALID); +} + +#if USERFILE +/* + * Set up a user command table, based on a "lesskey" file. + */ + public int +add_cmdtable(filename) + char *filename; +{ + register struct tablelist *t; + register POSITION len; + register long n; + register int f; + + /* + * Try to open the lesskey file. + * If we can't, return an error. + */ + f = open(filename, O_RDONLY); + if (f < 0) + return (-1); + + /* + * Read the file into the user table. + * We first figure out the size of the file and allocate space for it. + * {{ Minimal error checking is done here. + * A garbage .less file will produce strange results. + * To avoid a large amount of error checking code here, we + * rely on the lesskey program to generate a good .less file. }} + */ + len = filesize(f); + if (len == NULL_POSITION || len < 3) + { + /* + * Bad file (valid file must have at least 3 chars). + */ + close(f); + return (-1); + } + if ((t = (struct tablelist *) + calloc(1, sizeof(struct tablelist))) == NULL) + { + close(f); + return (-1); + } + if ((t->t_start = (char *) calloc(len, sizeof(char))) == NULL) + { + free((char *)t); + close(f); + return (-1); + } + if (lseek(f, (offset_t)0, 0) == BAD_LSEEK) + { + free(t->t_start); + free((char *)t); + close(f); + return (-1); + } + n = read(f, t->t_start, (size_t) len); + close(f); + + /* + * In a valid lesskey file, the last byte or + * the second to the last byte must be zero. + */ + if (n != len || (t->t_start[n-1] != '\0' && t->t_start[n-2] != '\0')) + { + free(t->t_start); + free((char *)t); + return (-1); + } + t->t_end = t->t_start + n; + + /* + * Link it into the list of tables. + */ + t->t_next = tables; + tables = t; + return (0); +} + +/* + * Try to add the lesskey file "$HOME/.less" + */ + public void +add_hometable(void) +{ + char *filename; + +#if __MSDOS__ + filename = homefile("_less"); +#else + filename = homefile("lessrc"); +#endif + if (filename == NULL) + return; + /* + * Ignore errors. + */ + (void) add_cmdtable(filename); + free(filename); +} +#endif diff --git a/bin/less/defines.h b/bin/less/defines.h new file mode 100644 index 0000000..7ba095b --- /dev/null +++ b/bin/less/defines.h @@ -0,0 +1,134 @@ +/* Definition file for less */ +/* Generated Sat May 2 16:21:05 CDT 1992 by linstall. */ + +/* + * Define XENIX if running under XENIX 3.0. + */ +#define XENIX 0 + +/* + * VOID is 1 if your C compiler supports the "void" type, + * 0 if it does not. + */ +#define VOID 1 + +/* + * VOID_POINTER is the definition of a pointer to any object. + */ +#define VOID_POINTER void * + +/* + * offset_t is the type which lseek() returns. + * It is also the type of lseek()'s second argument. + */ +#define offset_t long + +/* + * STAT is 1 if your system has the stat() call. + */ +#define STAT 1 + +/* + * PERROR is 1 if your system has the perror() call. + * (Actually, if it has sys_errlist, sys_nerr and errno.) + */ +#define PERROR 1 + +/* + * GET_TIME is 1 if your system has the time() call. + */ +#define GET_TIME 1 + +/* + * TERMIO is 1 if your system has /usr/include/termio.h. + * This is normally the case for System 5. + * If TERMIO is 0 your system must have /usr/include/sgtty.h. + * This is normally the case for BSD. + */ +#define TERMIO 0 + +/* + * HAS__SETJMP is 1 if your system has the _setjmp() call. + * This is normally the case only for BSD 4.2 and up, + * not for BSD 4.1 or System 5. + */ +#define HAS__SETJMP 0 + +/* + * SIGSETMASK is 1 if your system has the sigsetmask() call. + * This is normally the case only for BSD 4.2, + * not for BSD 4.1 or System 5. + */ +#define SIGSETMASK 1 + +/* + * NEED_PTEM_H is 1 if your system needs sys/ptem.h to declare struct winsize. + * This is normally the case only for SCOs System V. + */ +#define NEED_PTEM_H 0 + +/* + * REGCMP is 1 if your system has the regcmp() function. + * This is normally the case for System 5. + * RECOMP is 1 if your system has the re_comp() function. + * This is normally the case for BSD. + * If neither is 1, pattern matching is supported, but without metacharacters. + */ +#define REGCMP 0 +#define RECOMP 0 +#define REGCOMP 1 + +/* + * SHELL_ESCAPE is 1 if you wish to allow shell escapes. + * (This is possible only if your system supplies the system() function.) + */ +#define SHELL_ESCAPE 1 + +/* + * EDITOR is 1 if you wish to allow editor invocation (the "v" command). + * (This is possible only if your system supplies the system() function.) + * EDIT_PGM is the name of the (default) editor to be invoked. + */ +#define EDITOR 1 +#define EDIT_PGM "vi" + +/* + * TAGS is 1 if you wish to support tag files. + */ +#define TAGS 1 + +/* + * USERFILE is 1 if you wish to allow a .less file to specify + * user-defined key bindings. + */ +#define USERFILE 1 + +/* + * GLOB is 1 if you wish to have shell metacharacters expanded in filenames. + * This will generally work if your system provides the "popen" function + * and the "echo" shell command. + */ +#define GLOB 0 + +/* + * PIPEC is 1 if you wish to have the "|" command + * which allows the user to pipe data into a shell command. + */ +#define PIPEC 0 + +/* + * LOGFILE is 1 if you wish to allow the -l option (to create log files). + */ +#define LOGFILE 0 + +/* + * ONLY_RETURN is 1 if you want RETURN to be the only input which + * will continue past an error message. + * Otherwise, any key will continue past an error message. + */ +#define ONLY_RETURN 0 + +/* + * HELPFILE is the full pathname of the help file. + */ +#define HELPFILE "31:bin:less.hlp" diff --git a/bin/less/edit.c b/bin/less/edit.c new file mode 100644 index 0000000..417c481 --- /dev/null +++ b/bin/less/edit.c @@ -0,0 +1,461 @@ +#pragma noroot +#include "less.h" + +#if __MSDOS__ +#include +#include +#include +#include +#endif + +#ifdef _ORCAC_ +#include +#include +segment "LoadSegONE"; +#endif + +#define ISPIPE(fd) ((fd)==STDIN_FILENO) +extern int ispipe; +extern int new_file; +extern int errmsgs; +extern int quit_at_eof; +extern int file; +extern int cbufs; +extern char *every_first_cmd; +extern int any_display; +extern int force_open; +extern int is_tty; +extern IFILE curr_ifile; +extern IFILE old_ifile; +extern struct scrpos initial_scrpos; + +#if LOGFILE +extern int logfile; +extern int force_logfile; +extern char *namelogfile; +#endif + + +/* + * Edit a new file. + * Filename == "-" means standard input. + * Filename == NULL means just close the current file. + */ + public int +edit(filename, just_looking) + register char *filename; + int just_looking; +{ + register int f; + char *s; + int answer; + int no_display; + struct scrpos scrpos; + PARG parg; + + if (filename == NULL) + { + /* + * Close the current file, but don't open a new one. + */ + f = -1; + } else if (strcmp(filename, "-") == 0) + { + /* + * Use standard input. + */ + f = STDIN_FILENO; + } else if ((parg.p_string = bad_file(filename)) != NULL) + { + error("\r%s\r", &parg); + free(parg.p_string); + return (1); +#if __MSDOS__ + } else if ((f = open(filename, O_RDONLY|O_BINARY)) < 0) +#else +/* } else if ((f = open(filename, 0)) < 0)*/ + } else if ((f = open(filename, O_RDONLY|O_BINARY)) < 0) +#endif + { + parg.p_string = errno_message(filename); + error("%s", &parg); + free(parg.p_string); + return (1); + } else if (!force_open && !just_looking && bin_file(f)) + { + parg.p_string = filename; + answer = query("\"%s\" may be a binary file. Continue? ", + &parg); + if (answer != 'y' && answer != 'Y') + { + close(f); + return (1); + } + } + + if (f >= 0 && isatty(f)) + { + /* + * Not really necessary to call this an error, + * but if the control terminal (for commands) + * and the input file (for data) are the same, + * we get weird results at best. + */ +#if __MSDOS__ + parg.p_string = "less -?"; +#else + parg.p_string = "less -\\?"; +#endif + error("Cannot take input from a terminal (\"%s\" for help)", + &parg); + if (!ISPIPE(f)) + close(f); + return (1); + } + +#if LOGFILE + s = namelogfile; + end_logfile(); + if (f >= 0 && ISPIPE(f) && s != NULL && is_tty) + use_logfile(s); +#endif + + /* + * We are now committed to using the new file. + * Close the current input file and set up to use the new one. + */ + if (curr_ifile != NULL_IFILE) + { + /* + * Save the current position so that we can return to + * the same position if we edit this file again. + */ + get_scrpos(&scrpos); + if (scrpos.pos != NULL_POSITION) + { + store_pos(curr_ifile, &scrpos); + lastmark(); + } + } + + /* + * Close the current file, unless it is a pipe. + */ + if (!ISPIPE(file)) + close(file); + file = f; + + if (f < 0) + return (1); + + /* + * Get the new ifile. + * Get the saved position for that file. + */ + old_ifile = curr_ifile; + curr_ifile = get_ifile(filename, curr_ifile); + get_pos(curr_ifile, &initial_scrpos); + + ispipe = ISPIPE(f); + if (ispipe) + ch_pipe(); + else + ch_nonpipe(); + (void) ch_nbuf(cbufs); + ch_flush(); + + new_file = 1; + +#if __MSDOS__ + top_filename(); +#endif + + if (every_first_cmd != NULL) + ungetsc(every_first_cmd); + + no_display = !any_display; + flush(); + any_display = 1; + + if (is_tty) + { + /* + * Output is to a real tty. + */ + + /* + * Indicate there is nothing displayed yet. + */ + pos_clear(); + clr_linenum(); + if (no_display && errmsgs > 0) + { + /* + * We displayed some messages on error output + * (file descriptor 2; see error() function). + * Before erasing the screen contents, + * display the file name and wait for a keystroke. + */ + parg.p_string = filename; + error("%s", &parg); + } + } + return (0); +} + +/* + * Edit a space-separated list of files. + * For each filename in the list, enter it into the ifile list. + * Then edit the first one. + */ + public void +edit_list(list) + char *list; +{ + register char *s; + register char *es; + register char *filename; + char *good_filename; + IFILE save_curr_ifile; + + /* + * good_filename keeps track of the first valid filename. + */ + good_filename = NULL; + s = list; + es = s + strlen(s); + save_curr_ifile = curr_ifile; + while ((s = skipsp(s)) < es) + { + /* + * Get the next filename and null terminate it. + */ + filename = s; + while (*s != ' ' && *s != '\0') + s++; + if (*s != '\0') + *s++ = '\0'; + /* + * Try to edit the file. + * This enters it into the command line list (if it is good). + * If it is the first good file we've seen, remember it. + * {{ A little weirdness here: if any of the filenames + * are already in the list, subsequent ones get + * entered after the position where that one already + * was, instead of at the end. }} + */ + if (edit(filename, 1) == 0 && good_filename == NULL) + good_filename = filename; + } + + /* + * Edit the first valid filename in the list. + */ + if (good_filename != NULL) + { + curr_ifile = save_curr_ifile; + (void) edit(good_filename, 0); + } +} + +/* + * Edit the first file in the command line (ifile) list. + */ + public int +edit_first(void) +{ + curr_ifile = NULL_IFILE; + return (edit_next(1)); +} + +/* + * Edit the last file in the command line (ifile) list. + */ + public int +edit_last(void) +{ + curr_ifile = NULL_IFILE; + return (edit_prev(1)); +} + + +/* + * Edit the next file in the command line (ifile) list. + */ + public int +edit_next(n) + int n; +{ + IFILE h; + + h = curr_ifile; + while (--n >= 0 || edit(get_filename(h), 0)) + { + if ((h = next_ifile(h)) == NULL_IFILE) + /* + * Reached end of the ifile list. + */ + return (1); + } + /* + * Found a file that we can edit. + */ + return (0); +} + +/* + * Edit the previous file in the command line list. + */ + public int +edit_prev(n) + int n; +{ + IFILE h; + + h = curr_ifile; + while (--n >= 0 || edit(get_filename(h), 0)) + { + if ((h = prev_ifile(h)) == NULL_IFILE) + /* + * Reached beginning of the ifile list. + */ + return (1); + } + /* + * Found a file that we can edit. + */ + return (0); +} + +/* + * Edit a specific file in the command line (ifile) list. + */ + public int +edit_index(n) + int n; +{ + IFILE h; + + h = NULL_IFILE; + do + { + if ((h = next_ifile(h)) == NULL_IFILE) + { + /* + * Reached end of the list without finding it. + */ + return (1); + } + } while (get_index(h) != n); + + return (edit(get_filename(h), 0)); +} + +/* + * Copy a file directly to standard output. + * Used if standard output is not a tty. + */ + public void +cat_file(void) +{ + register int c; + + while ((c = ch_forw_get()) != EOI) + putchr(c); + flush(); +} + +#if LOGFILE + +/* + * If the user asked for a log file and our input file + * is standard input, create the log file. + * We take care not to blindly overwrite an existing file. + */ + public void +use_logfile(filename) + char *filename; +{ + register int exists; + register int answer; + PARG parg; + + /* + * {{ We could use access() here. }} + */ +/* exists = open(filename, 0);*/ + exists = open(filename, O_RDWR); + close(exists); + exists = (exists >= 0); + + /* + * Decide whether to overwrite the log file or append to it. + * (If it doesn't exist we "overwrite" it. + */ + if (!exists || force_logfile) + { + /* + * Overwrite (or create) the log file. + */ + answer = 'O'; + } else + { + /* + * Ask user what to do. + */ + parg.p_string = filename; + answer = query("Warning: \"%s\" exists; Overwrite, Append or Don't log? ", &parg); + } + +loop: + switch (answer) + { + case 'O': case 'o': + /* + * Overwrite: create the file. + */ + logfile = creat(filename, 0644); + break; + case 'A': case 'a': + /* + * Append: open the file and seek to the end. + */ +#if __MSDOS__ + logfile = open(filename, O_APPEND|O_WRONLY); +#else +/* logfile = open(filename, 1);*/ + logfile = open(filename, O_APPEND|O_WRONLY); +#endif + if (lseek(logfile, (offset_t)0, 2) == BAD_LSEEK) + { + close(logfile); + logfile = -1; + } + break; + case 'D': case 'd': + /* + * Don't do anything. + */ + return; + case 'q': + quit(0); + /*NOTREACHED*/ + default: + /* + * Eh? + */ + answer = query("Overwrite, Append, or Don't log? ", NULL_PARG); + goto loop; + } + + if (logfile < 0) + { + /* + * Error in opening logfile. + */ + parg.p_string = filename; + error("Cannot write to \"%s\"", &parg); + } +} + +#endif diff --git a/bin/less/filename.c b/bin/less/filename.c new file mode 100644 index 0000000..124cb6e --- /dev/null +++ b/bin/less/filename.c @@ -0,0 +1,369 @@ +/* + * Routines to mess around with filenames (and files). + * Much of this is very OS dependent. + */ +#pragma noroot +#include +#include +#include "less.h" +#include + +#ifdef _ORCAC_ +segment "LoadSegONE"; +#endif + +extern char *getenv(char *); + +extern int force_open; +extern IFILE curr_ifile; +extern IFILE old_ifile; + +static POSITION seek_filesize(int f); + +/* + * Return the full pathname of the given file in the "home directory". + */ + public char * +homefile(filename) + char *filename; +{ + register char *pathname; + register char *homedir; + + homedir = getenv("HOME"); +#if __MSDOS__ + /* + * Most MSDOS users do not have $HOME defined, + * so if no $HOME then look for "_less" anywhere + * on search path (always begins at current directory). + */ + if (homedir == NULL) + { + extern char *searchpath(); + pathname = searchpath(filename); + if (pathname == NULL) + return (NULL); + pathname = save(pathname); + } else + { + pathname = (char *) calloc(strlen(homedir)+strlen(filename)+2, + sizeof(char)); + if (pathname == NULL) + return (NULL); + sprintf(pathname, "%s\\%s", homedir, filename); + } +#else + if (homedir == NULL) + return (NULL); + pathname = (char *) calloc(strlen(homedir)+strlen(filename)+2, + sizeof(char)); + if (pathname == NULL) + return (NULL); + sprintf(pathname, "%s/%s", homedir, filename); +#endif + return (pathname); +} + +/* + * Find out where the help file is. + */ + public char * +find_helpfile(void) +{ + register char *helpfile; +#if __MSDOS__ + extern char *searchpath(); + + /* + * Look in current directory. + */ + if (access(HELPFILE,0) == 0) + return (HELPFILE); + /* + * Find the basename of HELPFILE, + * and look for it in each directory in the search path. + */ + if ((helpfile = strrchr(HELPFILE, '\\')) == NULL) + helpfile = HELPFILE; + else + helpfile++; + return (save(searchpath(helpfile))); +#else + if ((helpfile = getenv("LESSHELP")) != NULL) + return (save(helpfile)); + return (save(HELPFILE)); +#endif +} + +/* + * Expand a string, substituting any "%" with the current filename, + * and any "#" with the previous filename. + */ + public char * +fexpand(s) + char *s; +{ + register char *fr, *to; + register int n; + register char *e; + + /* + * Make one pass to see how big a buffer we + * need to allocate for the expanded string. + */ + n = 0; + for (fr = s; *fr != '\0'; fr++) + { + switch (*fr) + { + case '%': + n += strlen(get_filename(curr_ifile)); + break; + case '#': + if (old_ifile == NULL_IFILE) + { + error("No previous file", NULL_PARG); + return (NULL); + } + n += strlen(get_filename(old_ifile)); + break; + default: + n++; + break; + } + } + + e = (char *) ecalloc(n+1, sizeof(char)); + + /* + * Now copy the string, expanding any "%" or "#". + */ + to = e; + for (fr = s; *fr != '\0'; fr++) + { + switch (*fr) + { + case '%': + strcpy(to, get_filename(curr_ifile)); + to += strlen(to); + break; + case '#': + strcpy(to, get_filename(old_ifile)); + to += strlen(to); + break; + default: + *to++ = *fr; + break; + } + } + *to = '\0'; + return (e); +} + +/* + * Try to determine if a file is "binary". + * This is just a guess, and we need not try too hard to make it accurate. + */ + int +bin_file(f) + int f; +{ + int i; + int n; + char data[64]; + + n = read(f, data, sizeof(data)); + for (i = 0; i < n; i++) + if (binary_char((int) data[i])) + return (1); + return (0); +} + +/* + * Try to determine the size of a file by seeking to the end. + */ + static POSITION +seek_filesize(f) + int f; +{ + offset_t spos; + + spos = lseek(f, (offset_t)0, 2); + if (spos == BAD_LSEEK) + return (NULL_POSITION); + return ((POSITION) spos); +} + +/* + * Expand a filename, substituting any environment variables, etc. + */ +#if GLOB + +FILE *popen(char *command, char *type); + + public char * +glob(filename) + char *filename; +{ + FILE *f; + char *p; + int ch; + int len; + char *cmd; + char *gfilename; + + filename = fexpand(filename); + if (filename == NULL) + return (NULL); + + /* + * We get the shell to expand the filename for us by passing + * an "echo" command to the shell and reading its output. + */ + p = getenv("SHELL"); + if (p == NULL || *p == '\0') + { + /* + * Read the output of . + */ + cmd = (char *) ecalloc(strlen(filename)+6, sizeof(char)); + sprintf(cmd, "echo %s", filename); + } else + { + /* + * Read the output of <$SHELL -c "echo filename">. + */ + cmd = (char *) ecalloc(strlen(p)+strlen(filename)+12, sizeof(char)); + sprintf(cmd, "%s -c \"echo %s\"", p, filename); + } + + f = popen(cmd, "r"); + free(cmd); + if (f == NULL) + return (filename); + free(filename); + + len = 100; + gfilename = (char *) ecalloc(len, sizeof(char)); + for (p = gfilename; ; p++) + { + if ((ch = getc(f)) == '\n' || ch == EOF) + break; + if (p - gfilename >= len-1) + { + len *= 2; + *p = '\0'; + p = (char *) ecalloc(len, sizeof(char)); + strcpy(p, gfilename); + free(gfilename); + gfilename = p; + p = gfilename + strlen(gfilename); + } + *p = ch; + } + *p = '\0'; + pclose(f); + if (*gfilename == '\0') + return (NULL); + return (gfilename); +} + +#else + + public char * +glob(filename) + char *filename; +{ + return (fexpand(filename)); +} + +#endif + + +#if STAT + +#include +#include + +/* + * Returns NULL if the file can be opened and + * is an ordinary file, otherwise an error message + * (if it cannot be opened or is a directory, etc.) + */ + public char * +bad_file(filename) + char *filename; +{ + register char *m; + struct stat statbuf; + + if (stat(filename, &statbuf) < 0) + return (errno_message(filename)); + + if (force_open) + return (NULL); + + if ((statbuf.st_mode & S_IFMT) == S_IFDIR) + { + static char is_dir[] = " is a directory"; + m = (char *) ecalloc(strlen(filename) + sizeof(is_dir), + sizeof(char)); + strcpy(m, filename); + strcat(m, is_dir); + return (m); + } + if ((statbuf.st_mode & S_IFMT) != S_IFREG) + { + static char not_reg[] = " is not a regular file"; + m = (char *) ecalloc(strlen(filename) + sizeof(not_reg), + sizeof(char)); + strcpy(m, filename); + strcat(m, not_reg); + return (m); + } + + return (NULL); +} + +/* + * Return the size of a file, as cheaply as possible. + * In Unix, we can stat the file. + */ + public POSITION +filesize(f) + int f; +{ + struct stat statbuf; + + if (fstat(f, &statbuf) < 0) + /* + * Can't stat; try seeking to the end. + */ + return (seek_filesize(f)); + + return ((POSITION) statbuf.st_size); +} + +#else + +/* + * If we have no way to find out, just say the file is good. + */ + public char * +bad_file(filename) + char *filename; +{ + return (NULL); +} + +/* + * We can find the file size by seeking. + */ + public POSITION +filesize(f) + int f; +{ + return (seek_filesize(f)); +} + +#endif diff --git a/bin/less/forwback.c b/bin/less/forwback.c new file mode 100644 index 0000000..470797b --- /dev/null +++ b/bin/less/forwback.c @@ -0,0 +1,380 @@ +/* + * Primitives for displaying the file on the screen, + * scrolling either forward or backward. + */ +#pragma noroot +#include "less.h" +#include "position.h" + +#ifdef _ORCAC_ +segment "LoadSegONE"; +#endif + +public int hit_eof; /* Keeps track of how many times we hit end of file */ +public int screen_trashed; +public int squished; + +extern int sigs; +extern int top_scroll; +extern int quiet; +extern int sc_width, sc_height; +extern int quit_at_eof; +extern int plusoption; +extern int forw_scroll; +extern int back_scroll; +extern int need_clr; +extern int ignore_eoi; +#if TAGS +extern int tagoption; +#endif + +static void eof_bell(void); +static void eof_check(void); +static void squish_check(void); + +/* + * Sound the bell to indicate user is trying to move past end of file. + */ + static void +eof_bell(void) +{ + if (quiet == NOT_QUIET) + bell(); + else + vbell(); +} + +/* + * Check to see if the end of file is currently "displayed". + */ + static void +eof_check(void) +{ + POSITION pos; + + if (ignore_eoi) + return; + if (sigs) + return; + /* + * If the bottom line is empty, we are at EOF. + * If the bottom line ends at the file length, + * we must be just at EOF. + */ + pos = position(BOTTOM_PLUS_ONE); + if (pos == NULL_POSITION || pos == ch_length()) + hit_eof++; +} + +/* + * If the screen is "squished", repaint it. + * "Squished" means the first displayed line is not at the top + * of the screen; this can happen when we display a short file + * for the first time. + */ + static void +squish_check(void) +{ + if (!squished) + return; + squished = 0; + repaint(); +} + +/* + * Display n lines, scrolling forward, + * starting at position pos in the input file. + * "force" means display the n lines even if we hit end of file. + * "only_last" means display only the last screenful if n > screen size. + * "nblank" is the number of blank lines to draw before the first + * real line. If nblank > 0, the pos must be NULL_POSITION. + * The first real line after the blanks will start at ch_zero(). + */ + public void +forw(n, pos, force, only_last, nblank) + register int n; + POSITION pos; + int force; + int only_last; + int nblank; +{ + int eof = 0; + int nlines = 0; + int do_repaint; + static int first_time = 1; + + squish_check(); + + /* + * do_repaint tells us not to display anything till the end, + * then just repaint the entire screen. + * We repaint if we are supposed to display only the last + * screenful and the request is for more than a screenful. + * Also if the request exceeds the forward scroll limit + * (but not if the request is for exactly a screenful, since + * repainting itself involves scrolling forward a screenful). + */ + do_repaint = (only_last && n > sc_height-1) || + (forw_scroll >= 0 && n > forw_scroll && n != sc_height-1); + + if (!do_repaint) + { + if (top_scroll && n >= sc_height - 1 && pos != ch_length()) + { + /* + * Start a new screen. + * {{ This is not really desirable if we happen + * to hit eof in the middle of this screen, + * but we don't yet know if that will happen. }} + */ + if (top_scroll == 2 || first_time) + clear(); + home(); + force = 1; + } else + { + lower_left(); + clear_eol(); + } + + if (pos != position(BOTTOM_PLUS_ONE) || empty_screen()) + { + /* + * This is not contiguous with what is + * currently displayed. Clear the screen image + * (position table) and start a new screen. + */ + pos_clear(); + add_forw_pos(pos); + force = 1; + if (top_scroll) + { + if (top_scroll == 2) + clear(); + home(); + } else if (!first_time) + { + putstr("...skipping...\r"); + } + } + } + + while (--n >= 0) + { + /* + * Read the next line of input. + */ + if (nblank > 0) + { + /* + * Still drawing blanks; don't get a line + * from the file yet. + * If this is the last blank line, get ready to + * read a line starting at ch_zero() next time. + */ + if (--nblank == 0) + pos = ch_zero(); + } else + { + /* + * Get the next line from the file. + */ + pos = forw_line(pos); + if (pos == NULL_POSITION) + { + /* + * End of file: stop here unless the top line + * is still empty, or "force" is true. + */ + eof = 1; + if (!force && position(TOP) != NULL_POSITION) + break; + } + } + /* + * Add the position of the next line to the position table. + * Display the current line on the screen. + */ + add_forw_pos(pos); + nlines++; + if (do_repaint) + continue; + /* + * If this is the first screen displayed and + * we hit an early EOF (i.e. before the requested + * number of lines), we "squish" the display down + * at the bottom of the screen. + * But don't do this if a + option or a -t option + * was given. These options can cause us to + * start the display after the beginning of the file, + * and it is not appropriate to squish in that case. + */ + if (first_time && pos == NULL_POSITION && !top_scroll && +#if TAGS + !tagoption && +#endif + !plusoption) + { + squished = 1; + continue; + } + if (top_scroll == 1) + clear_eol(); + put_line(); + } + + if (ignore_eoi) + hit_eof = 0; + else if (eof && !sigs) + hit_eof++; + else + eof_check(); + if (nlines == 0) + eof_bell(); + else if (do_repaint) + repaint(); + first_time = 0; + (void) currline(BOTTOM); +} + +/* + * Display n lines, scrolling backward. + */ + public void +back(n, pos, force, only_last) + register int n; + POSITION pos; + int force; + int only_last; +{ + int nlines = 0; + int do_repaint; + + squish_check(); + do_repaint = (n > get_back_scroll() || (only_last && n > sc_height-1)); + hit_eof = 0; + while (--n >= 0) + { + /* + * Get the previous line of input. + */ + pos = back_line(pos); + if (pos == NULL_POSITION) + { + /* + * Beginning of file: stop here unless "force" is true. + */ + if (!force) + break; + } + /* + * Add the position of the previous line to the position table. + * Display the line on the screen. + */ + add_back_pos(pos); + nlines++; + if (!do_repaint) + { + home(); + add_line(); + put_line(); + } + } + + eof_check(); + if (nlines == 0) + eof_bell(); + else if (do_repaint) + repaint(); + (void) currline(BOTTOM); +} + +/* + * Display n more lines, forward. + * Start just after the line currently displayed at the bottom of the screen. + */ + public void +forward(n, force, only_last) + int n; + int force; + int only_last; +{ + POSITION pos; + + if (quit_at_eof && hit_eof) + { + /* + * If the -e flag is set and we're trying to go + * forward from end-of-file, go on to the next file. + */ + if (edit_next(1)) + quit(0); + return; + } + + pos = position(BOTTOM_PLUS_ONE); + if (pos == NULL_POSITION && (!force || empty_lines(2, sc_height-1))) + { + if (ignore_eoi) + { + /* + * ignore_eoi is to support A_F_FOREVER. + * Back up until there is a line at the bottom + * of the screen. + */ + if (empty_screen()) + pos = ch_zero(); + else + { + do + { + back(1, position(TOP), 1, 0); + pos = position(BOTTOM_PLUS_ONE); + } while (pos == NULL_POSITION); + } + } else + { + eof_bell(); + hit_eof++; + return; + } + } + forw(n, pos, force, only_last, 0); +} + +/* + * Display n more lines, backward. + * Start just before the line currently displayed at the top of the screen. + */ + public void +backward(n, force, only_last) + int n; + int force; + int only_last; +{ + POSITION pos; + + pos = position(TOP); + if (pos == NULL_POSITION && (!force || position(BOTTOM) == 0)) + { + eof_bell(); + return; + } + back(n, pos, force, only_last); +} + +/* + * Get the backwards scroll limit. + * Must call this function instead of just using the value of + * back_scroll, because the default case depends on sc_height and + * top_scroll, as well as back_scroll. + */ + public int +get_back_scroll(void) +{ + if (back_scroll >= 0) + return (back_scroll); + if (top_scroll) + return (sc_height - 2); + return (10000); /* infinity */ +} diff --git a/bin/less/gsos.c b/bin/less/gsos.c new file mode 100644 index 0000000..8e6658e --- /dev/null +++ b/bin/less/gsos.c @@ -0,0 +1,32 @@ +#pragma noroot +#pragma optimize -1 +#include +#include +#include +#include + +char *getenv(char *name) +{ +Get_VarPB gv; +char *vn; +int l; + + l = strlen(name); + gv.var_name = malloc(l+1); + gv.var_name[0] = l; memcpy(gv.var_name+1,name,(size_t)l); + gv.value = malloc(256l); + GET_VAR(&gv); + gv.value[gv.value[0]+1] = 0; + free(gv.var_name); + if (gv.value[0] == 0) { free(gv.value); return NULL; } + vn = malloc(gv.value[0]+1); + memcpy(vn,gv.value+1,gv.value[0]); + vn[gv.value[0]] = 0; + free(gv.value); + return vn; +} + +/*void exit(int blah) +{ + rexit(blah); +} */ diff --git a/bin/less/help.c b/bin/less/help.c new file mode 100644 index 0000000..c8b2960 --- /dev/null +++ b/bin/less/help.c @@ -0,0 +1,58 @@ +/* + * Display some help. + * Just invoke another "less" to display the help file. + * + * {{ This makes this function very simple, and makes changing the + * help file very easy, but it may present difficulties on + * (non-Unix) systems which do not supply the "system()" function. }} + */ +#pragma noroot +#include "less.h" + +#if __MSDOS__ +#include +#include +#include +#include +extern int output_mode; +#endif + +#ifdef _ORCAC_ +segment "LoadSegONE"; +#endif + +extern char *progname; + + public void +help(void) +{ + char *helpfile; + char *cmd; + + helpfile = find_helpfile(); + if (helpfile == NULL) + { + error("Cannot find help file", NULL_PARG); + return; + } +#if __MSDOS__ + putenv("LESS=-+v -+E -+s -mHPmHELP -- ?eEND -- Press g to see " + "it again:Press RETURN for more., or q when done "); + cmd = (char *) ecalloc(strlen(helpfile) + strlen(progname) + 50, + sizeof(char)); + if (output_mode == 0) + sprintf(cmd, "-%s %s", progname, helpfile); + else + sprintf(cmd, "-%s -qVW4,4,76,23,Help %s", progname, helpfile); +#else + cmd = (char *) ecalloc(strlen(helpfile) + strlen(progname) + 150, + sizeof(char)); + sprintf(cmd, + "-%s -m -H -+E -+s '-PmHELP -- ?eEND -- Press g to see it again:Press RETURN for more., or q when done ' %s", + progname, helpfile); +#endif + free(helpfile); + lsystem(cmd); + error("End of help", NULL_PARG); + free(cmd); +} diff --git a/bin/less/ifile.c b/bin/less/ifile.c new file mode 100644 index 0000000..314d01f --- /dev/null +++ b/bin/less/ifile.c @@ -0,0 +1,199 @@ +/* + * An IFILE represents an input file. + * + * It is actually a pointer to an ifile structure, + * but is opaque outside this module. + * Ifile structures are kept in a linked list in the order they + * appear on the command line. + * Any new file which does not already appear in the list is + * inserted after the current file. + */ +#pragma noroot +#include "less.h" + +#ifdef _ORCAC_ +segment "LoadSegONE"; +#endif + +struct ifile { + struct ifile *h_next; /* Links for command line list */ + struct ifile *h_prev; + int h_index; /* Index within command line list */ + char *h_filename; /* Name of the file */ + struct scrpos h_scrpos; /* Saved position within the file */ +}; + +/* + * Convert an IFILE (external representation) + * to a struct file (internal representation), and vice versa. + */ +#define int_ifile(h) ((struct ifile *)(h)) +#define ext_ifile(h) ((IFILE)(h)) + +/* + * Anchor for linked list. + */ +static struct ifile anchor = { &anchor, &anchor, 0 }; +static int ifiles = 0; + +static struct ifile *new_ifile(char *filename, struct ifile *prev); +static struct ifile *find_ifile(char *filename); + +/* + * Allocate a new ifile structure and stick a filename in it. + * It should go after "prev" in the list + * (or at the beginning of the list if "prev" is NULL). + * Return a pointer to the new ifile structure. + */ + static struct ifile * +new_ifile(filename, prev) + char *filename; + struct ifile *prev; +{ + register struct ifile *p; + register struct ifile *np; + + /* + * Allocate and initialize structure. + */ + p = (struct ifile *) ecalloc(1, sizeof(struct ifile)); + p->h_filename = filename; + p->h_scrpos.pos = NULL_POSITION; + + /* + * Link into list. + */ + if (prev == NULL) + prev = &anchor; + p->h_next = prev->h_next; + p->h_prev = prev; + prev->h_next->h_prev = p; + prev->h_next = p; + + /* + * Calculate index for the new one, + * and adjust the indexes for subsequent ifiles in the list. + */ + p->h_index = prev->h_index + 1; + for (np = p->h_next; np != &anchor; np = np->h_next) + np->h_index++; + + ifiles++; + return (p); +} + +/* + * Get the ifile after a given one in the list. + */ + public IFILE +next_ifile(h) + IFILE h; +{ + register struct ifile *p; + + p = (h == NULL_IFILE) ? &anchor : int_ifile(h); + if (p->h_next == &anchor) + return (NULL_IFILE); + return (ext_ifile(p->h_next)); +} + +/* + * Get the ifile before a given one in the list. + */ + public IFILE +prev_ifile(h) + IFILE h; +{ + register struct ifile *p; + + p = (h == NULL_IFILE) ? &anchor : int_ifile(h); + if (p->h_prev == &anchor) + return (NULL_IFILE); + return (ext_ifile(p->h_prev)); +} + +/* + * Return the number of ifiles. + */ + public int +nifile(void) +{ + return (ifiles); +} + +/* + * Find an ifile structure, given a filename. + */ + static struct ifile * +find_ifile(filename) + char *filename; +{ + register struct ifile *p; + + for (p = anchor.h_next; p != &anchor; p = p->h_next) + if (strcmp(filename, p->h_filename) == 0) + return (p); + return (NULL); +} + +/* + * Get the ifile associated with a filename. + * If the filename has not been seen before, + * insert the new ifile after "prev" in the list. + */ + public IFILE +get_ifile(filename, prev) + char *filename; + IFILE prev; +{ + register struct ifile *p; + + if ((p = find_ifile(filename)) == NULL) + p = new_ifile(save(filename), int_ifile(prev)); + return (ext_ifile(p)); +} + +/* + * Get the filename associated with a ifile. + */ + public char * +get_filename(ifile) + IFILE ifile; +{ + if (ifile == NULL) + return (NULL); + return (int_ifile(ifile)->h_filename); +} + +/* + * Get the index of the file associated with a ifile. + */ + public int +get_index(ifile) + IFILE ifile; +{ + return (int_ifile(ifile)->h_index); +} + +/* + * Save the file position to be associated with a given file. + */ + public void +store_pos(ifile, scrpos) + IFILE ifile; + struct scrpos *scrpos; +{ + int_ifile(ifile)->h_scrpos = *scrpos; +} + +/* + * Recall the file position associated with a file. + * If no position has been associated with the file, return NULL_POSITION. + */ + public void +get_pos(ifile, scrpos) + IFILE ifile; + struct scrpos *scrpos; +{ + *scrpos = int_ifile(ifile)->h_scrpos; +} diff --git a/bin/less/input.c b/bin/less/input.c new file mode 100644 index 0000000..01fed50 --- /dev/null +++ b/bin/less/input.c @@ -0,0 +1,270 @@ +/* + * High level routines dealing with getting lines of input + * from the file being viewed. + * + * When we speak of "lines" here, we mean PRINTABLE lines; + * lines processed with respect to the screen width. + * We use the term "raw line" to refer to lines simply + * delimited by newlines; not processed with respect to screen width. + */ +#pragma noroot +#include "less.h" + +#ifdef _ORCAC_ +segment "LoadSegONE"; +#endif + +extern int squeeze; +extern int chopline; +extern int sigs; + +/* + * Get the next line. + * A "current" position is passed and a "new" position is returned. + * The current position is the position of the first character of + * a line. The new position is the position of the first character + * of the NEXT line. The line obtained is the line starting at curr_pos. + */ + public POSITION +forw_line(curr_pos) + POSITION curr_pos; +{ + POSITION new_pos; + register int c; + int blankline; + int endline; + + if (curr_pos == NULL_POSITION || ch_seek(curr_pos)) + { + null_line(); + return (NULL_POSITION); + } + + prewind(); + plinenum(curr_pos); + (void) ch_seek(curr_pos); + + c = ch_forw_get(); + if (c == EOI) + { + null_line(); + return (NULL_POSITION); + } + blankline = (c == '\n' || c == '\r'); + + for (;;) + { + if (sigs) + { + null_line(); + return (NULL_POSITION); + } +/* if (c == '\n' || c == EOI) */ + if (c == '\r' || c == '\n' || c == EOI) /* switch CR and LF meanings */ + { + /* + * End of the line. + */ + new_pos = ch_tell(); + endline = 1; + break; + } + + /* + * Append the char to the line and get the next char. + */ + if (pappend(c)) + { + /* + * The char won't fit in the line; the line + * is too long to print in the screen width. + * End the line here. + */ + if (chopline) + { + do + { + c = ch_forw_get(); +/* } while (c != '\n' && c != EOI); */ + } while (c != '\r' && c != '\n' && c != EOI); + new_pos = ch_tell(); + endline = 1; + } else + { + new_pos = ch_tell() - 1; + endline = 0; + } + break; + } + c = ch_forw_get(); + } + pdone(endline); + + if (squeeze && blankline) + { + /* + * This line is blank. + * Skip down to the last contiguous blank line + * and pretend it is the one which we are returning. + */ + while ((c = ch_forw_get()) == '\n' || c == '\r') + if (sigs) + { + null_line(); + return (NULL_POSITION); + } + if (c != EOI) + (void) ch_back_get(); + new_pos = ch_tell(); + } + + return (new_pos); +} + +/* + * Get the previous line. + * A "current" position is passed and a "new" position is returned. + * The current position is the position of the first character of + * a line. The new position is the position of the first character + * of the PREVIOUS line. The line obtained is the one starting at new_pos. + */ + public POSITION +back_line(curr_pos) + POSITION curr_pos; +{ + POSITION new_pos, begin_new_pos; + int c; + int endline; + + if (curr_pos == NULL_POSITION || curr_pos <= ch_zero() || + ch_seek(curr_pos-1)) + { + null_line(); + return (NULL_POSITION); + } + + if (squeeze) + { + /* + * Find out if the "current" line was blank. + */ + (void) ch_forw_get(); /* Skip the newline */ + c = ch_forw_get(); /* First char of "current" line */ + (void) ch_back_get(); /* Restore our position */ + (void) ch_back_get(); + +/* if (c == '\n') */ + if (c == '\r' || c == '\n') + { + /* + * The "current" line was blank. + * Skip over any preceding blank lines, + * since we skipped them in forw_line(). + */ + while ((c = ch_back_get()) == '\n' || c == '\r') + if (sigs) + { + null_line(); + return (NULL_POSITION); + } + if (c == EOI) + { + null_line(); + return (NULL_POSITION); + } + (void) ch_forw_get(); + } + } + + /* + * Scan backwards until we hit the beginning of the line. + */ + for (;;) + { + if (sigs) + { + null_line(); + return (NULL_POSITION); + } + c = ch_back_get(); +/* if (c == '\n') */ + if (c == '\r' || c == '\n') + { + /* + * This is the newline ending the previous line. + * We have hit the beginning of the line. + */ + new_pos = ch_tell() + 1; + break; + } + if (c == EOI) + { + /* + * We have hit the beginning of the file. + * This must be the first line in the file. + * This must, of course, be the beginning of the line. + */ + new_pos = ch_tell(); + break; + } + } + + /* + * Now scan forwards from the beginning of this line. + * We keep discarding "printable lines" (based on screen width) + * until we reach the curr_pos. + * + * {{ This algorithm is pretty inefficient if the lines + * are much longer than the screen width, + * but I don't know of any better way. }} + */ + if (ch_seek(new_pos)) + { + null_line(); + return (NULL_POSITION); + } + endline = 0; + loop: + begin_new_pos = new_pos; + prewind(); + plinenum(new_pos); + (void) ch_seek(new_pos); + + do + { + c = ch_forw_get(); + if (c == EOI || sigs) + { + null_line(); + return (NULL_POSITION); + } + new_pos++; +/* if (c == '\n') */ + if (c == '\r' || c == '\n') + { + endline = 1; + break; + } + if (pappend(c)) + { + /* + * Got a full printable line, but we haven't + * reached our curr_pos yet. Discard the line + * and start a new one. + */ + if (chopline) + { + endline = 1; + break; + } + pdone(0); + (void) ch_back_get(); + new_pos--; + goto loop; + } + } while (new_pos < curr_pos); + + pdone(endline); + + return (begin_new_pos); +} diff --git a/bin/less/jump.c b/bin/less/jump.c new file mode 100644 index 0000000..be2bff1 --- /dev/null +++ b/bin/less/jump.c @@ -0,0 +1,267 @@ +/* + * Routines which jump to a new location in the file. + */ +#pragma noroot +#include "less.h" +#include "position.h" + +#ifdef _ORCAC_ +segment "LoadSegONE"; +#endif + +extern int hit_eof; +extern int jump_sline; +extern int squished; +extern int screen_trashed; +extern int sc_width, sc_height; + +/* + * Jump to the end of the file. + */ + public void +jump_forw(void) +{ + POSITION pos; + + if (ch_end_seek()) + { + error("Cannot seek to end of file", NULL_PARG); + return; + } + /* + * Position the last line in the file at the last screen line. + * Go back one line from the end of the file + * to get to the beginning of the last line. + */ + pos = back_line(ch_tell()); + if (pos == NULL_POSITION) + jump_loc((POSITION)0, sc_height-1); + else + jump_loc(pos, sc_height-1); +} + +/* + * Jump to line n in the file. + */ + public void +jump_back(n) + int n; +{ + POSITION pos; + PARG parg; + + /* + * Find the position of the specified line. + * If we can seek there, just jump to it. + * If we can't seek, but we're trying to go to line number 1, + * use ch_beg_seek() to get as close as we can. + */ + pos = find_pos(n); + if (pos != NULL_POSITION && ch_seek(pos) == 0) + { + jump_loc(pos, jump_sline); + } else if (n <= 1 && ch_beg_seek() == 0) + { + jump_loc(ch_tell(), jump_sline); + error("Cannot seek to beginning of file", NULL_PARG); + } else + { + parg.p_int = n; + error("Cannot seek to line number %d", &parg); + } +} + +/* + * Repaint the screen. + */ + public void +repaint(void) +{ + struct scrpos scrpos; + /* + * Start at the line currently at the top of the screen + * and redisplay the screen. + */ + get_scrpos(&scrpos); + pos_clear(); + jump_loc(scrpos.pos, scrpos.ln); +} + +/* + * Jump to a specified percentage into the file. + */ + public void +jump_percent(percent) + int percent; +{ + POSITION pos, len; + + /* + * Determine the position in the file + * (the specified percentage of the file's length). + */ + if ((len = ch_length()) == NULL_POSITION) + { + error("Don't know length of file", NULL_PARG); + return; + } + /* + * {{ This calculation may overflow! }} + */ + pos = (percent * len) / 100; + if (pos >= len) + pos = len-1; + + jump_line_loc(pos, jump_sline); +} + +/* + * Jump to a specified position in the file. + * Like jump_loc, but the position need not be + * the first character in a line. + */ + public void +jump_line_loc(pos, sline) + POSITION pos; + int sline; +{ + int c; + + if (ch_seek(pos) == 0) + { + /* + * Back up to the beginning of the line. + */ +/* while ((c = ch_back_get()) != '\n' && c != EOI) */ + while ((c = ch_back_get()) != '\r' && c != '\n' && c != EOI) + ; +/* if (c == '\n') */ + if (c == '\r' || c == '\n') + (void) ch_forw_get(); + pos = ch_tell(); + } + jump_loc(pos, sline); +} + +/* + * Jump to a specified position in the file. + * The position must be the first character in a line. + * Place the target line on a specified line on the screen. + */ + public void +jump_loc(pos, sline) + POSITION pos; + int sline; +{ + register int nline; + POSITION tpos; + POSITION bpos; + + /* + * Normalize sline. + */ + sline = adjsline(sline); + + if ((nline = onscreen(pos)) >= 0) + { + /* + * The line is currently displayed. + * Just scroll there. + */ + nline -= sline; + if (nline > 0) + forw(nline, position(BOTTOM_PLUS_ONE), 1, 0, 0); + else + back(-nline, position(TOP), 1, 0); + return; + } + + /* + * Line is not on screen. + * Seek to the desired location. + */ + if (ch_seek(pos)) + { + error("Cannot seek to that file position", NULL_PARG); + return; + } + + /* + * See if the desired line is before or after + * the currently displayed screen. + */ + tpos = position(TOP); + bpos = position(BOTTOM_PLUS_ONE); + if (tpos == NULL_POSITION || pos >= tpos) + { + /* + * The desired line is after the current screen. + * Move back in the file far enough so that we can + * call forw() and put the desired line at the + * sline-th line on the screen. + */ + for (nline = 0; nline < sline; nline++) + { + if (bpos != NULL_POSITION && pos <= bpos) + { + /* + * Surprise! The desired line is + * close enough to the current screen + * that we can just scroll there after all. + */ + forw(sc_height-sline+nline-1, bpos, 1, 0, 0); + return; + } + pos = back_line(pos); + if (pos == NULL_POSITION) + { + /* + * Oops. Ran into the beginning of the file. + * Exit the loop here and rely on forw() + * below to draw the required number of + * blank lines at the top of the screen. + */ + break; + } + } + lastmark(); + hit_eof = 0; + squished = 0; + screen_trashed = 0; + forw(sc_height-1, pos, 1, 0, sline-nline); + } else + { + /* + * The desired line is before the current screen. + * Move forward in the file far enough so that we + * can call back() and put the desired line at the + * sline-th line on the screen. + */ + for (nline = sline; nline < sc_height - 1; nline++) + { + pos = forw_line(pos); + if (pos == NULL_POSITION) + { + /* Cannot happen! */ + error("Program error: EOI in jump_loc (forw)", + NULL_PARG); + quit(1); + } + if (pos >= tpos) + { + /* + * Surprise! The desired line is + * close enough to the current screen + * that we can just scroll there after all. + */ + back(nline+1, tpos, 1, 0); + return; + } + } + lastmark(); + clear(); + screen_trashed = 0; + add_back_pos(pos); + back(sc_height-1, pos, 1, 0); + } +} diff --git a/bin/less/less.1 b/bin/less/less.1 new file mode 100644 index 0000000..7498c0f --- /dev/null +++ b/bin/less/less.1 @@ -0,0 +1,946 @@ +.TH LESS 1 +.SH NAME +less \- opposite of more +.SH SYNOPSIS +.B "less -?" +.br +.B "less [-[+]aBcCdeEfHimMnNqQrsSuUw]" +.br +.B " [-b \fIbufs\fP] [-h \fIlines\fP] [-j \fIline\fP] [-k \fIkeyfile\fP]" +.br +.B " [-{oO} \fIlogfile\fP] [-p \fIpattern\fP] [-P \fIprompt\fP] [-t \fItag\fP]" +.br +.B " [-T \fItagfile\fP] [-x \fItab\fP] [-y \fIlines\fP] [-[z] \fIlines\fP]" +.br +.B " [+[+]\fIcmd\fP] [\fIfilename\fP]..." + +.SH DESCRIPTION +.I Less +is a program similar to +.I more +(1), but which allows backward movement +in the file as well as forward movement. +Also, +.I less +does not have to read the entire input file before starting, +so with large input files it starts up faster than text editors like +.I vi +(1). +.I Less +uses termcap (or terminfo on some systems), +so it can run on a variety of terminals. +There is even limited support for hardcopy terminals. +(On a hardcopy terminal, lines which should be printed at the top +of the screen are prefixed with an up-arrow.) +.PP +Commands are based on both +.I more +and +.I vi. +Commands may be preceded by a decimal number, +called N in the descriptions below. +The number is used by some commands, as indicated. + +.SH COMMANDS +In the following descriptions, ^X means control-X. +ESC stands for the ESCAPE key; for example ESC-v means the +two character sequence "ESCAPE", then "v". +.IP "h or H" +Help: display a summary of these commands. +If you forget all the other commands, remember this one. +.PP +.IP "SPACE or ^V or f or ^F" +Scroll forward N lines, default one window (see option -z below). +If N is more than the screen size, only the final screenful is displayed. +Warning: some systems use ^V as a special literalization character. +.PP +.IP "z" +Like SPACE, but if N is specified, it becomes the new window size. +.PP +.IP "RETURN or ^N or e or ^E or j or ^J" +Scroll forward N lines, default 1. +The entire N lines are displayed, even if N is more than the screen size. +.PP +.IP "d or ^D" +Scroll forward N lines, default one half of the screen size. +If N is specified, it becomes the new default for +subsequent d and u commands. +.PP +.IP "b or ^B or ESC-v" +Scroll backward N lines, default one window (see option -z below). +If N is more than the screen size, only the final screenful is displayed. +.PP +.IP "w" +Like ESC-v, but if N is specified, it becomes the new window size. +.PP +.IP "y or ^Y or ^P or k or ^K" +Scroll backward N lines, default 1. +The entire N lines are displayed, even if N is more than the screen size. +Warning: some systems use ^Y as a special job control character. +.PP +.IP "u or ^U" +Scroll backward N lines, default one half of the screen size. +If N is specified, it becomes the new default for +subsequent d and u commands. +.PP +.IP "r or ^R or ^L" +Repaint the screen. +.PP +.IP R +Repaint the screen, discarding any buffered input. +Useful if the file is changing while it is being viewed. +.PP +.IP "F" +Scroll forward, and keep trying to read when the +end of file is reached. +Normally this command would be used when already at the end of the file. +It is a way to monitor the tail of a file which is growing +while it is being viewed. +(The behavior is similar to the "tail -f" command.) +.PP +.IP "g or < or ESC-<" +Go to line N in the file, default 1 (beginning of file). +(Warning: this may be slow if N is large.) +.PP +.IP "G or > or ESC->" +Go to line N in the file, default the end of the file. +(Warning: this may be slow if N is large, +or if N is not specified and +standard input, rather than a file, is being read.) +.PP +.IP "p or %" +Go to a position N percent into the file. +N should be between 0 and 100. +(This works if standard input is being read, but only if +.I less +has already read to the end of the file. +It is always fast, but not always useful.) +.PP +.IP "{" +If a left curly bracket appears in the top line displayed +on the screen, +the { command will go to the matching right curly bracket. +The matching right curly bracket is positioned on the bottom +line of the screen. +If there is more than one left curly bracket on the top line, +a number N may be used to specify the N-th bracket on the line. +.PP +.IP "}" +If a right curly bracket appears in the bottom line displayed +on the screen, +the } command will go to the matching left curly bracket. +The matching left curly bracket is positioned on the top +line of the screen. +If there is more than one right curly bracket on the top line, +a number N may be used to specify the N-th bracket on the line. +.PP +.IP "(" +Like {, but applies to parentheses rather than curly brackets. +.PP +.IP ")" +Like }, but applies to parentheses rather than curly brackets. +.PP +.IP "[" +Like {, but applies to square brackets rather than curly brackets. +.PP +.IP "]" +Like }, but applies to square brackets rather than curly brackets. +.PP +.IP "ESC-^F" +Followed by two characters, +acts like {, but uses the two characters as open and close brackets, +respectively. +For example, "ESC ^F < >" could be used to +go forward to the > which matches the < in the top displayed line. +.IP "ESC-^B" +Followed by two characters, +acts like }, but uses the two characters as open and close brackets, +respectively. +For example, "ESC ^B < >" could be used to +go backward to the < which matches the > in the bottom displayed line. +.IP m +Followed by any lowercase letter, +marks the current position with that letter. +.PP +.IP "'" +(Single quote.) +Followed by any lowercase letter, returns to the position which +was previously marked with that letter. +Followed by another single quote, returns to the position at +which the last "large" movement command was executed. +Followed by a ^ or $, jumps to the beginning or end of the +file respectively. +Marks are preserved when a new file is examined, +so the ' command can be used to switch between input files. +.PP +.IP "^X^X" +Same as single quote. +.PP +.IP /pattern +Search forward in the file for the N-th line containing the pattern. +N defaults to 1. +The pattern is a regular expression, as recognized by +.I ed. +The search starts at the second line displayed +(but see the -a and -j options, which change this). +.sp +Certain characters are special +if entered at the beginning of the pattern; +they modify the type of search rather than become part of the pattern: +.RS +.IP ! +Search for lines which do NOT match the pattern. +.IP * +Search multiple files. +That is, if the search reaches the end of the current file +without finding a match, +the search continues in the next file in the command line list. +.IP @ +Begin the search at the first line of the first file +in the command line list, +regardless of what is currently displayed on the screen +or the settings of the -a or -j options. +.RE +.PP +.IP ?pattern +Search backward in the file for the N-th line containing the pattern. +The search starts at the line immediately before the top line displayed. +.sp +Certain characters are special as in the / command: +.RS +.IP ! +Search for lines which do NOT match the pattern. +.IP * +Search multiple files. +That is, if the search reaches the beginning of the current file +without finding a match, +the search continues in the previous file in the command line list. +.IP @ +Begin the search at the last line of the last file +in the command line list, +regardless of what is currently displayed on the screen +or the settings of the -a or -j options. +.RE +.PP +.IP "ESC-/pattern" +Same as "/*". +.PP +.IP "ESC-?pattern" +Same as "?*". +.PP +.IP n +Repeat previous search, for N-th line containing the last pattern. +If the previous search was modified by !, the search is made for the +N-th line NOT containing the pattern. +If the previous search was modified by *, the search continues +in the next (or previous) file if not satisfied in the current file. +There is no effect if the previous search was modified by @. +.PP +.IP N +Repeat previous search, but in the reverse direction. +.PP +.IP "ESC-n" +Repeat previous search, but crossing file boundaries. +The effect is as if the previous search were modified by *. +.PP +.IP "ESC-N" +Repeat previous search, but in the reverse direction +and crossing file boundaries. +.PP +.IP ":e [filename]" +Examine a new file. +If the filename is missing, the "current" file (see the :n and :p commands +below) from the list of files in the command line is re-examined. +A percent sign (%) in the filename is replaced by the name of the +current file. +A pound sign (#) is replaced by the name of the previously examined file. +The filename is inserted into the command line list of files +so that it can be seen by subsequent :n and :p commands. +If the filename consists of several files, they are all inserted into +the list of files and the first one is examined. +.PP +.IP "^X^V or E" +Same as :e. +Warning: some systems use ^V as a special literalization character. +.PP +.IP ":n" +Examine the next file (from the list of files given in the command line). +If a number N is specified, the N-th next file is examined. +.PP +.IP ":p" +Examine the previous file in the command line list. +If a number N is specified, the N-th previous file is examined. +.PP +.IP ":x" +Examine the first file in the command line list. +If a number N is specified, the N-th file in the list is examined. +.PP +.IP "= or ^G or :f" +Prints some information about the file being viewed, +including its name +and the line number and byte offset of the bottom line being displayed. +If possible, it also prints the length of the file, +the number of lines in the file +and the percent of the file above the last displayed line. +.PP +.IP \- +Followed by one of the command line option letters (see below), +this will change the setting of that option +and print a message describing the new setting. +If the option letter has a numeric value (such as -b or -h), +or a string value (such as -P or -t), +a new value may be entered after the option letter. +If no new value is entered, a message describing +the current setting is printed and nothing is changed. +.PP +.IP \-+ +Followed by one of the command line option letters (see below), +this will reset the option to its default setting +and print a message describing the new setting. +(The "\-+\fIX\fP" command does the same thing +as "\-+\fIX\fP" on the command line.) +This does not work for string-valued options. +.PP +.IP \-\- +Followed by one of the command line option letters (see below), +this will reset the option to the "opposite" of its default setting +and print a message describing the new setting. +(The "\-\-\fIX\fP" command does the same thing +as "\-\fIX\fP" on the command line.) +This does not work for numeric or string-valued options. +.PP +.IP _ +(Underscore.) +Followed by one of the command line option letters (see below), +this will print a message describing the current setting of that option. +The setting of the option is not changed. +.PP +.IP +cmd +Causes the specified cmd to be executed each time a new file is examined. +For example, +G causes +.I less +to initially display each file starting at the end +rather than the beginning. +.PP +.IP V +Prints the version number of +.I less +being run. +.PP +.IP "q or :q or :Q or ZZ or ESC ESC" +Exits +.I less. +.PP +The following +three +commands may or may not be valid, depending on your particular installation. +.PP +.IP v +Invokes an editor to edit the current file being viewed. +The editor is taken from the environment variable EDITOR, +or defaults to "vi". +See also the discussion of LESSEDIT under the section on PROMPTS below. +.PP +.IP "! shell-command" +Invokes a shell to run the shell-command given. +A percent sign (%) in the command is replaced by the name of the +current file. +A pound sign (#) is replaced by the name of the previously examined file. +"!!" repeats the last shell command. +"!" with no shell command simply invokes a shell. +In all cases, the shell is taken from the environment variable SHELL, +or defaults to "sh". +.PP +.IP "| shell-command" + represents any mark letter. +Pipes a section of the input file to the given shell command. +The section of the file to be piped is between the first line on +the current screen and the position marked by the letter. + may also be ^ or $ to indicate beginning or end of file respectively. +If is . or newline, the current screen is piped. +.PP +.SH OPTIONS +Command line options are described below. +Most options may be changed while +.I less +is running, via the "\-" command. +.PP +Options are also taken from the environment variable "LESS". +For example, +to avoid typing "less -options ..." each time +.I less +is invoked, you might tell +.I csh: +.sp +setenv LESS "-options" +.sp +or if you use +.I sh: +.sp +LESS="-options"; export LESS +.sp +The environment variable is parsed before the command line, +so command line options override the LESS environment variable. +If an option appears in the LESS variable, it can be reset +to its default on the command line by beginning the command +line option with "-+". +.sp +A dollar sign ($) may be used to signal the end of an option string. +This is important only for options like -P which take a +following string. +.IP -? +This option displays a summary of the commands accepted by +.I less +(the same as the h command). +If this option is given, all other options are ignored, and +.I less +exits after the help screen is viewed. +(Depending on how your shell interprets the question mark, +it may be necessary to quote the question mark, thus: "-\\?".) +.IP -a +Causes searches to start after the last line +displayed on the screen, +thus skipping all lines displayed on the screen. +By default, searches start at the second line on the screen +(or after the last found line; see the -j option). +.IP -b\fIn\fP +Causes +.I less +to use a non-standard number of buffers. +Buffers are 1K, and by default 10 buffers are used +(except if data in coming from standard input; see the -B option). +The number \fIn\fP specifies a different number of buffers to use. +.IP -B +Disables automatic allocation of buffers, +so that only the default number of buffers are used. +If more data is read than will fit in the buffers, the oldest +data is discarded. +By default, when data is coming from standard input, +buffers are allocated automatically as needed +to avoid loss of data. +.IP -c +Causes full screen repaints to be painted from the top line down. +By default, +full screen repaints are done by scrolling from the bottom of the screen. +.IP -C +The -C option is like -c, but the screen is cleared before it is repainted. +.IP -d +The -d option suppresses the error message +normally displayed if the terminal is dumb; +that is, lacks some important capability, +such as the ability to clear the screen or scroll backward. +The -d option does not otherwise change the behavior of +.I less +on a dumb terminal). +.IP -e +Causes +.I less +to automatically exit +the second time it reaches end-of-file. +By default, the only way to exit +.I less +is via the "q" command. +.IP -E +Causes +.I less +to automatically exit the first time it reaches end-of-file. +.IP -f +Forces non-regular files to be opened. +(A non-regular file is a directory or a device special file.) +Also suppresses the warning message when a binary file is opened. +By default, +.I less +will refuse to open non-regular files. +.IP -h\fIn\fP +Specifies a maximum number of lines to scroll backward. +If it is necessary to scroll backward more than \fIn\fP lines, +the screen is repainted in a forward direction instead. +(If the terminal does not have the ability to scroll +backward, -h0 is implied.) +.IP -i +Causes searches to ignore case; that is, +uppercase and lowercase are considered identical. +Also, text which is overstruck or underlined can be searched for. +This option is ignored if any uppercase letters +appear in the search pattern. +.IP -j\fIn\fP +Specifies a line on the screen where "target" lines +are to be positioned. +Target lines are the object of text searches, +tag searches, jumps to a line number, +jumps to a file percentage, and jumps to a marked position. +The screen line is specified by a number: the top line on the screen +is 1, the next is 2, and so on. +The number may be negative to specify a line relative to the bottom +of the screen: the bottom line on the screen is -1, the second +to the bottom is -2, and so on. +If the -j option is used, searches begin at the line immediately +after the target line. +For example, if "-j4" is used, the target line is the +fourth line on the screen, so searches begin at the fifth line +on the screen. +.IP -k\fIfilename\fP +Causes +.I less +to open and interpret the named file as a +.I lesskey +(1) file. +Multiple -k options may be specified. +If a file called .less exists in the user's home directory, this +file is also used as a +.I lesskey +file. +.IP -m +Causes +.I less +to prompt verbosely (like \fImore\fP), +with the percent into the file. +By default, +.I less +prompts with a colon. +.IP -M +Causes +.I less +to prompt even more verbosely than +.I more. +.IP -n +Suppresses line numbers. +The default (to use line numbers) may cause +.I less +to run more slowly in some cases, especially with a very large input file. +Suppressing line numbers with the -n flag will avoid this problem. +Using line numbers means: the line number will be displayed in the verbose +prompt and in the = command, +and the v command will pass the current line number to the editor +(see also the discussion of LESSEDIT in PROMPTS below). +.IP -N +Causes a line number to be displayed at the beginning of +each line in the display. +.IP -o\fIfilename\fP +Causes +.I less +to copy its input to the named file as it is being viewed. +This applies only when the input file is a pipe, +not an ordinary file. +If the file already exists, +.I less +will ask for confirmation before overwriting it. +.IP -O\fIfilename\fP +The -O option is like -o, but it will overwrite an existing +file without asking for confirmation. +.sp +If no log file has been specified, +the -o and -O options can be used from within +.I less +to specify a log file. +Without a file name, they will simply report the name of the log file. +The "s" command is equivalent to specifying -o from within +.I less. +.IP -p\fIpattern\fP +The -p option on the command line is equivalent to +specifying +/\fIpattern\fP; +that is, it tells +.I less +to start at the first occurence of \fIpattern\fP in the file. +.IP -P\fIprompt\fP +Provides a way to tailor the three prompt +styles to your own preference. +This option would normally be put in the LESS environment +variable, rather than being typed in with each +.I less +command. +Such an option must either be the last option in the LESS variable, +or be terminated by a dollar sign. +-P followed by a string changes the default (short) prompt to that string. +-Pm changes the medium (-m) prompt to the string, and +-PM changes the long (-M) prompt. +Also, -P= changes the message printed by the = command to the given string. +All prompt strings consist of a sequence of +letters and special escape sequences. +See the section on PROMPTS for more details. +.IP -q +Causes moderately "quiet" operation: +the terminal bell is not rung +if an attempt is made to scroll past the end of the file +or before the beginning of the file. +If the terminal has a "visual bell", it is used instead. +The bell will be rung on certain other errors, +such as typing an invalid character. +The default is to ring the terminal bell in all such cases. +.IP -Q +Causes totally "quiet" operation: +the terminal bell is never rung. +.IP -r +Causes "raw" control characters to be displayed. +The default is to display control characters using the caret notation; +for example, a control-A (octal 001) is displayed as "^A". +Warning: when the -r flag is used, +.I less +cannot keep track of the actual appearance of the screen +(since this depends on how the screen responds to +each type of control character). +Thus, various display problems may result, +such as long lines being split in the wrong place. +.IP -s +Causes consecutive blank lines to be squeezed into a single blank line. +This is useful when viewing +.I nroff +output. +.IP -S +Causes lines longer than the screen width to be +chopped rather than folded. +That is, the remainder of a long line is simply discarded. +The default is to fold long lines; that is, display the remainder +on the next line. +.IP -t\fItag\fP +The -t option, followed immediately by a TAG, +will edit the file containing that tag. +For this to work, there must be a file called "tags" in the +current directory, which was previously built by the +.I ctags +(1) command. +This option may also be specified from within +.I less +(using the \- command) as a way of examining a new file. +The command ":t" is equivalent to specifying -t from within +.I less. +.IP -T\fItagsfile\fP +Specifies a tags file to be used instead of "tags". +.IP -u +Causes backspaces and carriage returns to be treated as printable characters; +that is, they are sent to the terminal when they appear in the input. +.IP -U +Causes backspaces and carriage returns to be treated as control characters; +that is, they are handled as specified by the -r option. +.sp +By default, if neither -u nor -U is given, +backspaces which appear adjacent to an underscore character +are treated specially: +the underlined text is displayed +using the terminal's hardware underlining capability. +Also, backspaces which appear between two identical characters +are treated specially: +the overstruck text is printed +using the terminal's hardware boldface capability. +Other backspaces are deleted, along with the preceding character. +Carriage returns immediately followed by a newline are deleted. +Other carriage returns are handled as specified by the -r option. +.IP -w +Causes blank lines to be used to represent lines +past the end of the file. +By default, +a tilde character is used. +.IP -x\fIn\fP +Sets tab stops every \fIn\fP positions. +The default for \fIn\fP is 8. +.IP -y\fIn\fP +Specifies a maximum number of lines to scroll forward. +If it is necessary to scroll forward more than \fIn\fP lines, +the screen is repainted instead. +The -c or -C option may be used to repaint from the top of +the screen if desired. +By default, any forward movement causes scrolling. +.IP -[z]\fIn\fP +Changes the default scrolling window size to \fIn\fP lines. +The default is one screenful. +The z and w commands can also be used to change the window size. +The "z" may be omitted, as in "-\fIn\fP" for compatibility with +.I more. +.IP + +If a command line option begins with \fB+\fP, +the remainder of that option is taken to be an initial command to +.I less. +For example, +G tells +.I less +to start at the end of the file rather than the beginning, +and +/xyz tells it to start at the first occurrence of "xyz" in the file. +As a special case, + acts like +g; +that is, it starts the display at the specified line number +(however, see the caveat under the "g" command above). +If the option starts with ++, the initial command applies to +every file being viewed, not just the first one. +The + command described previously +may also be used to set (or change) an initial command for every file. + +.SH "KEY BINDINGS" +You may define your own +.I less +commands by using the program +.I lesskey +(1) +to create a file called ".less" in your home directory. +This file specifies a set of command keys and an action +associated with each key. +See the +.I lesskey +manual page for more details. + +.SH "NATIONAL CHARACTER SETS" +There are three types of characters in the input file: +.IP "normal characters" +can be displayed directly to the screen. +.IP "control characters" +should not be displayed directly, but are expected to be found +in ordinary text files (such as backspace and tab). +.IP "binary characters" +cannot be displayed directly and are not expected to be found +in text files. +.PP +By default, +.I less +uses the ASCII character set. +In the ASCII character set, characters +with values between 128 and 255 are treated as binary. +The LESSCHARSET environment variable may be used to select +another character set. +If it is set to the value "latin1", +the ISO 8859/1 character set is assumed. +Latin-1 is the same as ASCII, except characters between 128 and 255 are +treated as normal characters. +The only valid values for LESSCHARSET currently are "ascii" and "latin1". +.PP +In special cases, it may be desired to tailor +.I less +to use a character set other than the ones definable by LESSCHARSET. +In this case, the environment variable LESSCHARDEF can be used +to define a character set. +It should be set to a string where each character in the string represents +one character in the character set. +The character "." is used for a normal character, "c" for control, +and "b" for binary. +A decimal number may be used for repetition. +For example, "bccc4b." would mean character 0 is binary, +1, 2 and 3 are control, 4, 5, 6 and 7 are binary, and 8 is normal. +All characters after the last are taken to be the same as the last, +so characters 9 through 255 would be normal. +(This is an example, and does not necessarily +represent any real character set.) +.PP +Setting LESSCHARDEF to "8bcccbcc18b95.b" is the same as setting +LESSCHARSET to "ascii". +Setting LESSCHARDEF to "8bcccbcc18b95.33b." is the same as setting +LESSCHARSET to "latin1". +.PP +Control and binary characters are displayed in blinking mode. +Each such character is displayed in caret notation if possible +(e.g. ^A for control-A). Caret notation is used only if +inverting the 0100 bit results in a normal printable character. +Otherwise, the character is displayed as an octal number preceded +by a backslash. +This octal format can be changed by +setting the LESSBINFMT environment variable +to a printf-style format string; the default is '\\%o'. +The blinking mode display of control and binary characters can +be changed or disabled by preceding the LESSBINFMT format +string with a "*" and one character to select the mode: +"*k" is blinking, "*d" is bold, "*u" is underlined, +and "*n" is normal (no special display attribute). +For example, if LESSBINFMT is "*u[%x]", binary characters +are displayed in underlined hexadecimal surrounded by brackets. + +.SH "PROMPTS" +The -P option allows you to tailor the prompt to your preference. +The string given to the -P option replaces the specified prompt string. +Certain characters in the string are interpreted specially. +The prompt mechanism is rather complicated to provide flexibility, +but the ordinary user need not understand the details of constructing +personalized prompt strings. +.sp +A percent sign followed by a single character is expanded +according to what the following character is: +.IP "%b\fIX\fP" +Replaced by the byte offset into the current input file. +The b is followed by a single character (shown as \fIX\fP above) +which specifies the line whose byte offset is to be used. +If the character is a "t", the byte offset of the top line in the +display is used, +an "m" means use the middle line, +a "b" means use the bottom line, +a "B" means use the line just after the bottom line, +and a "j" means use the "target" line, as specified by the -j option. +.IP "%B" +Replaced by the size of the current input file. +.IP "%E" +Replaced by the name of the editor (from the EDITOR environment variable). +See the discussion of the LESSEDIT feature below. +.IP "%f" +Replaced by the name of the current input file. +.IP "%i" +Replaced by the index of the current file in the list of +input files. +.IP "%l\fIX\fP" +Replaced by the line number of a line in the input file. +The line to be used is determined by the \fIX\fP, as with the %b option. +.IP "%L" +Replaced by the line number of the last line in the input file. +.IP "%m" +Replaced by the total number of input files. +.IP "%p\fIX\fP" +Replaced by the percent into the current input file. +The line used is determined by the \fIX\fP as with the %b option. +.IP "%s" +Same as %B. +.IP "%t" +Causes any trailing spaces to be removed. +Usually used at the end of the string, but may appear anywhere. +.IP "%x" +Replaced by the name of the next input file in the list. +.PP +If any item is unknown (for example, the file size if input +is a pipe), a question mark is printed instead. +.PP +The format of the prompt string can be changed +depending on certain conditions. +A question mark followed by a single character acts like an "IF": +depending on the following character, a condition is evaluated. +If the condition is true, any characters following the question mark +and condition character, up to a period, are included in the prompt. +If the condition is false, such characters are not included. +A colon appearing between the question mark and the +period can be used to establish an "ELSE": any characters between +the colon and the period are included in the string if and only if +the IF condition is false. +Condition characters (which follow a question mark) may be: +.IP "?a" +True if any characters have been included in the prompt so far. +.IP "?b\fIX\fP" +True if the byte offset of the specified line is known. +.IP "?B" +True if the size of current input file is known. +.IP "?e" +True if at end-of-file. +.IP "?f" +True if there is an input filename +(that is, if input is not a pipe). +.IP "?l\fIX\fP" +True if the line number of the specified line is known. +.IP "?L" +True if the line number of the last line in the file is known. +.IP "?m" +True if there is more than one input file. +.IP "?n" +True if this is the first prompt in a new input file. +.IP "?p\fIX\fP" +True if the percent into the current input file +of the specified line is known. +.IP "?s" +Same as "?B". +.IP "?x" +True if there is a next input file +(that is, if the current input file is not the last one). +.PP +Any characters other than the special ones +(question mark, colon, period, percent, and backslash) +become literally part of the prompt. +Any of the special characters may be included in the prompt literally +by preceding it with a backslash. +.PP +Some examples: +.sp +?f%f:Standard input. +.sp +This prompt prints the filename, if known; +otherwise the string "Standard input". +.sp +?f%f .?ltLine %lt:?pt%pt\\%:?btByte %bt:-... +.sp +This prompt would print the filename, if known. +The filename is followed by the line number, if known, +otherwise the percent if known, otherwise the byte offset if known. +Otherwise, a dash is printed. +Notice how each question mark has a matching period, +and how the % after the %pt +is included literally by escaping it with a backslash. +.sp +?n?f%f\ .?m(file\ %i\ of\ %m)\ ..?e(END)\ ?x-\ Next\\:\ %x..%t +.sp +This prints the filename if this is the first prompt in a file, +followed by the "file N of N" message if there is more +than one input file. +Then, if we are at end-of-file, the string "(END)" is printed +followed by the name of the next file, if there is one. +Finally, any trailing spaces are truncated. +This is the default prompt. +For reference, here are the defaults for +the other two prompts (-m and -M respectively). +Each is broken into two lines here for readability only. +.nf +.sp +?n?f%f\ .?m(file\ %i\ of\ %m)\ ..?e(END)\ ?x-\ Next\\:\ %x.: + ?pB%pB\\%:byte\ %bB?s/%s...%t +.sp +?f%f\ .?n?m(file\ %i\ of\ %m)\ ..?ltline\ %lt?L/%L.\ :byte\ %bB?s/%s.\ . + ?e(END)\ ?x-\ Next\\:\ %x.:?pB%pB\\%..%t +.sp +.fi +And here is the default message produced by the = command: +.nf +.sp +?f%f\ .?m(file\ %i\ of\ %m)\ .?ltline\ %lt?L/%L.\ . + byte\ %bB?s/%s.\ ?e(END)\ :?pB%pB\\%..%t +.fi +.PP +The prompt expansion features are also used for another purpose: +if an environment variable LESSEDIT is defined, it is used +as the command to be executed when the v command is invoked. +The LESSEDIT string is expanded in the same way as the prompt strings. +The default value for LESSEDIT is: +.nf +.sp + %E\ ?lm+%lm.\ %f +.sp +.fi +Note that this expands to the editor name, followed by a + and the +line number, followed by the file name. +If your editor does not accept the "+linenumber" syntax, or has other +differences in invocation syntax, the LESSEDIT variable can be +changed to modify this default. + +.SH "ENVIRONMENT VARIABLES" +.IP COLUMNS +Sets the number of columns on the screen. +Takes precedence over the number of columns specified by the TERM variable. +.IP EDITOR +The name of the editor (used for the v command). +.IP HOME +Name of the user's home directory (used to find a .less file). +.IP LESS +Flags which are passed to +.I less +automatically. +.IP LESSBINFMT +Format for displaying non-printable, non-control characters. +.IP LESSCHARDEF +Defines a character set. +.IP LESSCHARSET +Selects a predefined character set. +.IP LESSEDIT +Editor prototype string (used for the v command). +See discussion under PROMPTS. +.IP LESSHELP +Name of the help file. +.IP LINES +Sets the number of lines on the screen. +Takes precedence over the number of lines specified by the TERM variable. +.IP SHELL +The shell used to execute the ! command, as well as to expand filenames. +.IP TERM +The type of terminal on which +.I less +is being run. + +.SH "SEE ALSO" +lesskey(1) + +.SH WARNINGS +The = command and prompts (unless changed by -P) +report the line number of the line at the top of the screen, +but the byte and percent of the line at the bottom of the screen. +.PP +If the :e command is used to name more than one file, +and one of the named files has been viewed previously, +the new files may be entered into the list in an unexpected order. +.PP +The handling of national character sets is nonstandard as well as +insufficient for multibyte characters. +It will probably change in a later release. diff --git a/bin/less/less.h b/bin/less/less.h new file mode 100644 index 0000000..3f3b19a --- /dev/null +++ b/bin/less/less.h @@ -0,0 +1,123 @@ +/* + * Standard include file for "less". + */ +#ifdef __ORCAC__ +#define _ORCAC_ +#pragma optimize -1 +/* #pragma lint -1 */ +#endif + +#include +#include +#include + +/* + * Include the file of compile-time options. + */ +#include "defines.h" + + +/* + * Language details. + */ +#if !VOID +#define void int +#endif +#define public /* PUBLIC FUNCTION */ + +/* + * Special types and constants. + */ +typedef long POSITION; +/* + * {{ Warning: if POSITION is changed to other than "long", + * you may have to change some of the printfs which use "%ld" + * to print a variable of type POSITION. }} + */ + +#define NULL_POSITION ((POSITION)(-1)) + +/* + * The type of an interrupt handler. + */ +#define HANDLER void + +/* + * An IFILE represents an input file. + */ +#define IFILE VOID_POINTER +#define NULL_IFILE ((IFILE)NULL) + +/* + * The structure used to represent a "screen position". + * This consists of a file position, and a screen line number. + * The meaning is that the line starting at the given file + * position is displayed on the ln-th line of the screen. + * (Screen lines before ln are empty.) + */ +struct scrpos +{ + POSITION pos; + int ln; +}; + +typedef union parg +{ + char *p_string; + int p_int; +} PARG; + +#define NULL_PARG ((PARG *)NULL) + +#define EOI (-1) + +#ifndef NULL +#define NULL (0) +#endif + +#define READ_INTR (-2) + +/* How quiet should we be? */ +#define NOT_QUIET 0 /* Ring bell at eof and for errors */ +#define LITTLE_QUIET 1 /* Ring bell only for errors */ +#define VERY_QUIET 2 /* Never ring bell */ + +/* How should we prompt? */ +#define PR_SHORT 0 /* Prompt with colon */ +#define PR_MEDIUM 1 /* Prompt with message */ +#define PR_LONG 2 /* Prompt with longer message */ + +/* How should we handle backspaces? */ +#define BS_SPECIAL 0 /* Do special things for underlining and bold */ +#define BS_NORMAL 1 /* \b treated as normal char; actually output */ +#define BS_CONTROL 2 /* \b treated as control char; prints as ^H */ + +/* How should we search? */ +#define SRCH_FORW 0 /* Search forward from current position */ +#define SRCH_BACK 1 /* Search backward from current position */ +#define SRCH_NOMATCH 0100 /* Search for non-matching lines */ +#define SRCH_PAST_EOF 0200 /* Search past end-of-file, into next file */ +#define SRCH_FIRST_FILE 0400 /* Search starting at the first file */ + +#define SRCH_DIR(t) ((t) & 01) +#define SRCH_REVERSE(t) ((t) ^ 01) + +/* Special chars used to tell put_line() to do something special */ +#define NORMAL (0) +#define UNDERLINE (1) +#define BOLD (2) +#define BLINK (3) +#define INVIS (4) + +#define CONTROL(c) ((c)&037) +#define ESC CONTROL('[') + +#define SIGNAL(sig,func) signal(sig,func) + +/* Library function declarations */ +offset_t lseek(); +#define BAD_LSEEK ((offset_t)-1) +VOID_POINTER calloc(); + +#define ch_zero() ((POSITION)0) +#include "proto.h" diff --git a/bin/less/less.hlp b/bin/less/less.hlp new file mode 100644 index 0000000..9ec5a41 --- /dev/null +++ b/bin/less/less.hlp @@ -0,0 +1,104 @@ + + SUMMARY OF COMMANDS + + Commands marked with * may be preceded by a number, N. + Notes in parentheses indicate the behavior if N is given. + + h H Display this help. + q :q :Q ZZ Exit. + + e ^E j ^N CR * Forward one line (or N lines). + y ^Y k ^K ^P * Backward one line (or N lines). + f ^F ^V SPACE * Forward one window (or N lines). + b ^B ESC-v * Backward one window (or N lines). + z * Forward one window (and set window to N). + w * Backward one window (and set window to N). + d ^D * Forward one half-window (and set half-window to N). + u ^U * Backward one half-window (and set half-window to N). + F Forward forever; like "tail -f". + r ^R ^L Repaint screen. + R Repaint screen, discarding buffered input. + + NOTE: default "window" is the screen height. + default "half-window" is half of the screen height. + + /pattern * Search forward for (N-th) matching line. + ?pattern * Search backward for (N-th) matching line. + + NOTE: search commands may be modified by one or more of: + ! search for NON-matching lines. + * search multiple files. + @ start search at first file (for /) or last file (for ?). + + n * Repeat previous search (for N-th occurrence). + N * Repeat previous search in reverse direction. + ESC-n * Repeat previous search, spanning files. + ESC-N * Repeat previous search, reverse dir. & spanning files. + + g < ESC-< * Go to first line in file (or line N). + G > ESC-> * Go to last line in file (or line N). + p % * Go to beginning of file (or N percent into file). + { * Go to the } matching the (N-th) { in the top line. + } * Go to the { matching the (N-th) } in the bottom line. + ( * Go to the ) matching the (N-th) ( in the top line. + ) * Go to the ( matching the (N-th) ) in the bottom line. + [ * Go to the ] matching the (N-th) [ in the top line. + ] * Go to the [ matching the (N-th) ] in the bottom line. + ESC-^F * Go to the c1 matching the (N-th) c2 in the top line + ESC-^B * Go to the c2 matching the (N-th) c1 in the bottom line. + m Mark the current position with . + ' Go to a previously marked position. + '' Go to the previous position. + ^X^X Same as '. + + E [file] Examine a new file. + :e ^X^V Same as E. + :n * Examine the (N-th) next file from the command line. + :p * Examine the (N-th) previous file from the command line. + = ^G :f Print current file name. + V Print version number of "less". + + - Toggle a command line flag [see FLAGS below]. + _ Display the setting of a command line flag. + +cmd Execute the less cmd each time a new file is examined. + + !command Passes the command to $SHELL to be executed. + |Xcommand Pipe file between current pos & mark X to shell command. + v Edit the current file with $EDITOR. + + + FLAGS + + Most flags may be changed either on the command line, + or from within less by using the - command. + + -? Display help (from command line). + -a Set forward search starting location. + -b [N] Number of buffers. + -B Automatically allocate buffers. + -c -C Repaint by scrolling/clearing. + -d Dumb terminal. + -e -E Quit at end of file. + -f Force open non-regular files. + -h [N] Backward scroll limit. + -i Ignore case in searches. + -j [N] Screen position of target lines. + -k [file] Use a lesskey file. + -m -M Set prompt style. + -n -N Use line numbers. + -o [file] Log file. + -O [file] Log file (unconditionally overwrite). + -p [pattern] Start at pattern (from command line). + -P [prompt] Define new prompt. + -q -Q Quiet the terminal bell. + -r Translate control characters. + -s Squeeze multiple blank lines. + -S Chop long lines. + -t [tag] Find a tag. + -T [tagsfile] Use an alternate tags file. + -u -U Change handling of backspaces. + -w Display ~ for lines after end-of-file. + -x [N] Set tab stops. + -y [N] Forward scroll limit. + -z [N] Set size of window. + diff --git a/bin/less/lesskey.c b/bin/less/lesskey.c new file mode 100644 index 0000000..b82bd6b --- /dev/null +++ b/bin/less/lesskey.c @@ -0,0 +1,361 @@ +/* + * lesskey [-o output] [input] + * + * Make a .less file. + * If no input file is specified, standard input is used. + * If no output file is specified, $HOME/.less is used. + * + * The .less file is used to specify (to "less") user-defined + * key bindings. Basically any sequence of 1 to MAX_CMDLEN + * keystrokes may be bound to an existing less function. + * + * The input file is an ascii file consisting of a + * sequence of lines of the form: + * string action [chars] + * + * "string" is a sequence of command characters which form + * the new user-defined command. The command + * characters may be: + * 1. The actual character itself. + * 2. A character preceded by ^ to specify a + * control character (e.g. ^X means control-X). + * 3. Any character (other than an octal digit) preceded by + * a \ to specify the character itself (characters which + * must be preceded by \ include ^, \, and whitespace. + * 4. A backslash followed by one to three octal digits + * to specify a character by its octal value. + * "action" is the name of a "less" action, from the table below. + * "chars" is an optional sequence of characters which is treated + * as keyboard input after the command is executed. + * + * Blank lines and lines which start with # are ignored. + * + * + * The output file is a non-ascii file, consisting of + * zero or more byte sequences of the form: + * string <0> + * or + * string <0> chars <0> + * + * "string" is the command string. + * "<0>" is one null byte. + * "" is one byte containing the action code (the A_xxx value). + * If action is ORed with A_EXTRA, the action byte is followed + * by the null-terminated "chars" string. + */ + +#include +#include +#include "less.h" +#include "cmd.h" + +char usertable[MAX_USERCMD]; + +struct cmdname +{ + char *cn_name; + int cn_action; +} cmdnames[] = +{ + "back-bracket", A_B_BRACKET, + "back-line", A_B_LINE, + "back-line-force", A_BF_LINE, + "back-screen", A_B_SCREEN, + "back-scroll", A_B_SCROLL, + "back-search", A_B_SEARCH, + "back-window", A_B_WINDOW, + "debug", A_DEBUG, + "display-flag", A_DISP_OPTION, + "display-option", A_DISP_OPTION, + "end", A_GOEND, + "examine", A_EXAMINE, + "first-cmd", A_FIRSTCMD, + "firstcmd", A_FIRSTCMD, + "flush-repaint", A_FREPAINT, + "forw-bracket", A_F_BRACKET, + "forw-forever", A_F_FOREVER, + "forw-line", A_F_LINE, + "forw-line-force", A_FF_LINE, + "forw-screen", A_F_SCREEN, + "forw-scroll", A_F_SCROLL, + "forw-search", A_F_SEARCH, + "forw-window", A_F_WINDOW, + "goto-end", A_GOEND, + "goto-line", A_GOLINE, + "goto-mark", A_GOMARK, + "help", A_HELP, + "index-file", A_INDEX_FILE, + "invalid", A_UINVALID, + "next-file", A_NEXT_FILE, + "noaction", A_NOACTION, + "percent", A_PERCENT, + "pipe", A_PIPE, + "prev-file", A_PREV_FILE, + "quit", A_QUIT, + "repaint", A_REPAINT, + "repaint-flush", A_FREPAINT, + "repeat-search", A_AGAIN_SEARCH, + "repeat-search-all", A_T_AGAIN_SEARCH, + "reverse-search", A_REVERSE_SEARCH, + "reverse-search-all", A_T_REVERSE_SEARCH, + "set-mark", A_SETMARK, + "shell", A_SHELL, + "status", A_STAT, + "toggle-flag", A_OPT_TOGGLE, + "toggle-option", A_OPT_TOGGLE, + "version", A_VERSION, + "visual", A_VISUAL, + NULL, 0 +}; + +main(argc, argv) + int argc; + char *argv[]; +{ + char *p; /* {{ Can't be register since we use &p }} */ + register char *up; /* Pointer into usertable */ + FILE *desc; /* Description file (input) */ + FILE *out; /* Output file */ + int linenum; /* Line number in input file */ + char *currcmd; /* Start of current command string */ + int errors; + int i, j; + char line[200]; + char *outfile; + + extern char *getenv(); + + /* + * Process command line arguments. + */ + outfile = NULL; + while (--argc > 0 && **(++argv) == '-') + { + switch (argv[0][1]) + { + case 'o': + outfile = &argv[0][2]; + if (*outfile == '\0') + { + if (--argc <= 0) + usage(); + outfile = *(++argv); + } + break; + default: + usage(); + } + } + if (argc > 1) + usage(); + + + /* + * Open the input file, or use standard input if none specified. + */ + if (argc > 0) + { + if ((desc = fopen(*argv, "r")) == NULL) + { + perror(*argv); + exit(1); + } + } else + desc = stdin; + + /* + * Read the input file, one line at a time. + * Each line consists of a command string, + * followed by white space, followed by an action name. + */ + linenum = 0; + errors = 0; + up = usertable; + while (fgets(line, sizeof(line), desc) != NULL) + { + ++linenum; + + /* + * Skip leading white space. + * Replace the final newline with a null byte. + * Ignore blank lines and comment lines. + */ + p = line; + while (*p == ' ' || *p == '\t') + ++p; + for (i = 0; p[i] != '\n' && p[i] != '\0'; i++) + ; + p[i] = '\0'; + if (*p == '#' || *p == '\0') + continue; + + /* + * Parse the command string and store it in the usertable. + */ + currcmd = up; + do + { + if (up >= usertable + MAX_USERCMD) + { + fprintf(stderr, "too many commands, line %d\n", + linenum); + exit(1); + } + if (up >= currcmd + MAX_CMDLEN) + { + fprintf(stderr, "command too long on line %d\n", + linenum); + errors++; + break; + } + + *up++ = tchar(&p); + + } while (*p != ' ' && *p != '\t' && *p != '\0'); + + /* + * Terminate the command string with a null byte. + */ + *up++ = '\0'; + + /* + * Skip white space between the command string + * and the action name. + * Terminate the action name with a null byte if it + * is followed by whitespace or a # comment. + */ + if (*p == '\0') + { + fprintf(stderr, "missing whitespace on line %d\n", + linenum); + errors++; + continue; + } + while (*p == ' ' || *p == '\t') + ++p; + for (j = 0; p[j] != ' ' && p[j] != '\t' && + p[j] != '#' && p[j] != '\0'; j++) + ; + p[j] = '\0'; + + /* + * Parse the action name and store it in the usertable. + */ + for (i = 0; cmdnames[i].cn_name != NULL; i++) + if (strcmp(cmdnames[i].cn_name, p) == 0) + break; + if (cmdnames[i].cn_name == NULL) + { + fprintf(stderr, "unknown action <%s> on line %d\n", + p, linenum); + errors++; + continue; + } + *up++ = cmdnames[i].cn_action; + + /* + * See if an extra string follows the action name. + */ + for (j = j+1; p[j] == ' ' || p[j] == '\t'; j++) + ; + p += j; + if (*p != '\0') + { + /* + * OR the special value A_EXTRA into the action byte. + * Put the extra string after the action byte. + */ + up[-1] |= A_EXTRA; + while (*p != '\0') + *up++ = tchar(&p); + *up++ = '\0'; + } + } + + if (errors > 0) + { + fprintf(stderr, "%d errors; no output produced\n", errors); + exit(1); + } + + /* + * Write the output file. + * If no output file was specified, use "$HOME/.less" + */ + if (outfile == NULL) + { + p = getenv("HOME"); + if (p == NULL || *p == '\0') + { + fprintf(stderr, "cannot find $HOME - using current directory\n"); +#if __MSDOS__ + strcpy(line, "_less"); +#else + strcpy(line, "lessrc"); +#endif + } else + { + strcpy(line, p); +#if __MSDOS__ + strcat(line, "\\_less"); +#else + strcat(line, "/lessrc"); +#endif + } + outfile = line; + } + if ((out = fopen(outfile, "w")) == NULL) + perror(outfile); + else + fwrite((char *)usertable, 1, up-usertable, out); + exit(0); +} + +/* + * Parse one character of a string. + */ +tchar(pp) + char **pp; +{ + register char *p; + register char ch; + register int i; + + p = *pp; + switch (*p) + { + case '\\': + if (*++p >= '0' && *p <= '7') + { + /* + * Parse an octal number. + */ + ch = 0; + i = 0; + do + ch = 8*ch + (*p - '0'); + while (*++p >= '0' && *p <= '7' && ++i < 3); + *pp = p; + return (ch); + } + /* + * Backslash followed by a char just means that char. + */ + *pp = p+1; + return (*p); + case '^': + /* + * Carat means CONTROL. + */ + *pp = p+2; + return (CONTROL(p[1])); + } + *pp = p+1; + return (*p); +} + +usage() +{ + fprintf(stderr, "usage: lesskey [-o output] [input]\n"); + exit(1); +} diff --git a/bin/less/line.c b/bin/less/line.c new file mode 100644 index 0000000..bca9a44 --- /dev/null +++ b/bin/less/line.c @@ -0,0 +1,540 @@ +/* + * Routines to manipulate the "line buffer". + * The line buffer holds a line of output as it is being built + * in preparation for output to the screen. + */ +#pragma noroot +#include "less.h" + +#ifdef _ORCAC_ +segment "LoadSegONE"; +#endif + +static char linebuf[1024]; /* Buffer which holds the current output line */ +static char attr[1024]; /* Extension of linebuf to hold attributes */ +static int curr; /* Index into linebuf */ +static int column; /* Printable length, accounting for + backspaces, etc. */ +static int overstrike; /* Next char should overstrike previous char */ +static int is_null_line; /* There is no current line */ +static char pendc; + +extern int bs_mode; +extern int tabstop; +extern int linenums; +extern int ctldisp; +extern int twiddle; +extern int binattr; +extern int auto_wrap, ignaw; +extern int bo_s_width, bo_e_width; +extern int ul_s_width, ul_e_width; +extern int bl_s_width, bl_e_width; +extern int sc_width, sc_height; + +static int pwidth(int c, int a); +static void backc(void); +static int storec(int c, int a); +static int do_append(int c); + +/* + * Rewind the line buffer. + */ + public void +prewind(void) +{ + curr = 0; + column = 0; + overstrike = 0; + is_null_line = 0; + pendc = '\0'; +} + +/* + * Insert the line number (of the given position) into the line buffer. + */ + public void +plinenum(pos) + POSITION pos; +{ + register int lno; + register int i; + register int n; + + /* + * We display the line number at the start of each line + * only if the -N option is set. + */ + if (linenums != 2) + return; + + /* + * Get the line number and put it in the current line. + * {{ Note: since find_linenum calls forw_raw_line, + * it may seek in the input file, requiring the caller + * of plinenum to re-seek if necessary. }} + */ + lno = find_linenum(pos); + + sprintf(&linebuf[curr], "%6d", lno); + n = strlen(&linebuf[curr]); + column += n; + for (i = 0; i < n; i++) + attr[curr++] = 0; + + /* + * Append enough spaces to bring us to the next tab stop. + * {{ We could avoid this at the cost of adding some + * complication to the tab stop logic in pappend(). }} + */ + do + { + linebuf[curr] = ' '; + attr[curr++] = 0; + column++; + } while ((column % tabstop) != 0); +} + +/* + * Return the printing width of the start (enter) sequence + * for a given character attribute. + */ + int +attr_swidth(a) + int a; +{ + switch (a) + { + case BOLD: return (bo_s_width); + case UNDERLINE: return (ul_s_width); + case BLINK: return (bl_s_width); + } + return (0); +} + +/* + * Return the printing width of the end (exit) sequence + * for a given character attribute. + */ + int +attr_ewidth(a) + int a; +{ + switch (a) + { + case BOLD: return (bo_e_width); + case UNDERLINE: return (ul_e_width); + case BLINK: return (bl_e_width); + } + return (0); +} + +/* + * Return the printing width of a given character and attribute, + * if the character were added to the current position in the line buffer. + * Adding a character with a given attribute may cause an enter or exit + * attribute sequence to be inserted, so this must be taken into account. + */ + static int +pwidth(c, a) + int c; + int a; +{ + register int w; + + if (c == '\b') + /* + * Backspace moves backwards one position. + */ + return (-1); + + if (control_char(c)) + /* + * Control characters do unpredicatable things, + * so we don't even try to guess; say it doesn't move. + * This can only happen if the -r flag is in effect. + */ + return (0); + + /* + * Other characters take one space, + * plus the width of any attribute enter/exit sequence. + */ + w = 1; + if (curr > 0 && attr[curr-1] != a) + w += attr_ewidth(attr[curr-1]); + if (a && (curr == 0 || attr[curr-1] != a)) + w += attr_swidth(a); + return (w); +} + +/* + * Delete the previous character in the line buffer. + */ + static void +backc(void) +{ + curr--; + column -= pwidth(linebuf[curr], attr[curr]); +} + +/* + * Append a character and attribute to the line buffer. + */ + static int +storec(c, a) + int c; + int a; +{ + register int w; + + w = pwidth(c, a); + if (ctldisp > 0 && column + w + attr_ewidth(a) > sc_width) + /* + * Won't fit on screen. + */ + return (1); + + if (curr >= sizeof(linebuf)-2) + /* + * Won't fit in line buffer. + */ + return (1); + + /* + * Special handling for "magic cookie" terminals. + * If an attribute enter/exit sequence has a printing width > 0, + * and the sequence is adjacent to a space, delete the space. + * We just mark the space as invisible, to avoid having too + * many spaces deleted. + * {{ Note that even if the attribute width is > 1, we + * delete only one space. It's not worth trying to do more. + * It's hardly worth doing this much. }} + */ + if (curr > 0 && a != NORMAL && + linebuf[curr-1] == ' ' && attr[curr-1] == NORMAL && + attr_swidth(a) > 0) + { + /* + * We are about to append an enter-attribute sequence + * just after a space. Delete the space. + */ + attr[curr-1] = INVIS; + column--; + } else if (curr > 0 && attr[curr-1] != NORMAL && + attr[curr-1] != INVIS && c == ' ' && a == NORMAL && + attr_ewidth(attr[curr-1]) > 0) + { + /* + * We are about to append a space just after an + * exit-attribute sequence. Delete the space. + */ + a = INVIS; + column--; + } + /* End of magic cookie handling. */ + + linebuf[curr] = c; + attr[curr] = a; + column += w; + return (0); +} + +/* + * Append a character to the line buffer. + * Expand tabs into spaces, handle underlining, boldfacing, etc. + * Returns 0 if ok, 1 if couldn't fit in buffer. + */ + public int +pappend(c) + register int c; +{ + if (pendc) + { + if (do_append(pendc)) + /* + * Oops. We've probably lost the char which + * was in pendc, since caller won't back up. + */ + return (1); + pendc = '\0'; + } + +/* if (c == '\r' && bs_mode == BS_SPECIAL)*/ + if ((c == '\r' || c == '\n') && bs_mode == BS_SPECIAL) + { + /* + * Don't put the CR into the buffer until we see + * the next char. If the next char is a newline, + * discard the CR. + */ + pendc = c; + return (0); + } + + return (do_append(c)); +} + + static int +do_append(c) + int c; +{ + register char *s; + register int a; + +#define STOREC(c,a) if (storec((c),(a))) return (1); else curr++ + + if (overstrike) + { + /* + * Overstrike the character at the current position + * in the line buffer. This will cause either + * underline (if a "_" is overstruck), + * bold (if an identical character is overstruck), + * or just deletion of the character in the buffer. + */ + overstrike = 0; + if (c == linebuf[curr]) + STOREC(linebuf[curr], BOLD); + else if (c == '_') + STOREC(linebuf[curr], UNDERLINE); + else if (linebuf[curr] == '_') + STOREC(c, UNDERLINE); + else if (control_char(c)) + goto do_control_char; + else + STOREC(c, NORMAL); + } else if (c == '\b') + { + switch (bs_mode) + { + case BS_NORMAL: + STOREC(c, NORMAL); + break; + case BS_CONTROL: + goto do_control_char; + case BS_SPECIAL: + if (curr == 0) + break; + backc(); + overstrike = 1; + break; + } + } else if (c == '\t') + { + /* + * Expand a tab into spaces. + */ + do + { + STOREC(' ', NORMAL); + } while ((column % tabstop) != 0); + } else if (control_char(c)) + { + do_control_char: + if (ctldisp == 0) + { + /* + * Output as a normal character. + */ + STOREC(c, NORMAL); + } else + { + /* + * Output in the (blinking) ^X format. + */ + s = prchar(c); + a = binattr; + + /* + * Make sure we can get the entire representation + * the character on this line. + */ + if (column + strlen(s) + + attr_swidth(a) + attr_ewidth(a) > sc_width) + return (1); + + for ( ; *s != 0; s++) + STOREC(*s, a); + } + } else + { + STOREC(c, NORMAL); + } + + return (0); +} + +/* + * Terminate the line in the line buffer. + */ + public void +pdone(endline) + int endline; +{ +/* if (pendc && (pendc != '\r' || !endline)) */ + if (pendc && ((pendc != '\n' && pendc != '\n') || !endline)) + /* + * If we had a pending character, put it in the buffer. + * But discard a pending CR if we are at end of line + * (that is, discard the CR in a CR/LF sequence). + */ + (void) do_append(pendc); + + /* + * Add a newline if necessary, + * and append a '\0' to the end of the line. + */ + if (column < sc_width || !auto_wrap || ignaw || ctldisp == 0) + { +/* linebuf[curr] = '\n'; */ + linebuf[curr] = '\r'; + attr[curr] = NORMAL; + curr++; + } + linebuf[curr] = '\0'; + attr[curr] = NORMAL; +} + +/* + * Get a character from the current line. + * Return the character as the function return value, + * and the character attribute in *ap. + */ + public int +gline(i, ap) + register int i; + register int *ap; +{ + if (is_null_line) + { + /* + * If there is no current line, we pretend the line is + * either "~" or "", depending on the "twiddle" flag. + */ + *ap = NORMAL; + if (twiddle) +/* return ("~\n"[i]); + return ("\n"[i]); */ + return ("~\r"[i]); + return ("\r"[i]); + } + + *ap = attr[i]; + return (linebuf[i] & 0377); +} + +/* + * Indicate that there is no current line. + */ + public void +null_line(void) +{ + is_null_line = 1; +} + +/* + * Analogous to forw_line(), but deals with "raw lines": + * lines which are not split for screen width. + * {{ This is supposed to be more efficient than forw_line(). }} + */ + public POSITION +forw_raw_line(curr_pos, linep) + POSITION curr_pos; + char **linep; +{ + register char *p; + register int c; + POSITION new_pos; + + if (curr_pos == NULL_POSITION || ch_seek(curr_pos) || + (c = ch_forw_get()) == EOI) + return (NULL_POSITION); + + p = linebuf; + + for (;;) + { +/* if (c == '\n' || c == EOI) */ + if (c == '\r' || c == '\n' || c == EOI) + { + new_pos = ch_tell(); + break; + } + if (p >= &linebuf[sizeof(linebuf)-1]) + { + /* + * Overflowed the input buffer. + * Pretend the line ended here. + * {{ The line buffer is supposed to be big + * enough that this never happens. }} + */ + new_pos = ch_tell() - 1; + break; + } + *p++ = c; + c = ch_forw_get(); + } + *p = '\0'; + if (linep != NULL) + *linep = linebuf; + return (new_pos); +} + +/* + * Analogous to back_line(), but deals with "raw lines". + * {{ This is supposed to be more efficient than back_line(). }} + */ + public POSITION +back_raw_line(curr_pos, linep) + POSITION curr_pos; + char **linep; +{ + register char *p; + register int c; + POSITION new_pos; + + if (curr_pos == NULL_POSITION || curr_pos <= ch_zero() || + ch_seek(curr_pos-1)) + return (NULL_POSITION); + + p = &linebuf[sizeof(linebuf)]; + *--p = '\0'; + + for (;;) + { + c = ch_back_get(); +/* if (c == '\n') */ + if (c == '\r' || c == '\n') + { + /* + * This is the newline ending the previous line. + * We have hit the beginning of the line. + */ + new_pos = ch_tell() + 1; + break; + } + if (c == EOI) + { + /* + * We have hit the beginning of the file. + * This must be the first line in the file. + * This must, of course, be the beginning of the line. + */ + new_pos = ch_zero(); + break; + } + if (p <= linebuf) + { + /* + * Overflowed the input buffer. + * Pretend the line ended here. + */ + new_pos = ch_tell() + 1; + break; + } + *--p = c; + } + if (linep != NULL) + *linep = p; + return (new_pos); +} diff --git a/bin/less/linenum.c b/bin/less/linenum.c new file mode 100644 index 0000000..ad082ba --- /dev/null +++ b/bin/less/linenum.c @@ -0,0 +1,452 @@ +/* + * Code to handle displaying line numbers. + * + * Finding the line number of a given file position is rather tricky. + * We don't want to just start at the beginning of the file and + * count newlines, because that is slow for large files (and also + * wouldn't work if we couldn't get to the start of the file; e.g. + * if input is a long pipe). + * + * So we use the function add_lnum to cache line numbers. + * We try to be very clever and keep only the more interesting + * line numbers when we run out of space in our table. A line + * number is more interesting than another when it is far from + * other line numbers. For example, we'd rather keep lines + * 100,200,300 than 100,101,300. 200 is more interesting than + * 101 because 101 can be derived very cheaply from 100, while + * 200 is more expensive to derive from 100. + * + * The function currline() returns the line number of a given + * position in the file. As a side effect, it calls add_lnum + * to cache the line number. Therefore currline is occasionally + * called to make sure we cache line numbers often enough. + */ +#pragma noroot +#include "less.h" +#include "position.h" + +#ifdef _ORCAC_ +segment "LoadSegONE"; +#endif + +/* + * Structure to keep track of a line number and the associated file position. + * A doubly-linked circular list of line numbers is kept ordered by line number. + */ +struct linenum +{ + struct linenum *next; /* Link to next in the list */ + struct linenum *prev; /* Line to previous in the list */ + POSITION pos; /* File position */ + POSITION gap; /* Gap between prev and next */ + int line; /* Line number */ +}; +/* + * "gap" needs some explanation: the gap of any particular line number + * is the distance between the previous one and the next one in the list. + * ("Distance" means difference in file position.) In other words, the + * gap of a line number is the gap which would be introduced if this + * line number were deleted. It is used to decide which one to replace + * when we have a new one to insert and the table is full. + */ + +#define NPOOL 50 /* Size of line number pool */ + +#define LONGTIME (2) /* In seconds */ + +public int lnloop = 0; /* Are we in the line num loop? */ + +static struct linenum anchor; /* Anchor of the list */ +static struct linenum *freelist; /* Anchor of the unused entries */ +static struct linenum pool[NPOOL]; /* The pool itself */ +static struct linenum *spare; /* We always keep one spare entry */ + +extern int linenums; +extern int sigs; +extern int sc_height; + +static void calcgap(register struct linenum *p); +static void longloopmessage(void); +static void longish(void); + +/* + * Initialize the line number structures. + */ + public void +clr_linenum(void) +{ + register struct linenum *p; + + /* + * Put all the entries on the free list. + * Leave one for the "spare". + */ + for (p = pool; p < &pool[NPOOL-2]; p++) + p->next = p+1; + pool[NPOOL-2].next = NULL; + freelist = pool; + + spare = &pool[NPOOL-1]; + + /* + * Initialize the anchor. + */ + anchor.next = anchor.prev = &anchor; + anchor.gap = 0; + anchor.pos = (POSITION)0; + anchor.line = 1; +} + +/* + * Calculate the gap for an entry. + */ + static void +calcgap(p) + register struct linenum *p; +{ + /* + * Don't bother to compute a gap for the anchor. + * Also don't compute a gap for the last one in the list. + * The gap for that last one should be considered infinite, + * but we never look at it anyway. + */ + if (p == &anchor || p->next == &anchor) + return; + p->gap = p->next->pos - p->prev->pos; +} + +/* + * Add a new line number to the cache. + * The specified position (pos) should be the file position of the + * FIRST character in the specified line. + */ + public void +add_lnum(lno, pos) + int lno; + POSITION pos; +{ + register struct linenum *p; + register struct linenum *new; + register struct linenum *nextp; + register struct linenum *prevp; + register POSITION mingap; + + /* + * Find the proper place in the list for the new one. + * The entries are sorted by position. + */ + for (p = anchor.next; p != &anchor && p->pos < pos; p = p->next) + if (p->line == lno) + /* We already have this one. */ + return; + nextp = p; + prevp = p->prev; + + if (freelist != NULL) + { + /* + * We still have free (unused) entries. + * Use one of them. + */ + new = freelist; + freelist = freelist->next; + } else + { + /* + * No free entries. + * Use the "spare" entry. + */ + new = spare; + spare = NULL; + } + + /* + * Fill in the fields of the new entry, + * and insert it into the proper place in the list. + */ + new->next = nextp; + new->prev = prevp; + new->pos = pos; + new->line = lno; + + nextp->prev = new; + prevp->next = new; + + /* + * Recalculate gaps for the new entry and the neighboring entries. + */ + calcgap(new); + calcgap(nextp); + calcgap(prevp); + + if (spare == NULL) + { + /* + * We have used the spare entry. + * Scan the list to find the one with the smallest + * gap, take it out and make it the spare. + * We should never remove the last one, so stop when + * we get to p->next == &anchor. This also avoids + * looking at the gap of the last one, which is + * not computed by calcgap. + */ + mingap = anchor.next->gap; + for (p = anchor.next; p->next != &anchor; p = p->next) + { + if (p->gap <= mingap) + { + spare = p; + mingap = p->gap; + } + } + spare->next->prev = spare->prev; + spare->prev->next = spare->next; + } +} + +/* + * If we get stuck in a long loop trying to figure out the + * line number, print a message to tell the user what we're doing. + */ + static void +longloopmessage(void) +{ + ierror("Calculating line numbers", NULL_PARG); + /* + * Set the lnloop flag here, so if the user interrupts while + * we are calculating line numbers, the signal handler will + * turn off line numbers (linenums=0). + */ + lnloop = 1; +} + +static int loopcount; +#if GET_TIME +static long startime; +#endif + + static void +longish(void) +{ +#if GET_TIME + if (loopcount >= 0 && ++loopcount > 100) + { + loopcount = 0; + if (get_time() >= startime + LONGTIME) + { + longloopmessage(); + loopcount = -1; + } + } +#else + if (loopcount >= 0 && ++loopcount > LONGLOOP) + { + longloopmessage(); + loopcount = -1; + } +#endif +} + +/* + * Find the line number associated with a given position. + * Return 0 if we can't figure it out. + */ + public int +find_linenum(pos) + POSITION pos; +{ + register struct linenum *p; + register int lno; + POSITION cpos; + + if (!linenums) + /* + * We're not using line numbers. + */ + return (0); + if (pos == NULL_POSITION) + /* + * Caller doesn't know what he's talking about. + */ + return (0); + if (pos <= ch_zero()) + /* + * Beginning of file is always line number 1. + */ + return (1); + + /* + * Find the entry nearest to the position we want. + */ + for (p = anchor.next; p != &anchor && p->pos < pos; p = p->next) + continue; + if (p->pos == pos) + /* Found it exactly. */ + return (p->line); + + /* + * This is the (possibly) time-consuming part. + * We start at the line we just found and start + * reading the file forward or backward till we + * get to the place we want. + * + * First decide whether we should go forward from the + * previous one or backwards from the next one. + * The decision is based on which way involves + * traversing fewer bytes in the file. + */ + flush(); +#if GET_TIME + startime = get_time(); +#endif + if (p == &anchor || pos - p->prev->pos < p->pos - pos) + { + /* + * Go forward. + */ + p = p->prev; + if (ch_seek(p->pos)) + return (0); + loopcount = 0; + for (lno = p->line, cpos = p->pos; cpos < pos; lno++) + { + /* + * Allow a signal to abort this loop. + */ + cpos = forw_raw_line(cpos, (char **)NULL); + if (sigs || cpos == NULL_POSITION) + return (0); + longish(); + } + lnloop = 0; + /* + * We might as well cache it. + */ + add_lnum(lno, cpos); + /* + * If the given position is not at the start of a line, + * make sure we return the correct line number. + */ + if (cpos > pos) + lno--; + } else + { + /* + * Go backward. + */ + if (ch_seek(p->pos)) + return (0); + loopcount = 0; + for (lno = p->line, cpos = p->pos; cpos > pos; lno--) + { + /* + * Allow a signal to abort this loop. + */ + cpos = back_raw_line(cpos, (char **)NULL); + if (sigs || cpos == NULL_POSITION) + return (0); + longish(); + } + lnloop = 0; + /* + * We might as well cache it. + */ + add_lnum(lno, cpos); + } + + return (lno); +} + +/* + * Find the position of a given line number. + * Return NULL_POSITION if we can't figure it out. + */ + public POSITION +find_pos(lno) + int lno; +{ + register struct linenum *p; + POSITION cpos; + int clno; + + if (lno <= 1) + /* + * Line number 1 is beginning of file. + */ + return (ch_zero()); + + /* + * Find the entry nearest to the line number we want. + */ + for (p = anchor.next; p != &anchor && p->line < lno; p = p->next) + continue; + if (p->line == lno) + /* Found it exactly. */ + return (p->pos); + + flush(); + if (p == &anchor || lno - p->prev->line < p->line - lno) + { + /* + * Go forward. + */ + p = p->prev; + if (ch_seek(p->pos)) + return (NULL_POSITION); + for (clno = p->line, cpos = p->pos; clno < lno; clno++) + { + /* + * Allow a signal to abort this loop. + */ + cpos = forw_raw_line(cpos, (char **)NULL); + if (sigs || cpos == NULL_POSITION) + return (NULL_POSITION); + } + } else + { + /* + * Go backward. + */ + if (ch_seek(p->pos)) + return (NULL_POSITION); + for (clno = p->line, cpos = p->pos; clno > lno; clno--) + { + /* + * Allow a signal to abort this loop. + */ + cpos = back_raw_line(cpos, (char **)NULL); + if (sigs || cpos == NULL_POSITION) + return (NULL_POSITION); + } + } + /* + * We might as well cache it. + */ + add_lnum(clno, cpos); + return (cpos); +} + +/* + * Return the line number of the "current" line. + * The argument "where" tells which line is to be considered + * the "current" line (e.g. TOP, BOTTOM, MIDDLE, etc). + */ + public int +currline(where) + int where; +{ + POSITION pos; + POSITION len; + int lnum; + + pos = position(where); + len = ch_length(); + while (pos == NULL_POSITION && where >= 0 && where < sc_height) + pos = position(++where); + if (pos == NULL_POSITION) + pos = len; + lnum = find_linenum(pos); + if (pos == len) + lnum--; + return (lnum); +} diff --git a/bin/less/linkscr b/bin/less/linkscr new file mode 100755 index 0000000..9b22a04 --- /dev/null +++ b/bin/less/linkscr @@ -0,0 +1,33 @@ +13/direct256 +o/MAIN +o/GSOS +o/BRAC +o/CH +o/CHARSET +o/CMDBUF +o/COMMAND +o/DECODE +o/EDIT +o/FILENAME +o/FORWBACK +o/HELP +o/IFILE +o/INPUT +o/JUMP +o/LINE +o/LINENUM +o/LSYSTEM +o/MARK +o/OPTFUNC +o/OPTION +o/OPTTBL +o/OS +o/OUTPUT +o/POSITION +o/PROMPT +o/SCREEN +o/SEARCH +o/SIGNAL +o/TAGS +o/TTYIN +o/VERSION diff --git a/bin/less/lsystem.c b/bin/less/lsystem.c new file mode 100644 index 0000000..ffa2f03 --- /dev/null +++ b/bin/less/lsystem.c @@ -0,0 +1,323 @@ +/* + * Routines to execute other programs. + * Necessarily very OS dependent. + */ +#pragma noroot +#include +#include + +#include "less.h" +#include "position.h" +#include +#include +#include +#include +#include +#include + +#if __MSDOS__ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +char get_swchar(); +void swchar_to_dos(); +void swchar_to_unix(); +#endif + +#ifdef _ORCAC_ +segment "LoadSegONE"; +#endif + +extern char *getenv(char *); + +extern int screen_trashed; +extern IFILE curr_ifile; + + +/* + * Pass the specified command to a shell to be executed. + * Like plain "system()", but handles resetting terminal modes, etc. + */ + public void +lsystem(cmd) + char *cmd; +{ + register int inp; + register char *shell; + register char *p; + register char *curr_filename; + DeviceRec oldstdin; + + /* + * Print the command which is to be executed, + * unless the command starts with a "-". + */ + if (cmd[0] == '-') + cmd++; + else + { + lower_left(); + clear_eol(); + putstr("!"); + putstr(cmd); + putstr("\n"); + } + + /* + * Close the current input file. + */ + curr_filename = get_filename(curr_ifile); + (void) edit(NULL, 0); + + /* + * De-initialize the terminal and take out of raw mode. + */ + deinit(); + flush(); /* Make sure the deinit chars get out */ + raw_mode(0); + + /* + * Restore signals to their defaults. + */ + init_signals(0); + + /* + * Force standard input to be the user's terminal + * (the normal standard input), even if less's standard input + * is coming from a pipe. + */ +#if __MSDOS__ +{ + register int inp2; + + inp = dup(0); + inp2 = open("CON", O_TEXT|O_RDONLY); + dup2(0,inp2); +} +#else + inp = dup(STDIN_FILENO); + close(STDIN_FILENO); + if (open(".tty", O_RDWR) < 0) + dup(inp); +#endif + + /* + * Pass the command to the system to be executed. + * If we have a SHELL environment variable, use + * <$SHELL -c "command"> instead of just . + * If the command is empty, just invoke a shell. + */ +#if __MSDOS__ +{ + int result; + char sw_char; + + sw_char = get_swchar(); + swchar_to_dos(); + result = system(cmd); + if (result != 0) + perror("less"); + if (sw_char == '-') + swchar_to_unix(); +} +#else + p = NULL; + if ((shell = getenv("SHELL")) != NULL && *shell != '\0') + { + if (*cmd == '\0') + p = save(shell); + else + { + p = (char *) ecalloc(strlen(shell) + strlen(cmd) + 7, + sizeof(char)); +/* sprintf(p, "%s -c \"%s\"", shell, cmd);*/ + sprintf(p, "%s", cmd); + } + } + if (p == NULL) + { + if (*cmd == '\0') + p = save("gsh"); + else + p = save(cmd); + } + oldstdin=GetInputDevice(); + SetInputDevice(3,1L); + system(p); + SetInputDevice(oldstdin.deviceType, oldstdin.ptrOrSlot); + free(p); +#endif + + /* + * Restore standard input, reset signals, raw mode, etc. + */ +#if __MSDOS__ + close(inp2); + dup2(0,inp); + close(inp); +#else + close(STDIN_FILENO); + dup(inp); + close(inp); + open_getchr(); +/* above line added to reopen the damn terminal */ + +#endif + + init_signals(1); + raw_mode(1); + init(); + screen_trashed = 1; + + /* + * Reopen the current input file. + */ + (void) edit(curr_filename, 0); + +#if defined(SIGWINCH) || defined(SIGWIND) + /* + * Since we were ignoring window change signals while we executed + * the system command, we must assume the window changed. + * Warning: this leaves a signal pending (in "sigs"), + * so psignals() should be called soon after lsystem(). + */ + winch(); +#endif +} + +#if PIPEC + +/* + * Pipe a section of the input file into the given shell command. + * The section to be piped is the section "between" the current + * position and the position marked by the given letter. + * + * The "current" position means the top line displayed if the mark + * is after the current screen, or the bottom line displayed if + * the mark is before the current screen. + * If the mark is on the current screen, the whole screen is displayed. + */ + public int +pipe_mark(c, cmd) + int c; + char *cmd; +{ + POSITION mpos, tpos, bpos; + + /* + * mpos = the marked position. + * tpos = top of screen. + * bpos = bottom of screen. + */ + mpos = markpos(c); + if (mpos == NULL_POSITION) + return (-1); + tpos = position(TOP); + if (tpos == NULL_POSITION) + tpos = ch_zero(); + bpos = position(BOTTOM); + + if (c == '.') + return (pipe_data(cmd, tpos, bpos)); + else if (mpos <= tpos) + return (pipe_data(cmd, mpos, tpos)); + else if (bpos == NULL_POSITION) + return (pipe_data(cmd, tpos, bpos)); + else + return (pipe_data(cmd, tpos, mpos)); +} + +/* + * Create a pipe to the given shell command. + * Feed it the file contents between the positions spos and epos. + */ + public int +pipe_data(cmd, spos, epos) + char *cmd; + POSITION spos; + POSITION epos; +{ + register FILE *f; + register int c; + extern FILE *popen(); + + /* + * This is structured much like lsystem(). + * Since we're running a shell program, we must be careful + * to perform the necessary deinitialization before running + * the command, and reinitialization after it. + */ + if (ch_seek(spos) != 0) + { + error("Cannot seek to start position", NULL_PARG); + return (-1); + } + + if ((f = popen(cmd, "w")) == NULL) + { + error("Cannot create pipe", NULL_PARG); + return (-1); + } + lower_left(); + clear_eol(); + putstr("!"); + putstr(cmd); + putstr("\n"); + + deinit(); + flush(); + raw_mode(0); + init_signals(0); +#ifdef SIGPIPE + SIGNAL(SIGPIPE, SIG_IGN); +#endif + + while (epos == NULL_POSITION || spos++ <= epos) + { + /* + * Read a character from the file and give it to the pipe. + */ + c = ch_forw_get(); + if (c == EOI) + break; + if (putc(c, f) == EOF) + break; + } + + /* + * Finish up the last line. + */ +/* while (c != '\n' && c != EOI ) */ + while (c != '\r' && c != '\n' && c != EOI ) + { + c = ch_forw_get(); + if (c == EOI) + break; + if (putc(c, f) == EOF) + break; + } + + pclose(f); + +#ifdef SIGPIPE + SIGNAL(SIGPIPE, SIG_DFL); +#endif + init_signals(1); + raw_mode(1); + init(); + screen_trashed = 1; +#if defined(SIGWINCH) || defined(SIGWIND) + /* {{ Probably don't need this here. }} */ + winch(); +#endif + return (0); +} + +#endif diff --git a/bin/less/main.c b/bin/less/main.c new file mode 100644 index 0000000..644648c --- /dev/null +++ b/bin/less/main.c @@ -0,0 +1,293 @@ +/* + * Entry point, initialization, miscellaneous routines. + */ + +#pragma stacksize 1280 + +#include +#include +#include +#include "less.h" +#include "position.h" + +#ifdef _ORCAC_ +segment "LoadSegONE"; +#endif + +public int ispipe; +public char * every_first_cmd = NULL; +public int new_file; +public int is_tty; +public IFILE curr_ifile = NULL_IFILE; +public IFILE old_ifile = NULL_IFILE; +public struct scrpos initial_scrpos; +public int any_display = 0; +public int scroll; +public char * progname; +public int quitting; + +extern int file; +extern int quit_at_eof; +extern int cbufs; +extern int errmsgs; +extern int screen_trashed; +extern int force_open; + +#if LOGFILE +public int logfile = -1; +public int force_logfile = 0; +public char * namelogfile = NULL; +#endif + +#if EDITOR +public char * editor; +public char * editproto; +#endif + +#if TAGS +extern char * tagfile; +extern char * tagpattern; +extern int tagoption; +#endif + + + +/* + * Entry point. + */ +int main(argc, argv) + int argc; + char *argv[]; +{ + IFILE h; + int nofiles; + extern char *getenv(); + extern int _INITGNOSTDIO(void); + + /*if(!_INITGNOSTDIO()) + { + fprintf(stderr, "\n%s: requires GNO/ME to operate\n\n"); + exit(1); + } */ + + progname = *argv++; + + /* + * Process command line arguments and LESS environment arguments. + * Command line arguments override environment arguments. + */ + + init_prompt(); + init_charset(); + init_option(); + scan_option(getenv("LESS")); + +#define isoptstring(s) (((s)[0] == '-' || (s)[0] == '+') && (s)[1] != '\0') + while (--argc > 0 && (isoptstring(argv[0]) || isoptpending())) + scan_option(*argv++); +#undef isoptstring + + if (isoptpending()) + { + /* + * Last command line option was a flag requiring a + * following string, but there was no following string. + */ + nopendopt(); + quit(0); + } + +#if USERFILE + /* + * Try to use the lesskey file "$HOME/.less". + */ + add_hometable(); +#endif +#if EDITOR + editor = getenv("EDITOR"); + if (editor == NULL || *editor == '\0') + editor = EDIT_PGM; + editproto = getenv("LESSEDIT"); + if (editproto == NULL || *editproto == '\0') + editproto = "%E ?lm+%lm. %f"; +#endif + + /* + * Set up terminal, etc. + */ +/* is_tty = isatty(1);*/ + is_tty = isatty(STDOUT_FILENO); + if (!is_tty) + { + /* + * Output is not a tty. + * Just copy the input file(s) to output. + */ + if (argc <= 0) + { + if (edit("-", 0) == 0) + cat_file(); + } else + { + while (--argc >= 0) + { + if (edit(*argv++, 0) == 0) + cat_file(); + } + } + quit(0); + } + + /* + * Call get_ifile with all the command line filenames + * to "register" them with the ifile system. + */ + h = NULL_IFILE; + while (--argc >= 0) + h = get_ifile(*argv++, h); + + init_mark(); + raw_mode(1); + get_term(); + open_getchr(); + + init_signals(1); + + /* + * Select the first file to examine. + */ +#if TAGS + if (tagoption) + { + /* + * A -t option was given. + * Verify that no filenames were also given. + * Edit the file selected by the "tags" search, + * and search for the proper line in the file. + */ + if (nifile() > 0) + { + error("No filenames allowed with -t option", NULL_PARG); + quit(1); + } + if (tagfile == NULL) + quit(1); + if (edit(tagfile, 0) || tagsearch()) + quit(1); + nofiles = 0; + } else +#endif + if (nifile() == 0) + nofiles = edit("-", 0); /* Standard input */ + else + nofiles = edit_first(); + + if (nofiles) + { + quit(1); + /*NOTREACHED*/ + } + + init(); + commands(); + quit(0); + /*NOTREACHED*/ +} + +/* + * Copy a string, truncating to the specified length if necessary. + * Unlike strncpy(), the resulting string is guaranteed to be null-terminated. + */ + public void +strtcpy(to, from, len) + char *to; + char *from; + unsigned int len; +{ + strncpy(to, from, len); + to[len-1] = '\0'; +} + +/* + * Copy a string to a "safe" place + * (that is, to a buffer allocated by calloc). + */ + public char * +save(s) + char *s; +{ + register char *p; + + p = (char *) ecalloc(strlen(s)+1, sizeof(char)); + strcpy(p, s); + return (p); +} + + public VOID_POINTER +ecalloc(count, size) + int count; + unsigned int size; +{ + register VOID_POINTER p; + + p = calloc(count, size); + if (p != NULL) + return (p); + error("Cannot allocate memory", NULL_PARG); + quit(1); + /*NOTREACHED*/ +} + +/* + * Skip leading spaces in a string. + */ + public char * +skipsp(s) + register char *s; +{ + while (*s == ' ' || *s == '\t') + s++; + return (s); +} + +/* + * Exit the program. + */ + public void +quit(status) + int status; +{ + static int save_status; + + /* + * Put cursor at bottom left corner, clear the line, + * reset the terminal modes, and exit. + */ + if (status < 0) + status = save_status; + else + save_status = status; + quitting = 1; +#if LOGFILE + end_logfile(); +#endif + if (any_display) + { + lower_left(); + clear_eol(); + } + deinit(); + flush(); + raw_mode(0); +#if __MSDOS__ + restore_screen(); + /* + * If we don't close 2, we get some garbage from + * 2's buffer when it flushes automatically. + * I cannot track this one down RB + * The same bug shows up if we use ^C^C to abort. + */ + close(2); +#endif + exit(status); +} diff --git a/bin/less/mark.c b/bin/less/mark.c new file mode 100644 index 0000000..2e399a9 --- /dev/null +++ b/bin/less/mark.c @@ -0,0 +1,245 @@ +#pragma noroot +#include "less.h" +#include "position.h" + +#ifdef _ORCAC_ +segment "LoadSegONE"; +#endif + +extern IFILE curr_ifile; +extern int sc_height; +extern int jump_sline; + +/* + * A mark is an ifile (input file) plus a position within the file. + */ +struct mark { + IFILE m_ifile; + struct scrpos m_scrpos; +}; + +/* + * The table of marks. + * Each mark is identified by a lowercase or uppercase letter. + */ +#define NMARKS (2*26) /* a-z, A-Z */ +static struct mark marks[NMARKS]; + +/* + * Special mark for the "last mark"; addressed by the apostrophe. + */ +static struct mark lmark; + +static struct mark *getumark(int c); +static struct mark *getmark(int c); + +/* + * Initialize the mark table to show no marks are set. + */ + public void +init_mark(void) +{ + int i; + + for (i = 0; i < NMARKS; i++) + marks[i].m_scrpos.pos = NULL_POSITION; + lmark.m_scrpos.pos = NULL_POSITION; +} + +/* + * See if a mark letter is valid (between a and z). + */ + static struct mark * +getumark(c) + int c; +{ + if (c >= 'a' && c <= 'z') + return (&marks[c-'a']); + + if (c >= 'A' && c <= 'Z') + return (&marks[c-'A'+26]); + + error("Invalid mark letter", NULL_PARG); + return (NULL); +} + +/* + * Get the mark structure identified by a character. + * The mark struct may come either from the mark table + * or may be constructed on the fly for certain characters like ^, $. + */ + static struct mark * +getmark(c) + int c; +{ + register struct mark *m; + static struct mark sm; + + switch (c) + { + case '^': + /* + * Beginning of the current file. + */ + m = &sm; + m->m_scrpos.pos = ch_zero(); + m->m_scrpos.ln = 0; + m->m_ifile = curr_ifile; + break; + case '$': + /* + * End of the current file. + */ + if (ch_end_seek()) + { + error("Cannot seek to end of file", NULL_PARG); + return (NULL); + } + m = &sm; + m->m_scrpos.pos = ch_tell(); + m->m_scrpos.ln = sc_height-1; + m->m_ifile = curr_ifile; + break; + case '.': + /* + * Current position in the current file. + */ + m = &sm; + m->m_scrpos.pos = ch_tell(); + m->m_scrpos.ln = 0; + m->m_ifile = curr_ifile; + break; + case '\'': + /* + * The "last mark". + */ + m = &lmark; + break; + default: + /* + * Must be a user-defined mark. + */ + m = getumark(c); + if (m == NULL) + break; + if (m->m_scrpos.pos == NULL_POSITION) + { + error("Mark not set", NULL_PARG); + return (NULL); + } + break; + } + return (m); +} + +/* + * Is a mark letter is invalid? + */ + public int +badmark(c) + int c; +{ + return (getmark(c) == NULL); +} + +/* + * Set a user-defined mark. + */ + public void +setmark(c) + int c; +{ + register struct mark *m; + struct scrpos scrpos; + + m = getumark(c); + if (m == NULL) + return; + get_scrpos(&scrpos); + m->m_scrpos = scrpos; + m->m_ifile = curr_ifile; +} + +/* + * Set lmark (the mark named by the apostrophe). + */ + public void +lastmark(void) +{ + struct scrpos scrpos; + + get_scrpos(&scrpos); + if (scrpos.pos == NULL_POSITION) + return; + lmark.m_scrpos = scrpos; + lmark.m_ifile = curr_ifile; +} + +/* + * Go to a mark. + */ + public void +gomark(c) + int c; +{ + register struct mark *m; + struct scrpos scrpos; + + m = getmark(c); + if (m == NULL) + return; + + /* + * If we're trying to go to the lastmark and + * it has not been set to anything yet, + * set it to the beginning of the current file. + */ + if (m == &lmark && m->m_scrpos.pos == NULL_POSITION) + { + m->m_ifile = curr_ifile; + m->m_scrpos.pos = ch_zero(); + m->m_scrpos.ln = jump_sline; + } + + /* + * If we're using lmark, we must save the screen position now, + * because if we call edit() below, lmark will change. + * (We save the screen position even if we're not using lmark.) + */ + scrpos = m->m_scrpos; + if (m->m_ifile != curr_ifile) + { + /* + * Not in the current file; edit the correct file. + */ + if (edit(get_filename(m->m_ifile), 0)) + return; + } + + jump_loc(scrpos.pos, scrpos.ln); +} + +/* + * Return the position associated with a given mark letter. + * + * We don't return which screen line the position + * is associated with, but this doesn't matter much, + * because it's always the first non-blank line on the screen. + */ + public POSITION +markpos(c) + int c; +{ + register struct mark *m; + + m = getmark(c); + if (m == NULL) + return (NULL_POSITION); + + if (m->m_ifile != curr_ifile) + { + error("Mark not in current file", NULL_PARG); + return (NULL_POSITION); + } + return (m->m_scrpos.pos); +} diff --git a/bin/less/note b/bin/less/note new file mode 100644 index 0000000..53ab7f1 --- /dev/null +++ b/bin/less/note @@ -0,0 +1,2 @@ +C 2.0.1 doesn't optimize anywhere near as well as C 2.0.1a3. There +is a noticeable speed difference in one particular test file. diff --git a/bin/less/optfunc.c b/bin/less/optfunc.c new file mode 100644 index 0000000..2924845 --- /dev/null +++ b/bin/less/optfunc.c @@ -0,0 +1,381 @@ +/* + * Handling functions for command line options. + * + * Most options are handled by the generic code in option.c. + * But all string options, and a few non-string options, require + * special handling specific to the particular option. + * This special processing is done by the "handling functions" in this file. + * + * Each handling function is passed a "type" and, if it is a string + * option, the string which should be "assigned" to the option. + * The type may be one of: + * INIT The option is being initialized from the command line. + * TOGGLE The option is being changed from within the program. + * QUERY The setting of the option is merely being queried. + */ +#pragma noroot +#include "less.h" +#include "option.h" + +#ifdef _ORCAC_ +segment "LoadSegONE"; +#endif + +extern int nbufs; +extern int ispipe; +extern int cbufs; +extern int pr_type; +extern int nohelp; +extern int plusoption; +extern char *prproto[]; +extern char *eqproto; +extern IFILE curr_ifile; +#if LOGFILE +extern char *namelogfile; +extern int force_logfile; +extern int logfile; +extern char *glob(); +#endif +#if TAGS +public int tagoption = 0; +extern char *tagfile; +extern char *tagpattern; +extern char *tags; +#endif +#if __MSDOS__ +public char *window_box = NULL; +extern int directvideo; +extern int output_mode; +#endif + + + +#if LOGFILE +/* + * Handler for -o option. + */ + public void +opt_o(type, s) + int type; + char *s; +{ + PARG parg; + + switch (type) + { + case INIT: + namelogfile = s; + break; + case TOGGLE: + if (!ispipe) + { + error("Input is not a pipe", NULL_PARG); + return; + } + if (logfile >= 0) + { + error("Log file is already in use", NULL_PARG); + return; + } + s = skipsp(s); + namelogfile = glob(s); + if (namelogfile == NULL) + namelogfile = save(s); + use_logfile(s); + sync_logfile(); + break; + case QUERY: + if (logfile < 0) + error("No log file", NULL_PARG); + else + { + parg.p_string = namelogfile; + error("Log file \"%s\"", &parg); + } + break; + } +} + +/* + * Handler for -O option. + */ + public void +opt__O(type, s) + int type; + char *s; +{ + force_logfile = 1; + opt_o(type, s); +} + +/* + * Handlers for obsolete -l and -L options. + */ + public void +opt_l(type, s) + int type; + char *s; +{ + error("The -l option is obsolete. Use -o", NULL_PARG); +} + + public void +opt__L(type, s) + int type; + char *s; +{ + error("The -L option is obsolete. Use -O", NULL_PARG); +} +#endif + +#if USERFILE + public void +opt_k(type, s) + int type; + char *s; +{ + PARG parg; + + switch (type) + { + case INIT: + if (add_cmdtable(s)) + { + parg.p_string = s; + error("Cannot use lesskey file \"%s\"", &parg); + } + break; + case QUERY: + case TOGGLE: + error("Cannot query the -k flag", NULL_PARG); + break; + } +} +#endif + +#if TAGS +/* + * Handler for -t option. + */ + public void +opt_t(type, s) + int type; + char *s; +{ + char *curr_filename; + + switch (type) + { + case INIT: + tagoption = 1; + findtag(s); + break; + case TOGGLE: + findtag(skipsp(s)); + if (tagfile != NULL) + { + curr_filename = get_filename(curr_ifile); + if (edit(tagfile, 0) == 0) + if (tagsearch()) + (void) edit(curr_filename, 0); + } + break; + case QUERY: + error("Tag is required after -t", NULL_PARG); + break; + } +} + +/* + * Handler for -T option. + */ + public void +opt__T(type, s) + int type; + char *s; +{ + PARG parg; + + switch (type) + { + case INIT: + tags = s; + break; + case TOGGLE: + s = skipsp(s); + tags = glob(s); + if (tags == NULL) + tags = save(s); + break; + case QUERY: + parg.p_string = tags; + error("Tags file \"%s\"", &parg); + break; + } +} +#endif + +/* + * Handler for -p option. + */ + public void +opt_p(type, s) + int type; + register char *s; +{ + switch (type) + { + case INIT: + /* + * Unget a search command for the specified string. + * {{ This won't work if the "/" command is + * changed or invalidated by a .lesskey file. }} + */ + plusoption = 1; + ungetsc(s); + ungetsc("/"); + break; + case QUERY: + error("Pattern is required after -p", NULL_PARG); + break; + } +} + +/* + * Handler for -P option. + */ + public void +opt__P(type, s) + int type; + register char *s; +{ + register char **proto; + PARG parg; + + switch (type) + { + case INIT: + case TOGGLE: + /* + * Figure out which prototype string should be changed. + */ + switch (*s) + { + case 'm': proto = &prproto[PR_MEDIUM]; s++; break; + case 'M': proto = &prproto[PR_LONG]; s++; break; + case '=': proto = &eqproto; s++; break; + default: proto = &prproto[pr_type]; break; + } + free(*proto); + *proto = save(s); + break; + case QUERY: + parg.p_string = prproto[pr_type]; + error("%s", &parg); + break; + } +} + +/* + * Handler for the -b option. + */ + /*ARGSUSED*/ + public void +opt_b(type, s) + int type; + char *s; +{ + switch (type) + { + case TOGGLE: + case QUERY: + /* + * Allocate the new number of buffers. + */ + cbufs = ch_nbuf(cbufs); + break; + case INIT: + break; + } +} + +#if __MSDOS__ +/* + * Handler for -v option. (use BIOS or direct video) + */ + public void +opt_v(type, s) + int type; + register char *s; +{ + switch (type) + { + case INIT: + case TOGGLE: + if (output_mode == 2) + directvideo = 1; + else + directvideo = 0; + break; + case QUERY: + break; + } +} + +/* + * Handler for -W option. (set/modify window boundaries) + */ + public void +opt_W(type, s) + int type; + register char *s; +{ + PARG parg; + + switch (type) + { + case INIT: + window_box = save(s); + break; /* get_term will take care of actually setting window */ +#ifdef MOVE_WINDOW + case TOGGLE: + if (window_box != NULL) + free(window_box); + window_box = save(s); + reset_window(); + break; +#endif + case QUERY: + parg.p_string = window_box; + error("%s", &parg); + break; + } +} +#endif + +/* + * "-?" means display a help message. + * If from the command line, exit immediately. + */ + /*ARGSUSED*/ + public void +opt_query(type, s) + int type; + char *s; +{ + if (nohelp) + return; + switch (type) + { + case QUERY: + case TOGGLE: + error("Use \"h\" for help", NULL_PARG); + break; + case INIT: + raw_mode(1); + init(); + help(); + quit(0); + /*NOTREACHED*/ + } +} diff --git a/bin/less/option.c b/bin/less/option.c new file mode 100644 index 0000000..ba20b40 --- /dev/null +++ b/bin/less/option.c @@ -0,0 +1,505 @@ +/* + * Process command line options. + * + * Each option is a single letter which controls a program variable. + * The options have defaults which may be changed via + * the command line option, toggled via the "-" command, + * or queried via the "_" command. + */ +#pragma noroot +#include "less.h" +#include "option.h" + +#ifdef _ORCAC_ +segment "LoadSegONE"; +#endif + +static struct option *pendopt; +public int plusoption; + +static char *propt(int c); +static char *optstring(char *s, int c); +static int flip_triple(int val, int lc); +static void nostring(int c); + +extern int screen_trashed; +extern char *every_first_cmd; + +/* + * Scan an argument (either from the command line or from the + * LESS environment variable) and process it. + */ + public void +scan_option(s) + char *s; +{ + register struct option *o; + register int c; + char *str; + int set_default; + PARG parg; + + if (s == NULL) + return; + + /* + * If we have a pending string-valued option, handle it now. + * This happens if the previous option was, for example, "-P" + * without a following string. In that case, the current + * option is simply the string for the previous option. + */ + if (pendopt != NULL) + { + (*pendopt->ofunc)(INIT, s); + pendopt = NULL; + return; + } + + set_default = 0; + + while (*s != '\0') + { + /* + * Check some special cases first. + */ + switch (c = *s++) + { + case ' ': + case '\t': + case END_OPTION_STRING: + continue; + case '-': + /* + * "-+" means set these options back to their defaults. + * (They may have been set otherwise by previous + * options.) + */ + if (set_default = (*s == '+')) + s++; + continue; + case '+': + /* + * An option prefixed by a "+" is ungotten, so + * that it is interpreted as less commands + * processed at the start of the first input file. + * "++" means process the commands at the start of + * EVERY input file. + */ + plusoption = 1; + if (*s == '+') + every_first_cmd = save(++s); + else + ungetsc(s); + s = optstring(s, c); + continue; + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + /* + * Special "more" compatibility form "-" + * instead of -z to set the scrolling + * window size. + */ + s--; + c = 'z'; + break; + } + + /* + * Not a special case. + * Look up the option letter in the option table. + */ + o = findopt(c); + if (o == NULL) + { + parg.p_string = propt(c); + error("There is no %s flag (\"less -\\?\" for help)", + &parg); + quit(1); + } + + switch (o->otype & OTYPE) + { + case BOOL: + if (set_default) + *(o->ovar) = o->odefault; + else + *(o->ovar) = ! o->odefault; + break; + case TRIPLE: + if (set_default) + *(o->ovar) = o->odefault; + else + *(o->ovar) = flip_triple(o->odefault, + (o->oletter == c)); + break; + case STRING: + if (*s == '\0') + { + /* + * Set pendopt and return. + * We will get the string next time + * scan_option is called. + */ + pendopt = o; + return; + } + /* + * Don't do anything here. + * All processing of STRING options is done by + * the handling function. + */ + str = s; + s = optstring(s, c); + break; + case NUMBER: + *(o->ovar) = getnum(&s, c, (int*)NULL); + break; + } + /* + * If the option has a handling function, call it. + */ + if (o->ofunc != NULL) + (*o->ofunc)(INIT, str); + } +} + +/* + * Toggle command line flags from within the program. + * Used by the "-" and "_" commands. + * how_toggle may be: + * OPT_NO_TOGGLE just report the current setting, without changing it. + * OPT_TOGGLE invert the current setting + * OPT_UNSET set to the default value + * OPT_SET set to the inverse of the default value + */ + public void +toggle_option(c, s, how_toggle) + int c; + char *s; + int how_toggle; +{ + register struct option *o; + register int num; + int err; + PARG parg; + + /* + * Look up the option letter in the option table. + */ + o = findopt(c); + if (o == NULL) + { + parg.p_string = propt(c); + error("There is no %s flag", &parg); + return; + } + + if (how_toggle == OPT_TOGGLE && (o->otype & NO_TOGGLE)) + { + parg.p_string = propt(c); + error("Cannot change the %s flag", &parg); + return; + } + + /* + * Check for something which appears to be a do_toggle + * (because the "-" command was used), but really is not. + * This could be a string option with no string, or + * a number option with no number. + */ + switch (o->otype & OTYPE) + { + case STRING: + case NUMBER: + if (how_toggle == OPT_TOGGLE && *s == '\0') + how_toggle = OPT_NO_TOGGLE; + break; + } + + /* + * Now actually toggle (change) the variable. + */ + if (how_toggle != OPT_NO_TOGGLE) + { + switch (o->otype & OTYPE) + { + case BOOL: + /* + * Boolean. + */ + switch (how_toggle) + { + case OPT_TOGGLE: + *(o->ovar) = ! *(o->ovar); + break; + case OPT_UNSET: + *(o->ovar) = o->odefault; + break; + case OPT_SET: + *(o->ovar) = ! o->odefault; + break; + } + break; + case TRIPLE: + /* + * Triple: + * If user gave the lower case letter, then switch + * to 1 unless already 1, in which case make it 0. + * If user gave the upper case letter, then switch + * to 2 unless already 2, in which case make it 0. + */ + switch (how_toggle) + { + case OPT_TOGGLE: + *(o->ovar) = flip_triple(*(o->ovar), + o->oletter == c); + break; + case OPT_UNSET: + *(o->ovar) = o->odefault; + break; + case OPT_SET: + *(o->ovar) = flip_triple(o->odefault, + o->oletter == c); + break; + } + break; + case STRING: + /* + * String: don't do anything here. + * The handling function will do everything. + */ + switch (how_toggle) + { + case OPT_SET: + case OPT_UNSET: + error("Can't use \"-+\" or \"--\" for a string flag", + NULL_PARG); + return; + } + break; + case NUMBER: + /* + * Number: set the variable to the given number. + */ + switch (how_toggle) + { + case OPT_TOGGLE: + num = getnum(&s, '\0', &err); + if (!err) + *(o->ovar) = num; + break; + case OPT_UNSET: + *(o->ovar) = o->odefault; + break; + case OPT_SET: + error("Can't use \"--\" for a numeric flag", + NULL_PARG); + return; + } + break; + } + } + + /* + * Call the handling function for any special action + * specific to this option. + */ + if (o->ofunc != NULL) + (*o->ofunc)((how_toggle==OPT_NO_TOGGLE) ? QUERY : TOGGLE, s); + + /* + * Print a message describing the new setting. + */ + switch (o->otype & OTYPE) + { + case BOOL: + case TRIPLE: + /* + * Print the odesc message. + */ + error(o->odesc[*(o->ovar)], NULL_PARG); + break; + case NUMBER: + /* + * The message is in odesc[1] and has a %d for + * the value of the variable. + */ + parg.p_int = *(o->ovar); + error(o->odesc[1], &parg); + break; + case STRING: + /* + * Message was already printed by the handling function. + */ + break; + } + + if (how_toggle != OPT_NO_TOGGLE && (o->otype & REPAINT)) + screen_trashed = 1; +} + +/* + * "Toggle" a triple-valued option. + */ + static int +flip_triple(val, lc) + int val; + int lc; +{ + if (lc) + return ((val == 1) ? 0 : 1); + else + return ((val == 2) ? 0 : 2); +} + +/* + * Return a string suitable for printing as the "name" of an option. + * For example, if the option letter is 'x', just return "-x". + */ + static char * +propt(c) + int c; +{ + static char buf[8]; + + sprintf(buf, "-%s", prchar(c)); + return (buf); +} + +/* + * Determine if an option is a single character option (BOOL or TRIPLE), + * or if it a multi-character option (NUMBER). + */ + public int +single_char_option(c) + int c; +{ + register struct option *o; + + o = findopt(c); + if (o == NULL) + return (1); + return (o->otype & (BOOL|TRIPLE|NOVAR|NO_TOGGLE)); +} + +/* + * Return the prompt to be used for a given option letter. + * Only string and number valued options have prompts. + */ + public char * +opt_prompt(c) + int c; +{ + register struct option *o; + + o = findopt(c); + if (o == NULL || (o->otype & (STRING|NUMBER)) == 0) + return (NULL); + return (o->odesc[0]); +} + +/* + * Return whether or not there is a string option pending; + * that is, if the previous option was a string-valued option letter + * (like -P) without a following string. + * In that case, the current option is taken to be the string for + * the previous option. + */ + public int +isoptpending(void) +{ + return (pendopt != NULL); +} + +/* + * Print error message about missing string. + */ + static void +nostring(c) + int c; +{ + PARG parg; + parg.p_string = propt(c); + error("String is required after %s", &parg); +} + +/* + * Print error message if a STRING type option is not followed by a string. + */ + public void +nopendopt(void) +{ + nostring(pendopt->oletter); +} + +/* + * Scan to end of string or to an END_OPTION_STRING character. + * In the latter case, replace the char with a null char. + * Return a pointer to the remainder of the string, if any. + */ + static char * +optstring(s, c) + char *s; + int c; +{ + register char *p; + + if (*s == '\0') + { + nostring(c); + quit(1); + } + for (p = s; *p != '\0'; p++) + if (*p == END_OPTION_STRING) + { + *p = '\0'; + return (p+1); + } + return (p); +} + +/* + * Translate a string into a number. + * Like atoi(), but takes a pointer to a char *, and updates + * the char * to point after the translated number. + */ + public int +getnum(sp, c, errp) + char **sp; + int c; + int *errp; +{ + register char *s; + register int n; + register int neg; + PARG parg; + + s = skipsp(*sp); + neg = 0; + if (*s == '-') + { + neg = 1; + s++; + } + if (*s < '0' || *s > '9') + { + if (errp != NULL) + { + *errp = 1; + return (-1); + } + parg.p_string = propt(c); + error("Number is required after %s", &parg); + quit(1); + } + + n = 0; + while (*s >= '0' && *s <= '9') + n = 10 * n + *s++ - '0'; + *sp = s; + if (errp != NULL) + *errp = 0; + if (neg) + n = -n; + return (n); +} diff --git a/bin/less/option.h b/bin/less/option.h new file mode 100644 index 0000000..74b3f13 --- /dev/null +++ b/bin/less/option.h @@ -0,0 +1,38 @@ +#define END_OPTION_STRING ('$') + +/* + * Types of options. + */ +#define BOOL 01 /* Boolean option: 0 or 1 */ +#define TRIPLE 02 /* Triple-valued option: 0, 1 or 2 */ +#define NUMBER 04 /* Numeric option */ +#define STRING 010 /* String-valued option */ +#define NOVAR 020 /* No associated variable */ +#define REPAINT 040 /* Repaint screen after toggling option */ +#define NO_TOGGLE 0100 /* Option cannot be toggled with "-" cmd */ + +#define OTYPE (BOOL|TRIPLE|NUMBER|STRING|NOVAR) + +/* + * Argument to a handling function tells what type of activity: + */ +#define INIT 0 /* Initialization (from command line) */ +#define QUERY 1 /* Query (from _ or - command) */ +#define TOGGLE 2 /* Change value (from - command) */ + +/* Flag to toggle_option to specify how to "toggle" */ +#define OPT_NO_TOGGLE 0 +#define OPT_TOGGLE 1 +#define OPT_UNSET 2 +#define OPT_SET 3 + +struct option +{ + char oletter; /* The controlling letter (a-z) */ + char otype; /* Type of the option */ + int odefault; /* Default value */ + int *ovar; /* Pointer to the associated variable */ + void (*ofunc)(); /* Pointer to special handling function */ + char *odesc[3]; /* Description of each value */ +}; + diff --git a/bin/less/opttbl.c b/bin/less/opttbl.c new file mode 100644 index 0000000..a11f5ac --- /dev/null +++ b/bin/less/opttbl.c @@ -0,0 +1,265 @@ +/* + * The option table. + */ +#pragma noroot +#include "less.h" +#include "option.h" + +#ifdef _ORCAC_ +segment "LoadSegONE"; +#endif + +#define toupper(c) ((c)-'a'+'A') + +/* + * Variables controlled by command line options. + */ +public int quiet; /* Should we suppress the audible bell? */ +public int how_search; /* Where should forward searches start? */ +public int top_scroll; /* Repaint screen from top? + (alternative is scroll from bottom) */ +public int pr_type; /* Type of prompt (short, medium, long) */ +public int bs_mode; /* How to process backspaces */ +public int know_dumb; /* Don't complain about dumb terminals */ +public int quit_at_eof; /* Quit after hitting end of file twice */ +public int squeeze; /* Squeeze multiple blank lines into one */ +public int tabstop; /* Tab settings */ +public int back_scroll; /* Repaint screen on backwards movement */ +public int forw_scroll; /* Repaint screen on forward movement */ +public int twiddle; /* Display "~" for lines after EOF */ +public int caseless; /* Do "caseless" searches */ +public int linenums; /* Use line numbers */ +public int cbufs; /* Current number of buffers */ +public int autobuf; /* Automatically allocate buffers as needed */ +public int nohelp; /* Disable the HELP command */ +public int ctldisp; /* Send control chars to screen untranslated */ +public int force_open; /* Open the file even if not regular file */ +public int swindow; /* Size of scrolling window */ +public int jump_sline; /* Screen line of "jump target" */ +public int chopline; /* Truncate displayed lines at screen width */ +#if __MSDOS__ +public int output_mode; /* Which screen output method */ +public int refresh_on_quit; /* Repaint screen on quit, if possible */ +#endif + +/* + * Table of all options and their semantics. + */ +static struct option option[] = +{ + { 'a', BOOL, 0, &how_search, NULL, + "Search includes displayed screen", + "Search skips displayed screen", + NULL + }, + { 'b', NUMBER, 10, &cbufs, opt_b, + "Buffers: ", + "%d buffers", + NULL + }, + { 'B', BOOL, 1, &autobuf, NULL, + "Don't automatically allocate buffers", + "Automatically allocate buffers when needed", + NULL + }, + { 'c', TRIPLE, 0, &top_scroll, NULL, + "Repaint by scrolling from bottom of screen", + "Repaint by clearing each line", + "Repaint by painting from top of screen" + }, + { 'd', BOOL|NO_TOGGLE, 0, &know_dumb, NULL, + "Assume intelligent terminal", + "Assume dumb terminal", + NULL + }, +/* { 'e', TRIPLE, 0, &quit_at_eof, NULL,*/ + { 'e', TRIPLE, 1, &quit_at_eof, NULL, + "Don't quit at end-of-file", + "Quit at end-of-file", + "Quit immediately at end-of-file" + }, + { 'f', BOOL, 0, &force_open, NULL, + "Open only regular files", + "Open even non-regular files", + NULL + }, + { 'h', NUMBER, -1, &back_scroll, NULL, + "Backwards scroll limit: ", + "Backwards scroll limit is %d lines", + NULL + }, + { 'H', BOOL|NO_TOGGLE, 0, &nohelp, NULL, + "Allow help command", + "Don't allow help command", + NULL + }, + { 'i', BOOL, 0, &caseless, NULL, + "Case is significant in searches", + "Ignore case in searches", + NULL + }, + { 'j', NUMBER, 1, &jump_sline, NULL, + "Target line: ", + "Position target at screen line %d", + NULL + }, +#if USERFILE + { 'k', STRING|NO_TOGGLE, 0, NULL, opt_k, + NULL, NULL, NULL + }, +#endif +#if LOGFILE + { 'l', STRING, 0, NULL, opt_l, + NULL, NULL, NULL + }, + { 'L', STRING, 0, NULL, opt__L, + NULL, NULL, NULL + }, +#endif +/* { 'm', TRIPLE, 0, &pr_type, NULL,*/ + { 'm', TRIPLE, 0, &pr_type, NULL, + "Short prompt", + "Medium prompt", + "Long prompt" + }, + { 'n', TRIPLE|REPAINT, 1, &linenums, NULL, + "Don't use line numbers", + "Use line numbers", + "Constantly display line numbers" + }, +#if LOGFILE + { 'o', STRING, 0, NULL, opt_o, + "log file: ", NULL, NULL + }, + { 'O', STRING, 0, NULL, opt__O, + "Log file: ", NULL, NULL + }, +#endif + { 'p', STRING|NO_TOGGLE, 0, NULL, opt_p, + NULL, NULL, NULL + }, + { 'P', STRING, 0, NULL, opt__P, + "prompt: ", NULL, NULL + }, + { 'q', TRIPLE, 0, &quiet, NULL, + "Ring the bell for errors AND at eof/bof", + "Ring the bell for errors but not at eof/bof", + "Never ring the bell" + }, +/* { 'r', BOOL|REPAINT, 1, &ctldisp, NULL,*/ + { 'r', BOOL|REPAINT, 0, &ctldisp, NULL, + "Display control characters directly", + "Display control characters as ^X", + NULL + }, +#if __MSDOS__ + { 'R', BOOL|REPAINT, 0, &refresh_on_quit, NULL, + "Don't repaint screen on quit", + "Repaint screen on quit", + NULL + }, +#endif + { 's', BOOL|REPAINT, 0, &squeeze, NULL, + "Display all blank lines", + "Squeeze multiple blank lines", + NULL + }, + { 'S', BOOL|REPAINT, 0, &chopline, NULL, + "Fold long lines", + "Chop long lines", + NULL + }, +#if TAGS + { 't', STRING, 0, NULL, opt_t, + "tag: ", NULL, NULL + }, + { 'T', STRING, 0, NULL, opt__T, + "tags file: ", NULL, NULL + }, +#endif + { 'u', TRIPLE|REPAINT, 0, &bs_mode, NULL, + "Display underlined text in underline mode", + "Backspaces cause overstrike", + "Print backspace as ^H" + }, +#if __MSDOS__ + { 'v', TRIPLE|NO_TOGGLE, 0, &output_mode, opt_v, + "Output is to standard output, using ansi screen control", + "Output is to video BIOS", + "Output is directly to memory mapped video" + }, +#endif + { 'w', BOOL|REPAINT, 1, &twiddle, NULL, + "Display nothing for lines after end-of-file", + "Display ~ for lines after end-of-file", + NULL + }, +#if __MSDOS__ +#if MOVE_WINDOW +#define W_FLAGS STRING +#else +#define W_FLAGS STRING|NO_TOGGLE +#endif + { 'W', W_FLAGS, 0, NULL, opt_W, + "window boundaries: ", NULL, NULL + }, +#undef W_FLAGS +#endif + { 'x', NUMBER|REPAINT, 8, &tabstop, NULL, + "Tab stops: ", + "Tab stops every %d spaces", + NULL + }, + { 'y', NUMBER, -1, &forw_scroll, NULL, + "Forward scroll limit: ", + "Forward scroll limit is %d lines", + NULL + }, + { 'z', NUMBER, -1, &swindow, NULL, + "Scroll window size: ", + "Scroll window size is %d lines", + NULL + }, + { '?', NOVAR, 0, NULL, opt_query, + NULL, NULL, NULL + }, + { '\0' } +}; + + +/* + * Initialize each option to its default value. + */ + public void +init_option(void) +{ + register struct option *o; + + for (o = option; o->oletter != '\0'; o++) + { + /* + * Set each variable to its default. + */ + if (o->ovar != NULL) + *(o->ovar) = o->odefault; + } +} + +/* + * Find an option in the option table. + */ + public struct option * +findopt(c) + int c; +{ + register struct option *o; + + for (o = option; o->oletter != '\0'; o++) + { + if (o->oletter == c) + return (o); + if ((o->otype & TRIPLE) && toupper(o->oletter) == c) + return (o); + } + return (NULL); +} diff --git a/bin/less/os.c b/bin/less/os.c new file mode 100644 index 0000000..effd9ef --- /dev/null +++ b/bin/less/os.c @@ -0,0 +1,143 @@ +/* + * Operating system dependent routines. + * + * Most of the stuff in here is based on Unix, but an attempt + * has been made to make things work on other operating systems. + * This will sometimes result in a loss of functionality, unless + * someone rewrites code specifically for the new operating system. + * + * The makefile provides defines to decide whether various + * Unix features are present. + */ +#pragma noroot +#include +#include +#include +#include +#include "less.h" + +#ifdef _ORCAC_ +segment "LoadSegONE"; +#include +#endif + +/* + * BSD setjmp() saves (and longjmp() restores) the signal mask. + * This costs a system call or two per setjmp(), so if possible we clear the + * signal mask with sigsetmask(), and use _setjmp()/_longjmp() instead. + * On other systems, setjmp() doesn't affect the signal mask and so + * _setjmp() does not exist; we just use setjmp(). + */ +#if HAS__SETJMP && SIGSETMASK +#define SET_JUMP _setjmp +#define LONG_JUMP _longjmp +#else +#define SET_JUMP setjmp +#define LONG_JUMP longjmp +#endif + +extern char *getenv(char *); + +public int reading; + +static jmp_buf read_label; + +/* + * Like read() system call, but is deliberately interruptible. + * A call to intread() from a signal handler will interrupt + * any pending iread(). + */ +public int iread(fd, buf, len) + int fd; + char *buf; + unsigned int len; +{ + register int n; + + if (SET_JUMP(read_label)) + { + /* + * We jumped here from intread. + */ + reading = 0; +#if SIGSETMASK + sigsetmask(0); +#endif + return (READ_INTR); + } + + flush(); + reading = 1; + n = read(fd, buf, len); + reading = 0; + if (n < 0) + return (-1); + return (n); +} + +/* + * Interrupt a pending iread(). + */ + public void +intread(void) +{ + LONG_JUMP(read_label, 1); +} + +#if GET_TIME + public long +get_time(void) +{ + long t; + + time(&t); + return (t); +} +#endif + +/* + * errno_message: Return an error message based on the value of "errno". + */ + +#if PERROR + +extern char *sys_errlist[]; +extern int sys_nerr; +extern int errno; + + public char * +errno_message(filename) + char *filename; +{ + register char *p; + register char *m; + char msg[16]; + + if (errno < sys_nerr) + p = sys_errlist[errno]; + else + { + sprintf(msg, "Error %d", errno); + p = msg; + } + m = (char *) ecalloc(strlen(filename) + strlen(p) + 3, sizeof(char)); + sprintf(m, "%s: %s", filename, p); + return (m); +} + +#else + + public char * +errno_message(filename) + char *filename; +{ + register char *m; + static char msg[] = ": cannot open"; + + m = (char *) ecalloc(strlen(filename) + sizeof(msg), sizeof(char)); + strcpy(m, filename); + strcat(m, msg); + return (m); +} + +#endif diff --git a/bin/less/output.c b/bin/less/output.c new file mode 100644 index 0000000..228a1ab --- /dev/null +++ b/bin/less/output.c @@ -0,0 +1,440 @@ +/* + * High level routines dealing with the output to the screen. + */ +#pragma noroot +#include "less.h" +#include +#include + +#ifdef _ORCAC_ +#define FAST +segment "LoadSegONE"; +#endif + +public int errmsgs; /* Count of messages displayed by error() */ +public int need_clr; + +extern int sigs; +extern int sc_width; +extern int so_s_width, so_e_width; +extern int screen_trashed; +extern int any_display; +#if __MSDOS__ +extern int output_mode; +#endif + +static int iprintnum(int num, int radix); +static int iprintf(char *fmt, PARG *parg); + +/* + * Display the line which is in the line buffer. + */ + public void +put_line(void) +{ + register int c; + register int i; + int a; + int curr_attr; + + if (sigs) + { + /* + * Don't output if a signal is pending. + */ + screen_trashed = 1; + return; + } + + curr_attr = NORMAL; + + for (i = 0; (c = gline(i, &a)) != '\0'; i++) + { + if (a != curr_attr) + { + /* + * Changing attributes. + * Display the exit sequence for the old attribute + * and the enter sequence for the new one. + */ + switch (curr_attr) + { + case UNDERLINE: ul_exit(); break; + case BOLD: bo_exit(); break; + case BLINK: bl_exit(); break; + } + switch (a) + { + case UNDERLINE: ul_enter(); break; + case BOLD: bo_enter(); break; + case BLINK: bl_enter(); break; + } + curr_attr = a; + } + if (curr_attr == INVIS) + continue; + if (c == '\b') + putbs(); + else + putchr(c); + } +} + +/*static char obuf[1024];*/ +static char obuf[1024]; +static char *ob = obuf; + +/* + * Flush buffered output. + * + * If we haven't displayed any file data yet, + * output messages on error output (file descriptor 2), + * otherwise output on standard output (file descriptor 1). + * + * This has the desirable effect of producing all + * error messages on error output if standard output + * is directed to a file. It also does the same if + * we never produce any real output; for example, if + * the input file(s) cannot be opened. If we do + * eventually produce output, code in edit() makes + * sure these messages can be seen before they are + * overwritten or scrolled away. + */ + public void +flush(void) +{ + register int n; + register int fd; + +#if __MSDOS__ + if (output_mode == 0) + { + *ob = '\0'; + cputs(obuf); + ob = obuf; + return; + } +#endif + n = ob - obuf; + if (n == 0) + return; +/* fd = (any_display) ? 1 : 2;*/ + fd = (any_display) ? STDOUT_FILENO : STDERR_FILENO; + if (write(fd, obuf, n) != n) + screen_trashed = 1; + ob = obuf; +} + +/* + * Output a character. + */ +#ifdef FAST +public void putchr(c) + int c; +{ + static char *sz=obuf+sizeof(obuf); + char *oo; + asm { + lda ob+2 + sta oo+2 + lda ob + sta oo + cmp sz + bcc nofl + jsl flush + lda ob + sta oo + nofl: lda need_clr + beq noncl + stz need_clr + jsl lower_left + jsl clear_eol + noncl: lda c + cmp #10 + bne notnl + lda #13 + notnl: pha + lda [oo] + and #0xff00 + ora 1,s + sta [oo] + pla + inc ob + } +} +#else +public void putchr(c) + int c; +{ + if (ob >= &obuf[sizeof(obuf)]) + flush(); + if (need_clr) + { + need_clr = 0; + lower_left(); + clear_eol(); + } +#if __MSDOS__ + if (c == '\n') + *ob++ = '\r'; +#endif +#ifdef __ORCAC__ + if (c == '\n') + *ob++ = '\r'; + else +#endif + *ob++ = c; +} +#endif + +/* + * Output a string. + */ +#ifdef FAST +public void putstr(s) + register char *s; +{ + static char *sz=obuf+sizeof(obuf); + char *oo; + int ytmp; + + asm { + stz ytmp + lda ob+2 + sta oo+2 + lda ob + sta oo + lp: ldy ytmp + lda [s],y + and #0xff + beq done + pha + /* jsl putchr*/ + lda oo + cmp sz + bcc nofl + jsl flush + lda ob + sta oo + nofl: lda need_clr + beq noncl + stz need_clr + jsl lower_left + jsl clear_eol + noncl: lda 1,s + cmp #10 + bne notnl + lda #13 + notnl: pha + lda [oo] + and #0xff00 + ora 1,s + sta [oo] + pla + pla + inc ob + inc oo + inc ytmp + bra lp + done: + } +} +#else +public void putstr(s) + register char *s; +{ + while (*s != '\0') + putchr(*s++); +} +#endif + +/* + * Output an integer in a given radix. + */ + static int +iprintnum(num, radix) + int num; + int radix; +{ + register char *s; + int r; + int neg; + char buf[10]; + + if (neg = (num < 0)) + num = -num; + + s = buf; + do + { + *s++ = (num % radix) + '0'; + } while ((num /= radix) != 0); + + if (neg) + *s++ = '-'; + r = s - buf; + + while (s > buf) + putchr(*--s); + return (r); +} + +/* + * This function implements printf-like functionality + * using a more portable argument list mechanism than printf's. + */ + static int +iprintf(fmt, parg) + register char *fmt; + PARG *parg; +{ + register char *s; + register int n; + register int col; + + col = 0; + while (*fmt != '\0') + { + if (*fmt != '%') + { + putchr(*fmt++); + col++; + } else + { + ++fmt; + switch (*fmt++) { + case 's': + s = parg->p_string; + parg++; + while (*s != '\0') + { + putchr(*s++); + col++; + } + break; + case 'd': + n = parg->p_int; + parg++; + col += iprintnum(n, 10); + break; + } + } + } + return (col); +} + +/* + * Output a message in the lower left corner of the screen + * and wait for carriage return. + */ + public void +error(fmt, parg) + char *fmt; + PARG *parg; +{ + int c; + int col = 0; + static char return_to_continue[] = " (press RETURN)"; + + errmsgs++; + + if (any_display) + { + lower_left(); + clear_eol(); + so_enter(); + col += so_s_width; + } + + col += iprintf(fmt, parg); + + if (!any_display) + { +/* putchr('\n');*/ + putchr('\r'); + return; + } + + putstr(return_to_continue); + so_exit(); + col += sizeof(return_to_continue) + so_e_width; + +#if ONLY_RETURN + while ((c = getchr()) != '\n' && c != '\r') + bell(); +#else + c = getchr(); + if (c != '\n' && c != '\r' && c != ' ' && c != READ_INTR) + ungetcc(c); +#endif + lower_left(); + + if (col >= sc_width) + /* + * Printing the message has probably scrolled the screen. + * {{ Unless the terminal doesn't have auto margins, + * in which case we just hammered on the right margin. }} + */ + screen_trashed = 1; + + flush(); +} + +static char intr_to_abort[] = "... (interrupt to abort)"; + +/* + * Output a message in the lower left corner of the screen + * and don't wait for carriage return. + * Usually used to warn that we are beginning a potentially + * time-consuming operation. + */ + public void +ierror(fmt, parg) + char *fmt; + PARG *parg; +{ + lower_left(); + clear_eol(); + so_enter(); + (void) iprintf(fmt, parg); + putstr(intr_to_abort); + so_exit(); + flush(); + need_clr = 1; +} + +/* + * Output a message in the lower left corner of the screen + * and return a single-character response. + */ + public int +query(fmt, parg) + char *fmt; + PARG *parg; +{ + register int c; + int col = 0; + + if (any_display) + { + lower_left(); + clear_eol(); + } + + (void) iprintf(fmt, parg); + c = getchr(); + + if (!any_display) + { +/* putchr('\n');*/ + putchr('\r'); + return (c); + } + + lower_left(); + if (col >= sc_width) + screen_trashed = 1; + flush(); + + return (c); +} diff --git a/bin/less/position.c b/bin/less/position.c new file mode 100644 index 0000000..9732853 --- /dev/null +++ b/bin/less/position.c @@ -0,0 +1,212 @@ +/* + * Routines dealing with the "position" table. + * This is a table which tells the position (in the input file) of the + * first char on each currently displayed line. + * + * {{ The position table is scrolled by moving all the entries. + * Would be better to have a circular table + * and just change a couple of pointers. }} + */ +#pragma noroot +#include "less.h" +#include "position.h" + +#ifdef _ORCAC_ +segment "LoadSegONE"; +#endif + +static POSITION *table = NULL; /* The position table */ +static int table_size; + +extern int sc_width, sc_height; + +/* + * Return the starting file position of a line displayed on the screen. + * The line may be specified as a line number relative to the top + * of the screen, but is usually one of these special cases: + * the top (first) line on the screen + * the second line on the screen + * the bottom line on the screen + * the line after the bottom line on the screen + */ + public POSITION +position(where) + int where; +{ + switch (where) + { + case BOTTOM: + where = sc_height - 2; + break; + case BOTTOM_PLUS_ONE: + where = sc_height - 1; + break; + case MIDDLE: + where = sc_height / 2; + } + return (table[where]); +} + +/* + * Add a new file position to the bottom of the position table. + */ + public void +add_forw_pos(pos) + POSITION pos; +{ + register int i; + + /* + * Scroll the position table up. + */ + for (i = 1; i < sc_height; i++) + table[i-1] = table[i]; + table[sc_height - 1] = pos; +} + +/* + * Add a new file position to the top of the position table. + */ + public void +add_back_pos(pos) + POSITION pos; +{ + register int i; + + /* + * Scroll the position table down. + */ + for (i = sc_height - 1; i > 0; i--) + table[i] = table[i-1]; + table[0] = pos; +} + +/* + * Initialize the position table, done whenever we clear the screen. + */ + public void +pos_clear(void) +{ + register int i; + + for (i = 0; i < sc_height; i++) + table[i] = NULL_POSITION; +} + +/* + * Allocate the position table. + */ + public void +pos_init(void) +{ + if (sc_height <= table_size) + return; + if (table != NULL) + free((char*)table); + table = (POSITION *) ecalloc(sc_height, sizeof(POSITION)); + table_size = sc_height; +} + +/* + * See if the byte at a specified position is currently on the screen. + * Check the position table to see if the position falls within its range. + * Return the position table entry if found, -1 if not. + */ + public int +onscreen(pos) + POSITION pos; +{ + register int i; + + if (pos < table[0]) + return (-1); + for (i = 1; i < sc_height; i++) + if (pos < table[i]) + return (i-1); + return (-1); +} + +/* + * See if the entire screen is empty. + */ + public int +empty_screen(void) +{ + return (empty_lines(0, sc_height-1)); +} + + public int +empty_lines(s, e) + int s; + int e; +{ + register int i; + + for (i = s; i <= e; i++) + if (table[i] != NULL_POSITION) + return (0); + return (1); +} + +/* + * Get the current screen position. + * The screen position consists of both a file position and + * a screen line number where the file position is placed on the screen. + * Normally the screen line number is 0, but if we are positioned + * such that the top few lines are empty, we may have to set + * the screen line to a number > 0. + */ + public void +get_scrpos(scrpos) + struct scrpos *scrpos; +{ + register int i; + + /* + * Find the first line on the screen which has something on it, + * and return the screen line number and the file position. + */ + for (i = 0; i < sc_height; i++) + if (table[i] != NULL_POSITION) + { + scrpos->ln = i+1; + scrpos->pos = table[i]; + return; + } + /* + * The screen is empty. + */ + scrpos->pos = NULL_POSITION; +} + +/* + * Adjust a screen line number to be a simple positive integer + * in the range { 0 .. sc_height-2 }. + * (The bottom line, sc_height-1, is reserved for prompts, etc.) + * The given "sline" may be in the range { 1 .. sc_height-1 } + * to refer to lines relative to the top of the screen (starting from 1), + * or it may be in { -1 .. -(sc_height-1) } to refer to lines + * relative to the bottom of the screen. + */ + public int +adjsline(sline) + int sline; +{ + /* + * Negative screen line number means + * relative to the bottom of the screen. + */ + if (sline < 0) + sline += sc_height; + /* + * Can't be less than 1 or greater than sc_height-1. + */ + if (sline <= 0) + sline = 1; + if (sline >= sc_height) + sline = sc_height - 1; + /* + * Return zero-based line number, not one-based. + */ + return (sline-1); +} diff --git a/bin/less/position.h b/bin/less/position.h new file mode 100644 index 0000000..844c2e1 --- /dev/null +++ b/bin/less/position.h @@ -0,0 +1,8 @@ +/* + * Include file for interfacing to position.c modules. + */ +#define TOP (0) +#define TOP_PLUS_ONE (1) +#define BOTTOM (-1) +#define BOTTOM_PLUS_ONE (-2) +#define MIDDLE (-3) diff --git a/bin/less/prompt.c b/bin/less/prompt.c new file mode 100644 index 0000000..8031c45 --- /dev/null +++ b/bin/less/prompt.c @@ -0,0 +1,444 @@ +/* + * Prompting and other messages. + * There are three flavors of prompts, SHORT, MEDIUM and LONG, + * selected by the -m/-M options. + * There is also the "equals message", printed by the = command. + * A prompt is a message composed of various pieces, such as the + * name of the file being viewed, the percentage into the file, etc. + */ +#pragma noroot +#include + +#include "less.h" +#include "position.h" + +#ifdef _ORCAC_ +segment "LoadSegONE"; +#endif + +extern int pr_type; +extern int hit_eof; +extern int new_file; +extern int sc_width; +extern int so_s_width, so_e_width; +extern int linenums; +extern int sc_height; +extern int jump_sline; +extern IFILE curr_ifile; +#if EDITOR +extern char *editor; +#endif + +/* + * Prototypes for the three flavors of prompts. + * These strings are expanded by pr_expand(). + */ +static char s_proto[] = + "?n?f%f .?m(file %i of %m) ..?e(END) ?x- Next\\: %x..%t"; +static char m_proto[] = + "?n?f%f .?m(file %i of %m) ..?e(END) ?x- Next\\: %x.:?pB%pB\\%:byte %bB?s/%s...%t"; +static char M_proto[] = + "?f%f .?n?m(file %i of %m) ..?ltline %lt?L/%L. :byte %bB?s/%s. .?e(END) ?x- Next\\: %x.:?pB%pB\\%..%t"; +static char e_proto[] = + "?f%f .?m(file %i of %m) .?ltline %lt?L/%L. .byte %bB?s/%s. ?e(END) :?pB%pB\\%..%t"; + +public char *prproto[3]; +public char *eqproto = e_proto; + +static char message[250]; +static char *mp; + +static void setmp(void); +static void ap_pos(POSITION pos); +static void ap_int(int n); +static void ap_str(char *s); +static void ap_quest(void); +static POSITION curr_byte(int where); +static int cond(char c, int where); +static void protochar(int c, int where); +static char *skipcond(char *p); +static char *wherechar(char *p, int *wp); + +/* + * Initialize the prompt prototype strings. + */ + public void +init_prompt(void) +{ + prproto[0] = save(s_proto); + prproto[1] = save(m_proto); + prproto[2] = save(M_proto); + eqproto = save(e_proto); +} + +/* + * Set the message pointer to the end of the message string. + */ + static void +setmp(void) +{ + while (*mp != '\0') + mp++; +} + +/* + * Append a POSITION (as a decimal integer) to the end of the message. + */ + static void +ap_pos(pos) + POSITION pos; +{ + sprintf(mp, "%ld", (long)pos); + setmp(); +} + +/* + * Append an integer to the end of the message. + */ + static void +ap_int(n) + int n; +{ + sprintf(mp, "%d", n); + setmp(); +} + +/* + * Append a string to the end of the message. + */ + static void +ap_str(s) + char *s; +{ + strtcpy(mp, s, (unsigned int)(&message[sizeof(message)] - mp)); + setmp(); +} + +/* + * Append a question mark to the end of the message. + */ + static void +ap_quest() +{ + *mp++ = '?'; +} + +/* + * Return the "current" byte offset in the file. + */ + static POSITION +curr_byte(where) + int where; +{ + POSITION pos; + + pos = position(where); + while (pos == NULL_POSITION && where >= 0 && where < sc_height) + pos = position(++where); + if (pos == NULL_POSITION) + pos = ch_length(); + return (pos); +} + +/* + * Return the value of a prototype conditional. + * A prototype string may include conditionals which consist of a + * question mark followed by a single letter. + * Here we decode that letter and return the appropriate boolean value. + */ + static int +cond(c, where) + char c; + int where; +{ + switch (c) + { + case 'a': /* Anything in the message yet? */ + return (mp > message); + case 'b': /* Current byte offset known? */ + return (curr_byte(where) != NULL_POSITION); + case 'e': /* At end of file? */ + return (hit_eof); + case 'f': /* Filename known? */ + return (strcmp(get_filename(curr_ifile), "-") != 0); + case 'l': /* Line number known? */ + return (linenums); + case 'L': /* Final line number known? */ + return (linenums && ch_length() != NULL_POSITION); + case 'm': /* More than one file? */ + return (nifile() > 1); + case 'n': /* First prompt in a new file? */ + return (new_file); + case 'p': /* Percent into file known? */ + return (curr_byte(where) != NULL_POSITION && + ch_length() > 0); + case 's': /* Size of file known? */ + case 'B': + return (ch_length() != NULL_POSITION); + case 'x': /* Is there a "next" file? */ + return (next_ifile(curr_ifile) != NULL_IFILE); + } + return (0); +} + +/* + * Decode a "percent" prototype character. + * A prototype string may include various "percent" escapes; + * that is, a percent sign followed by a single letter. + * Here we decode that letter and take the appropriate action, + * usually by appending something to the message being built. + */ + static void +protochar(c, where) + int c; + int where; +{ + POSITION pos; + POSITION len; + int n; + IFILE h; + + switch (c) + { + case 'b': /* Current byte offset */ + pos = curr_byte(where); + if (pos != NULL_POSITION) + ap_pos(pos); + else + ap_quest(); + break; +#if EDITOR + case 'E': /* Editor name */ + ap_str(editor); + break; +#endif + case 'f': /* File name */ + ap_str(get_filename(curr_ifile)); + break; + case 'i': /* Index into list of files */ + ap_int(get_index(curr_ifile)); + break; + case 'l': /* Current line number */ + n = currline(where); + if (n != 0) + ap_int(n); + else + ap_quest(); + break; + case 'L': /* Final line number */ + len = ch_length(); + if (len == NULL_POSITION || len == ch_zero() || + (n = find_linenum(len)) <= 0) + ap_quest(); + else + ap_int(n-1); + break; + case 'm': /* Number of files */ + ap_int(nifile()); + break; + case 'p': /* Percent into file */ + pos = curr_byte(where); + len = ch_length(); + if (pos != NULL_POSITION && len > 0) + /* + * {{ This calculation may overflow! }} + */ + ap_int((int)(100*pos / len)); + else + ap_quest(); + break; + case 's': /* Size of file */ + case 'B': + len = ch_length(); + if (len != NULL_POSITION) + ap_pos(len); + else + ap_quest(); + break; + case 't': /* Truncate trailing spaces in the message */ + while (mp > message && mp[-1] == ' ') + mp--; + break; + case 'x': /* Name of next file */ + h = next_ifile(curr_ifile); + if (h != NULL_IFILE) + ap_str(get_filename(h)); + else + ap_quest(); + break; + } +} + +/* + * Skip a false conditional. + * When a false condition is found (either a false IF or the ELSE part + * of a true IF), this routine scans the prototype string to decide + * where to resume parsing the string. + * We must keep track of nested IFs and skip them properly. + */ + static char * +skipcond(p) + register char *p; +{ + register int iflevel; + + /* + * We came in here after processing a ? or :, + * so we start nested one level deep. + */ + iflevel = 1; + + for (;;) switch (*++p) + { + case '?': + /* + * Start of a nested IF. + */ + iflevel++; + break; + case ':': + /* + * Else. + * If this matches the IF we came in here with, + * then we're done. + */ + if (iflevel == 1) + return (p); + break; + case '.': + /* + * Endif. + * If this matches the IF we came in here with, + * then we're done. + */ + if (--iflevel == 0) + return (p); + break; + case '\\': + /* + * Backslash escapes the next character. + */ + ++p; + break; + case '\0': + /* + * Whoops. Hit end of string. + * This is a malformed conditional, but just treat it + * as if all active conditionals ends here. + */ + return (p-1); + } + /*NOTREACHED*/ +} + + static char * +wherechar(p, wp) + char *p; + int *wp; +{ + switch (*p) + { + case 'b': case 'l': case 'p': + switch (*++p) + { + case 't': *wp = TOP; break; + case 'm': *wp = MIDDLE; break; + case 'b': *wp = BOTTOM; break; + case 'B': *wp = BOTTOM_PLUS_ONE; break; + case 'j': *wp = adjsline(jump_sline); break; + default: *wp = TOP; p--; break; + } + } + return (p); +} + +/* + * Construct a message based on a prototype string. + */ + public char * +pr_expand(proto, maxwidth) + char *proto; + int maxwidth; +{ + register char *p; + register int c; + int where; + + mp = message; + + if (*proto == '\0') + return (""); + + for (p = proto; *p != '\0'; p++) + { + switch (*p) + { + default: /* Just put the character in the message */ + *mp++ = *p; + break; + case '\\': /* Backslash escapes the next character */ + p++; + *mp++ = *p; + break; + case '?': /* Conditional (IF) */ + if ((c = *++p) == '\0') + --p; + else + { + p = wherechar(p, &where); + if (!cond(c, where)) + p = skipcond(p); + } + break; + case ':': /* ELSE */ + p = skipcond(p); + break; + case '.': /* ENDIF */ + break; + case '%': /* Percent escape */ + if ((c = *++p) == '\0') + --p; + else + { + p = wherechar(p, &where); + protochar(c, where); + } + break; + } + } + + new_file = 0; + if (mp == message) + return (NULL); + *mp = '\0'; + if (maxwidth > 0 && mp >= message + maxwidth) + { + /* + * Message is too long. + * Return just the final portion of it. + */ + return (mp - maxwidth); + } + return (message); +} + +/* + * Return a message suitable for printing by the "=" command. + */ + public char * +eq_message(void) +{ + return (pr_expand(eqproto, 0)); +} + +/* + * Return a prompt. + * This depends on the prompt type (SHORT, MEDIUM, LONG), etc. + * If we can't come up with an appropriate prompt, return NULL + * and the caller will prompt with a colon. + */ + public char * +pr_string(void) +{ + return (pr_expand(prproto[pr_type], sc_width-so_s_width-so_e_width-2)); +} diff --git a/bin/less/proto.h b/bin/less/proto.h new file mode 100644 index 0000000..3acbd74 --- /dev/null +++ b/bin/less/proto.h @@ -0,0 +1,232 @@ +/* brac.c */ +public void match_brac(int obrac, int cbrac, int forwdir, int n); +/* ch.c */ +public void end_logfile(void); +public void synch_logfile(void); +public int ch_seek(register POSITION pos); +public int ch_end_seek(void); +public int ch_beg_seek(void); +public POSITION ch_length(void); +public POSITION ch_tell(void); +public int ch_forw_get(void); +public int ch_back_get(void); +public int ch_nbuf(int want_nbufs); +public void ch_flush(void); +public void ch_pipe(void); +public void ch_nonpipe(void); +/* from charset.c */ +public void init_charset(void); +public int binary_char(int c); +public int control_char(int c); +public char *prchar(int c); +/* from cmdbuf.c */ +public void cmd_reset(void); +public int len_cmdbuf(void); +public int cmd_erase(void); +public int cmd_char(int c); +public int cmd_int(void); +public void cmd_putstr(char *s); +public char *get_cmdbuf(void); +/* command.c */ +public void ungetcc(int c); +public void ungetsc(char *s); +public void commands(void); +/* decode.c */ +public int cmd_decode(char *cmd, char **sp); +public int add_cmdtable(char *filename); +public void add_hometable(void); +/* edit.c */ +public int edit(register char *filename, int just_looking); +public void edit_list(char *list); +public int edit_first(void); +public int edit_last(void); +public int edit_next(int n); +public int edit_prev(int n); +public int edit_index(int n); +public void cat_file(void); +public void use_logfile(char *filename); +/* filename.c */ +public char *homefile(char *filename); +public char *find_helpfile(void); +public char *fexpand(char *s); +int bin_file(int f); +public char *glob(char *filename); +public char *load_file(char *filename); +public POSITION filesize(int f); +public char *bad_file(char *filename); +public POSITION filesize(int f); +/* forwback.c */ +public void forw(register int n, POSITION pos, int force, int only_last, + int nblank); +public void back(register int n, POSITION pos, int force, int only_last); +public void forward(register int n, int force, int only_last); +public void backward(register int n, int force, int only_last); +public int get_back_scroll(void); +/* help.c */ +public void help(void); +/* ifile.c */ +public IFILE next_ifile(IFILE h); +public IFILE prev_ifile(IFILE h); +public int nifile(void); +public IFILE get_ifile(char *filename, IFILE prev); +public char *get_filename(IFILE ifile); +public int get_index(IFILE ifile); +public void store_pos(IFILE ifile, struct scrpos *scrpos); +public void get_pos(IFILE ifile, struct scrpos *scrpos); + +/* from input.c */ +public POSITION forw_line(POSITION curr_pos); +public POSITION back_line(POSITION curr_pos); + +/* jump.c */ +public void jump_forw(void); +public void jump_back(int n); +public void repaint(void); +public void jump_repaint(int percent); +public void jump_line_loc(POSITION pos, int sline); +public void jump_loc(POSITION pos, int sline); +/* line.c */ +void prewind(void); +public void plinenum(POSITION pos); +int attr_swidth(int a); +int attr_ewidth(int a); +public int pappend(register int c); +public void pdone(int endline); +public int gline(register int i, register int *ap); +public void null_line(void); +public POSITION forw_raw_line(POSITION curr_pos, char **linep); +public POSITION back_raw_line(POSITION curr_pos, char **linep); +/* linenum.c */ +public void clr_linenum(void); +public void add_lnum(int lno, POSITION pos); +public int find_linenum(POSITION pos); +public POSITION find_pos(int lno); +public int currline(int where); +/* lsystem.c */ +public void lsystem(char *cmd); +public int pipe_mark(int c, char *cmd); +public int pipe_data(char *cmd, POSITION spos, POSITION epos); +/* main.c */ +public void strtcpy(char *to, char *from, unsigned int len); +public char *save(char *s); +public VOID_POINTER ecalloc(int count, unsigned int size); +public char *skipsp(register char *s); +public void quit(int status); +/* mark.c */ +public void init_mark(void); +public int badmark(int c); +public void setmark(int c); +public void lastmark(void); +public void gomark(int c); +public POSITION markpos(int c); +/* optfunc */ +public void opt_o(int type, char *s); +public void opt__O(int type, char *s); +public void opt_l(int type, char *s); +public void opt__L(int type, char *s); +public void opt_k(int type, char *s); +public void opt_t(int type, char *s); +public void opt__T(int type, char *s); +public void opt_p(int type, register char *s); +public void opt__P(int type, register char *s); +public void opt_b(int type, char *s); +public void opt_v(int type, register char *s); +public void opt_W(int type, register char *s); +public void opt_query(int type, char *s); + +/* from option.c */ + +public void scan_option(char *s); +public void toggle_option(int c, char *s, int how_toggle); +public int single_char_option(int c); +public char *opt_prompt(int c); +public int isoptpending(void); +public void nopendopt(void); +public int getnum(char **sp, int c, int *errp); + +/* from opttbl.c */ +public void init_option(void); +public struct option *findopt(int c); + +/* from os.c */ + +public int iread(int fd, char *buf, unsigned int len); +public void intread(void); +public long get_time(void); +public char *errno_message(char *filename); + +/* from output.c */ + +public void put_line(void); +public void flush(void); +public void putchr(int c); +public void putstr(char *s); +public void error(char *fmt, PARG *parg); +public void ierror(char *fmt, PARG *parg); +public int query(char *fmt, PARG *parg); + +/* from position.c */ + +public POSITION position(int where); +public void add_forw_pos(POSITION pos); +public void add_back_pos(POSITION pos); +public void pos_clear(void); +public void pos_init(void); +public int onscreen(POSITION pos); +public int empty_screen(void); +public int empty_lines(int s, int e); +public void get_scrpos(struct scrpos *scrpos); +public int adjsline(int sline); + +/* from prompt.c */ + +public void init_prompt(void); +public char *pr_expand(char *proto, int maxwidth); +public char *eq_message(void); +public char *pr_string(void); + +/* from screen.c */ + +public void raw_mode(int on); +public void scrsize(int *p_height, int *p_width); +public void get_term(void); +public void init(void); +public void deinit(void); +public void home(void); +public void add_line(void); +public void lower_left(void); +public void bell(void); +public void vbell(void); +public void clear(void); +public void clear_eol(void); +public void so_enter(void); +public void so_exit(void); +public void ul_enter(void); +public void ul_exit(void); +public void bo_enter(void); +public void bo_exit(void); +public void bl_enter(void); +public void bl_exit(void); +public void backspace(void); +public void putbs(void); + +/* from search.c */ + +public int search(int search_type, char *pattern, int n); + +/* from signal.c */ + +public void fake_interrupt(void); +public HANDLER winch(int type); +public void init_signals(int on); +public void psignals(void); + +/* from tags.c */ + +public void findtag(char *tag); +public int tagsearch(void); + +/* from ttyin.c */ + +public void open_getchr(void); +public int getchr(void); diff --git a/bin/less/screen.c b/bin/less/screen.c new file mode 100644 index 0000000..8b46a9b --- /dev/null +++ b/bin/less/screen.c @@ -0,0 +1,712 @@ +/* + * Routines which deal with the characteristics of the terminal. + * Uses termcap to be as terminal-independent as possible. + * + * {{ Someday this should be rewritten to use curses. }} + */ +#pragma noroot +#include +#include "less.h" +#if XENIX +#include +#include +#endif + +#ifdef _ORCAC_ +segment "LoadSegONE"; +#endif + +#if TERMIO +#include +#else +#include +#endif + +#if !TERMIO && defined(TIOCGWINSZ) +#include +#else +/* + * For the Unix PC (ATT 7300 & 3B1): + * Since WIOCGETD is defined in sys/window.h, we can't use that to decide + * whether to include sys/window.h. Use SIGPHONE from signal.h instead. + */ +#include +#ifdef SIGPHONE +#include +#endif +#endif + +#if NEED_PTEM_H && defined(TIOCGWINSZ) +/* + * All this just to get struct winsize. Sigh. + */ +#include +#include +#include +#endif + +/* + * Strings passed to tputs() to do various terminal functions. + */ +static char + *sc_pad, /* Pad string */ + *sc_home, /* Cursor home */ + *sc_addline, /* Add line, scroll down following lines */ + *sc_lower_left, /* Cursor to last line, first column */ + *sc_move, /* General cursor positioning */ + *sc_clear, /* Clear screen */ + *sc_eol_clear, /* Clear to end of line */ + *sc_s_in, /* Enter standout (highlighted) mode */ + *sc_s_out, /* Exit standout mode */ + *sc_u_in, /* Enter underline mode */ + *sc_u_out, /* Exit underline mode */ + *sc_b_in, /* Enter bold mode */ + *sc_b_out, /* Exit bold mode */ + *sc_bl_in, /* Enter blink mode */ + *sc_bl_out, /* Exit blink mode */ + *sc_visual_bell, /* Visual bell (flash screen) sequence */ + *sc_backspace, /* Backspace cursor */ + *sc_init, /* Startup terminal initialization */ + *sc_deinit; /* Exit terminal de-initialization */ + +static int init_done = 0; + +public int auto_wrap; /* Terminal does \r\n when write past margin */ +public int ignaw; /* Terminal ignores \n immediately after wrap */ +public int erase_char, kill_char; /* The user's erase and line-kill chars */ +public int sc_width, sc_height; /* Height & width of screen */ +public int bo_s_width, bo_e_width; /* Printing width of boldface seq */ +public int ul_s_width, ul_e_width; /* Printing width of underline seq */ +public int so_s_width, so_e_width; /* Printing width of standout seq */ +public int bl_s_width, bl_e_width; /* Printing width of blink seq */ + +static void cannot(char *s); +static void inc_costcount(int c); +static int cost(char *t); +static char *cheaper(char *t1, char *t2, char *doit, char *def); + +/* + * These two variables are sometimes defined in, + * and needed by, the termcap library. + * It may be necessary on some systems to declare them extern here. + */ +extern short ospeed; /* Terminal output baud rate */ +extern char PC; /* Pad character */ + +extern int quiet; /* If VERY_QUIET, use visual bell for bell */ +extern int know_dumb; /* Don't complain about a dumb terminal */ +extern int back_scroll; +extern int swindow; +extern char *tgetstr(char *, char **); +extern char *tgoto(char *, int, int); +extern char *getenv(char *); + + +/* + * Change terminal to "raw mode", or restore to "normal" mode. + * "Raw mode" means + * 1. An outstanding read will complete on receipt of a single keystroke. + * 2. Input is not echoed. + * 3. On output, \n is mapped to \r\n. + * 4. \t is NOT expanded into spaces. + * 5. Signal-causing characters such as ctrl-C (interrupt), + * etc. are NOT disabled. + * It doesn't matter whether an input \n is mapped to \r, or vice versa. + */ + public void +raw_mode(on) + int on; +{ + static int curr_on = 0; + + if (on == curr_on) + return; +#if TERMIO + { + struct termio s; + static struct termio save_term; + + if (on) + { + /* + * Get terminal modes. + */ + ioctl(2, TCGETA, &s); + + /* + * Save modes and set certain variables dependent on modes. + */ + save_term = s; + ospeed = s.c_cflag & CBAUD; + erase_char = s.c_cc[VERASE]; + kill_char = s.c_cc[VKILL]; + + /* + * Set the modes to the way we want them. + */ + s.c_lflag &= ~(ICANON|ECHO|ECHOE|ECHOK|ECHONL); + s.c_oflag |= (OPOST|ONLCR|TAB3); + s.c_oflag &= ~(OCRNL|ONOCR|ONLRET); + s.c_cc[VMIN] = 1; + s.c_cc[VTIME] = 0; + } else + { + /* + * Restore saved modes. + */ + s = save_term; + } + ioctl(2, TCSETAW, &s); + } +#else + { + struct sgttyb s; + static struct sgttyb save_term; + + if (on) + { + /* + * Get terminal modes. + */ + ioctl(2, TIOCGETP, &s); + + /* + * Save modes and set certain variables dependent on modes. + */ + save_term = s; + ospeed = s.sg_ospeed; + erase_char = s.sg_erase; + kill_char = s.sg_kill; + + /* + * Set the modes to the way we want them. + */ + s.sg_flags |= CBREAK; + s.sg_flags &= ~(ECHO|XTABS); + } else + { + /* + * Restore saved modes. + */ + s = save_term; + } + ioctl(2, TIOCSETN, &s); + } +#endif + curr_on = on; +} + + static void +cannot(s) + char *s; +{ + PARG parg; + + if (know_dumb) + /* + * User knows this is a dumb terminal, so don't tell him. + */ + return; + + parg.p_string = s; + error("WARNING: terminal cannot %s", &parg); +} + +/* + * Get size of the output screen. + */ + public void +scrsize(p_height, p_width) + int *p_height; + int *p_width; +{ + register char *s; +#ifdef TIOCGWINSZ + struct winsize w; +#else +#ifdef WIOCGETD + struct uwdata w; +#endif +#endif + +#ifdef TIOCGWINSZ + if (ioctl(2, TIOCGWINSZ, &w) == 0 && w.ws_row > 0) + *p_height = w.ws_row; + else +#else +#ifdef WIOCGETD + if (ioctl(2, WIOCGETD, &w) == 0 && w.uw_height > 0) + *p_height = w.uw_height/w.uw_vs; + else +#endif +#endif + if ((s = getenv("LINES")) != NULL) + *p_height = atoi(s); + else + *p_height = tgetnum("li"); + + if (*p_height <= 0) + *p_height = 24; + +#ifdef TIOCGWINSZ + if (ioctl(2, TIOCGWINSZ, &w) == 0 && w.ws_col > 0) + *p_width = w.ws_col; + else +#ifdef WIOCGETD + if (ioctl(2, WIOCGETD, &w) == 0 && w.uw_width > 0) + *p_width = w.uw_width/w.uw_hs; + else +#endif +#endif + if ((s = getenv("COLUMNS")) != NULL) + *p_width = atoi(s); + else + *p_width = tgetnum("co"); + + if (*p_width <= 0) + *p_width = 80; +} + +/* + * Get terminal capabilities via termcap. + */ + public void +get_term(void) +{ + char *sp; + register char *t1, *t2; + register int hard; + char *term; + static char termbuf[2048]; + + static char sbuf[1024]; + + /* + * Find out what kind of terminal this is. + */ + if ((term = getenv("TERM")) == NULL) + term = "unknown"; + if (tgetent(termbuf, term) <= 0) + strcpy(termbuf, "dumb:hc:"); + + hard = tgetflag("hc"); + + /* + * Get size of the screen. + */ + scrsize(&sc_height, &sc_width); + pos_init(); + if (swindow < 0) + swindow = sc_height - 1; + + auto_wrap = tgetflag("am"); + ignaw = tgetflag("xn"); + + /* + * Assumes termcap variable "sg" is the printing width of: + * the standout sequence, the end standout sequence, + * the underline sequence, the end underline sequence, + * the boldface sequence, and the end boldface sequence. + */ + if ((so_s_width = tgetnum("sg")) < 0) + so_s_width = 0; + so_e_width = so_s_width; + + bo_s_width = bo_e_width = so_s_width; + ul_s_width = ul_e_width = so_s_width; + bl_s_width = bl_e_width = so_s_width; + + /* + * Get various string-valued capabilities. + */ + sp = sbuf; + + sc_pad = tgetstr("pc", &sp); + if (sc_pad != NULL) + PC = *sc_pad; + + sc_init = tgetstr("ti", &sp); + if (sc_init == NULL) + sc_init = ""; + + sc_deinit= tgetstr("te", &sp); + if (sc_deinit == NULL) + sc_deinit = ""; + + sc_eol_clear = tgetstr("ce", &sp); + if (hard || sc_eol_clear == NULL || *sc_eol_clear == '\0') + { + cannot("clear to end of line"); + sc_eol_clear = ""; + } + + sc_clear = tgetstr("cl", &sp); + if (hard || sc_clear == NULL || *sc_clear == '\0') + { + cannot("clear screen"); + sc_clear = "\n\n"; + } + + sc_move = tgetstr("cm", &sp); + if (hard || sc_move == NULL || *sc_move == '\0') + { + /* + * This is not an error here, because we don't + * always need sc_move. + * We need it only if we don't have home or lower-left. + */ + sc_move = ""; + } + + sc_s_in = tgetstr("so", &sp); + if (hard || sc_s_in == NULL) + sc_s_in = ""; + + sc_s_out = tgetstr("se", &sp); + if (hard || sc_s_out == NULL) + sc_s_out = ""; + + sc_u_in = tgetstr("us", &sp); + if (hard || sc_u_in == NULL) + sc_u_in = sc_s_in; + + sc_u_out = tgetstr("ue", &sp); + if (hard || sc_u_out == NULL) + sc_u_out = sc_s_out; + + sc_b_in = tgetstr("md", &sp); + if (hard || sc_b_in == NULL) + { + sc_b_in = sc_s_in; + sc_b_out = sc_s_out; + } else + { + sc_b_out = tgetstr("me", &sp); + if (hard || sc_b_out == NULL) + sc_b_out = ""; + } + + sc_bl_in = tgetstr("mb", &sp); + if (hard || sc_bl_in == NULL) + { + sc_bl_in = sc_s_in; + sc_bl_out = sc_s_out; + } else + { + sc_bl_out = sc_b_out; + } + + sc_visual_bell = tgetstr("vb", &sp); + if (hard || sc_visual_bell == NULL) + sc_visual_bell = ""; + + if (tgetflag("bs")) + sc_backspace = "\b"; + else + { + sc_backspace = tgetstr("bc", &sp); + if (sc_backspace == NULL || *sc_backspace == '\0') + sc_backspace = "\b"; + } + + /* + * Choose between using "ho" and "cm" ("home" and "cursor move") + * to move the cursor to the upper left corner of the screen. + */ + t1 = tgetstr("ho", &sp); + if (hard || t1 == NULL) + t1 = ""; + if (*sc_move == '\0') + t2 = ""; + else + { + strcpy(sp, tgoto(sc_move, 0, 0)); + t2 = sp; + sp += strlen(sp) + 1; + } + sc_home = cheaper(t1, t2, "home cursor", "|\b^"); + + /* + * Choose between using "ll" and "cm" ("lower left" and "cursor move") + * to move the cursor to the lower left corner of the screen. + */ + t1 = tgetstr("ll", &sp); + if (hard || t1 == NULL) + t1 = ""; + if (*sc_move == '\0') + t2 = ""; + else + { + strcpy(sp, tgoto(sc_move, 0, sc_height-1)); + t2 = sp; + sp += strlen(sp) + 1; + } + sc_lower_left = cheaper(t1, t2, + "move cursor to lower left of screen", "\r"); + + /* + * Choose between using "al" or "sr" ("add line" or "scroll reverse") + * to add a line at the top of the screen. + */ + t1 = tgetstr("al", &sp); + if (hard || t1 == NULL) + t1 = ""; + t2 = tgetstr("sr", &sp); + if (hard || t2 == NULL) + t2 = ""; + sc_addline = cheaper(t1, t2, "scroll backwards", ""); + if (*sc_addline == '\0') + { + /* + * Force repaint on any backward movement. + */ + back_scroll = 0; + } +} + +/* + * Return the cost of displaying a termcap string. + * We use the trick of calling tputs, but as a char printing function + * we give it inc_costcount, which just increments "costcount". + * This tells us how many chars would be printed by using this string. + * {{ Couldn't we just use strlen? }} + */ +static int costcount; + +/*ARGSUSED*/ + static void +inc_costcount(c) + int c; +{ + costcount++; +} + + static int +cost(t) + char *t; +{ + costcount = 0; + tputs(t, sc_height, inc_costcount); + return (costcount); +} + +/* + * Return the "best" of the two given termcap strings. + * The best, if both exist, is the one with the lower + * cost (see cost() function). + */ + static char * +cheaper(t1, t2, doit, def) + char *t1, *t2; + char *doit; + char *def; +{ + if (*t1 == '\0' && *t2 == '\0') + { + cannot(doit); + return (def); + } + if (*t1 == '\0') + return (t2); + if (*t2 == '\0') + return (t1); + if (cost(t1) < cost(t2)) + return (t1); + return (t2); +} + + +/* + * Below are the functions which perform all the + * terminal-specific screen manipulation. + */ + + +/* + * Initialize terminal + */ + public void +init(void) +{ + tputs(sc_init, sc_height, putchr); + init_done = 1; +} + +/* + * Deinitialize terminal + */ + public void +deinit(void) +{ + if (!init_done) + return; + tputs(sc_deinit, sc_height, putchr); + init_done = 0; +} + +/* + * Home cursor (move to upper left corner of screen). + */ + public void +home(void) +{ + tputs(sc_home, 1, putchr); +} + +/* + * Add a blank line (called with cursor at home). + * Should scroll the display down. + */ + public void +add_line(void) +{ + tputs(sc_addline, sc_height, putchr); +} + +/* + * Move cursor to lower left corner of screen. + */ + public void +lower_left(void) +{ + tputs(sc_lower_left, 1, putchr); +} + +/* + * Ring the terminal bell. + */ + public void +bell(void) +{ + if (quiet == VERY_QUIET) + vbell(); + else + putchr('\7'); +} + +/* + * Output the "visual bell", if there is one. + */ + public void +vbell(void) +{ + if (*sc_visual_bell == '\0') + return; + tputs(sc_visual_bell, sc_height, putchr); +} + +/* + * Clear the screen. + */ + public void +clear(void) +{ + tputs(sc_clear, sc_height, putchr); +} + +/* + * Clear from the cursor to the end of the cursor's line. + * {{ This must not move the cursor. }} + */ + public void +clear_eol(void) +{ + tputs(sc_eol_clear, 1, putchr); +} + +/* + * Begin "standout" (bold, underline, or whatever). + */ + public void +so_enter(void) +{ + tputs(sc_s_in, 1, putchr); +} + +/* + * End "standout". + */ + public void +so_exit(void) +{ + tputs(sc_s_out, 1, putchr); +} + +/* + * Begin "underline" (hopefully real underlining, + * otherwise whatever the terminal provides). + */ + public void +ul_enter(void) +{ + tputs(sc_u_in, 1, putchr); +} + +/* + * End "underline". + */ + public void +ul_exit(void) +{ + tputs(sc_u_out, 1, putchr); +} + +/* + * Begin "bold" + */ + public void +bo_enter(void) +{ + tputs(sc_b_in, 1, putchr); +} + +/* + * End "bold". + */ + public void +bo_exit(void) +{ + tputs(sc_b_out, 1, putchr); +} + +/* + * Begin "blink" + */ + public void +bl_enter(void) +{ + tputs(sc_bl_in, 1, putchr); +} + +/* + * End "blink". + */ + public void +bl_exit(void) +{ + tputs(sc_bl_out, 1, putchr); +} + +/* + * Erase the character to the left of the cursor + * and move the cursor left. + */ + public void +backspace(void) +{ + /* + * Try to erase the previous character by overstriking with a space. + */ + tputs(sc_backspace, 1, putchr); + putchr(' '); + tputs(sc_backspace, 1, putchr); +} + +/* + * Output a plain backspace, without erasing the previous char. + */ + public void +putbs(void) +{ + tputs(sc_backspace, 1, putchr); +} diff --git a/bin/less/search.c b/bin/less/search.c new file mode 100644 index 0000000..724fd4e --- /dev/null +++ b/bin/less/search.c @@ -0,0 +1,360 @@ +/* + * Routines to search a file for a pattern. + */ +#pragma noroot +#include +#include "less.h" +#include "position.h" + +#ifdef _ORCAC_ +segment "LoadSegONE"; +#endif + +#if REGCOMP +#include +#endif + +extern int sigs; +extern int how_search; +extern int caseless; +extern int linenums; +extern int jump_sline; + +/* + * Search for the n-th occurrence of a specified pattern, + * either forward or backward. + * Return the number of matches not yet found in this file + * (that is, n minus the number of matches found). + * Return -1 if the search should be aborted. + * Caller may continue the search in another file + * if less than n matches are found in this file. + */ + public int +search(search_type, pattern, n) + int search_type; + char *pattern; + int n; +{ + POSITION pos, linepos, oldpos; + register char *p; + register char *q; + register int goforw; + register int want_match; + char *line; + int linenum; + int line_match; + static int is_caseless; +#if RECOMP + char *re_comp(); + PARG parg; +#else +#if REGCMP + char *regcmp(); + static char *cpattern = NULL; +#else +#if REGCOMP + static struct regexp *regpattern = NULL; +#else + static char lpbuf[100]; + static char *last_pattern = NULL; +#endif +#endif +#endif + + /* + * Extract flags and type of search. + */ + goforw = (SRCH_DIR(search_type) == SRCH_FORW); + want_match = !(search_type & SRCH_NOMATCH); + + if (pattern != NULL && *pattern != '\0' && (is_caseless = caseless)) + { + /* + * Search will ignore case, unless + * there are any uppercase letters in the pattern. + */ + for (p = pattern; *p != '\0'; p++) + if (*p >= 'A' && *p <= 'Z') + { + is_caseless = 0; + break; + } + } +#if RECOMP + + /* + * (re_comp handles a null pattern internally, + * so there is no need to check for a null pattern here.) + */ + if ((parg.p_string = re_comp(pattern)) != NULL) + { + error("%s", &parg); + return (-1); + } +#else +#if REGCMP + if (pattern == NULL || *pattern == '\0') + { + /* + * A null pattern means use the previous pattern. + * The compiled previous pattern is in cpattern, so just use it. + */ + if (cpattern == NULL) + { + error("No previous regular expression", NULL_PARG); + return (-1); + } + } else + { + /* + * Otherwise compile the given pattern. + */ + char *s; + if ((s = regcmp(pattern, 0)) == NULL) + { + error("Invalid pattern", NULL_PARG); + return (-1); + } + if (cpattern != NULL) + free(cpattern); + cpattern = s; + } +#else +#if REGCOMP + if (pattern == NULL || *pattern == '\0') + { + /* + * A null pattern means use the previous pattern. + * The compiled previous pattern is in regpattern, + * so just use it. + */ + if (regpattern == NULL) + { + error("No previous regular expression", NULL_PARG); + return (-1); + } + } else + { + /* + * Otherwise compile the given pattern. + */ + struct regexp *s; + if ((s = regcomp(pattern)) == NULL) + { + error("Invalid pattern", NULL_PARG); + return (-1); + } + if (regpattern != NULL) + free(regpattern); + regpattern = s; + } +#else + if (pattern == NULL || *pattern == '\0') + { + /* + * Null pattern means use the previous pattern. + */ + if (last_pattern == NULL) + { + error("No previous regular expression", NULL_PARG); + return (-1); + } + pattern = last_pattern; + } else + { + strcpy(lpbuf, pattern); + last_pattern = lpbuf; + } +#endif +#endif +#endif + + /* + * Figure out where to start the search. + */ + if (empty_screen()) + { + /* + * Start at the beginning (or end) of the file. + * (The empty_screen() case is mainly for + * command line initiated searches; + * for example, "+/xyz" on the command line.) + */ + if (goforw) + pos = ch_zero(); + else + { + pos = ch_length(); + if (pos == NULL_POSITION) + pos = ch_zero(); + } + } else + { + if (how_search) + { + if (goforw) + linenum = BOTTOM_PLUS_ONE; + else + linenum = TOP; + pos = position(linenum); + } else + { + linenum = adjsline(jump_sline); + pos = position(linenum); + if (goforw) + pos = forw_raw_line(pos, (char **)NULL); + } + } + + if (pos == NULL_POSITION) + { + /* + * Can't find anyplace to start searching from. + */ + error("Nothing to search", NULL_PARG); + return (-1); + } + + linenum = find_linenum(pos); + oldpos = pos; + for (;;) + { + /* + * Get lines until we find a matching one or + * until we hit end-of-file (or beginning-of-file + * if we're going backwards). + */ + if (sigs) + /* + * A signal aborts the search. + */ + return (-1); + + if (goforw) + { + /* + * Read the next line, and save the + * starting position of that line in linepos. + */ + linepos = pos; + pos = forw_raw_line(pos, &line); + if (linenum != 0) + linenum++; + } else + { + /* + * Read the previous line and save the + * starting position of that line in linepos. + */ + pos = back_raw_line(pos, &line); + linepos = pos; + if (linenum != 0) + linenum--; + } + + if (pos == NULL_POSITION) + { + /* + * We hit EOF/BOF without a match. + */ + return (n); + } + + /* + * If we're using line numbers, we might as well + * remember the information we have now (the position + * and line number of the current line). + * Don't do it for every line because it slows down + * the search. Remember the line number only if + * we're "far" from the last place we remembered it. + */ + if (linenums && abs(pos - oldpos) > 1024) + { + add_lnum(linenum, pos); + oldpos = pos; + } + + if (is_caseless) + { + /* + * If this is a caseless search, convert + * uppercase in the input line to lowercase. + * While we're at it, remove any backspaces + * along with the preceding char. + * This allows us to match text which is + * underlined or overstruck. + */ + for (p = q = line; *p != '\0'; p++, q++) + { + if (*p >= 'A' && *p <= 'Z') + /* Convert uppercase to lowercase. */ + *q = *p + 'a' - 'A'; + else if (q > line && *p == '\b') + /* Delete BS and preceding char. */ + q -= 2; + else + /* Otherwise, just copy. */ + *q = *p; + } + } + + /* + * Test the next line to see if we have a match. + * This is done in a variety of ways, depending + * on what pattern matching functions are available. + */ +#if REGCMP + line_match = (regex(cpattern, line) != NULL); +#else +#if RECOMP + line_match = (re_exec(line) == 1); +#else +#if REGCOMP + line_match = regexec(regpattern, line); +#else + line_match = match(pattern, line); +#endif +#endif +#endif + /* + * We are successful if want_match and line_match are + * both true (want a match and got it), + * or both false (want a non-match and got it). + */ + if (((want_match && line_match) || (!want_match && !line_match)) && + --n <= 0) + /* + * Found the line. + */ + break; + } + + jump_loc(linepos, jump_sline); + return (0); +} + +#if (!REGCMP) && (!RECOMP) && (!REGCOMP) +/* + * We have neither regcmp() nor re_comp(). + * We use this function to do simple pattern matching. + * It supports no metacharacters like *, etc. + */ +static int match(char *pattern, char *buf); + + static int +match(pattern, buf) + char *pattern, *buf; +{ + register char *pp, *lp; + + for ( ; *buf != '\0'; buf++) + { + for (pp = pattern, lp = buf; *pp == *lp; pp++, lp++) + if (*pp == '\0' || *lp == '\0') + break; + if (*pp == '\0') + return (1); + } + return (0); +} +#endif diff --git a/bin/less/signal.c b/bin/less/signal.c new file mode 100644 index 0000000..011a062 --- /dev/null +++ b/bin/less/signal.c @@ -0,0 +1,242 @@ +/* + * Routines dealing with signals. + * + * A signal usually merely causes a bit to be set in the "signals" word. + * At some convenient time, the mainline code checks to see if any + * signals need processing by calling psignal(). + * If we happen to be reading from a file [in iread()] at the time + * the signal is received, we call intread to interrupt the iread. + */ +#pragma noroot +#include "less.h" +#include + +#ifdef _ORCAC_ +#pragma databank 1 +segment "LoadSegONE"; +#endif + +/* + * "sigs" contains bits indicating signals which need to be processed. + */ +public int sigs; + +#define S_INTERRUPT 01 +#ifdef SIGTSTP +#define S_STOP 02 +#endif +#if defined(SIGWINCH) || defined(SIGWIND) +#define S_WINCH 04 +#endif + +extern int sc_width, sc_height; +extern int swindow; +extern int screen_trashed; +extern int lnloop; +extern int linenums; +extern int scroll; +extern int reading; + +static HANDLER u_interrupt(int type, int code); +static HANDLER stop(int type, int code); + +/* + * Interrupt signal handler. + */ + /* ARGSUSED*/ + static HANDLER +u_interrupt(type, code) + int type; + int code; +{ + SIGNAL(SIGINT, u_interrupt); + sigs |= S_INTERRUPT; + if (reading) + intread(); +} + + public void +fake_interrupt(void) +{ + sigs |= S_INTERRUPT; +} + +#ifdef SIGTSTP +/* + * "Stop" (^Z) signal handler. + */ + /* ARGSUSED*/ + static HANDLER +stop(type, code) + int type; + int code; +{ + SIGNAL(SIGTSTP, stop); + sigs |= S_STOP; + if (reading) + intread(); +} +#endif + +#ifdef SIGWINCH +/* + * "Window" change handler + */ + /* ARGSUSED*/ + public HANDLER +winch(type, code) + int type; + int code; +{ + SIGNAL(SIGWINCH, winch); + sigs |= S_WINCH; + if (reading) + intread(); +} +#else +#ifdef SIGWIND +/* + * "Window" change handler + */ + /* ARGSUSED*/ + public HANDLER +winch(type) + int type; +{ + SIGNAL(SIGWIND, winch); + sigs |= S_WINCH; + if (reading) + intread(); +} +#endif +#endif +#ifdef __ORCAC__ +#pragma databank 0 +#endif + +/* + * Set up the signal handlers. + */ + public void +init_signals(on) + int on; +{ + if (on) + { + /* + * Set signal handlers. + */ + (void) SIGNAL(SIGINT, u_interrupt); +#ifdef SIGTSTP + (void) SIGNAL(SIGTSTP, stop); +#endif +#ifdef SIGWINCH + (void) SIGNAL(SIGWINCH, winch); +#else +#ifdef SIGWIND + (void) SIGNAL(SIGWIND, winch); +#endif +#endif + } else + { + /* + * Restore signals to defaults. + */ + (void) SIGNAL(SIGINT, SIG_DFL); +#ifdef SIGTSTP + (void) SIGNAL(SIGTSTP, SIG_DFL); +#endif +#ifdef SIGWINCH + (void) SIGNAL(SIGWINCH, SIG_IGN); +#endif +#ifdef SIGWIND + (void) SIGNAL(SIGWIND, SIG_IGN); +#endif + } +} + +/* + * Process any signals we have received. + * A received signal cause a bit to be set in "sigs". + */ + public void +psignals(void) +{ + register int tsignals; + + if ((tsignals = sigs) == 0) + return; + sigs = 0; + +#ifdef S_WINCH + if (tsignals & S_WINCH) + { + int old_width, old_height; + /* + * Re-execute get_term() to read the new window size. + */ + old_width = sc_width; + old_height = sc_height; + swindow = -1; + get_term(); + if (sc_width != old_width || sc_height != old_height) + { + scroll = (sc_height + 1) / 2; + screen_trashed = 1; + } + } +#endif +#ifdef SIGTSTP + if (tsignals & S_STOP) + { + /* + * Clean up the terminal. + */ +#ifdef SIGTTOU + SIGNAL(SIGTTOU, SIG_IGN); +#endif + lower_left(); + clear_eol(); + deinit(); + flush(); + raw_mode(0); +#ifdef SIGTTOU + SIGNAL(SIGTTOU, SIG_DFL); +#endif + SIGNAL(SIGTSTP, SIG_DFL); + kill(getpid(), SIGTSTP); + /* + * ... Bye bye. ... + * Hopefully we'll be back later and resume here... + * Reset the terminal and arrange to repaint the + * screen when we get back to the main command loop. + */ + SIGNAL(SIGTSTP, stop); + raw_mode(1); + init(); + screen_trashed = 1; + } +#endif + if (tsignals & S_INTERRUPT) + { + bell(); + /* + * {{ You may wish to replace the bell() with + * error("Interrupt", NULL_PARG); }} + */ + + /* + * If we were interrupted while in the "calculating + * line numbers" loop, turn off line numbers. + */ + if (lnloop) + { + lnloop = 0; + if (linenums == 2) + screen_trashed = 1; + linenums = 0; + error("Line numbers turned off", NULL_PARG); + } + + } +} diff --git a/bin/less/tags.c b/bin/less/tags.c new file mode 100644 index 0000000..6d9e253 --- /dev/null +++ b/bin/less/tags.c @@ -0,0 +1,179 @@ +#pragma noroot +#include +#include "less.h" + +#ifdef _ORCAC_ +segment "LoadSegONE"; +#endif + +#define WHITESP(c) ((c)==' ' || (c)=='\t') +#if TAGS + +public char *tagfile; +public char *tagpattern; + +public char *tags = "tags"; + +extern int linenums; +extern int sigs; +extern int jump_sline; + +/* + * Find a tag in the "tags" file. + * Sets "tagfile" to the name of the file containing the tag, + * and "tagpattern" to the search pattern which should be used + * to find the tag. + */ + public void +findtag(tag) + register char *tag; +{ + register char *p; + register FILE *f; + register int taglen; + int search_char; + static char tline[200]; + + if ((f = fopen(tags, "r")) == NULL) + { + error("No tags file", NULL_PARG); + tagfile = NULL; + return; + } + + taglen = strlen(tag); + + /* + * Search the tags file for the desired tag. + */ + while (fgets(tline, sizeof(tline), f) != NULL) + { + if (strncmp(tag, tline, taglen) != 0 || !WHITESP(tline[taglen])) + continue; + + /* + * Found it. + * The line contains the tag, the filename and the + * pattern, separated by white space. + * The pattern is surrounded by a pair of identical + * search characters. + * Parse the line and extract these parts. + */ + tagfile = tagpattern = NULL; + + /* + * Skip over the whitespace after the tag name. + */ + for (p = tline; !WHITESP(*p) && *p != '\0'; p++) + continue; + while (WHITESP(*p)) + p++; + if (*p == '\0') + /* File name is missing! */ + continue; + + /* + * Save the file name. + * Skip over the whitespace after the file name. + */ + tagfile = p; + while (!WHITESP(*p) && *p != '\0') + p++; + *p++ = '\0'; + while (WHITESP(*p)) + p++; + if (*p == '\0') + /* Pattern is missing! */ + continue; + + /* + * Save the pattern. + * Skip to the end of the pattern. + * Delete the initial "^" and the final "$" from the pattern. + */ + search_char = *p++; + if (*p == '^') + p++; + tagpattern = p; + while (*p != search_char && *p != '\0') + p++; + if (p[-1] == '$') + p--; + *p = '\0'; + + fclose(f); + return; + } + fclose(f); + error("No such tag in tags file", NULL_PARG); + tagfile = NULL; +} + +/* + * Search for a tag. + * This is a stripped-down version of search(). + * We don't use search() for several reasons: + * - We don't want to blow away any search string we may have saved. + * - The various regular-expression functions (from different systems: + * regcmp vs. re_comp) behave differently in the presence of + * parentheses (which are almost always found in a tag). + */ + public int +tagsearch(void) +{ + POSITION pos, linepos; + int linenum; + char *line; + + pos = ch_zero(); + linenum = find_linenum(pos); + + for (;;) + { + /* + * Get lines until we find a matching one or + * until we hit end-of-file. + */ + if (sigs) + return (1); + + /* + * Read the next line, and save the + * starting position of that line in linepos. + */ + linepos = pos; + pos = forw_raw_line(pos, &line); + if (linenum != 0) + linenum++; + + if (pos == NULL_POSITION) + { + /* + * We hit EOF without a match. + */ + error("Tag not found", NULL_PARG); + return (1); + } + + /* + * If we're using line numbers, we might as well + * remember the information we have now (the position + * and line number of the current line). + */ + if (linenums) + add_lnum(linenum, pos); + + /* + * Test the line to see if we have a match. + * Use strncmp because the pattern may be + * truncated (in the tags file) if it is too long. + */ + if (strncmp(tagpattern, line, strlen(tagpattern)) == 0) + break; + } + + jump_loc(linepos, jump_sline); + return (0); +} + +#endif diff --git a/bin/less/termcap b/bin/less/termcap new file mode 100644 index 0000000..0951c0d --- /dev/null +++ b/bin/less/termcap @@ -0,0 +1,41 @@ +# +# GNO console +# +gno|gnocon|gno-console|GNO Console Driver:\ +ae=^N:am:bl=^G:bs:bw:cl=^L:cm=^^%r%+ %+ :co#80:ce=^]:\ +cd=^K:do=^J:eo:eA=^O^[:ho=^Y:il=^N:i3=^L:vi=^F:vs=^B^E:ve=^A^E:\ +is=^L^N:kd=^J:kl=^H:kr=^U:ku=^_:le=^H:li#24:ll=^^ 8:mb=^O:md=^O:\ +me=^N:mh=^O:mr=^O:nd=^U:nl=^J:pt:rs=^N:se=^N:so=^O:ta=^I:\ +ue=^N:up=^_:us=^O:xn:ns:as=^O^[:bc=^H:r1=^N:r2=^N:r3=^N:\ +ms:mi:sf=^W:sr=^V:i1=^N:NP:dn=^J: +# +# ProTerm Special +# +pt|pse|proterm-special|Proterm Special Emulation:\ +ae=^N:am:al=^V:bl=^T^A^E@:bs:bw:cl=^L:cm=^^%r%+ %+ :co#80:ce=^Y:\ +cd=^W:dc=^D:dl=^Z:do=^J:eo:eA=^P:ho=^X:ic=^F:il=^N:i3=^L:\ +is=^L^N:kd=^J:kl=^H:kr=^U:ku=^K:le=^H:li#24:ll=^^ 8:mb=^O:md=^O:\ +me=^N:mh=^O:mr=^O:nd=^U:nl=\n+^A:pt:rs=^N:se=^N:so=^O:ta=^I:\ +ue=^N:up=^K:us=^O:xn:ns:as=^P:bc=^H:r1=^N:r2=^N:r3=^N:\ +ms:mi:sf=^J:sr=^K:as=^P:i1=^N:i3=^N:NP:dn=^J: +# +# DEC VT-100 +# +d0|vt100|vt100-am|vt100am|dec-vt100|dec vt100:\ + :do=^J:co#80:li#24:cl=50\E[;H\E[2J:sf=2*\ED:\ + :le=^H:bs:am:cm=5\E[%i%d;%dH:nd=2\E[C:up=2\E[A:\ + :ce=3\E[K:cd=50\E[J:so=2\E[7m:se=2\E[m:us=2\E[4m:ue=2\E[m:\ + :md=2\E[1m:mr=2\E[7m:mb=2\E[5m:me=2\E[m:is=\E[1;24r\E[24;1H:\ + :rs=\E>\E[?3l\E[?4l\E[?5l\E[?7h\E[?8h:ks=\E[?1h\E=:ke=\E[?1l\E>:\ + :ku=\EOA:kd=\EOB:kr=\EOC:kl=\EOD:kb=^H:\ + :ho=\E[H:k1=\EOP:k2=\EOQ:k3=\EOR:k4=\EOS:pt:sr=2*\EM:vt#3:xn:\ + :sc=\E7:rc=\E8:cs=\E[%i%d;%dr: +# +# MS-DOS ANSI.SYS +# +P1|ansi.sys|ansisys|PC-DOS 3.1 ANSI.SYS:\ + :am:bs:ce=\E[K:cl=\E[m\E[7h\E[2J:cm=\E[%i%d;%dH:co#80:\ + :ku=^K:kd=^J:kl=^H:kr=^L:kh=^^:ma=^Hh\012j^Kk^Ll^^H:\ + :ho=\E[H:li#25:nd=\E[C:up=\E[A:\ + :ms:md=\E[1m:me=\E[m:mr=\E[7m:se=\E[m:so=\E[1m:ue=\E[m:us=\E[4m:\ + :is=U1 PC-DOS 3.1 ANSI.SYS 9-23-86\n\E[m\E[7h: diff --git a/bin/less/ttyin.c b/bin/less/ttyin.c new file mode 100644 index 0000000..330c879 --- /dev/null +++ b/bin/less/ttyin.c @@ -0,0 +1,86 @@ +/* + * Routines dealing with getting input from the keyboard (i.e. from the user). + */ +#pragma noroot +#include "less.h" +#include +#include + +#if __MSDOS__ +#include +#include +#include +#include +#endif + +#ifdef _ORCAC_ +segment "LoadSegONE"; +#endif + +static int tty; + +/* + * Open keyboard for input. + */ + public void +open_getchr(void) +{ +#if __MDDOS__ + /* + * Open a new handle to CON: in binary mode + * for unbuffered keyboard read. + */ + tty = open("CON", O_RDONLY|O_BINARY); +#else + /* + * Try /dev/tty. + * If that doesn't work, use file descriptor 2, + * which in Unix is usually attached to the screen, + * but also usually lets you read from the keyboard. + */ + tty = open(".tty", O_RDWR); + if (tty < 0) + tty = 2; +#endif +} + +/* + * Get a character from the keyboard. + */ + public int +getchr(void) +{ + char c; + int result; + + do + { + result = iread(tty, &c, sizeof(char)); + if (result == READ_INTR) + return (READ_INTR); + if (result < 0) + { + /* + * Don't call error() here, + * because error calls getchr! + */ + quit(1); + } +#if __MSDOS__ + /* + * In raw read, we don't see ^C so look here for it. + */ + if (c == '\003') + raise(SIGINT); +#endif + /* + * Various parts of the program cannot handle + * an input character of '\0'. + * If a '\0' was actually typed, convert it to '\200' here. + */ + if (c == '\0') + c = '\200'; + } while (result != 1); + + return (c); +} diff --git a/bin/less/version.c b/bin/less/version.c new file mode 100644 index 0000000..3fee0a4 --- /dev/null +++ b/bin/less/version.c @@ -0,0 +1,320 @@ +#pragma noroot +#include "less.h" +#ifdef _ORCAC_ +segment " "; +#endif +/* + * less + * Copyright (c) 1984,1985,1989 Mark Nudelman + * + * This program may be freely used and/or modified, + * with the following provisions: + * 1. This notice and the above copyright notice must remain intact. + * 2. Neither this program, nor any modification of it, + * may be sold for profit without written consent of the author. + * + * --------------------------------------------------------------- + * | Special note to the person who ported "less" to the Amiga: | + * | If you're going to be vain enough to splash your name on | + * | the screen every time someone runs less, you might at | + * | least credit the author. | + * --------------------------------------------------------------- + * + * This program is a paginator similar to "more", + * but allows you to move both forward and backward in the file. + * Commands are based on "more" and "vi". + * + * ----------------------- CHANGES --------------------------------- + * + * Allowed use on standard input 1/29/84 markn + * Added E, N, P commands 2/1/84 markn + * Added '=' command, 'stop' signal handling 4/17/84 markn + * Added line folding 4/20/84 markn + * v2: Fixed '=' command to use BOTTOM_PLUS_ONE, + * instead of TOP, added 'p' & 'v' commands 4/27/84 markn + * v3: Added -m and -t options, '-' command 5/3/84 markn + * v4: Added LESS environment variable 5/3/84 markn + * v5: New comments, fixed '-' command slightly 5/3/84 markn + * v6: Added -Q, visual bell 5/15/84 markn + * v7: Fixed jump_back(n) bug: n should count real + * lines, not folded lines. Also allow number + * on G command. 5/24/84 markn + * v8: Re-do -q and -Q commands 5/30/84 markn + * v9: Added "+" argument 9/25/84 markn + * v10: Fixed bug in -b argument processing 10/10/84 markn + * v11: Made error() ring bell if \n not entered. 10/18/84 markn + * ----------------------------------------------------------------- + * v12: Reorganized signal handling and made + * portable to 4.2bsd. 2/13/85 mark + * v13: Reword error message for '-' command. 2/16/85 mark + * v14: Added -bf and -bp variants of -b. 2/22/85 mark + * v15: Miscellaneous changes. 2/25/85 mark + * v16: Added -u flag for backspace processing. 3/13/85 mark + * v17: Added j and k commands, + * changed -t default. 4/13/85 mark + * v18: Rewrote signal handling code. 4/20/85 mark + * v19: Got rid of "verbose" eq_message(). 5/2/85 mark + * Made search() scroll in some cases. + * v20: Fixed screen.c ioctls for System V. 5/21/85 mark + * v21: Fixed some first_cmd bugs. 5/23/85 mark + * v22: Added support for no RECOMP nor REGCMP. 5/24/85 mark + * v23: Miscellanous changes and prettying up. 5/25/85 mark + * Posted to USENET. + * ----------------------------------------------------------------- + * v24: Added ti,te terminal init & de-init 6/3/85 Mike Kersenbrock + * v25: Added -U flag, standout mode underlining. 6/8/85 mark + * v26: Added -M flag. 6/9/85 mark + * Use underline termcap (us) if it exists. + * v27: Renamed some variables to make unique in 6/15/85 mark + * 6 chars. Minor fix to -m. + * v28: Fixed right margin bug. 6/28/85 mark + * v29: Incorporated M.Rose's changes to signal.c 6/28/85 mark + * v30: Fixed stupid bug in argument processing. 6/29/85 mark + * v31: Added -p flag, changed repaint algorithm. 7/15/85 mark + * Added kludge for magic cookie terminals. + * v32: Added cat_file if output not a tty. 7/16/85 mark + * v33: Added -e flag and EDITOR. 7/23/85 mark + * v34: Added -s flag. 7/26/85 mark + * v35: Rewrote option handling; added option.c. 7/27/85 mark + * v36: Fixed -e flag to work if not last file. 7/29/85 mark + * v37: Added -x flag. 8/10/85 mark + * v38: Changed prompting; created prompt.c. 8/19/85 mark + * v39: (Not -p) does not initially clear screen. 8/24/85 mark + * v40: Added "skipping" indicator in forw(). 8/26/85 mark + * Posted to USENET. + * ----------------------------------------------------------------- + * v41: ONLY_RETURN, control char commands, 9/17/85 mark + * faster search, other minor fixes. + * v42: Added ++ command line syntax; 9/25/85 mark + * ch_fsize for pipes. + * v43: Added -h flag, changed prim.c algorithms. 10/15/85 mark + * v44: Made END print in all cases of eof; 10/16/85 mark + * ignore SIGTTOU after receiving SIGTSTP. + * v45: Never print backspaces unless -u. 10/16/85 mark + * v46: Backwards scroll in jump_loc. 10/24/85 mark + * v47: Fixed bug in edit(): *first_cmd==0 10/30/85 mark + * v48: Use TIOCSETN instead of TIOCSETP. 11/16/85 mark + * Added marks (m and ' commands). + * Posted to USENET. + * ----------------------------------------------------------------- + * v49: Fixed bug: signal didn't clear mcc. 1/9/86 mark + * v50: Added ' (quote) to gomark. 1/15/86 mark + * v51: Added + cmd, fixed problem if first_cmd + * fails, made g cmd sort of "work" on pipes + * even if bof is no longer buffered. 1/16/86 mark + * v52: Made short files work better. 1/17/86 mark + * v53: Added -P option. 1/20/86 mark + * v54: Changed help to use HELPFILE. 1/20/86 mark + * v55: Messages work better if not tty output. 1/23/86 mark + * v56: Added -l option. 1/24/86 mark + * v57: Fixed -l to get confirmation before + * overwriting an existing file. 1/31/86 mark + * v58: Added filename globbing. 8/28/86 mark + * v59: Fixed some bugs with very long filenames. 9/15/86 mark + * v60: Incorporated changes from Leith (Casey) + * Leedom for boldface and -z option. 9/26/86 mark + * v61: Got rid of annoying repaints after ! cmd. 9/26/86 mark + * Posted to USENET. + * ----------------------------------------------------------------- + * v62: Added is_directory(); change -z default to + * -1 instead of 24; cat-and-exit if -e and + * file is less than a screenful. 12/23/86 mark + * v63: Fixed bug in cat-and-exit if > 1 file. 1/8/87 mark + * v64: Changed puts/putstr, putc/putchr, + * getc/getchr to avoid name conflict with + * stdio functions. 1/12/87 mark + * v65: Allowed '-' command to change NUMBER + * valued options (thanks to Gary Puckering) 1/26/87 mark + * v66: Fixed bug: prepaint should use force=1. 2/13/87 mark + * v67: Added !! and % expansion to ! command. 2/24/87 mark + * v68: Added SIGWINCH and TIOCGWINSZ support; + * changed is_directory to bad_file. + * (thanks to J. Robert Ward) 2/25/87 mark + * v69: Added SIGWIND and WIOCGETD (for Unix PC). 2/25/87 mark + * v70: Changed help cmd from 'h' to 'H'; better + * error msgs in bad_file, errno_message. 3/13/87 mark + * v71: Changed -p to -c, made triple -c/-C + * for clear-eol like more's -c. 5/11/87 mark + * v72: Added -E, -L, use $SHELL in lsystem(). 6/26/87 mark + * (thanks to Steve Spearman) + * v73: Allow Examine "#" for previous file. 6/26/87 mark + * Posted to USENET 8/25/87. + * ----------------------------------------------------------------- + * v74: Fix conflict in EOF symbol with stdio.h, 9/18/87 mark + * Make os.c more portable to BSD. + * v75: Fix problems in get_term (thanks to 9/23/87 mark + * Paul Eggert); new backwards scrolling in + * jump_loc (thanks to Marion Hakanson). + * v76: Added -i flag; allow single "!" to 9/23/87 mark + * invoke a shell (thanks to Franco Barber). + * v77: Added -n flag and line number support. 9/24/87 mark + * v78: Fixed problem with prompts longer than 9/25/87 mark + * the screen width. + * v79: Added the _ command. 9/29/87 mark + * v80: Allow signal to break out of linenum scan. 10/6/87 mark + * v81: Allow -b to be changed from within less. 10/6/87 mark + * v82: Add cmd_decode to use a table for key 10/7/87 mark + * binding (thanks to David Nason). + * v83: Allow .less file for user-defined keys. 10/9/87 mark + * v84: Fix -e/-E problems (thanks to Felix Lee). 10/11/87 mark + * v85: Search now keeps track of line numbers. 10/15/87 mark + * v86: Added -B option and autobuf; fixed 10/20/87 mark + * "pipe error" bug. + * v87: Fix bug re BSD signals while reading file. 3/1/88 mark + * v88: Use new format for -P option (thanks to 3/12/88 mark + * der Mouse), allow "+-c" without message, + * fix bug re BSD hangup. + * v89: Turn off line numbers if linenum scan 3/18/88 mark + * is interrupted. + * v90: Allow -P from within less. 3/30/88 mark + * v91: Added tags file support (new -t option) 3/30/88 mark + * (thanks to Brian Campbell). + * v92: Added -+option syntax. 4/4/88 mark + * v93: Add support for slow input (thanks to 4/11/88 mark + * Joe Orost & apologies for taking almost + * 3 years to get this in!) + * v94: Redo reading/signal stuff. 4/11/88 mark + * v95: Repaint screen better after signal. 4/20/88 mark + * v96: Add /! and ?! commands. 4/21/88 mark + * v97: Allow -l/-L from within less. 5/17/88 mark + * Eliminate some static arrays (use calloc). + * Posted to USENET. + * ----------------------------------------------------------------- + * v98: Fix incorrect calloc call; uninitialized 10/14/88 mark + * var in exec_mca; core dump on unknown TERM. + * Make v cmd work if past last line of file. + * Fix some signal bugs. + * v99: Allow space between -X and string, 10/29/88 mark + * when X is a string-valued option. + * v100: Fix globbing bug when $SHELL not set; 1/5/89 mark + * allow spaces after -t command. + * v101: Fix problem with long (truncated) lines 1/6/89 mark + * in tags file (thanks to Neil Dixon). + * v102: Fix bug with E# when no prev file; 1/6/89 mark + * allow spaces after -l command. + * v103: Add -N, -f and -? options. Add z and w 3/14/89 mark + * commands. Add %L for prompt strings. + * v104: Added EDITPROTO. 3/16/89 mark + * v105: Fix bug in find_linenum which cached 3/20/89 mark + * incorrectly on long lines. + * v106: Added -k option and multiple lesskey 3/31/89 mark + * files. + * v107: Add 8-bit char support and -g option. 4/27/89 mark + * Split option code into 3 files. + * v108: Allocate position table dynamically 5/5/89 mark + * (thanks to Paul Eggert); change % command + * from "percent" to vi-style brace finder. + * v109: Added ESC-% command, split prim.c. 5/10/89 mark + * v110: Fixed bug in + option; fixed repaint bug 5/24/89 mark + * under Sun windows (thanks to Paul Eggert). + * v111: Generalized # and % expansion; use 5/25/89 mark + * calloc for some error messages. + * v112: Get rid of ESC-%, add {}()[] commands. 5/30/89 mark + * v113: Optimize lseeks (thanks to Paul Eggert). 5/31/89 mark + * v114: Added ESC-/ and ESC-/! commands. 7/25/89 mark + * v115: Added ESC-n command. 7/26/89 mark + * v116: Added find_pos to optimize g command. 7/31/89 mark + * v117: Change -f option to -r. 8/1/89 mark + * v118: Save positions for all previous files, 8/2/89 mark + * not just the immediately previous one. + * v119: Save marks across file boundaries. 8/7/89 mark + * Add file handle stuff. + * v120: Add :ta command. 8/11/89 mark + * v121: Add -f option. 8/16/89 mark + * v122: Fix performance with many buffers. 8/30/89 mark + * v123: Verbose prompts for string options. 8/31/89 mark + * Posted beta to USENET. + * ----------------------------------------------------------------- + * v124: Reorganize search commands, 9/18/89 mark + * N = rev, ESC-n = span, add ESC-N. + * v125: Fix tab bug (thanks to Alex Liu). 9/18/89 mark + * Fix EOF bug when both -w and -c. + * v126: Add -j option. 10/25/89 mark + * v127: Fix problems with blank lines before BOF. 10/27/89 mark + * v128: Add %bj, etc. to prompt strings. 10/27/89 mark + * v129: Add -+,-- commands; add set-option and 11/3/89 mark + * unset-option to lesskey. + * v130: Generalize A_EXTRA to string, remove 11/6/89 mark + * set-option, unset-option from lesskey. + * v131: Changed name of EDITPROTO to LESSEDIT. 11/7/89 mark + * v132: Allow editing of command prefix. 11/8/89 mark + * v133: Add -y option (thanks to Jeff Sullivan). 11/16/89 mark + * v134: Glob filenames in the -l command. 12/1/89 mark + * v135: Combined {}()[] commands into one, and 12/5/89 mark + * added ESC-^F and ESC-^B commands. + * v136: Added -S, -R flags. Added | command. 1/20/90 mark + * Added warning for binary files. (thanks + * to Richard Brittain and J. Sullivan). + * v137: Rewrote horrible pappend code. 1/21/90 mark + * Added * notation for hi-bit chars. + * v138: Fix magic cookie terminal handling. 1/24/90 mark + * Get rid of "cleanup" loop in ch_get. + * v139: Added MSDOS support. (many thanks 1/27/90 mark + * to Richard Brittain). + * v140: Editing a new file adds it to the 2/7/90 mark + * command line list. + * v141: Add edit_list for editing >1 file. 2/8/90 mark + * v142: Add :x command. 2/10/90 mark + * v143: Add * and @ modifies to search cmds. 2/11/90 mark + * Change ESC-/ cmd from /@* to / *. + * v144: Messed around with ch_zero; 3/1/90 mark + * no real change. + * v145: Added -R and -v/-V for MSDOS; 3/2/90 mark + * renamed FILENAME to avoid conflict. + * v146: Pull cmdbuf functions out of command.c 3/5/90 mark + * v147: Implement ?@; fix multi-file edit bugs. 3/7/90 mark + * v148: Fixed bug in :e then :e#. 3/29/90 mark + * v149: Change error,ierror,query to use PARG. 4/3/90 mark + * v150: Add LESS_CHARSET, LESS_CHARDEF. 4/6/90 mark + * v151: Remove -g option; clean up ispipe. 4/13/90 mark + * v152: lsystem() closes input file, for 4/14/90 mark + * editors which require exclusive open. + * v153: Fix bug if SHELL unset; 4/18/90 mark + * fix bug in overstrike control char. + * v154: Output to fd 2 via buffer. 4/25/90 mark + * v155: Ignore -i if uppercase in pattern 4/30/90 mark + * (thanks to Michael Rendell.) + * v156: Remove scroll limits in forw() & back(); 5/3/90 mark + * causes problems with -c. + * v157: Forward search starts at next real line 5/4/90 mark + * (not screen line) after jump target. + * v158: Added F command. 6/14/90 mark + * v159: Fix bug in exiting: output not flushed. 7/29/90 mark + * v160: Clear screen before initial output w/ -c. 7/29/90 mark + * v161: Add -T flag. 7/29/90 mark + * v162: Fix bug with +F on command line. 8/14/90 mark + * v163: Added LESSBINFMT variable. 8/21/90 mark + * v164: Added -p, LINES, COLUMNS and 9/5/90 mark + * unset mark ' == BOF, for 1003.2 D5. + * v165: At EOF with -c set, don't display empty 9/6/90 mark + * screen when try to page forward. + * v166: Fix G when final line in file wraps. 9/6/90 mark + * v167: Translate CR/LF -> LF for 1003.2. 9/11/90 mark + * v168: Return to curr file if "tag not found". 9/13/90 mark + * v169: G goes to EOF even if file has grown. 12/12/90 mark + * v170: Add optimization for BSD _setjmp; 1/17/91 mark + * fix #include ioctl.h TERMIO problem. + * (thanks to Paul Eggert) + * Posted to USENET. + * ----------------------------------------------------------------- + * v171: Fix -? bug in get_filename. 3/6/91 mark + * v172: Fix G bug in empty file. 3/15/91 mark + * Fix bug with ?\n and -i and uppercase + * pattern at EOF! + * (thanks to Paul Eggert) + * v173: Change N cmd to not permanently change 3/17/91 mark + * direction. (thanks to Brian Matthews) + * v174: Fix bug with namelogfile not getting 3/18/91 mark + * cleared when change files. + * v175: Fix bug with ++cmd on command line. 3/18/91 mark + * (thanks to Jim Meyering) + * v176: Change | to not force current screen, 4/2/91 mark + * include marked line, start/end from + * top of screen. Improve search speed. + * (thanks to Don Mears) + * v177: Add LESSHELP variable. 4/2/91 mark + * Fix bug with F command with -e. + * Try /dev/tty for input before using fd 2. + */ + +char version[] = "@(#) less version 177"; diff --git a/bin/ls/Makefile b/bin/ls/Makefile new file mode 100644 index 0000000..a727ecb --- /dev/null +++ b/bin/ls/Makefile @@ -0,0 +1,8 @@ +ls.root: ls.c + compile -i ls.c keep=ls + +itqsort.a: itqsort.c + compile +O itqsort.c keep=itqsort + +ls: ls.root itqsort.a + link ls itqsort keep=ls diff --git a/bin/ls/itqsort.c b/bin/ls/itqsort.c new file mode 100644 index 0000000..947f2d1 --- /dev/null +++ b/bin/ls/itqsort.c @@ -0,0 +1,156 @@ +/* From: few@autodesk.com (Frank Whaley) */ + +/* + * qsort.c - iterative Quicksort + */ + +#pragma noroot + +#define DEPTH 20 /* should be adequate for most sorts */ + + + static +swb(a, b, len) + register char *a; + register char *b; + register unsigned len; +{ + register char temp; + + /* Handle the odd-number case */ + if (len & 1) { + temp = *a; + *a++ = *b; + *b++ = temp; + len--; + } + asm { + ldy len +loop: cpy #0 + beq done + dey + dey + lda [a],y + tax + lda [b],y + sta [a],y + txa + sta [b],y + bra loop + done: } + + /* while ( len-- ) + { + temp = *a; + *a++ = *b; + *b++ = temp; + } */ +} + + + int +nqsort(bas, n, wid, cmp) + char *bas; /* base of data */ + unsigned n; /* number of items to sort */ + unsigned wid; /* width of an element */ + int (*cmp)(); /* key comparison function */ +{ + unsigned mumble; + unsigned j; + unsigned k; + unsigned pvt; + unsigned cnt; + unsigned lo[DEPTH]; + unsigned hi[DEPTH]; + + if ( n < 2 ) + return 0; + + /* init */ + cnt = 1; + lo[0] = 0; + hi[0] = n - 1; + + while ( cnt-- ) + { + pvt = lo[cnt]; + j = pvt + 1; + n = k = hi[cnt]; + while ( j < k ) + { + while ( (j < k) && + (*cmp)(bas + (j * wid), bas + (pvt * wid)) < 1 ) + ++j; + + while ( (j <= k) && + (*cmp)(bas + (pvt * wid), bas + (k * wid)) < 1) + --k; + + if ( j < k ) + swb(bas + (j++ * wid), bas + (k-- * wid), wid); + } + + if ( (*cmp)(bas + (pvt * wid), bas + (k * wid)) > 0 ) + swb(bas + (pvt * wid), bas + (k * wid), wid); + + if ( k > pvt ) + --k; + + if ( (k > pvt) && (n > j) && ((k - pvt) < (n - j)) ) + { + mumble = k; + k = n; + n = mumble; + mumble = pvt; + pvt = j; + j = mumble; + } + + if ( k > pvt ) + { + lo[cnt] = pvt; + hi[cnt++] = k; + } + + if ( n > j ) + { + lo[cnt] = j; + hi[cnt++] = n; + } + + if ( cnt >= DEPTH ) + return -1; + } + + return 0; +} + +/* END of qsort.c */ + +#if 0 +short int in[] = {10, 32, -1, 567, 3, 18, 1, -51, 789, 0}; + +int compr(short *a, short *b) +{ + if (*a < *b) return -1; + else if (*a > *b) return 1; + else return 0; +} + +int compr1(short *a, short *b) +{ + if (*a < *b) return 1; + else if (*a > *b) return -1; + else return 0; +} + +int main() +{ +unsigned i; + + nqsort(in,10,2,compr1); + for (i = 0; i < 10; i++) printf("%d\n",in[i]); + nqsort(in,10,2,compr); + for (i = 0; i < 10; i++) printf("%d\n",in[i]); +} +#endif diff --git a/bin/ls/ls.c b/bin/ls/ls.c new file mode 100644 index 0000000..9bba2fd --- /dev/null +++ b/bin/ls/ls.c @@ -0,0 +1,586 @@ +/* + * ls + * + * version 2.0 + * + * Rewritten for speed, memory usage, and reliability + * + * now checks TERM var and displays MouseText folder only if set to gnocon + * does not apply name quicksort if files are on an HFS volume + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#pragma stacksize 1024 +#pragma optimize -1 +/*#pragma optimize 8*/ + +#define CALCULATE -1 +#define ONLYONE -2 + +unsigned fl_all,fl_reverse; +unsigned fl_gnocon; +unsigned fl_sortname,fl_sortcreate,fl_sortmod; +unsigned firstflag; + +struct directory { + ResultBuf32Ptr dir_name; + struct directory *parent; + unsigned fake; + word num_entries; + longword num_bytes; + word no_sort; + unsigned width; + DirEntryRecPtrGS *entry_ptrs; + DirEntryRecPtrGS entries; +}; + +DirEntryRecGS dirinfo; +ResultBuf32 nameb; + +lsexit(int x_code) +{ + SYSTEMQUITFLAGS(0x4000); + SYSTEMQUITPATH(NULL); + exit(x_code); +} +/*#define lsexit(x) exit(x)*/ + +#define qsort(a,b,c,d) nqsort(a,b,c,d) +extern void SortList(void *a, int c, int b, void *d); +void nqsort(void *,unsigned, unsigned,int (*cmp)(void *,void *)); + +void printGS(GSString255Ptr g) +{ + fwrite(g->text,g->length,1,stdout); +} + +typedef struct listStruct *list; +const char month[12][4] = + { "Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec" }; + +typedef struct FileTypeConv { + word type; + char rep[10]; +} FileTypeConv; + +FileTypeConv FTTable[] = { + 0x0f, "DIR ", /* directory */ + 0xB0, "src ", /* apw source file */ + 0xb1, "obj ", /* apw object file */ + 0x04, "txt ", /* ascii text file */ + 0xb3, "s16 ", /* gs/os program file */ + 0xb5, "exe ", /* gs/os shell program file */ + 0xb8, "nda ", + 0xb9, "cda ", + 0xba, "tol ", + 0x00, "non ", /* typeless file */ + 0x01, "bad ", /* bad block file */ + 0x06, "bin ", /* general binary file */ + 0x08, "fot ", /* graphics screen file */ + 0x19, "adb ", /* appleworks data base file */ + 0x1a, "awp ", /* appleworks word processor file */ + 0x1b, "asp ", /* appleworks spreadsheet */ + 0xb2, "lib ", /* apw library file */ + 0xb4, "rtl ", /* apw runtime library */ + 0xef, "pas ", /* pascal partition */ + 0xf0, "cmd ", + 0xfa, "int ", /* integer basic program */ + 0xfb, "var ", /* int basic var file */ + 0xfc, "bas ", /* applesloth basic file */ + 0xfd, "var ", /* applesloth variable file */ + 0xfe, "rel ", /* EDASM relocatable code */ + 0xff, "sys ", /* prodos 8 system program file */ + -1, ""}; + +char conv[10]; +char *getFileType(int type) +{ +int i; + + i = 0; + while (FTTable[i].type != -1) + { + if (FTTable[i].type == type) + { + return FTTable[i].rep; + } + else i++; + } + sprintf(conv,"$%2X ",type); + return conv; +} + +word curYear; + +void *sortRoutine; +int columns; +int openDirectory, fl_longOutput, inK; +int dirOnly, idType, fl_recursive, fl_nosort; +int more,less; +unsigned whichTime; + +void printDirName(struct directory *d, unsigned level) +{ + if (d == NULL) return; + else { + printDirName(d->parent,level+1); + if (d->dir_name != NULL) { + printGS((GSString255Ptr)&d->dir_name->bufString); + if (level) putchar('/'); + } + } + if (!level) putchar(':'); +} + +void long_out(DirEntryRecGS *entry) +{ +TimeRec *time; + + if (whichTime) time = &entry->createDateTime; + else time = &entry->modDateTime; + + printf("%c%c%c%c%c%c%c %04lX %4s %8ld", + (entry->fileType == 0x0f) ? 'd' : + ((entry->flags & 0x8000) ? 'e' : '-'), + (entry->access & 0x01) ? 'r' : '-', + (entry->access & 0x02) ? 'w' : '-', + ((entry->fileType == 0xff) || (entry->fileType == 0xb5) || + (entry->fileType == 0xb3) || + ((entry->fileType == 0xb0) && (entry->auxType == 6l))) + ? 'x' : '-', + (entry->access & 0x20) ? 'b' : '-', + (entry->access & 0x40) ? 'r' : '-', + (entry->access & 0x80) ? 'd' : '-', + entry->auxType, + getFileType(entry->fileType), + entry->eof+entry->resourceEOF); + if (time->year+1900 == curYear) + printf(" %3s %2d %02d:%02d ", + month[time->month],time->day+1,time->hour,time->minute); + else printf(" %3s %2d %4d ",month[time->month],time->day+1, + time->year+1900); + /*puts(entry->name->bufString.text);*/ +} + +/* fake a directory read for command-line arguments */ +struct directory *fakeDirect(int argc, char **argv,struct directory *par) +{ +struct directory *new; +unsigned i,file_count = 0,maxWidth = 0; +unsigned err; +DirEntryRecPtrGS newents,nptr; +FileInfoRecGS fi; +ResultBuf255Ptr newn; +extern GSString255Ptr __C2GSMALLOC(char *); + + new = malloc(sizeof(struct directory)); + new->num_entries = argc; + new->num_bytes = 0l; + new->parent = par; + new->dir_name = NULL; + new->no_sort = 0; + new->fake = 1; + if ((argc * sizeof(DirEntryRecGS)) > 60000) + printf("exceeded ORCA pointer math bug range\n"); + newents = new->entries = malloc(argc * sizeof(DirEntryRecGS)); + new->entry_ptrs = malloc(argc * sizeof(DirEntryRecGS *)); + fi.pCount = 12; + fi.optionList = 0l; + + nptr = newents; + for (i = 0; i < argc; i++) { + new->entry_ptrs[file_count] = nptr; + nptr->pCount = 17; + nptr->optionList = NULL; + + fi.pathname = __C2GSMALLOC(argv[i]); + GetFileInfoGS(&fi); + if (err=_toolErr) { + fprintf(stderr,"ls: %s: %s\n",strerror(_mapErr(err)),argv[i]); + lsexit(1); + } + nptr->name = malloc(fi.pathname->length+5); + nptr->name->bufSize = fi.pathname->length+5; + memcpy(&nptr->name->bufString,fi.pathname, fi.pathname->length+2); + nptr->name->bufString.text[fi.pathname->length] = 0; + free(fi.pathname); + + if (fi.storageType == 0x05) /* extended file? */ + nptr->flags = 0x8000; + else nptr->flags = 0; + nptr->fileType = fi.fileType; + /* this is used by the qsort routine to maintain + command-line ordering for directories */ + if (fi.fileType == 0x0F) nptr->entryNum = i; + nptr->auxType = fi.auxType; + nptr->eof = fi.eof; + nptr->blockCount = fi.blocksUsed; + nptr->createDateTime = fi.createDateTime; + nptr->modDateTime = fi.modDateTime; + nptr->access = fi.access; + nptr->resourceEOF = fi.resourceEOF; + nptr->resourceBlocks = fi.resourceBlocks; + + file_count++; + new->num_bytes += (nptr->eof + nptr->resourceEOF); + if (nptr->name->bufString.length > maxWidth) + maxWidth = nptr->name->bufString.length; + /* NOTHING AFTER THIS LINE, PLEASE */ + nptr = (DirEntryRecPtrGS) ((unsigned long) nptr + sizeof(DirEntryRecGS)); + } + new->width = maxWidth; + new->num_entries = file_count; + return new; +} + +struct directory *readDirect(int fd,struct directory *par, ResultBuf32Ptr n) +{ +struct directory *new; +unsigned i,file_count = 0,maxWidth = 0; +DirEntryRecPtrGS newents,nptr; +ResultBuf255Ptr newn; + + nameb.bufSize = 36; + + dirinfo.pCount = 14; + dirinfo.refNum = fd; + dirinfo.base = dirinfo.displacement = 0; + dirinfo.name = (ResultBuf255Ptr) &nameb; + GetDirEntry(&dirinfo); + new = malloc(sizeof(struct directory)); + new->num_entries = dirinfo.entryNum; + new->num_bytes = 0l; + new->parent = par; + new->dir_name = n; + new->fake = 0; + if (dirinfo.fileSysID == hfsFSID) new->no_sort = 1; + else new->no_sort = 0; + if ((dirinfo.entryNum * sizeof(DirEntryRecGS)) > 60000) + printf("exceeded ORCA pointer math bug range\n"); + newents = new->entries = malloc(dirinfo.entryNum * sizeof(DirEntryRecGS)); + new->entry_ptrs = malloc(dirinfo.entryNum * sizeof(DirEntryRecGS *)); + + nptr = newents; + for (i = 0; i < new->num_entries; i++) { + new->entry_ptrs[file_count] = nptr; + nptr->pCount = 17; + nptr->refNum = fd; + nptr->base = nptr->displacement = 1; + nptr->name = (ResultBuf255Ptr) &nameb; + nptr->optionList = NULL; + GetDirEntry(nptr); + if ((fl_all) || (!(nptr->access & fileInvisible))) { + file_count++; + newn = malloc(37); + nameb.bufString.text[nameb.bufString.length] = 0; + memcpy(newn,&nameb,36); + nptr->name = newn; + new->num_bytes += (nptr->eof + nptr->resourceEOF); + if (nptr->name->bufString.length > maxWidth) + maxWidth = nptr->name->bufString.length; + /* NOTHING AFTER THIS LINE, PLEASE */ + nptr = (DirEntryRecPtrGS) ((unsigned long) nptr + sizeof(DirEntryRecGS)); + } + } + new->width = maxWidth; + new->num_entries = file_count; + return new; +} + +int sortByType(DirEntryRecGS **a,DirEntryRecGS **b) +{ +unsigned ea,eb; + + if ((*a)->fileType == 0x0F) { + if ((*b)->fileType == 0x0F) { + /* if both are dirs, maintain command-line ordering */ + ea = (*a)->entryNum; + eb = (*b)->entryNum; + if (ea > eb) return 1; + else if (ea == eb) return 0; + else return -1; + } + else return 1; + } else { + if ((*b)->fileType == 0x0F) return -1; + } +} + +int sortByName(DirEntryRecGS **a,DirEntryRecGS **b) +{ + return strcmp((*a)->name->bufString.text,(*b)->name->bufString.text) * more; +} + +int sortByCreate(DirEntryRecGS **a,DirEntryRecGS **b) +{ +TimeRec *time1, *time2; + + time1 = &((*a)->createDateTime); + time2 = &((*b)->createDateTime); + if (time1->year > time2->year) return less; + if (time1->year < time2->year) return more; + if (time1->month > time2->month) return less; + if (time1->month < time2->month) return more; + if (time1->day > time2->day) return less; + if (time1->day < time2->day) return more; + if (time1->hour > time2->hour) return less; + if (time1->hour < time2->hour) return more; + if (time1->minute > time2->minute) return less; + if (time1->minute < time2->minute) return more; + if (time1->second > time2->second) return less; + if (time1->second < time2->second) return more; + return 0; +} + +int sortByMod(DirEntryRecGS **a,DirEntryRecGS **b) +{ +TimeRec *time1, *time2; + + time1 = &((*a)->modDateTime); + time2 = &((*b)->modDateTime); + if (time1->year > time2->year) return less; + if (time1->year < time2->year) return more; + if (time1->month > time2->month) return less; + if (time1->month < time2->month) return more; + if (time1->day > time2->day) return less; + if (time1->day < time2->day) return more; + if (time1->hour > time2->hour) return less; + if (time1->hour < time2->hour) return more; + if (time1->minute > time2->minute) return less; + if (time1->minute < time2->minute) return more; + if (time1->second > time2->second) return less; + if (time1->second < time2->second) return more; + return 0; +} + +void listDir(struct directory *d) +{ +unsigned i; +unsigned dirflag; +struct directory *dd; +int fd; +int Col,Row,Height; +int j, afile = FALSE, numColumns = 1; +unsigned rWidth,width; +unsigned fakeind; +unsigned num_entries; +char *cwd; +DirEntryRecGS *entry; + + num_entries = d->num_entries; + if ((fl_longOutput) && (!d->fake)) + printf("total %ldk\n",(d->num_bytes / 1024l)); + if (!num_entries) return; + if (d->fake && openDirectory) { + /* sort by type first */ + qsort(d->entry_ptrs, num_entries, sizeof(DirEntryRecGS *), sortByType); + num_entries = 0; + while ((num_entries < d->num_entries) && + (d->entry_ptrs[num_entries]->fileType != 0x0F)) + num_entries++; + /* finally, sort the non-directory files the way they wanted */ + if (num_entries) + qsort(d->entry_ptrs, num_entries, sizeof(DirEntryRecGS *), sortRoutine); + } else + if ((!fl_nosort) && (!(d->no_sort && fl_sortname))) + qsort(d->entry_ptrs, num_entries, sizeof(DirEntryRecGS *),sortRoutine); + if (!fl_longOutput && columns == CALCULATE) { + rWidth = d->width; + width = rWidth; + if (inK) width+=5; + if (idType) width++; + if (width < 15) { + rWidth = 15 - (width - rWidth); + width = 15; + } /* for jawaid :-) */ + numColumns = 80 / (++width); + } + Height = (num_entries / numColumns); + if (num_entries % numColumns) Height++; + for (Row=0; Row < Height; Row ++) { + for (Col = 0; Col < numColumns; Col ++) { + i = Col * Height + Row; + if (i >= num_entries) continue; + + entry = d->entry_ptrs[i]; + if (entry->fileType == 0x0F) dirflag = TRUE; + /*if (mult && entry->fileType == 0x0f) continue;*/ + afile = TRUE; + if (inK) printf("%4ld ",((entry->eof+entry->resourceEOF)/1024)+1); + /* time = 0 means do mod date, = 1 means do create date */ + /*time = (sortRoutine == CompareCreate) ? 1 : 0;*/ + if (fl_longOutput) long_out(d->entry_ptrs[i]); + printGS(&entry->name->bufString); + if (idType) { + if (entry->fileType == 0x0f) putchar('/'); + else if ((entry->fileType == 0xff) || (entry->fileType == 0xb5) || + (entry->fileType == 0xb3) || + ((entry->fileType == 0xb0) && (entry->auxType == 6l))) + putchar('*'); + else putchar(' '); + } + if (Col+1 < numColumns) { + for (j=0;j<(rWidth-(int)(entry->name->bufString.length));j++) + putchar(' '); + putchar(' '); + } + } + putchar('\n'); + } + + if ((dirflag && fl_recursive) || (d->fake && openDirectory)) { + unsigned start_ent = (d->fake ? num_entries : 0); + + /* if there were regular files in a command-line list, + put a blank line in there but don't count as first */ + if (d->fake && (num_entries != 0)) { + putchar('\n'); + } + for (i = start_ent; i < d->num_entries; i++) { + if (d->entry_ptrs[i]->fileType == 0x0F) { + ResultBuf32Ptr p; + + p = (ResultBuf32Ptr) d->entry_ptrs[i]->name; + /* let the danged kernel do some of the work! */ + if (fl_recursive) { + cwd = malloc(1024l); + getwd(cwd); + chdir(p->bufString.text); + fd = open(".",O_RDONLY); + } + else fd = open(p->bufString.text,O_RDONLY); + if (fd <= 0) { + perror("could not open"); + exit(1); + } + dd = readDirect(fd,d,p); + close(fd); + /* print the directory name for recursive listings */ + if (!firstflag) firstflag = 1; + else putchar('\n'); + printDirName(dd,0); + putchar('\n'); + listDir(dd); + if (fl_recursive) { + chdir(cwd); + free(cwd); + } + /* DISPOSE of 'dd' at this point */ + } + } + } +} + +int main(int argc, char **argv) +{ +int fd; +struct directory *dd; +TimeRec curtime; +int ch; +extern int getopt_restart(void); + + getopt_restart(); + curtime = ReadTimeHex(); + curYear = curtime.year + 1900; + + if (isatty(STDOUT_FILENO) && (!strncmp(ttyname(STDOUT_FILENO),".ttyco",6))) + strcpy(FTTable[0].rep,"\x1B\xFXY\xE\x18 "); + else strcpy(FTTable[0].rep,"DIR "); /* we restart, remember? */ + + /* initialize our restartable global variables */ + fl_nosort = 0; fl_reverse = 0; + more = 1; less = -1; + sortRoutine = sortByName; + columns = CALCULATE; + openDirectory = TRUE; + fl_all = FALSE; + fl_longOutput = FALSE; + inK = FALSE; + dirOnly = FALSE; + idType = FALSE; + fl_recursive = FALSE; + whichTime = 0; + firstflag = 0; + + while ((ch = getopt(argc, argv, "acdflnqrst1CFR")) != EOF) { + switch(ch) { + case 'n' : + fl_nosort = TRUE; + break; + case 'r' : + more = -1; less = 1; + break; + case 't' : + sortRoutine = sortByMod; + whichTime = 0; + break; + case 'c' : + sortRoutine = sortByCreate; + whichTime = 1; + break; + case '1' : + columns = ONLYONE; + break; + case 'C' : + columns = CALCULATE; + break; + case 'a' : + fl_all = TRUE;; + break; + case 'l' : + fl_longOutput = TRUE; + break; + case 's' : + inK = TRUE; + break; + case 'f' : + dirOnly = TRUE; + fl_longOutput = FALSE; + inK = FALSE; + fl_all = TRUE; + break; + case 'q' : + break; + case 'F' : + idType = TRUE; + break; + case 'R' : + fl_recursive = TRUE; + break; + case 'd' : + openDirectory = FALSE; + fl_recursive = FALSE; + break; + default: + (void)fprintf(stderr, + "usage: ls [-acdfilnqrstu1ACLFR] [name ...]\n"); + lsexit(1); + } + } + argv += optind; + argc -= optind; + + if (!argc) { + fd = open(".",O_RDONLY); + if (fd <= 0) { + perror("ls:"); + exit(1); + } + dd = readDirect(fd,NULL,NULL); + close(fd); + listDir(dd); + } else { /* files as arguments */ + /* add the args to a special list one by one with GetFileInfo */ + dd = fakeDirect(argc, argv, NULL); + if (dd->num_entries) listDir(dd); + } + lsexit(0); +} diff --git a/bin/make/build.make b/bin/make/build.make new file mode 100755 index 0000000..a26b2fd --- /dev/null +++ b/bin/make/build.make @@ -0,0 +1,2 @@ +compile -p make.cc keep=make +link make 2/direct256 keep=make diff --git a/bin/make/make.c b/bin/make/make.c new file mode 100644 index 0000000..0f046e6 --- /dev/null +++ b/bin/make/make.c @@ -0,0 +1,492 @@ +#pragma stacksize 2048 +#pragma debug 24 + +#include +#include +#include +#include +#include +#include +#include + +#pragma lint -1 +/* #define DEBUG_MODE */ + +#define version "Make - Version 1.1" +#define NAME_SIZE 80 /* max file name size */ +#define MSG_SIZE 80 /* max size of any generated message */ +#define PARAM_SIZE 256 /* max size of any input parameter */ +#define d_flag 'D' /* display date/time info */ +#define p_flag 'P' /* programmer (debug) mode */ +#define s_flag 'S' /* silent mode */ +#define target_delim ':' /* target file delimiters */ +#define dep_delims " ," /* dependant file delimiters */ +#define comment_char '#' /* start of comment */ + +/* System error return values */ + +#define BAD_OPTION 1 +#define NO_INPUT_FILE 2 +#define NO_MEM_AVAIL 3 +#define SYNTAX_ERR 4 +#define USER_ABORT 5 +#define SHELL_ERR 6 +#define EOF_ERR 7 +#define ILLEGAL_FILE 8 + +#define MEM_UNAVAIL "Not enough memory available to execute." + +typedef struct readVariableDCB { + Str255 *varName, *value; +} readVariableDCB; + +typedef struct executeDCB { + int flag; + char *commandString; +} executeDCB; + +/* Routine prototypes */ + +void ShowLastParam(void); +int GetInfo(FileInfoRecPtrGS file_info, int params, char *file_name); +int FileExists(char *); +void ErrorMessage(char *); +void ErrorAbort(char *, int); +int CheckDepFiles(char *dep_params, char *target_file); +int GetTargetFile(char *params, char *target); +int ExecuteMakeCommand(char *make_command, char *target_file); + +enum MODES {PARAM, CONTINUATION, COMMAND, DONE}; + +unsigned char valid_types[] = { 0x04, 0xB0, 0 }; /* valid file types TXT, SRC */ + +FILE *make_file; +char msg[MSG_SIZE], make_param[PARAM_SIZE], prog_name[NAME_SIZE]; +int silent, debugging, disp_date_time; + +int main(int argc, char *argv[]) +{ + int i, type_ok; + enum MODES mode; + char *tmp_file, *make_file_name; + char first_char, *dep_params, *target_file; + FileInfoRecGS file_info; + StopPB stop_info; +/* + Initialize all global variables so that we can run as a re-startable + command in the shell environment. +*/ + make_param[0] = '\0'; /* init to NULL string */ + /*dep_params[0] = '\0'; + target_file[0] = '\0'; */ /* NONONONONONONO */ + msg[0] = '\0'; + strcpy(prog_name, argv[0]); /* for error message processing */ + silent = FALSE; + debugging = FALSE; + disp_date_time = FALSE; + +/* Allocate some dynamic work variables */ + + if ( (make_file_name = malloc(NAME_SIZE)) == NULL ) + ErrorAbort(MEM_UNAVAIL, NO_MEM_AVAIL); + if ( (dep_params = malloc(PARAM_SIZE)) == NULL ) + ErrorAbort(MEM_UNAVAIL, NO_MEM_AVAIL); + if ( (target_file = malloc(NAME_SIZE)) == NULL ) + ErrorAbort(MEM_UNAVAIL, NO_MEM_AVAIL); + *make_file_name = 0; /* this was a bug- jb */ + +/* Parse the command line information */ + + for (i = 1; i < argc; i++) + if ( argv[i][0] == '-' || argv[i][0] == '+' ) { /* command line option */ + switch (toupper(argv[i][1])) { + case p_flag: debugging = TRUE; /* set appropriate flag */ + break; + case s_flag: silent = TRUE; + break; + case d_flag: disp_date_time = TRUE; + break; + default: ErrorAbort("Invalid option specified.", BAD_OPTION); + } + } + else + strcpy(make_file_name, argv[i]); /* not an opt, must be a file */ + + if ( !silent ) + puts(version); /* display version info */ + + if ( strlen(make_file_name) == 0 ) { /* did we get an input file name */ + strcpy(make_file_name, "makefile"); +/* fputs("MAKE file: ", stdout); /* nope, ask for one */ +/* if ( gets(make_file_name) == NULL ) /* make sure we got one */ +/* exit(NO_INPUT_FILE); /* if not, give up */ + } + +/* + Allocate some work variables: + tmp_file - will be used to check for a file with the default extention. +*/ + + if ( (tmp_file = malloc(NAME_SIZE)) == NULL ) + exit(NO_MEM_AVAIL); + + strcpy(tmp_file, make_file_name); /* get a copy of the specified name */ + strcat(tmp_file, ".make"); /* default extention */ + + if ( FileExists(tmp_file) ) /* check for default name */ + strcpy(make_file_name, tmp_file); /* got it */ + else + if ( !FileExists(make_file_name) ) { /* nope check specified name */ + sprintf(msg, "Make file %s does not exist.", make_file_name); + ErrorAbort(msg, NO_INPUT_FILE); /* still not there, give up */ + } + + free(tmp_file); /* done with this memory */ + + GetInfo(&file_info, 3, make_file_name); /* Check input file type */ + type_ok = FALSE; + for ( i=0; i < sizeof(valid_types); i++ ) /* search valid types */ + if ( file_info.fileType == valid_types[i] ) { + type_ok = TRUE; + break; + } + + if ( !type_ok ) + ErrorAbort("Input file must be either TXT or SRC.", ILLEGAL_FILE); + + /* Open the input file */ + + if ( (make_file = fopen(make_file_name, "r")) == NULL ) { + perror(prog_name); + sprintf(msg, "Error opening input file %s.", make_file_name); + ErrorAbort(msg, NO_INPUT_FILE); + } + + mode = PARAM; /* initial mode is parameter search */ + + /* Read the input file and process the make commands */ + + while ( fgets(make_param, sizeof(make_param), make_file) != NULL ) { + if ( (i = strpos(make_param, '\n')) >= 0 ) + make_param[i] = '\0'; /* remove NL character */ + + if ( debugging ) + printf("make_param:[%s] i: %d, mode: %d\n", make_param, i, mode); + + first_char = make_param[0]; /* we will check this char often */ + + if ( (first_char == '\0') || (first_char == comment_char) ) { + mode = PARAM; /* blank line terminates COMMAND/CONTINUE mode */ + continue; /* ignore blank lines & comment lines */ + } + + if ( mode == CONTINUATION ) { /* CheckDepFiles wants more params */ + strcpy(dep_params, make_param); /* pre-load params */ + mode = CheckDepFiles(dep_params, target_file); + } + else if ( first_char != ' ' ) { + mode = PARAM; /* back into param search mode */ + strcpy(dep_params, make_param); /* set up for parsing */ + if ( GetTargetFile(dep_params, target_file) ) /* we should find one */ + mode = CheckDepFiles(dep_params, target_file); + else + ErrorAbort("Target file specification error, no ';' found.",SYNTAX_ERR); + } + else if ( mode == COMMAND ) + mode = ExecuteMakeCommand(make_param, target_file); + + STOP(&stop_info); /* Check for user termination */ + if ( stop_info.stop == 1 ) + ErrorAbort("User termination.", USER_ABORT); + } /* while reading parameters and not in DONE mode */ + + if ( mode == CONTINUATION ) + ErrorAbort("Unexpected End-Of-File encountered after continuation.", EOF_ERR); + + fclose(make_file); + exit(0); +} + +void ErrorMessage(char *error_mesg) +{ + printf("%s: %s\n", prog_name, error_mesg); +} + +void ShowLastParam(void) +{ + ErrorMessage("Error occured at:"); + ErrorMessage(make_param); +} + +void ErrorAbort(char *abort_mesg, int error_num) +{ + ErrorPB error_params; + + if ( (error_params.error = toolerror()) != 0 ) /* If a tool error */ + ERROR(&error_params); /* Have the shell display the msg */ + ErrorMessage(abort_mesg); /* Display my message */ + if ( strlen(make_param) > 0 ) /* If param parsing */ + ShowLastParam(); /* display the param */ + exit(error_num); +} + +GSString255Ptr strcpygs(GSString255Ptr gs_str, char *str) +{ + gs_str->length = strlen(str); + strcpy((char *) gs_str->text, str); + return(gs_str); +} + +int GetInfo(FileInfoRecPtrGS file_info, int params, char *file_name) +{ + GSString255 file_info_name; + +#ifdef DEBUG_MODE + puts("GetInfo"); +#endif + + file_info->pCount = params; + file_info->pathname = strcpygs(&file_info_name, file_name); + GetFileInfoGS(file_info); + return(toolerror()); +} + +int FileExists(char *file_name) +{ + FileInfoRecGS file_info; + + return(GetInfo(&file_info, 2, file_name) ? FALSE : TRUE); +} + +void TrimLeft(char *str) +{ + char *tmp; + + tmp = str; + while ( *tmp == ' ' && *tmp != '\0' ) tmp++; + strcpy(str, tmp); +} + +void TrimRight(char *str) +{ + int tmp; + + tmp = strlen(str); + while ( tmp >= 0 && str[tmp] == ' ') tmp--; + str[++tmp] = '\0'; +} + +int ExecuteMakeCommand(char *make_command, char *target_file) +{ + char var_name[80], var_value[80]; + Get_VarPB get_var; + executeDCB exec_params; + +#ifdef DEBUG_MODE + puts("ExecuteMakeCommand()"); +#endif + + TrimLeft(make_command); + + if ( !silent ) + printf("\n%s\n\n", make_command); + + make_command[strlen(make_command)+1] = '\0'; /* add an extra NULL */ + exec_params.flag = 1; /* use existing variable table */ + exec_params.commandString = make_command; + + EXECUTE(&exec_params); + + strcpy((char *) var_name, "\pStatus"); + get_var.var_name = var_name; + get_var.value = var_value; + + GET_VAR(&get_var); + if ( toolerror() != 0 ) + ErrorAbort("Error occured during READ_VARIABLE.", SHELL_ERR); + + var_value[var_value[0]+1] = '\0'; + if ( debugging ) + printf("var_name: %p = %p\n",var_name, var_value); + + if ( strcmp((char *) var_value+1, "0") != 0 ) { + remove(target_file); + ErrorAbort("Error occurred during the last command.", SHELL_ERR); + } /* Status != "0" */ + + return COMMAND; /* remain in COMMAND mode */ +} + +int GetTargetFile(char *params, char *target) +{ + int ch; + +#ifdef DEBUG_MODE + puts("GetTargetFile()"); +#endif + + if ( (ch = strpos(params, target_delim)) == -1 ) + return FALSE; + + strncpy(target, params, ch); + target[ch] = '\0'; + TrimLeft(target); + strcpy(params, ¶ms[ch+1]); + + if ( debugging ) + printf("params: [%s] target: [%s]\n", params, target); + + return TRUE; +} + +void GetModDate(TimeRec *info, char *date) +{ + sprintf(date, "%d/%02d/%02d", info->month+1, info->day+1, info->year); +} + +void GetModTime(TimeRec *info, char *time) +{ + sprintf(time, "%d:%02d:%02d", info->hour, info->minute, info->second); +} + +int DepFileOlder(char *target_file, char *dep_file) +{ + FileInfoRecGS target, dependant; + char mod_date[12]; + +#ifdef DEBUG_MODE + puts("DepFileOlder()"); + printf("target_file:[%s] dep_file:[%s]\n", target_file, dep_file); +#endif + + if ( GetInfo(&target, 7, target_file) != 0 ) { + ErrorMessage("target file does not exist."); + return TRUE; /* if any errors, assume target not found */ + } + + if ( debugging || disp_date_time ) { /* Diaplay mod date if in debug mode */ + GetModDate(&target.modDateTime, mod_date); + printf("[Date]Target: %s = %s\n", target_file, mod_date); + } + + if ( GetInfo(&dependant, 7, dep_file) != 0 ) { + ErrorMessage("dependant file does not exist."); + return FALSE; /* dependant file exist? No, user must be nuts */ + } + + if ( debugging || disp_date_time ) { /* Diaplay mod date if in debug mode */ + GetModDate(&dependant.modDateTime, mod_date); + printf("[Date]Dependant: %s = %s\n", dep_file, mod_date); + } + + /* Both the target file and the dependant file exists, check mod info */ + + if ( ( dependant.modDateTime.year == target.modDateTime.year ) && + ( dependant.modDateTime.month == target.modDateTime.month ) && + ( dependant.modDateTime.day == target.modDateTime.day ) ) { /* equal dates */ + + if ( debugging || disp_date_time ) { /* display debug info */ + GetModTime(&target.modDateTime, mod_date); + printf("[Time]Target: %s = %s\n", target_file, mod_date); + GetModTime(&dependant.modDateTime, mod_date); + printf("[Time]Dependant: %s = %s\n", dep_file, mod_date); + } + /* check times */ + if ( dependant.modDateTime.hour > target.modDateTime.hour ) + return TRUE; + else if ( dependant.modDateTime.hour < target.modDateTime.hour ) + return FALSE; + else if ( dependant.modDateTime.minute > target.modDateTime.minute) + return TRUE; + else if ( dependant.modDateTime.minute < target.modDateTime.minute) + return FALSE; + else if ( dependant.modDateTime.second > target.modDateTime.second) + return TRUE; /* dependant older */ + else + return FALSE; /* target older */ + } else /* unequal dates */ + if ( dependant.modDateTime.year > target.modDateTime.year ) + return TRUE; + else if ( dependant.modDateTime.year < target.modDateTime.year ) + return FALSE; + else if ( dependant.modDateTime.month > target.modDateTime.month ) + return TRUE; + else if ( dependant.modDateTime.month < target.modDateTime.month ) + return FALSE; + else if ( dependant.modDateTime.day > target.modDateTime.day ) + return TRUE; /* dependant is older */ + else + return FALSE; /* target is older */ +} + +void FlushParams(void) +{ + char *param_token; + int read_file = FALSE, flush_complete = FALSE; + +#ifdef DEBUG_MODE + puts("FlushParams()"); +#endif + + do { + if ( read_file ) { /* handle continuation, read another line */ + if ( fgets(make_param, sizeof(make_param), make_file) == NULL ) + ErrorAbort("Unexpected End-Of-File encountered after continuation.", EOF_ERR); + param_token = strtok(make_param, dep_delims); /* init parser */ + read_file = FALSE; /* reset read flag */ + } + else + param_token = strtok(NULL, dep_delims); /* continue reading */ + + if ( debugging ) + printf("param_token:[%s]\n", param_token); + + if ( param_token == NULL || *param_token == comment_char ) /* done yet? */ + flush_complete = TRUE; /* yes, signal end */ + else if ( *param_token == '\\' ) /* continuation? */ + read_file = TRUE; /* yes, read another line */ + } while ( !flush_complete ); +} + +int CheckDepFiles(char *dep_params, char *target_file) +{ + int first_file; + char *dep_file; + +#ifdef DEBUG_MODE + puts("CheckDepFiles()"); + printf("dep_params: [%s] target_file: [%s]\n",dep_params, target_file); +#endif + + first_file = TRUE; + + do { /* parse all dependant files from dep_params */ + if ( first_file ) { /* first time through, init parser */ + dep_file = strtok(dep_params, dep_delims); /* get 1st file name */ + if ( dep_file == NULL ) /* there must be at least 1 file name */ + ErrorAbort("No dependant files specified.", SYNTAX_ERR); + first_file = FALSE; + } + else /* all subsequent parsing is done from the previous params */ + dep_file = strtok(NULL, dep_delims); + + if ( dep_file == NULL ) /* any more parameters? */ + return PARAM; /* nope, back to param search mode */ + + if ( debugging ) + printf("dep_file:[%s]\n", dep_file); + + if ( *dep_file == '\\' ) /* continuation? */ + return CONTINUATION; /* yes, get the next line */ + else if ( *dep_file == comment_char ) /* trailing comment? */ + return PARAM; /* yes, skip all commands until next param */ + else if ( *dep_file == '\0' ) /* Null string? */ + continue; /* yes, look some more */ + else if ( DepFileOlder(target_file, dep_file) ) { + FlushParams(); /* skip over any continuation stuff */ + return COMMAND; /* dependant is older, rebuild target */ + } + } while ( dep_file != NULL ); /* until no more dependant files */ + + return PARAM; /* return to param search mode */ +} diff --git a/bin/makemake/makemake.c b/bin/makemake/makemake.c new file mode 100644 index 0000000..ae622f6 --- /dev/null +++ b/bin/makemake/makemake.c @@ -0,0 +1,82 @@ +/* + * I got tired of making new makefiles for the IIgs make that's + * available, so here's a bloody program to write them for us + * + * To create the dependency lists, the source files are searched for + * the following information: + * + * .c #include "filename" + * + * Please note that this program isn't particularly smart. + */ + +#pragma stacksize 2048 +#include +#include +#include + +int ndepend; +char dep[32][32]; /* 32 files, 32 characters long */ +char line[1024]; +char linenws[1024]; + +void removews(char *line, char *line2) +{ +int n = strlen(line); +int i,j; + + j = 0; + for (i = 0; i < n; i++) + if (!isspace(line[i])) line2[j++] = line[i]; + line2[j] = 0; +} + +finddepend(char *top, char *fname) +{ +FILE *s; +char *p,*q; +int n; + + printf("scanning '%s'",fname); + fflush(stdout); + s = fopen(fname,"r"); + if (s == NULL) { fprintf(stderr,"Couldn't open %s\n",fname); exit(1); } + while (!feof(s)) { + fgets(line,1023,s); + n = strlen(line); + removews(line,linenws); + if (!strncmp(linenws,"#include",8)) { + putchar('.'); fflush(stdout); + if (p = strchr(line+8,'<')) continue; + p = strchr(line+8,'"'); + if (p == NULL) continue; + q = strchr(p+1,'"'); + strncpy(dep[ndepend],p+1,(q-p)-1); + dep[ndepend++][q-p-1] = 0; + finddepend(top,dep[ndepend-1]); + } + } + fclose(s); + putchar('\n'); +} + +int main(int argc, char *argv[]) +{ +FILE *f; +char nbuf[80]; +int i,j; + + f = fopen("makefile","w"); + for (i = 1; i < argc; i++) { + strcpy(nbuf,argv[i]); + *strrchr(nbuf,'.') = 0; + ndepend = 0; + finddepend("#include",argv[i]); + fprintf(f,"o/%s.a: %s",nbuf,argv[i]); + for (j = 0; j < ndepend; j++) + fprintf(f," %s",dep[j]); + fprintf(f,"\n"); + fprintf(f," compile %s keep=o/%s\n\n",argv[i],nbuf); + } + fclose(f); +} diff --git a/bin/passwd/Makefile b/bin/passwd/Makefile new file mode 100644 index 0000000..ac3e3fb --- /dev/null +++ b/bin/passwd/Makefile @@ -0,0 +1,7 @@ +passwd.a: passwd.cc + chtyp -l cc passwd.cc + compile +w passwd.cc keep=passwd + +passwd: passwd.a + link passwd keep=passwd + setvers passwd \'GNO passwd^by Eric Shepherd\' v1.0.0 diff --git a/bin/passwd/passwd.1 b/bin/passwd/passwd.1 new file mode 100644 index 0000000..c116dad --- /dev/null +++ b/bin/passwd/passwd.1 @@ -0,0 +1,51 @@ +.\" @(#)passwd.1 1.00 93/07/01 EDS; +.TH PASSWD 1 "01 July 1993" +.SH NAME +passwd \- set a user's login password +.SH SYNOPSIS +.B passwd +[ +.BI \-? +| +.BI \-v +] [ +.BI username +] +.SH DESCRIPTION +.B passwd +changes the specified user's password. Only root is allowed to alter +passwords other than his own. If the +.IR username +is not given, the user's own login name is assumed. Users other than root +must then enter the old password to verify permission to change the password. +Finally, the user must type the desired new password twice to insure that no +mistakes are made. +.LP +To cancel +.B passwd +, type +.IR CTRL-@ +when asked to enter the new password. +The +.B \-? +flag causes +.B passwd +to display a brief usage message, and the +.B \-v +flag causes +.B passwd +to display version information. +.SH SEE ALSO +.LP +.BR login "(1)" +.SH FILES +.B /etc/passwd +\- contains the password information +.SH AUTHOR +.LP +.nf +Eric Shepherd + +Internet uerics@mcl.mcl.ucsb +AOL Sheppy +.fi diff --git a/bin/passwd/passwd.c b/bin/passwd/passwd.c new file mode 100644 index 0000000..48879a7 --- /dev/null +++ b/bin/passwd/passwd.c @@ -0,0 +1,209 @@ +/************************************************************ +** +** passwd - change user passwords +** +** Programmed by: Eric Shepherd +** Date: September 6, 1993 +** +** Requires GNO 2.0 +** +**=========================================================== +** Revision history: +** +** 1.0: Updated version numbers to final. +** 1.0b1: Changed the sleep(1) call to an asm { cop +** 0x7F };. Also removed the "conflict" error +** message. Instead, if the call to remove +** /etc/passwd fails, we sleep and retry until +** it succeeds, thus eliminating the problem... +** there's no significant reason why that file +** will stay open longer than a moment or two. +** 1.0a4: Now uses getpass(), since it's been fixed. +** 1.0a3: Adjusted closing code to shrink the window +** during which the system is vulnerable to +** becoming unprotected. Also added code to +** retry renaming passwd.new until it succeeds. +** 1.0a2: Optimized code, added optimize and stacksize +** pragmas, added new error message for the +** off chance that the rename could fail. Code +** size reduced by about 2K. +** 1.0a1: Works completely, except that getpass() +** crashes all the time, so I don't use it. +** Also, due to apparent login bug, sometimes +** the /etc/passwd file doesn't get updated -- +** the corrected file is in /etc/passwd.new. +*************************************************************/ + +#pragma optimize -1 +#pragma stacksize 512 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern char *crypt(char *key, char *salt); +extern char *getpass(char *prompt); + +char entry[129]; + +static unsigned char salttab[] = /* table of chars. for salt */ + "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + +void usage(void) { + fprintf(stderr, "usage: passwd [-v|?] [username]\n"); +} + +void makesalt(char *salt, long seed) { + int num = 2; + while (--num >= 0) { + *salt++ = salttab[seed&0x3f]; + seed >= 6; + } +} + +void main(int argc, char **argv) { + extern int optind; + int ch, uid, tries; + char password[9]; /* password string (8+\0) */ + char *p, *t; + char salt[3]; + FILE *passfile, *newpass; /* file /etc/newpasswd */ + char *uname; /* pointer to username to change passwd of */ + struct passwd *passwdRec; + + while ((ch = getopt(argc, argv, "v?")) != EOF) + switch (ch) { + case 'v': + fprintf(stderr, "GNO passwd v1.0 by Eric Shepherd (September 6, 1993)\n"); + exit(1); + case '?': + usage(); + exit(1); + } + + argc -= optind; /* move to next option */ + argv += optind; + + /* if no username supplied, use the current user's name */ + + uid = getuid(); /* get the user's ID */ + + switch(argc) { + case 1: + uname = argv[0]; + + if (!(passwdRec = getpwnam(uname))) { + fprintf(stderr, "Unknown user %s.\n", uname); + exit(1); + } + + /* the following code verifies that the user only tries to + change his own password, unless he is superuser */ + + if (uid && uid != passwdRec->pw_uid) { + fprintf(stderr, "You can't change other users' passwords.\n"); + exit(1); + } + break; + case 0: + passwdRec = getpwuid(uid); + break; + default: + usage(); + exit(1); + } + + uname = passwdRec->pw_name; /* make sure we have the right one */ + + /* Here's the code to handle changing passwords */ + + printf("Changing password for user %s.\n", uname); + + if (uid && strlen(passwdRec->pw_passwd) && + strcmp(crypt(getpass("Old password:"), passwdRec->pw_passwd), + passwdRec->pw_passwd)) { + fprintf(stderr,"Password incorrect.\n"); + exit(1); + } + + for (password[0] = '\0', tries = 0;;) { + p = getpass("New password:"); + if (!*p) { + printf("Password unchanged.\n"); + exit(0); + } + + if (strlen(p) <= 5 && (uid != 0 || ++tries < 2)) { + printf("Please enter a longer password.\n"); + continue; + } + + /* Let only root assign easy passwords */ + + for (t = p; *t && islower(*t); ++t); + if (!*t && (uid != 0 || ++tries < 2)) { + printf("All-lower-case passwords can be easy to break. Please use unusual\ncapitalization, control characters, and digits.\n"); + continue; + } + + strcpy(password, p); /* snag a copy of the password */ + if (!strcmp(password, getpass("Retype new password:"))) + break; /* ah! got it! */ + + printf("That's not what you typed the first time. Please start over, or\nhit CTRL-@ to quit.\n"); + } + + /* create salt randomly after seeding the random number generator */ + + srand((int) time((time_t *) NULL)); + makesalt(&salt[0], rand()); + + /* Copy /etc/passwd to /etc/passwd.new, line-by-line, skipping + the entry for the user whose password is changing -- insert + the new entry instead. */ + + if (!(passfile = fopen("/etc/passwd", "r"))) { + fprintf(stderr, "Unable to open /etc/passwd.\n"); + exit(1); + } + + if (!(newpass = fopen("/etc/passwd.new", "w"))) { + fclose(passfile); /* it's already open, so close it -- unlike login :) */ + fprintf(stderr, "Unable to write new /etc/passwd.\n"); + exit(1); + } + + while (!feof(passfile)) { + if (strncmp(passwdRec->pw_name, fgets(entry, 129, passfile), + strlen(passwdRec->pw_name))) + fputs(entry, newpass); /* not us -- just copy it */ + else + fprintf(newpass, "%s:%s:%d:%d:%s:%s:%s\n", passwdRec->pw_name, + crypt(password, salt), passwdRec->pw_uid, passwdRec->pw_gid, + passwdRec->pw_comment, passwdRec->pw_dir, passwdRec->pw_shell); + } + + fclose(passfile); + + asm { cop 0x7F }; /* Jawaid sez this is better than sleep(1) */ + + /* Erase the old file and rename the new one */ + + while (remove("/etc/passwd")) + sleep(1); /* Keep trying */ + + fclose(newpass); /* NOW close the new file */ + + /* As long as the rename fails, wait a moment, then try again */ + + while (rename("/etc/passwd.new", "/etc/passwd")) + sleep(1); /* sleep one second before trying again */ +} diff --git a/bin/ps/ps.1 b/bin/ps/ps.1 new file mode 100644 index 0000000..f88a4d8 --- /dev/null +++ b/bin/ps/ps.1 @@ -0,0 +1,46 @@ +.TH EPS 1 +.SH NAME +.LP +.B eps +\- display extensive process status information. +.SH SYNOPSIS +.LP +.BR eps " -anlwt <" tty "> u < " user " >" +.SH DESCRIPTION +.LP +.BR eps " is an extended " ps " command which displays more" +information than the gsh builtin +.BR ps ". " +.SH USAGE +.LP +.nf +.BR \-a " : Show all processes; normally " eps " limits the processes" +displayed to those that are owned by the current user. +.sp +.BR \-n " : Show username instead of userID, which is default." +.sp +.BR \-l " : Long list. This includes PPID (parent's PID), MMID" +(Memory Manager ID) and a longer time field. +.sp +.BR \-w " : Wider list. A single w results in a 132 column wide" +listing, and two results in the whole command line being +displayed. Normally the command line will be truncated +to either 80 (default) or 132 (-w) columns. +.sp +.BR \-t " <" tty "> : Display only those processes that are owned by " tty "." +.sp +.BR \-u " <" user "> : Display only those processes that are owned by " user "." +.fi +.SH SEE ALSO +.LP +.nf +.BR ps "(1)" +.BR parent "(1)" +.fi +.SH AUTHOR +.LP +.nf +James Brookes +bb252@cleveland.freenet.edu +jamesb@cscihp.ecst.csuchico.edu +.fi diff --git a/bin/ps/ps.c b/bin/ps/ps.c new file mode 100644 index 0000000..e2e5139 --- /dev/null +++ b/bin/ps/ps.c @@ -0,0 +1,350 @@ +/* */ +/* extended ps v1.0 for GNO/ME v1.1 */ +/* */ +/* 6/17/93 -- initial coding, this version does everything gsh's 'ps' */ +/* and also shows the Parent's PID, as well as giving a */ +/* larger time field. -u, -e and -n options implemented */ +/* */ +/* 6/18/93 -- fixed time calculations, which were off for minutes and */ +/* hours by a power of 10. */ +/* */ +/* 6/20/93 -- added 'sleeping' category to process state listing. */ +/* */ +/* 6/22/93 -- fixed display for forked child */ +/* added -t option flag */ +/* added rexit() call, and getopt_restart() */ +/* */ +/* 6/23/93 -- fixed -t option to work with ttyID of 00 */ +/* */ +/* 6/26/93 -- fixed miscellaneous problems with memory allocation */ +/* */ +/* 6/27/93 -- added -w, -ww, -l flags. */ +/* */ +/* Author: James Brookes */ +/* jamesb@cscihp.ecst.csuchico.edu */ +/* */ + +#pragma optimize -1 +#pragma stacksize 512 + +#include +#include +#include +#include +#include +#include +#include + +#define FALSE 0 +#define TRUE 1 + +void print_time(long ticks); +int getuid(void); +char *ttyname(int fd); +extern int getopt_restart(void); + +/* boolean global options */ + +int username; /* username to show */ +int allprocs; /* show all processes */ +int showname; /* show username */ +int tty; /* tty to show */ +int showtty; /* show only ttyID */ +int w_counter; /* width option */ +int long_list; /* long listing */ + +static char *status[] = { "unused", "running", "ready", "blocked", "new", + "suspended", "wait", "waitsigch", "paused", + "sleeping", "unknown" }; + +void usage(char *callname) + + { + fprintf(stderr,"usage: %s -anlwt u \n",callname); + rexit(0); + } + +/* Process option flags */ + +void getopts(int argc, char **argv) + + { + int c, username_select, tty_select; + struct passwd *pw_s; + + username_select = FALSE; + tty_select = FALSE; + + pw_s = (struct passwd *) malloc (sizeof(struct passwd)); + + username = getuid(); + allprocs = FALSE; + showname = FALSE; + showtty = FALSE; + long_list = FALSE; + w_counter = 0; + + if (argc == 1) + return; + + while((c = getopt(argc,argv,"anwlt:u:")) != EOF) + + { + switch((unsigned char)c) + + { + case 'a': + + allprocs = TRUE; + break; + + case 'n': + + showname = TRUE; + break; + + case 't': + + tty_select = TRUE; + showtty = TRUE; + tty = atoi(optarg); + break; + + case 'u': + + username_select = TRUE; + pw_s = getpwnam(optarg); + username = pw_s->pw_uid; + break; + + case 'w': + + w_counter++; + break; + + case 'l': + + long_list = TRUE; + break; + + default: + + usage(argv[0]); + } + + } + + if ((allprocs) && ((username_select) || (tty_select))) + usage(argv[0]); + } + +void print_time(long ticks) + + { + int secs, mins, hous, days, shortmins; + + secs = (ticks/60) % 60; + mins = (ticks/3600) % 60; + hous = (ticks/216000) % 24; + days = (ticks/5184000) % 365; + + if (!long_list) + shortmins = (ticks/3600) % 999; + + if (long_list) + + { + if (hous) + printf("%2d:",hous); + else + printf(" "); + + printf("%02d:",mins); + printf("%02d ",secs); + } + + else + + { + printf("%03d:",shortmins); + printf("%02d ",secs); + } + + } + +int main(int argc, char **argv) + + { + kvmt *proc_kvm; + struct pentry *proc_entry; + struct passwd *pw_s; + int i, pstate, p_uid, columns, num_chars; + char **name; + + getopt_restart(); + getopts(argc,argv); /* process command line options */ + + if (w_counter == 0) /* w option - width of screen */ + w_counter = 80; + else if (w_counter == 1) + w_counter = 132; + else + w_counter = INT_MAX; + + name = calloc (1024l,sizeof (char *)); /* up to 1024 usernames */ + pw_s = (struct passwd *) malloc (sizeof(struct passwd)); + + proc_kvm = kvm_open(); + + /* Print header */ + + printf(" ID"); + columns += 5; + + if (long_list) + + { + printf(" PPID"); + columns += 6; + } + + printf(" STATE TT"); + columns += 13; + + if (long_list) + + { + printf(" MMID"); + columns += 5; + } + + printf(" USER"); + columns+=5; + + if (showname) + + { + if (long_list) + + { + printf(" "); + columns += 10; + } + + else + + { + printf(" "); + columns += 7; + } + + } + + else /* !showname */ + + { + if (long_list) + + { + printf(" "); + columns += 5; + } + + else + + { + printf(" "); + columns += 3; + } + + } + + printf("TIME COMMAND\n"); + columns+=6; + + do /* while proc_entry != NULL */ + + { + i = 0; + proc_entry = kvmnextproc(proc_kvm); /* get next proc entry */ + + if (proc_entry == NULL) /* no more processes */ + continue; + + p_uid = proc_entry->p_uid; + + if ((tty != proc_entry->ttyID) && (showtty)); + + else if ((username == p_uid) || (allprocs) || (showtty)) + + { + printf("%5d ",proc_kvm->pid); /* pid */ + if (long_list) + printf("%5d ",proc_entry->parentpid); /* pid of parent process */ + + /* if processState is screwed, call it 'unknown' */ + + if ((proc_entry->processState < 0) || (proc_entry->processState > 9)) + pstate = 10; + else + pstate = proc_entry->processState; + + printf("%-9s ",status[pstate]); /* process state */ + + if (proc_entry->ttyID == 3) /* ttyID */ + printf("co "); /* console */ + else + printf("%02d ",proc_entry->ttyID); + + if (long_list) + printf("%4X ",proc_entry->userID); /* MMID */ + + if (showname) /* -n */ + + { + if (name[p_uid] == NULL) + + { + pw_s = getpwuid(p_uid); /* username */ + name[p_uid] = (char *) malloc (strlen(pw_s->pw_name) + 1); + strcpy(name[p_uid],pw_s->pw_name); + } + + printf("%-8s ",name[p_uid]); + } + + else /* default--no name, just id */ + printf("%04d ",p_uid); + + print_time(proc_entry->ticks); /* time process has run */ + + num_chars = (w_counter - columns) + 5; + for (i = 8; i < num_chars; i++) /* commandline */ + + { + if (proc_entry->args == NULL) + + { + printf("forked child of process %d",proc_entry->parentpid); + break; + } + + else if (proc_entry->args[i] == '\0') + break; + else + putchar(proc_entry->args[i]); + } + + if (i == num_chars) + printf("..."); + printf("\n"); + } + + } + + while(proc_entry != NULL); /* handle all proc entries */ + + kvm_close(proc_kvm); + free(pw_s); + rexit(0); + } diff --git a/bin/purge/purge.asm b/bin/purge/purge.asm new file mode 100644 index 0000000..7745045 --- /dev/null +++ b/bin/purge/purge.asm @@ -0,0 +1,206 @@ +************************************************************************** +* +* GNO Purge 3.0 +* +* Written by Tim Meekins, Procyon, Inc. +* Based on code originally written by Mike Westerfield. +* +* This program is public domain. enjoy it. +* +* Parts of this program are dependent on GNOlib and the ByteWorks SYSlib. +* +************************************************************************** + + keep purge + mcopy purge.mac + +Purge START + +handle equ 0 +location equ 0 +nextHandle equ 4 +argv equ 8 +argc equ 12 +verbosity equ 14 +arg equ 16 +; +; memory manager record +; +attributes equ 4 +userID equ 6 +length equ 8 +last equ 12 +next equ 16 + + phk + plb + + sta ~USER_ID + sty commandline + stx commandline+2 + stz verbosity + + jsl ~MM_INIT + + ph4 commandline + clc + tdc + adc #argv + pea 0 + pha + jsl GNO_PARSEARG + sta argc + + dec a + beq start + dec a + beq doopt +showusage WriteCString #Usage + jmp done + +doopt ldy #4 + lda [argv],y + sta arg + iny2 + lda [argv],y + sta arg+2 + + lda [arg] + and #$FF + cmp #'-' + bne showusage + ldy #1 + lda [arg],y + cmp #'v' ;the 0 too.. + bne doopt + inc verbosity + +start FreeMem before + FindHandle #Purge,handle + +lb1 ldy #last + lda [handle],y + tax + iny2 + ora [handle],y + beq lb1a + lda [handle],y + sta handle+2 + stx handle + bra lb1 + + +lb1a lda verbosity + beq lb2 + WriteCString #msg1 + +lb2 ora2 handle,handle+2,@a + jeq lb4 + ldy #next + lda [handle],y + sta nextHandle + iny2 + lda [handle],y + sta nextHandle+2 + ldy #attributes + lda [handle],y + jmi lb3 + and #$0300 + jeq lb3 + + lda verbosity + jeq dopurge + WriteChar #'$' + ldx handle+2 + lda handle + jsr PrintHex4 + WriteString #msg2 + ldy #2 + lda [handle],y + tax + lda [handle] + jsr PrintHex4 + WriteString #msg2 + ldy #attributes + lda [handle],y + jsr PrintHex2 + WriteString #msg2 + ldy #userID + lda [handle],y + jsr PrintHex2 + WriteString #msg2 + ldy #length+2 + lda [handle],y + tax + dey2 + lda [handle],y + jsr PrintHex4 + WriteChar #' ' + stz ref +apploop ldy #userID + lda [handle],y + and #%1111000011111111 + LGetPathname (@a,ref),@yx + if2 @a,eq,#0,appput + inc ref + if2 ref,cc,#128,apploop + bra oops +appput WriteString @xy +oops WriteLine #empty +dopurge PurgeHandle handle +lb3 mv4 nextHandle,handle + jmp lb2 +lb4 CompactMem + TotalMem @yx + NewHandle (@xy,~USER_ID,#$0000,#0),handle + ora2 handle,handle+2,@a + beq showstat + DisposeHandle handle + +showstat lda verbosity + beq Done + FreeMem After + WriteCString #beforestr + ldx before+2 + lda before + jsr PrintHex4 + WriteCString #leftstr + WriteCString #afterstr + ldx after+2 + lda after + jsr PrintHex4 + WriteCString #leftstr + +Done lda #0 + rtl + +empty str '' +ref ds 2 +commandline ds 4 +before ds 4 +after ds 4 + +msg1 dc c'GNO Purge 3.0',h'0d0a0a' + dc c'Handle Ptr Attr User Length App',h'0d0a00' +msg2 str ' $' +Usage dc c'Usage: purge [-v]',h'0d0a00' +beforestr dc h'0d0a',c'Before: $',h'00' +afterstr dc c'After: $',h'00' +leftstr dc c' bytes free',h'0d0a00' + +PrintHex1 Int2Hex (@a,#hex1str+1,#2) + WriteString #hex1str + rts +hex1str str '00' + +PrintHex4 pha + txa + jsr PrintHex1 + pla + +PrintHex2 Int2Hex (@a,#hex2str+1,#4) + WriteString #hex2str + rts +hex2str str '0000' + + END diff --git a/bin/purge/purge.mac b/bin/purge/purge.mac new file mode 100644 index 0000000..4917ae8 --- /dev/null +++ b/bin/purge/purge.mac @@ -0,0 +1,361 @@ + MACRO +&lab LGetPathname &a1,&a2 +&lab pha + pha + ph2 &a1(1) + ph2 &a1(2) + Tool $1111 + pl4 &a2 + mend + MACRO +&lab WriteChar &a1 +&lab ph2 &a1 + Tool $180c + mend + MACRO +&lab WriteLine &a1 +&lab ph4 &a1 + Tool $1a0c + mend + MACRO +&lab WriteString &a1 +&lab ph4 &a1 + Tool $1c0c + mend + MACRO +&lab Int2Hex &a1 +&lab ph2 &a1(1) + ph4 &a1(2) + ph2 &a1(3) + Tool $220b + mend + MACRO +&lab tool &a1 +&lab ldx #&a1 + jsl $e10000 + mend + MACRO +&lab ph2 &parm + lclc &char +&lab anop + aif c:&parm=0,.done +&char amid &parm,1,1 + aif "&char"="#",.immediate + aif "&char"="@",.at + aif s:longa=1,.chk + rep #%00100000 +.chk + aif "&char"<>"{",.absolute +&char amid &parm,l:&parm,1 + aif "&char"<>"}",.error +&parm amid &parm,2,l:&parm-2 + lda (&parm) + pha + ago .shorten +.absolute + lda &parm + pha + ago .shorten +.immediate +&parm amid &parm,2,l:&parm-1 + pea &parm + ago .done +.at +&char amid &parm,2,1 + ph&char +.shorten + aif s:longa=1,.done + sep #%00100000 +.done + mexit +.error + mnote "Missing closing '}'",16 + mend + MACRO +&lab ph4 &parm + lclc &char + lclc &char1 + lclc &char2 +&lab anop +&char amid &parm,1,1 + aif "&char"="#",.immediate + aif "&char"="@",.at + aif s:longa=1,.chk1 + rep #%00100000 +.chk1 + aif "&char"<>"{",.chk2 +&char amid &parm,l:&parm,1 + aif "&char"<>"}",.error +&parm amid &parm,2,l:&parm-2 + ldy #2 + lda (&parm),y + pha + lda (&parm) + pha + ago .shorten +.chk2 + aif "&char"<>"[",.absolute + ldy #2 + lda &parm,y + pha + lda &parm + pha + ago .shorten +.absolute + lda &parm+2 + pha + lda &parm + pha + ago .shorten +.at +&char1 amid &parm,2,1 +&char2 setc &char1 + ph&char1 + aif l:&parm<3,.chk2a +&char2 amid &parm,3,1 +.chk2a + ph&char2 + ago .shorten +.immediate +&parm amid &parm,2,l:&parm-1 + pea +(&parm)|-16 + pea &parm + ago .done +.shorten + aif s:longa=1,.done + sep #%00100000 +.done + mexit +.error + mnote "Missing closing '}'",16 + mend + MACRO +&lab pl4 &parm + lclc &char + lclc &char1 + lclc &char2 +&lab anop + aif s:longa=1,.start + rep #%00100000 +.start +&char amid &parm,1,1 + aif "&char"<>"{",.chk +&char amid &parm,l:&parm,1 + aif "&char"<>"}",.error +&parm amid &parm,2,l:&parm-2 + pla + sta (&parm) + ldy #2 + pla + sta (&parm),y + ago .shorten +.chk + aif "&char"<>"[",.chk2 + pla + sta &parm + ldy #2 + pla + sta &parm,y + ago .shorten +.chk2 + aif "&char"<>"@",.absolute +&char1 amid &parm,2,1 +&char2 setc &char1 + pl&char1 + aif l:&parm<3,.chk2a +&char2 amid &parm,3,1 +.chk2a + pl&char2 + ago .shorten +.absolute + pla + sta &parm + pla + sta &parm+2 +.shorten + aif s:longa=1,.done + sep #%00100000 +.done + mexit +.error + mnote "Missing closing '}'",16 + mend + MACRO +&lab Str &string +&lab dc i1'L:&string' + dc c"&string" + mend + MACRO +&lab MV4 &src,&adr +&lab lcla &count + lda &src +&count seta 1 +.loop1 + sta &adr(&count) +&count seta &count+1 + aif &count>c:&adr,.part2 + ago ^loop1 +.part2 + lda &src+2 +&count seta 1 +.loop2 + sta &adr(&count)+2 +&count seta &count+1 + aif &count>c:&adr,.done + ago ^loop2 +.done + mend + MACRO +&lab iny2 +&lab iny + iny + mend + MACRO +&lab dey2 +&lab dey + dey + mend + MACRO +&lab PurgeHandle &a1 +&lab ph4 &a1 + tool $1202 + mend + MACRO +&lab FindHandle &a1,&a2 +&lab pha + pha + ph4 &a1 + tool $1a02 + pl4 &a2 + mend + MACRO +&lab CompactMem +&lab tool $1f02 + mend + MACRO +&lab if2 &var,&rel,&val,&label +&lab ago .skip + ble + bgt +.skip + lclc &char1 + lclc &char2 +&char1 amid &var,1,1 +&char2 amid &var,2,1 + aif "&char1"="@",.index + lda &var +.cmp + cmp &val + ago .branch +.index + aif "&char2"="x",.x1 + aif "&char2"="X",.x1 + aif "&char2"="y",.y1 + aif "&char2"="Y",.y1 + ago ^cmp +.x1 + cpx &val + ago .branch +.y1 + cpy &val +.branch +&char1 amid &rel,1,1 + aif "&char1"="@",.done + b&rel &label +.done + mend + MACRO +&lab bgt &loc +&lab beq *+4 + bcs &loc + mend + MACRO +&lab ble &loc +&lab bcc &loc + beq &loc + mend + MACRO +&lab WriteCString &a1 +&lab ph4 &a1 + Tool $200c + mend + MACRO +&lab jeq &loc +&lab bne *+5 + jmp &loc + mend + MACRO +&lab jmi &loc +&lab bpl *+5 + jmp &loc + mend + MACRO +&lab ora2 &arg1,&arg2,&dest +&lab anop + lclc &char +&char amid &arg1,1,1 + aif "&char"="@",.at1 + lda &arg1 + ago .add +.at1 +&char amid &arg1,2,1 + aif "&char"="x",.x1 + aif "&char"="X",.x1 + aif "&char"="y",.y1 + aif "&char"="Y",.y1 + ago .add +.x1 + txa + ago .add +.y1 + tya +.add + ora &arg2 +&char amid &dest,1,1 + aif "&char"="@",.at2 + sta &dest + ago .b +.at2 +&char amid &dest,2,1 + aif "&char"="x",.x2 + aif "&char"="X",.x2 + aif "&char"="y",.y2 + aif "&char"="Y",.y2 + ago .b +.x2 + tax + ago .b +.y2 + tay +.b + mend + macro +&lab NewHandle &a1,&a2 +&lab pha + pha + ph4 &a1(1) + ph2 &a1(2) + ph2 &a1(3) + ph4 &a1(4) + tool $0902 + pl4 &a2 + mend + macro +&lab DisposeHandle &a1 +&lab ph4 &a1 + tool $1002 + mend + macro +&lab TotalMem &a1 +&lab pha + pha + tool $1d02 + pl4 &a1 + mend + macro +&lab FreeMem &a1 +&lab pha + pha + tool $1b02 + pl4 &a1 + mend diff --git a/bin/stty/Makefile b/bin/stty/Makefile new file mode 100644 index 0000000..9a76aee --- /dev/null +++ b/bin/stty/Makefile @@ -0,0 +1,6 @@ +# @(#)Makefile 5.4 (Berkeley) 6/5/91 + +PROG= stty +SRCS= cchar.c gfmt.c key.c modes.c print.c stty.c util.c + +.include diff --git a/bin/stty/stty.c b/bin/stty/stty.c new file mode 100644 index 0000000..25be09c --- /dev/null +++ b/bin/stty/stty.c @@ -0,0 +1,302 @@ +/* + * stty.c + * + * Set terminal parameters + */ + +#pragma stacksize 1280 +#pragma optimize 9 + +#include +#include +#include +#include +#include +#include + +typedef struct opts { + char *name; + int item; +} opts_s; + +/* B0 - B57600 are defined before these others */ +#define LCRTERA_ON 16 +#define LCRTERA_OFF 17 +#define LCTLECH_ON 18 +#define LCTLECH_OFF 19 +#define CRMOD_ON 20 +#define CRMOD_OFF 21 +#define ECHO_ON 22 +#define ECHO_OFF 23 +#define RAW_ON 24 +#define RAW_OFF 25 +#define CBREAK_ON 26 +#define CBREAK_OFF 27 +#define SUSP_C 28 +#define STOP_C 29 +#define START_C 30 +#define QUIT_C 31 +#define ERASE_C 32 +#define INTR_C 33 +#define EOF_C 34 + +char *baudtbl[] = { +"0", +"50", +"75", +"110", +"134.5", +"150", +"57600", +"300", +"600", +"1200", +"1800", +"2400", +"4800", +"9600", +"19200", +"38400"}; + +opts_s options[] = { + "75", B75, + "110", B110, + "134", B134, + "150", B150, + "300", B300, + "600", B600, + "1200", B1200, + "1800", B1800, + "2400", B2400, + "4800", B4800, + "9600", B9600, + "19200", B19200, + "38400", B38400, + "57600", B57600, + "lcrtera", LCRTERA_ON, + "-lcrtera", LCRTERA_OFF, + "lctlech", LCTLECH_ON, + "-lctlech", LCTLECH_OFF, + "crmod", CRMOD_ON, + "-crmod", CRMOD_OFF, + "echo", ECHO_ON, + "-echo", ECHO_OFF, + "raw", RAW_ON, + "-raw", RAW_OFF, + "cbreak", CBREAK_ON, + "-cbreak", CBREAK_OFF, + "susp", SUSP_C, + "stop", STOP_C, + "start", START_C, + "quit", QUIT_C, + "erase", ERASE_C, + "intr", INTR_C, + "eof", EOF_C, + "", 0 +}; + +int lookup(char *s) +{ +int i; + + for (i = 0; options[i].item; i++) { + if (!strcmp(options[i].name,s)) return (options[i].item); + } + return 0; +} + +void usage(void) +{ + fprintf(stderr,"usage: stty [ option ]...\n" + "\toption: [-]raw,[-]echo,[-]cbreak,[baud]\n" + "\toption c: intr, susp, stop, start, eof, erase\n" + "\t\twhere c is ^X or \\0OCTAL or \\xHEX\n"); + exit(1); +} + +char parsechar(char *s) +{ +int x; + + if (s[0] == '^') { + if (strlen(s) != 2) usage(); + if (s[1] == '?') return 0x7f; + else return toupper(s[1])-64; + } + else if (s[0] == '\\') { + if (isdigit(s[1]) && (s[0] != 0)) + sscanf(s+1,"%d",&x); + else if (toupper(s[1]) == 'X') + sscanf(s+2,"%x",&x); + else if (s[1] == '0') + sscanf(s+1,"%o",&x); + else usage(); + printf("char: %d\n",x); + return x; + } + if (strlen(s) != 1) usage(); + return s[0]; +} + +struct sgttyb sg; +struct tchars tc; +struct ltchars ltc; +struct winsize wz; +long localmode; + +char *dash[] = {"","-"}; + +char *doctrl(char c) +{ +static char ss[3] = " "; + + if (c == 0x7F) { + ss[0] = '^'; + ss[1] = '?'; + } + else if (c < 32) { + ss[0] = '^'; + ss[1] = c + '@'; + } + else { + ss[0] = c; + ss[1] = ' '; + } + return ss; +} + +void printCurSettings(void) +{ + printf("old tty, speed %s baud, %d rows, %d columns\n", + baudtbl[sg.sg_ispeed & 0xF],wz.ws_row,wz.ws_col); + printf("%seven %sodd %sraw %snl %secho %slcase %standem %stabs %scbreak\n", + dash[(sg.sg_flags & EVENP) == 0], + dash[(sg.sg_flags & ODDP) == 0], + dash[(sg.sg_flags & RAW) == 0], + dash[(sg.sg_flags & NLDELAY) == 0], + dash[(sg.sg_flags & ECHO) == 0], + dash[(sg.sg_flags & LCASE) == 0], + dash[(sg.sg_flags & TANDEM) == 0], + dash[(sg.sg_flags & XTABS) == 0], + dash[(sg.sg_flags & CBREAK) == 0]); + printf("%stilde %sflusho %slitout %spass8 %snohang\n", + dash[(localmode & LTILDE) == 0], + dash[(localmode & LFLUSHO) == 0], + dash[(localmode & LLITOUT) == 0], + dash[(localmode & LPASS8) == 0], + dash[(localmode & LNOHANG) == 0]); + printf("%spendin %snoflsh\n", + dash[(localmode & LPENDIN) == 0], + dash[(localmode & LNOFLSH) == 0]); + + printf("erase kill werase rprnt flush lnext susp intr quit stop eof\n"); + printf("%-7s",doctrl(sg.sg_erase)); + printf("%-7s",doctrl(sg.sg_kill)); + printf("%-7s",doctrl(ltc.t_werasc)); + printf("%-7s",doctrl(ltc.t_rprntc)); + printf("%-7s",doctrl(ltc.t_flushc)); + printf("%-7s",doctrl(ltc.t_lnextc)); + printf("%2s",doctrl(ltc.t_suspc)); + printf("/%2s ",doctrl(ltc.t_dsuspc)); + printf("%-7s",doctrl(tc.t_intrc)); + printf("%-7s",doctrl(tc.t_quitc)); + printf("%2s",doctrl(tc.t_stopc)); + printf("/%2s ",doctrl(tc.t_startc)); + printf("%-7s\n",doctrl(tc.t_eofc)); +} + +int main(int argc, char *argv[]) +{ +int i,item; + + ioctl(STDIN_FILENO,TIOCGETP,&sg); + ioctl(STDIN_FILENO,TIOCGETC,&tc); + ioctl(STDIN_FILENO,TIOCGLTC,<c); + ioctl(STDIN_FILENO,TIOCLGET,&localmode); + ioctl(STDIN_FILENO,TIOCGWINSZ,&wz); + if (argc < 2) { + printCurSettings(); + exit(0); + } + + for (i = 1; i < argc;) { + switch (item = lookup(argv[i])) { + + case INTR_C: + tc.t_intrc = parsechar(argv[i+1]); + i++; + break; + case SUSP_C: + ltc.t_suspc = parsechar(argv[i+1]); + i++; + break; + case STOP_C: + tc.t_stopc = parsechar(argv[i+1]); + i++; + break; + case START_C: + tc.t_startc = parsechar(argv[i+1]); + i++; + break; + case QUIT_C: + tc.t_quitc = parsechar(argv[i+1]); + i++; + break; + case ERASE_C: + sg.sg_erase = parsechar(argv[i+1]); + i++; + break; + case EOF_C: + tc.t_eofc = parsechar(argv[i+1]); + i++; + break; + case ECHO_ON: + sg.sg_flags |= ECHO; + break; + case ECHO_OFF: + sg.sg_flags &= ~ECHO; + break; + case RAW_OFF: + sg.sg_flags &= ~RAW; + break; + case RAW_ON: + sg.sg_flags |= RAW; + break; + case CBREAK_OFF: + sg.sg_flags &= ~CBREAK; + break; + case CBREAK_ON: + sg.sg_flags |= CBREAK; + break; + case CRMOD_OFF: + sg.sg_flags &= ~CRMOD; + break; + case CRMOD_ON: + sg.sg_flags |= CRMOD; + break; + case LCTLECH_ON: + localmode |= LCTLECH; + break; + case LCTLECH_OFF: + localmode &= ~LCTLECH; + break; + case LCRTERA_ON: + localmode |= LCRTERA; + break; + case LCRTERA_OFF: + localmode &= ~LCRTERA; + break; + default: + if ((item != 0) && (item <= B38400)) { + sg.sg_ispeed = item; + sg.sg_ospeed = item; + } else usage(); + break; + } + i++; + } + ioctl(STDIN_FILENO,TIOCSETP,&sg); + ioctl(STDIN_FILENO,TIOCSETC,&tc); + ioctl(STDIN_FILENO,TIOCSLTC,<c); + ioctl(STDIN_FILENO,TIOCLSET,&localmode); +} diff --git a/bin/upper/Makefile b/bin/upper/Makefile new file mode 100644 index 0000000..dbf4639 --- /dev/null +++ b/bin/upper/Makefile @@ -0,0 +1,2 @@ +upper.a: upper.c + compile upper.c keep=upper diff --git a/bin/upper/upper.c b/bin/upper/upper.c new file mode 100644 index 0000000..3f62db6 --- /dev/null +++ b/bin/upper/upper.c @@ -0,0 +1,22 @@ +/* converts text to upper case, used to gsh pipe mechanism */ +/* program by Tim Meekins, Copyright (C) 1991 Procyon, Inc. */ + +#include +#include +#pragma optimize -1 +#pragma stacksize 1024 + +main() +{ + char ch; + + while(1) + { + ch = ReadChar(0) & 0x7f; + if (ch==0) return; + if (ch >= 'a' && ch <= 'z') + ch = ch - ('a'-'A'); + WriteChar(ch); + if (ch==13) WriteChar(10); + } +} diff --git a/bin/vi/Makefile b/bin/vi/Makefile new file mode 100644 index 0000000..79149de --- /dev/null +++ b/bin/vi/Makefile @@ -0,0 +1,74 @@ +o/alloc.a: alloc.c stevie.h env.h ascii.h keymap.h param.h term.h macros.h gsos.h + compile alloc.c keep=o/alloc + +o/CHARSET.a: CHARSET.C stevie.h env.h ascii.h keymap.h param.h term.h macros.h gsos.h + compile CHARSET.C keep=o/CHARSET + +o/cmdline.a: cmdline.c stevie.h env.h ascii.h keymap.h param.h term.h macros.h gsos.h + compile cmdline.c keep=o/cmdline + +o/DEC.a: DEC.C stevie.h env.h ascii.h keymap.h param.h term.h macros.h gsos.h + compile DEC.C keep=o/DEC + +o/EDIT.a: EDIT.C stevie.h env.h ascii.h keymap.h param.h term.h macros.h gsos.h + compile EDIT.C keep=o/EDIT + +o/FILEIO.a: FILEIO.C stevie.h env.h ascii.h keymap.h param.h term.h macros.h gsos.h + compile FILEIO.C keep=o/FILEIO + +o/FORMAT.L.a: FORMAT.L.C stevie.h env.h ascii.h keymap.h param.h term.h macros.h gsos.h + compile FORMAT.L.C keep=o/FORMAT.L + +o/gsos.a: gsos.c gsos.h stevie.h env.h ascii.h keymap.h param.h term.h macros.h gsos.h + compile -p gsos.c keep=o/gsos + +o/HELP.a: HELP.C stevie.h env.h ascii.h keymap.h param.h term.h macros.h gsos.h + compile HELP.C keep=o/HELP + +o/INC.a: INC.C stevie.h env.h ascii.h keymap.h param.h term.h macros.h gsos.h + compile INC.C keep=o/INC + +o/LINEFUNC.a: LINEFUNC.C stevie.h env.h ascii.h keymap.h param.h term.h macros.h gsos.h + compile LINEFUNC.C keep=o/LINEFUNC + +o/main.a: main.c stevie.h env.h ascii.h keymap.h param.h term.h macros.h gsos.h + compile main.c keep=o/main + +o/MARK.a: MARK.C stevie.h env.h ascii.h keymap.h param.h term.h macros.h gsos.h + compile MARK.C keep=o/MARK + +o/misccmds.a: misccmds.c stevie.h env.h ascii.h keymap.h param.h term.h macros.h gsos.h + compile misccmds.c keep=o/misccmds + +o/MK.a: MK.C stevie.h env.h ascii.h keymap.h param.h term.h macros.h gsos.h + compile MK.C keep=o/MK + +o/PARAM.a: PARAM.C stevie.h env.h ascii.h keymap.h param.h term.h macros.h gsos.h + compile PARAM.C keep=o/PARAM + +o/regexp.a: regexp.c env.h regexp.h regmagic.h + compile regexp.c keep=o/regexp + +o/REGSUB.a: REGSUB.C env.h regexp.h regmagic.h + compile REGSUB.C keep=o/REGSUB + +o/S.IO.a: S.IO.C stevie.h env.h ascii.h keymap.h param.h term.h macros.h gsos.h + compile S.IO.C keep=o/S.IO + +o/SCREEN.a: SCREEN.C stevie.h env.h ascii.h keymap.h param.h term.h macros.h gsos.h + compile SCREEN.C keep=o/SCREEN + +o/search.a: search.c stevie.h env.h ascii.h keymap.h param.h term.h macros.h gsos.h regexp.h + compile search.c keep=o/search + +o/VERSION.a: VERSION.C + compile VERSION.C keep=o/VERSION + +o/normal.a: normal.c stevie.h env.h ascii.h keymap.h param.h term.h macros.h gsos.h + compile normal.c keep=o/normal + +vi: o/sio.a o/version.a o/search.a o/screen.a o/alloc.a o/cmdline.a \ + o/regsub.a o/regexp.a o/param.a o/normal.a o/mk.a o/gsos.a o/format.l.a \ + o/misccmds.a o/mark.a o/main.a o/linefunc.a o/inc.a o/help.a \ + o/fileio.a o/edit.a o/dec.a o/charset.a + compile linkscr keep=vi diff --git a/bin/vi/README b/bin/vi/README new file mode 100644 index 0000000..d7e993b --- /dev/null +++ b/bin/vi/README @@ -0,0 +1,72 @@ +STEVIE Source Release + +This is a source release of the STEVIE editor, a public domain clone +of the UNIX editor 'vi'. The program was originally developed for the +Atari ST, but has been ported to UNIX, OS/2, BSD 4.3 and the Amiga as well. + +There are currently two divergent versions of STEVIE. This version is the one +that was ported to the Amiga and then worked on by me (G. R. Walter). The other +one Tony Andrews continued to work on. My version is faster in some respects +then his version, and his version does a couple of things mine doesn't. + +The files included in this release are: + +README + This file. + +stevie.doc + Reference manual for STEVIE. Assumes familiarity with vi. + +source.doc + Quick overview of the major data structures used. + +porting.doc + Tips for porting STEVIE to other systems. + +makefile.dos +makefile.os2 +makefile.usg +makefile.tos +makefile.bsd +makefile.amiga.lattice + Makefiles for MS DOS, OS/2, UNIX System V, Atari ST, BSD 4.3 UNIX and +the Amiga respectively. + +amiga.c +amiga.h +bsd.c +bsd.h +dos.c +dos.h +os2.c +os2.h +unix.c +unix.h +tos.c +tos.h + System-dependent routines for the same. + +alloc.c ascii.h cmdline.c edit.c fileio.c help.c charset.c +keymap.h linefunc.c main.c mark.c misccmds.c normal.c param.c +regexp.c regsub.c version.c regexp.h regmagic.h +param.h ptrfunc.c screen.c search.c stevie.h term.h macros.h + + C source and header files for STEVIE. + +To compile STEVIE for one of the provided systems: + + 1. Compile the regular expression library and install as + appropriate for your system. + + 2. Edit the file 'env.h' to set the system defines as needed. + + 3. Check the makefile for your system, and modify as needed. + + 4. Compile. + +NOTE: implicit in the design is the assumption that char's are unsigned. Thus + if your compiler assumes different by default, change the default or + you may have to change the source. + +Tony Andrews March 12, 1988 +G. R. (Fred) Walter August 14, 1988 diff --git a/bin/vi/README.gno b/bin/vi/README.gno new file mode 100644 index 0000000..1043c94 --- /dev/null +++ b/bin/vi/README.gno @@ -0,0 +1,80 @@ +This version of the STEVIE sources and executable requires the GNO +multitasking environment, because STEVIE uses some GNO system calls +to enable it to work more like the real 'VI'. + +This archive contains only the Stevie executable. Look for the source +archive in the same place you got this one. + +This version of STEVIE has been modified to use the termcap library, +allowing STEVIE to operate on a wide range of terminal types, including +gno, vt100, proterm special, etc. +Apple IIgs BBS's now have a full-screen editor for posts, etc. + +The termcap file is _not_ provided; look in the same place you got +Stevie for the latest termcap file. +The termcap file should be placed in your $home directory +(see your gshrc file for where $home is) or in directory 31:etc +(choose an existing directory or create a new one, and set prefix 31 +in your gshrc). + +It shouldn't be too hard to modify STEVIE to work under ORCA, but considering +all the other advantages of GNO, and the growing library of software that +requires it, I recommend you purchase GNO immediately (ok, so this is a plug). + +Any comments, suggestions, and bug reports should be sent to: + +Internet bazyar@cs.uiuc.edu +America OnLine GNO Jawaid +GEnie PROCYON.INC +Delphi JBazyar + +You know exactly where to send any flames :-) + +Jawaid Bazyar + + +CHANGES + +2/3/94 +Fixed a MAJOR buffer overflow bug that was causing Stevie to barf a lot +when doing delete lines, undos, etc. Basically, my screen output buffering +routines were not checking for the end of buffer. + +7/20/92 +Added auto screen-redraw when Stevie is unsuspended from the shell + +Fixed termcap support again- we forgot to call tputs in _one_ +location, causing cursor goto's to have a '5' placed in front +of them. Also took care of the problem where Stevie wouldn't +scroll with vt100 (you need a new termcap file dated 7/19/92 or +later for this). + +I now use one of GNO's TextTools features so I know the difference +between right-arrow and control-U. Happy, Matt? :-) + +Sped up screen output slightly by doing buffering and going directly +through the GNO console rather than TextTools. + +Claimed Stevie as our own; we won't bother to keep ours current with +the original because the authors cannot be located. + +Started optimizing some stuff; done mainly for space considerations +right now (i.e. only turning off stack 'protection') + +Sped up file saves ~400% by assembly-izing and using a large, custom +file I/O buffer. (normal.c: 8 seconds to less than 2) + +Sped up file loads ~60-80% by using a large, custom file I/O buffer. +any further load speed increases will require changing Stevie's memory +allocation scheme-a proposition that will probably mean rewriting most +of the code in Stevie. (normal.c: 10 seconds to 6) + +Typing the suspend character while in insert or replace mode adds +that character to the buffer, rather than suspending Stevie. (e.g. +typing '^Z' will insert a '^Z'.) + +4/21/92 + +Fixed termcap support- now correctly calls termcap routine 'tputs' +to output all term control codes. Thus, Stevie now works with the +VT100 termcap entry (which has special termcap delay codes, etc in it) diff --git a/bin/vi/TAGS b/bin/vi/TAGS new file mode 100644 index 0000000..e3d9f76 --- /dev/null +++ b/bin/vi/TAGS @@ -0,0 +1,161 @@ +ISSPECIAL edit.c /^#define ISSPECIAL(c) ((c) == BS || (c) == NL ||/ +alloc alloc.c /^alloc(size)$/ +badcmd cmdline.c /^badcmd()$/ +beginline edit.c /^beginline(flag)$/ +canincrease alloc.c /^canincrease(n)$/ +dec dec.c /^dec(lp)$/ +doecmd cmdline.c /^doecmd(arg)$/ +doshell cmdline.c /^doshell()$/ +dotag cmdline.c /^dotag(tag, force)$/ +edit edit.c /^edit()$/ +emsg cmdline.c /^emsg(s)$/ +filealloc alloc.c /^filealloc()$/ +freeall alloc.c /^freeall()$/ +get_line cmdline.c /^get_line(cp)$/ +get_range cmdline.c /^get_range(cp)$/ +getout edit.c /^getout(r)$/ +gotocmdline cmdline.c /^gotocmdline(clr, firstc)$/ +insertchar edit.c /^insertchar(c)$/ +msg cmdline.c /^msg(s)$/ +newline alloc.c /^newline(nchars)$/ +onedown edit.c /^onedown(n)$/ +oneleft edit.c /^oneleft()$/ +oneright edit.c /^oneright()$/ +oneup edit.c /^oneup(n)$/ +readcmdline cmdline.c /^readcmdline(firstc, cmdline)$/ +screenalloc alloc.c /^screenalloc()$/ +scrolldown edit.c /^scrolldown(nlines)$/ +scrollup edit.c /^scrollup(nlines)$/ +smsg cmdline.c /^smsg(s, a1, a2, a3, a4, a5, a6, a7, a8, a9)$/ +strsave alloc.c /^strsave(string)$/ +wait_return cmdline.c /^wait_return()$/ +Mmain main.c /^main(argc, argv)$/ +clrall mark.c /^clrall()$/ +clrmark mark.c /^clrmark(line)$/ +coladvance linefunc.c /^coladvance(p, want_col)$/ +filemess fileio.c /^filemess(s)$/ +getmark mark.c /^getmark(c)$/ +help help.c /^help()$/ +inc inc.c /^inc(lp)$/ +longline help.c /^longline(p)$/ +nextline linefunc.c /^nextline(curr)$/ +prevline linefunc.c /^prevline(curr)$/ +readfile fileio.c /^readfile(fname, fromp, nochangename)$/ +renum fileio.c /^renum()$/ +setmark mark.c /^setmark(c)$/ +setpcmark mark.c /^setpcmark()$/ +stuffReadbuff main.c /^stuffReadbuff(s)$/ +stuffnumReadbuff main.c /^stuffnumReadbuff(n)$/ +usage main.c /^usage()$/ +vgetc main.c /^vgetc()$/ +vpeekc main.c /^vpeekc()$/ +writeit fileio.c /^writeit(fname, start, end)$/ +AppendNumberToRedobuff normal.c /^AppendNumberToRedobuff(n)$/ +AppendNumberToUndoUndobuff normal.c /^AppendNumberToUndoUndobuff(n)$/ +AppendNumberToUndobuff normal.c /^AppendNumberToUndobuff(n)$/ +AppendPositionToUndoUndobuff normal.c /^AppendPositionToUndoUndobuff(column, row)$/ +AppendPositionToUndobuff normal.c /^AppendPositionToUndobuff(column, row)$/ +AppendToInsbuff normal.c /^AppendToInsbuff(s)$/ +AppendToRedobuff normal.c /^AppendToRedobuff(s)$/ +AppendToUndoUndobuff normal.c /^AppendToUndoUndobuff(s)$/ +AppendToUndobuff normal.c /^AppendToUndobuff(s)$/ +DEFAULT1 normal.c /^#define DEFAULT1(x) (((x) == 0) ? 1 : (x))$/ +IDCHAR normal.c /^#define IDCHAR(c) (isalpha(c) || isdigit(c) / +OpenBackward misccmds.c /^OpenBackward(can_ai)$/ +OpenForward misccmds.c /^OpenForward(can_ai)$/ +ResetBuffers normal.c /^ResetBuffers()$/ +cntllines misccmds.c /^cntllines(pbegin, pend)$/ +cursupdate screen.c /^cursupdate(type)$/ +delchar misccmds.c /^delchar(fixpos, undo)$/ +delline misccmds.c /^delline(nlines)$/ +dochange normal.c /^dochange()$/ +dodelete normal.c /^dodelete(redraw, setup_for_undo, try_to_yank)$/ +dojoin normal.c /^dojoin(leading_space, strip_leading_spaces)$/ +doput normal.c /^doput(dir)$/ +doset param.c /^doset(arg, inter)$/ +doshift normal.c /^doshift(op)$/ +doyank normal.c /^doyank()$/ +fileinfo misccmds.c /^fileinfo()$/ +gotoline misccmds.c /^gotoline(n)$/ +inschar misccmds.c /^inschar(c)$/ +insstr misccmds.c /^insstr(s)$/ +linewhite normal.c /^linewhite(p)$/ +normal normal.c /^normal(c)$/ +plines misccmds.c /^plines(s)$/ +showparms param.c /^showparms(all)$/ +startinsert normal.c /^startinsert(startln)$/ +tabinout normal.c /^tabinout(shift_type, num)$/ +updateline screen.c /^updateline()$/ +C0 search.c /^#define C0(c) (((c) == ' ') || ((c) == '\\t') || / +C1 search.c /^#define C1(c) (isalpha(c) || isdigit(c) || ((c) / +OTHERDIR search.c /^#define OTHERDIR(x) (((x) == FORWARD) ? BACKWA/ +bck_word search.c /^bck_word(p, type)$/ +bcksearch search.c /^bcksearch(str)$/ +cls search.c /^cls(c)$/ +crepsearch search.c /^crepsearch(flag)$/ +doglob search.c /^doglob(lp, up, cmd)$/ +dosearch search.c /^dosearch(dir, str)$/ +dosub search.c /^dosub(lp, up, cmd)$/ +end_word search.c /^end_word(p, type)$/ +findfunc search.c /^findfunc(dir)$/ +format_line format_l.c /^format_line(ptr, len)$/ +fwd_word search.c /^fwd_word(p, type)$/ +fwdsearch search.c /^fwdsearch(str)$/ +mapstring search.c /^mapstring(s)$/ +regerror search.c /^regerror(s)$/ +repsearch search.c /^repsearch(flag)$/ +searchagain search.c /^searchagain(dir)$/ +searchc search.c /^searchc(c, dir, type)$/ +showmatch search.c /^showmatch()$/ +ssearch search.c /^ssearch(dir, str)$/ +CTRL ascii.h /^#define CTRL(x) ((x) & 0x1f)$/ +NotValidFromCurschar s_io.c /^NotValidFromCurschar()$/ +Update_Botchar s_io.c /^Update_Botchar()$/ +s_clear s_io.c /^s_clear()$/ +s_cursor_off s_io.c /^s_cursor_off()$/ +s_cursor_on s_io.c /^s_cursor_on()$/ +s_refresh s_io.c /^s_refresh(type)$/ +screen_del s_io.c /^screen_del(row, nlines, total_rows)$/ +screen_ins s_io.c /^screen_ins(row, nlines, total_rows)$/ +screen_refresh s_io.c /^screen_refresh(type)$/ +LINE stevie.h 134 +LINEOF stevie.h /^#define LINEOF(x) ((x)->linep->num)$/ +LPtr stevie.h 135 +P param.h /^#define P(n) (params[n].value)$/ +RowNumber macros.h /^#define RowNumber(p) (UndoInProgress ? 0 : cntllin/ +anyinput macros.h /^#define anyinput() (Readbuffptr != NULL)$/ +bool_t stevie.h 55 +buf1line macros.h /^#define buf1line() (Filemem->linep->next == Fileen/ +bufempty macros.h /^#define bufempty() (buf1line() && Filemem->linep->/ +endofline macros.h /^#define endofline(p) \\$/ +equal macros.h /^#define equal(a, b) (((a)->linep == (b)->linep) &&/ +gchar macros.h /^#define gchar(lp) ((lp)->linep->s[(lp)->index])$/ +gt macros.h /^#define gt(a, b) ((LINEOF(a) != LINEOF(b)) \\$/ +gtoreq macros.h /^#define gtoreq(a, b) ((LINEOF(a) != LINEOF(b)) \\$/ +lineempty macros.h /^#define lineempty(p) ((p)->linep->s[0] == NUL)$/ +lt macros.h /^#define lt(a, b) ((LINEOF(a) != LINEOF(b)) \\$/ +ltoreq macros.h /^#define ltoreq(a, b) ((LINEOF(a) != LINEOF(b)) \\$/ +mkline mk.c /^mkline(n)$/ +mkstr mk.c /^mkstr(c)$/ +pchar macros.h /^#define pchar(lp, c) ((lp)->linep->s[(lp)->index] / +pswap macros.h /^#define pswap(a, b) { LPtr pswaptmp; pswaptmp = a;/ +startofline macros.h /^#define startofline(p) ((p)->index == 0)$/ +ExpandWildCards amiga.c /^ExpandWildCards(num_pat, pat, num_file, file)$/ +GetCharacter amiga.c /^GetCharacter()$/ +beep amiga.c /^beep()$/ +cooked amiga.c /^cooked(afh)$/ +delay amiga.c /^delay()$/ +flushbuf amiga.c /^flushbuf()$/ +fopenb amiga.c /^fopenb(fname, mode)$/ +getCSIsequence amiga.c /^getCSIsequence()$/ +get_ConUnit amiga.c /^get_ConUnit(afh)$/ +get_Rows_and_Columns amiga.c /^get_Rows_and_Columns()$/ +inchar amiga.c /^inchar()$/ +outchar amiga.c /^outchar(c)$/ +outstr amiga.c /^outstr(s)$/ +raw amiga.c /^raw(afh)$/ +send_packet amiga.c /^send_packet(pid, action, args, nargs)$/ +sleep amiga.c /^sleep(n)$/ +windexit amiga.c /^windexit(r)$/ +windgoto amiga.c /^windgoto(r, c)$/ +windinit amiga.c /^windinit()$/ diff --git a/bin/vi/TODO b/bin/vi/TODO new file mode 100644 index 0000000..be248eb --- /dev/null +++ b/bin/vi/TODO @@ -0,0 +1,38 @@ +To Do +----- + +- Fix the update routines to not redisplay the whole bloody screen every + time a line is inserted or a character is typed. This may require + adding IIgs support for InsertLine and DeleteLine. + +- Change the static buffers for insert/undo/redo/undoundo/etc to dynamic + buffers. + +- Add #'d and named buffers. + IE. "xp = pull text from buffer x + +- Add 'U'. + +- Add { and } (move paragraph back and forward). + +- In cmdline.c make get_range() give the appropriate error messages when it + gets a bad line range. + +- Add & (do last search and replace on current line) and :[range]& + (do last search and replace on the range of lines). + +- add :set wrapmargin command +- add :set shiftwidth command +- add :set autowrite command +- add :set showmode command + +- should be able to d/pat +- should be able to ^d (backspace over entire indent (for :set ai)) +- should be able to ^X and ^W in input mode + +- should support '!' filters +- should support mode lines + +- add checks for out of memory when strsave() is used + +- after a screen resize, make sure cmdline stuff is re-displayed diff --git a/bin/vi/alloc.c b/bin/vi/alloc.c new file mode 100644 index 0000000..ace6c98 --- /dev/null +++ b/bin/vi/alloc.c @@ -0,0 +1,211 @@ +/* + * STEVIE - Simply Try this Editor for VI Enthusiasts + * + * Code Contributions By : Tim Thompson twitch!tjt + * Tony Andrews onecom!wldrdg!tony + * G. R. (Fred) Walter watmath!grwalter + */ + +#include "stevie.h" +#ifdef AMIGA +# include +# include +# define PANIC_FACTOR_CHIP 40000 +#endif +#ifdef GSOS +#include +#endif + + +/* + * This file contains various routines dealing with allocation and + * deallocation of data structures. + */ + +char *alloc(unsigned size) +{ + char *p; /* pointer to new storage space */ + + p = malloc(size); + if (p == (char *) NULL) { /* if there is no more room... */ + emsg("alloc() is unable to find memory!"); + sleep(5); + } +#ifdef AMIGA + if (AvailMem(MEMF_CHIP) < PANIC_FACTOR_CHIP) { + free(p); + p = (char *) NULL; + emsg("alloc() - not enough CHIP memory!"); + sleep(5); + } +#endif + + return (p); +} + +char *strsave(char *string) +{ + char *s; + + s = alloc((unsigned) (strlen(string) + 1)); + if (s != (char *) NULL) + strcpy(s, string); + return (s); +} + +void screenalloc(void) +{ + int i; + + /* + * If we're changing the size of the screen, free the old arrays + */ + if (LinePointers != (LINE **) NULL) + free((char *) LinePointers); + if (LineSizes != (char *) NULL) + free(LineSizes); + + LinePointers = (LINE **) malloc((unsigned) (Rows * sizeof(LINE *))); + LineSizes = malloc((unsigned) Rows); + if (LinePointers == (LINE **) NULL || LineSizes == (char *) NULL) { + fprintf(stderr, "Unable to allocate screen memory!\n"); + getout(1); + } + for (i = 0; i < Rows; i++) { + LinePointers[i] = (LINE *) NULL; + LineSizes[i] = (char) 0; + } + NumLineSizes = -1; +} + +/* + * Allocate and initialize a new line structure with room for 'nchars' + * characters. + */ +LINE *newline(int nchars) +{ + register LINE *l; + + if (nchars == 0) + nchars = 1; + + l = (LINE *) alloc((unsigned) sizeof(LINE)); + if (l != (LINE *) NULL) { + l->s = alloc((unsigned) nchars); /* the line is empty */ + if (l->s != (char *) NULL) { + l->s[0] = NUL; + l->size = nchars; + + l->prev = (LINE *) NULL; /* should be initialized by caller */ + l->next = (LINE *) NULL; + } else { + free((char *) l); + l = (LINE *) NULL; + } + } + return l; +} + +/* + * filealloc() - construct an initial empty file buffer + */ +void +filealloc(void) +{ + Filemem->linep = newline(1); + Filetop->linep = newline(1); + Fileend->linep = newline(1); + if (Filemem->linep == (LINE *) NULL || + Filetop->linep == (LINE *) NULL || + Fileend->linep == (LINE *) NULL) { + fprintf(stderr, "Unable to allocate file memory!\n"); + getout(1); + } + Filemem->index = 0; + Filetop->index = 0; + Fileend->index = 0; + + Filetop->linep->prev = (LINE *) NULL; + Filetop->linep->next = Filemem->linep; /* connect Filetop to Filemem */ + Filemem->linep->prev = Filetop->linep; + + Filemem->linep->next = Fileend->linep; /* connect Filemem to Fileend */ + Fileend->linep->prev = Filemem->linep; + Fileend->linep->next = (LINE *) NULL; + + *Curschar = *Filemem; + *Topchar = *Filemem; + + Filemem->linep->num = 0; + Fileend->linep->num = 0xffffffffL; + + clrall(); /* clear all marks */ +} + +/* + * freeall() - free the current buffer + * + * Free all lines in the current buffer. + */ +void +freeall(void) +{ + LINE *lp; + LINE *xlp; + int i; + + for (lp = Filetop->linep; lp != (LINE *) NULL; lp = xlp) { + if (lp->s != (char *) NULL) + free(lp->s); + xlp = lp->next; + free((char *) lp); + } + + Curschar->linep = (LINE *) NULL; /* clear pointers */ + Filemem->linep = (LINE *) NULL; + Filetop->linep = (LINE *) NULL; + Fileend->linep = (LINE *) NULL; + + for (i = 0; i < Rows; i++) {/* clear screen information */ + LinePointers[i] = (LINE *) NULL; + LineSizes[i] = (char) 0; + } + NumLineSizes = -1; +} + +/* + * canincrease(n) - returns TRUE if the current line can be increased 'n' + * bytes + * + * This routine returns immediately if the requested space is available. If not, + * it attempts to allocate the space and adjust the data structures + * accordingly. If everything fails it returns FALSE. + */ +bool_t canincrease(int n) +{ + register int nsize; + register char *s; /* pointer to new space */ + + nsize = strlen(Curschar->linep->s) + 1 + n; /* size required */ + + if (nsize <= Curschar->linep->size) + return TRUE; + + /* + * Need to allocate more space for the string. Allow some extra space on + * the assumption that we may need it soon. This avoids excessive numbers + * of calls to malloc while entering new text. + */ + s = alloc((unsigned) (nsize + SLOP)); + if (s == (char *) NULL) { + emsg("Can't add anything, file is too big!"); + State = NORMAL; + return FALSE; + } + Curschar->linep->size = nsize + SLOP; + strcpy(s, Curschar->linep->s); + free(Curschar->linep->s); + Curschar->linep->s = s; + + return TRUE; +} diff --git a/bin/vi/ascii.h b/bin/vi/ascii.h new file mode 100644 index 0000000..2753fca --- /dev/null +++ b/bin/vi/ascii.h @@ -0,0 +1,29 @@ +/* + * STEVIE - Simply Try this Editor for VI Enthusiasts + * + * Code Contributions By : Tim Thompson twitch!tjt + * Tony Andrews onecom!wldrdg!tony + * G. R. (Fred) Walter watmath!grwalter + */ + +/* + * Definitions of various common control characters + */ + +#define NUL '\000' +#define BS '\010' +#define BS_STR "\010" +#define TAB '\011' +#define NL '\012' +#define NL_STR "\012" +#define CR '\015' +#define ESC '\033' +#define ESC_STR "\033" + +#define UNDO_SHIFTJ '\333' +#define UNDO_SHIFTJ_STR "\333" + +#define ENABLE_REDRAWING '\334' +#define ENABLE_REDRAWING_STR "\334" + +#define CTRL(x) ((x) & 0x1f) diff --git a/bin/vi/bug2.c b/bin/vi/bug2.c new file mode 100644 index 0000000..26f0905 --- /dev/null +++ b/bin/vi/bug2.c @@ -0,0 +1,1683 @@ +/* + * STEVIE - Simply Try this Editor for VI Enthusiasts + * + * Code Contributions By : Tim Thompson twitch!tjt + * Tony Andrews onecom!wldrdg!tony + * G. R. (Fred) Walter watmath!grwalter + */ + +/* + * This file contains the main routine for processing characters in command + * mode as well as routines for handling the operators. + */ + +#ifdef __ORCAC__ +segment "normal"; +#endif + +#include "bugstevie.h" + +static void doshift(); +static void dodelete(); +static void doput(); +static void dochange(); +static void startinsert(); +static bool_t dojoin(); +static bool_t doyank(); + +/* + * Macro evaluates true if char 'c' is a valid identifier character + */ +#define IDCHAR(c) (isalpha(c) || isdigit(c) || (c) == '_') + +/* + * Operators + */ +#define NOP 0 /* no pending operation */ +#define DELETE 1 +#define YANK 2 +#define CHANGE 3 +#define LSHIFT 4 +#define RSHIFT 5 + +#define CLEAROP (operator = NOP)/* clear any pending operator */ + +static int operator = NOP; /* current pending operator */ + +/* + * When a cursor motion command is made, it is marked as being a character or + * line oriented motion. Then, if an operator is in effect, the operation + * becomes character or line oriented accordingly. + * + * Character motions are marked as being inclusive or not. Most char. motions + * are inclusive, but some (e.g. 'w') are not. + * + * Generally speaking, every command in normal() should either clear any pending + * operator (with CLEAROP), or set the motion type variable. + */ + +/* + * Motion types + */ +#define MBAD (-1) /* 'bad' motion type marks unusable yank buf */ +#define MCHAR 0 +#define MLINE 1 + +static int mtype; /* type of the current cursor motion */ +static bool_t mincl; /* true if char motion is inclusive */ +static int ybtype = MBAD; +static int ybcrossline = FALSE; + +static LPtr startop; /* cursor pos. at start of operator */ + +/* + * Operators can have counts either before the operator, or between the + * operator and the following cursor motion as in: + * + * d3w or 3dw + * + * If a count is given before the operator, it is saved in opnum. If normal() is + * called with a pending operator, the count in opnum (if present) overrides + * any count that came later. + */ +static int opnum = 0; + +#define DEFAULT1(x) (((x) == 0) ? 1 : (x)) + +/* + * normal + * + * Execute a command in normal mode. + */ + +void +normal(c) + char c; +{ + char *p; + int n; + int nn; + bool_t flag = FALSE; + int type = 0; /* used in some operations to modify type */ + int dir = FORWARD; /* search direction */ + char nchar = NUL; + bool_t finish_op; + LPtr temp_Curschar; + + last_command = NUL; + /* + * If there is an operator pending, then the command we take this time + * will terminate it. Finish_op tells us to finish the operation before + * returning this time (unless the operation was cancelled). + */ + finish_op = (operator != NOP); + + /* + * If we're in the middle of an operator AND we had a count before the + * operator, then that count overrides the current value of Prenum. What + * this means effectively, is that commands like "3dw" get turned into + * "d3w" which makes things fall into place pretty neatly. + */ + if (finish_op) { + if (opnum != 0) + Prenum = opnum; + } else + opnum = 0; + + switch (c) { + + case K_HELP: + CLEAROP; + if (help()) + s_clear(); + break; + + case CTRL('L'): + CLEAROP; + s_clear(); + break; + + case CTRL('D'): + CLEAROP; + if (Prenum) + P(P_SS) = (Prenum > Rows - 1) ? Rows - 1 : Prenum; + scrollup((P(P_SS) < Rows) ? P(P_SS) : Rows - 1); + onedown((P(P_SS) < Rows) ? P(P_SS) : Rows - 1); + break; + + case CTRL('U'): + CLEAROP; + if (Prenum) + P(P_SS) = (Prenum > Rows - 1) ? Rows - 1 : Prenum; + scrolldown((P(P_SS) < Rows) ? P(P_SS) : Rows - 1); + oneup((P(P_SS) < Rows) ? P(P_SS) : Rows - 1); + break; + + case CTRL('F'): + CLEAROP; + if (nextline(Topchar) == NULL) { + beep(); + break; + } + Prenum = DEFAULT1(Prenum); + while (Prenum > 0) { + *Curschar = *prevline(Botchar); + *Topchar = *Curschar; + Topchar->index = 0; + Update_Botchar(); + Prenum--; + } + beginline(TRUE); + s_clear(); + break; + + case CTRL('B'): + CLEAROP; + if (prevline(Topchar) == NULL) { + beep(); + break; + } + Prenum = DEFAULT1(Prenum); + while (Prenum > 0) { + *Curschar = *Topchar; + n = Rows - 1; + { + LPtr *lp = Curschar; + int l = 0; + + while ((l < n) && (lp != NULL)) { + l += plines(lp->linep->s); + *Topchar = *lp; + lp = prevline(lp); + } + } + Topchar->index = 0; + Prenum--; + } + beginline(TRUE); + s_clear(); + break; + + case CTRL('E'): + CLEAROP; + scrollup(DEFAULT1(Prenum)); + if (LINEOF(Curschar) < LINEOF(Topchar)) + Curschar->linep = Topchar->linep; + break; + + case CTRL('Y'): + CLEAROP; + scrolldown(DEFAULT1(Prenum)); + Update_Botchar(); + if (LINEOF(Curschar) >= LINEOF(Botchar)) { + LPtr *lp; + + lp = prevline(Botchar); + if (lp == NULL) + lp = Topchar; + Curschar->linep = lp->linep; + } + break; + + case 'z': + CLEAROP; + S_CHECK_TOPCHAR_AND_BOTCHAR; + switch (vgetc()) { + case NL: /* put Curschar at top of screen */ + case CR: + *Topchar = *Curschar; + Topchar->index = 0; + break; + + case '.': /* put Curschar in middle of screen */ + n = Rows / 2; + goto dozcmd; + + case '-': /* put Curschar at bottom of screen */ + n = Rows - 1; + /* FALLTHROUGH */ + + dozcmd: + { + register LPtr *lp = Curschar; + register int l = 0; + + while ((l < n) && (lp != NULL)) { + l += plines(lp->linep->s); + *Topchar = *lp; + lp = prevline(lp); + } + } + Topchar->index = 0; + break; + + default: + beep(); + } + break; + + case CTRL('G'): + CLEAROP; + fileinfo(); + break; + + case 'G': + mtype = MLINE; + *Curschar = *gotoline(Prenum); + if (!UndoInProgress) { + beginline(TRUE); + S_CHECK_TOPCHAR_AND_BOTCHAR; + } + break; + + case 'H': + mtype = MLINE; + *Curschar = *Topchar; + for (n = Prenum; n && onedown(1); n--); + beginline(TRUE); + break; + + case 'M': + mtype = MLINE; + *Curschar = *Topchar; + for (n = 0; n < Rows / 2 && onedown(1); n++); + beginline(TRUE); + break; + + case 'L': + mtype = MLINE; + *Curschar = *prevline(Botchar); + for (n = Prenum; n && oneup(1); n--); + beginline(TRUE); + break; + + case 'l': + case K_RARROW: + case ' ': + mtype = MCHAR; + mincl = FALSE; + n = DEFAULT1(Prenum); + while (n--) { + if (!oneright()) { + if (operator != DELETE && operator != CHANGE) { + beep(); + } else { + if (lineempty(Curschar)) { + CLEAROP; + beep(); + } else { + mincl = TRUE; + } + } + break; + } + } + set_want_col = TRUE; + break; + + case 'h': + case K_LARROW: + case CTRL('H'): + mtype = MCHAR; + mincl = FALSE; + Prenum = DEFAULT1(Prenum); + n = Prenum; + while (n--) { + if (!oneleft()) { + if (operator != DELETE && operator != CHANGE) { + beep(); + } else if (Prenum == 1) { + CLEAROP; + beep(); + } + break; + } + } + set_want_col = TRUE; + break; + + case '-': + flag = TRUE; + /* FALLTHROUGH */ + + case 'k': + case K_UARROW: + case CTRL('P'): + mtype = MLINE; + if (!oneup(DEFAULT1(Prenum))) { + CLEAROP; + beep(); + } else if (flag) + beginline(TRUE); + break; + + case '+': + case CR: + case NL: + flag = TRUE; + /* FALLTHROUGH */ + + case 'j': + case K_DARROW: + case CTRL('N'): + mtype = MLINE; + if (!onedown(DEFAULT1(Prenum))) { + CLEAROP; + beep(); + } else if (flag) + beginline(TRUE); + break; + + /* + * This is a strange motion command that helps make operators more + * logical. It is actually implemented, but not documented in the + * real 'vi'. This motion command actually refers to "the current + * line". Commands like "dd" and "yy" are really an alternate form of + * "d_" and "y_". It does accept a count, so "d3_" works to delete 3 + * lines. + */ + case '_': +lineop: + mtype = MLINE; + if (!onedown(DEFAULT1(Prenum) - 1)) { + CLEAROP; + beep(); + } else + beginline(TRUE); + break; + + case '|': + mtype = MCHAR; + mincl = TRUE; + beginline(FALSE); + if (Prenum > 0) + coladvance(Curschar, Prenum - 1); + Curswant = Prenum - 1; + break; + + case CTRL(']'): /* :ta to current identifier */ + CLEAROP; + { + char ch; + LPtr save; + + save = *Curschar; + /* + * First back up to start of identifier. This doesn't match the + * real vi but I like it a little better and it shouldn't bother + * anyone. + */ + ch = gchar(Curschar); + while (IDCHAR(ch)) { + if (!oneleft()) + break; + ch = gchar(Curschar); + } + if (!IDCHAR(ch)) + oneright(); + + stuffReadbuff(":ta "); + /* + * Now grab the chars in the identifier + */ + ch = gchar(Curschar); + while (IDCHAR(ch)) { + stuffReadbuff(mkstr(ch)); + if (!oneright()) + break; + ch = gchar(Curschar); + } + stuffReadbuff("\n"); + + *Curschar = save; /* restore, in case of error */ + } + break; + + case '%': + S_CHECK_TOPCHAR_AND_BOTCHAR; + mtype = MCHAR; + mincl = TRUE; + { + LPtr *pos; + + if ((pos = showmatch()) == NULL) { + CLEAROP; + beep(); + } else { + setpcmark(); + *Curschar = *pos; + set_want_col = TRUE; + } + } + break; + + /* + * Word Motions + */ + + case 'B': + type = 1; + /* FALLTHROUGH */ + + case 'b': + mtype = MCHAR; + mincl = FALSE; + set_want_col = TRUE; + for (n = DEFAULT1(Prenum); n > 0; n--) { + LPtr *pos; + + if ((Curschar->linep->prev == Filetop->linep) + && (Curschar->index == 0)) { + CLEAROP; + beep(); + break; + } + pos = bck_word(Curschar, type); + if (pos == NULL) { + CLEAROP; + beep(); + *Curschar = *gotoline(1); /* goto top of file */ + } else + *Curschar = *pos; + } + break; + + case 'W': + type = 1; + /* FALLTHROUGH */ + + case 'w': + /* + * This is a little strange. To match what the real vi does, we + * effectively map 'cw' to 'ce', and 'cW' to 'cE'. This seems + * impolite at first, but it's really more what we mean when we say + * 'cw'. + */ + if (operator == CHANGE) + goto do_e_cmd; + + mtype = MCHAR; + mincl = FALSE; + set_want_col = TRUE; + for (n = DEFAULT1(Prenum); n > 0; n--) { + LPtr *pos; + + if ((pos = fwd_word(Curschar, type)) == NULL) { + CLEAROP; + beep(); + break; + } else + *Curschar = *pos; + } + if (operator == DELETE && DEFAULT1(Prenum) == 1) { + if (LINEOF(&startop) != LINEOF(Curschar)) { + *Curschar = startop; + while (oneright()); + mincl = TRUE; + } + } + break; + + case 'E': + type = 1; + /* FALLTHROUGH */ + + case 'e': +do_e_cmd: + mtype = MCHAR; + mincl = TRUE; + set_want_col = TRUE; + if (c == 'e' || c == 'E') { + if (inc(Curschar) == -1) { + CLEAROP; + beep(); + break; + } + } + for (n = DEFAULT1(Prenum); n > 0; n--) { + LPtr *pos; + + if ((pos = end_word(Curschar, type)) == NULL) { + CLEAROP; + beep(); + break; + } else + *Curschar = *pos; + } + break; + + case '$': + mtype = MCHAR; + mincl = TRUE; + while (oneright()); + Curswant = 999; /* so we stay at the end */ + break; + + case '^': + flag = TRUE; + /* FALLTHROUGH */ + + case '0': + mtype = MCHAR; + mincl = TRUE; + beginline(flag); + break; + + case 'R': + ResetBuffers(); + AppendToRedobuff("R"); + CLEAROP; + n = RowNumber(Curschar); + AppendPositionToUndobuff(Curschar->index, n); + AppendPositionToUndoUndobuff(Curschar->index, n); + AppendToUndoUndobuff("R"); + + last_command = 'R'; + startinsert(FALSE); + break; + + case 'A': + set_want_col = TRUE; + while (oneright()); + ResetBuffers(); + AppendToRedobuff("A"); + goto doAPPENDcmd; + + case 'a': + ResetBuffers(); + AppendToRedobuff("a"); + +doAPPENDcmd: + CLEAROP; + /* Works just like an 'i'nsert on the next character. */ + n = RowNumber(Curschar); + AppendPositionToUndoUndobuff(Curschar->index, n); + AppendToUndoUndobuff("a"); + + if (!lineempty(Curschar)) + inc(Curschar); + + n = RowNumber(Curschar); + AppendPositionToUndobuff(Curschar->index, n); + + startinsert(FALSE); + break; + + case 'I': + beginline(TRUE); + ResetBuffers(); + AppendToRedobuff("I"); + goto doINSERTcmd; + /* FALLTHROUGH */ + + case 'i': + case K_INSERT: + ResetBuffers(); + AppendToRedobuff("i"); + +doINSERTcmd: + CLEAROP; + + n = RowNumber(Curschar); + AppendPositionToUndobuff(Curschar->index, n); + AppendPositionToUndoUndobuff(Curschar->index, n); + AppendToUndoUndobuff("i"); + + startinsert(FALSE); + break; + + case 'o': + CLEAROP; + ResetBuffers(); + + n = RowNumber(Curschar); + AppendToRedobuff("o"); + AppendPositionToUndobuff(Curschar->index, n); + AppendPositionToUndoUndobuff(Curschar->index, n); + AppendToUndoUndobuff("o"); + + if (OpenForward(!RedrawingDisabled)) + startinsert(TRUE); + + last_command = 'o'; + break; + + case 'O': + CLEAROP; + ResetBuffers(); + + n = RowNumber(Curschar); + AppendToRedobuff("O"); + AppendPositionToUndobuff(Curschar->index, n); + AppendPositionToUndoUndobuff(Curschar->index, n); + AppendToUndoUndobuff("O"); + + if (OpenBackward(!RedrawingDisabled)) + startinsert(TRUE); + + last_command = 'O'; + break; + + case 'd': + if (operator == DELETE) /* handle 'dd' */ + goto lineop; + if (Prenum != 0) + opnum = Prenum; + startop = *Curschar; + operator = DELETE; + break; + + /* + * Some convenient abbreviations... + */ + + case 'x': + if (Prenum) + stuffnumReadbuff(Prenum); + stuffReadbuff("dl"); + break; + + case 'X': + if (Prenum) + stuffnumReadbuff(Prenum); + stuffReadbuff("dh"); + break; + + case 'D': + stuffReadbuff("d$"); + break; + + case 'Y': + if (Prenum) + stuffnumReadbuff(Prenum); + stuffReadbuff("yy"); + break; + + case 'C': + stuffReadbuff("c$"); + break; + + case 'c': + if (operator == CHANGE) { /* handle 'cc' */ + CLEAROP; + stuffReadbuff("0c$"); + break; + } + if (Prenum != 0) + opnum = Prenum; + startop = *Curschar; + operator = CHANGE; + break; + + case 'y': + if (operator == YANK) /* handle 'yy' */ + goto lineop; + if (Prenum != 0) + opnum = Prenum; + startop = *Curschar; + operator = YANK; + break; + + case ENABLE_REDRAWING: + RedrawingDisabled = FALSE; + S_NOT_VALID; + break; + + case 'p': + if (Yankbuffptr != NULL) { + doput(FORWARD); + + stuffReadbuff(ENABLE_REDRAWING_STR); + RedrawingDisabled = TRUE; + } else + beep(); + break; + + case 'P': + if (Yankbuffptr != NULL) { + doput(BACKWARD); + + stuffReadbuff(ENABLE_REDRAWING_STR); + RedrawingDisabled = TRUE; + } else + beep(); + break; + + case '>': + if (operator == RSHIFT) /* handle >> */ + goto lineop; + if (operator == LSHIFT) { + CLEAROP; + beep(); + break; + } + if (Prenum != 0) + opnum = Prenum; + startop = *Curschar; /* save current position */ + operator = RSHIFT; + break; + + case '<': + if (operator == LSHIFT) /* handle << */ + goto lineop; + if (operator == RSHIFT) { + CLEAROP; + beep(); + break; + } + if (Prenum != 0) + opnum = Prenum; + startop = *Curschar; /* save current position */ + operator = LSHIFT; + break; + + case 's': /* substitute characters */ + if (Prenum) + stuffnumReadbuff(Prenum); + stuffReadbuff("cl"); + break; + + case '?': + case '/': + case ':': + CLEAROP; + readcmdline(c, (char *) NULL); + break; + + case 'n': + mtype = MCHAR; + mincl = FALSE; + set_want_col = TRUE; + if (!repsearch(0)) { + CLEAROP; + beep(); + } + break; + + case 'N': + mtype = MCHAR; + mincl = FALSE; + set_want_col = TRUE; + if (!repsearch(1)) { + CLEAROP; + beep(); + } + break; + + /* + * Character searches + */ + case 'T': + dir = BACKWARD; + /* FALLTHROUGH */ + + case 't': + type = 1; + goto docsearch; + + case 'F': + dir = BACKWARD; + /* FALLTHROUGH */ + + case 'f': +docsearch: + mtype = MCHAR; + mincl = TRUE; + set_want_col = TRUE; + if ((nchar = vgetc()) == ESC) /* search char */ + break; + if (!searchc(nchar, dir, type)) { + CLEAROP; + beep(); + } + break; + + case ',': + flag = 1; + /* FALLTHROUGH */ + + case ';': + mtype = MCHAR; + mincl = TRUE; + set_want_col = TRUE; + if (!crepsearch(flag)) { + CLEAROP; + beep(); + } + break; + + /* + * Function searches + */ + + case '[': + dir = BACKWARD; + /* FALLTHROUGH */ + + case ']': + mtype = MLINE; + set_want_col = TRUE; + if (vgetc() != c) { + CLEAROP; + beep(); + break; + } + if (!findfunc(dir)) { + CLEAROP; + beep(); + } + break; + + /* + * Marks + */ + + case 'm': + CLEAROP; + if (!setmark(vgetc())) + beep(); + break; + + case '\'': + flag = TRUE; + /* FALLTHROUGH */ + + case '`': + S_CHECK_TOPCHAR_AND_BOTCHAR; + { + LPtr mtmp; + LPtr *mark = getmark(vgetc()); + + if (mark == NULL) { + CLEAROP; + beep(); + } else { + mtmp = *mark; + setpcmark(); + *Curschar = mtmp; + if (flag) + beginline(TRUE); + } + mtype = flag ? MLINE : MCHAR; + mincl = TRUE; /* ignored if not MCHAR */ + set_want_col = TRUE; + } + break; + + case 'r': + CLEAROP; + if (lineempty(Curschar)) { /* Nothing to replace */ + beep(); + break; + } + nosuspend(); + nchar = vgetc(); + dosuspend(); + if (nchar == ESC) break; + + Prenum = DEFAULT1(Prenum); + n = strlen(Curschar->linep->s) - Curschar->index; + if (n < Prenum) { + beep(); + break; + } + ResetBuffers(); + + nn = RowNumber(Curschar); + AppendPositionToUndobuff(Curschar->index, nn); + AppendPositionToUndoUndobuff(Curschar->index, nn); + + while (Prenum > 0) { + AppendToRedobuff("r"); + AppendToRedobuff(mkstr(nchar)); + + AppendToUndobuff("r"); + AppendToUndobuff(mkstr(gchar(Curschar))); + + AppendToUndoUndobuff("r"); + AppendToUndoUndobuff(mkstr(nchar)); + + pchar(Curschar, nchar); /* Change current character. */ + + if (Prenum > 1) { + oneright(); + AppendToRedobuff("l"); + AppendToUndobuff("l"); + AppendToUndoUndobuff("l"); + } + Prenum--; + } + + CHANGED; + S_LINE_NOT_VALID; + break; + + case '~': /* swap case */ + CLEAROP; + if (lineempty(Curschar)) { + beep(); + break; + } + ResetBuffers(); + + n = RowNumber(Curschar); + AppendPositionToUndobuff(Curschar->index, n); + AppendPositionToUndoUndobuff(Curschar->index, n); + + Prenum = DEFAULT1(Prenum); + if (Prenum > 0) { + AppendNumberToRedobuff(Prenum); + AppendNumberToUndobuff(Prenum); + AppendNumberToUndoUndobuff(Prenum); + } + AppendToRedobuff("~"); + AppendToUndobuff("~"); + AppendToUndoUndobuff("~"); + + while (Prenum > 0) { + c = gchar(Curschar); + if (isalpha(c)) { + if (islower(c)) + pchar(Curschar, toupper(c)); + else + pchar(Curschar, tolower(c)); + } + if (!oneright()) + break; + Prenum--; + } + + CHANGED; + S_LINE_NOT_VALID; + break; + + case UNDO_SHIFTJ: + CLEAROP; + if (UndoInProgress) { + (void) dojoin(FALSE, FALSE); + break; + } + goto doSHIFTJcommand; + + case 'J': + CLEAROP; +doSHIFTJcommand: + if (nextline(Curschar) == NULL) { /* on last line */ + beep(); + break; + } + ResetBuffers(); + + temp_Curschar = *Curschar; + nn = strlen(Curschar->linep->s); + if (nn < 0) + nn = 0; + n = RowNumber(&temp_Curschar); + + AppendToRedobuff("J"); + + AppendPositionToUndobuff(nn, n); + + AppendPositionToUndoUndobuff(0, n); + AppendToUndoUndobuff("J"); + + if (linewhite(nextline(Curschar))) { + AppendToUndobuff("a\n"); + if (!dojoin(FALSE, TRUE)) { + beep(); + break; + } + } else if (lineempty(Curschar)) { + AppendToUndobuff("i\n"); + if (!dojoin(FALSE, TRUE)) { + beep(); + break; + } + } else { + AppendToUndobuff("dli\n"); + if (!dojoin(TRUE, TRUE)) { + beep(); + break; + } + } + + AppendToUndobuff(ESC_STR); + AppendPositionToUndobuff(nn, n); + break; + + case K_CGRAVE: /* shorthand command */ + CLEAROP; + stuffReadbuff(":e #\n"); + break; + + case 'Z': /* write, if changed, and exit */ + if (vgetc() != 'Z') { + beep(); + break; + } + if (Changed) { + if (Filename != NULL) { + if (!writeit(Filename, (LPtr *) NULL, (LPtr *) NULL)) + return; + } else { + emsg("No output file"); + return; + } + } + getout(0); + break; + + case '.': + CLEAROP; + if (Redobuffptr != NULL) { + stuffReadbuff(Redobuff); + + stuffReadbuff(ENABLE_REDRAWING_STR); + RedrawingDisabled = TRUE; + } else + beep(); + break; + + case 'u': + case K_UNDO: + CLEAROP; + if (UndoInProgress) { + p = UndoUndobuff; + UndoUndobuff = Undobuff; + Undobuff = p; + p = UndoUndobuffptr; + UndoUndobuffptr = Undobuffptr; + Undobuffptr = p; + + UndoInProgress = FALSE; + RedrawingDisabled = FALSE; + S_NOT_VALID; + } else if (Undobuffptr != NULL) { + stuffReadbuff(Undobuff); + stuffReadbuff("u"); + UndoInProgress = TRUE; + RedrawingDisabled = TRUE; + } else { + beep(); + } + break; + + default: + CLEAROP; + beep(); + break; + } + + /* + * If an operation is pending, handle it... + */ + if (finish_op) { /* we just finished an operator */ + if (operator == NOP) /* ... but it was cancelled */ + return; + + switch (operator) { + + case LSHIFT: + case RSHIFT: + ResetBuffers(); + + n = RowNumber(&startop); + AppendPositionToUndobuff(startop.index, n); + AppendPositionToUndoUndobuff(startop.index, n); + if (Prenum != 0) { + AppendNumberToRedobuff(Prenum); + AppendNumberToUndobuff(Prenum); + AppendNumberToUndoUndobuff(Prenum); + } + AppendToRedobuff((operator == LSHIFT) ? "<" : ">"); + AppendToUndobuff((operator == LSHIFT) ? ">" : "<"); + AppendToUndoUndobuff((operator == LSHIFT) ? "<" : ">"); + AppendToRedobuff(mkstr(c)); + if (c == '>') + AppendToUndobuff("<"); + else if (c == '<') + AppendToUndobuff(">"); + else + AppendToUndobuff(mkstr(c)); + AppendToUndoUndobuff(mkstr(c)); + + doshift(operator); + break; + + case DELETE: + ResetBuffers(); + + n = RowNumber(&startop); + AppendPositionToUndoUndobuff(startop.index, n); + + if (lt(&startop, Curschar)) + temp_Curschar = startop; + else + temp_Curschar = *Curschar; + n = RowNumber(&temp_Curschar); + if (Prenum != 0) { + AppendNumberToRedobuff(Prenum); + AppendNumberToUndoUndobuff(Prenum); + } + AppendToRedobuff("d"); + AppendToUndoUndobuff("d"); + AppendToRedobuff(mkstr(c)); + AppendToUndoUndobuff(mkstr(c)); + if (nchar != NUL) { + AppendToRedobuff(mkstr(nchar)); + AppendToUndoUndobuff(mkstr(nchar)); + } + AppendPositionToUndobuff(temp_Curschar.index, n); + + dodelete(!UndoInProgress, !UndoInProgress, !UndoInProgress); + + AppendPositionToUndobuff(temp_Curschar.index, n); + break; + + case YANK: + ResetBuffers(); /* no redo/undo/(undo of undo) on yank... */ + if (!doyank()) + msg("yank buffer exceeded"); + if (!ybcrossline) + *Curschar = startop; + else if (lt(&startop, Curschar)) + *Curschar = startop; + break; + + case CHANGE: + ResetBuffers(); + + n = RowNumber(&startop); + AppendPositionToUndoUndobuff(startop.index, n); + + if (lt(&startop, Curschar)) + temp_Curschar = startop; + else + temp_Curschar = *Curschar; + n = RowNumber(&temp_Curschar); + if (mtype == MLINE) + AppendPositionToUndobuff(0, n); + else + AppendPositionToUndobuff(temp_Curschar.index, n); + + if (Prenum != 0) { + AppendNumberToRedobuff(Prenum); + AppendNumberToUndoUndobuff(Prenum); + } + AppendToRedobuff("c"); + AppendToUndoUndobuff("c"); + AppendToRedobuff(mkstr(c)); + AppendToUndoUndobuff(mkstr(c)); + if (nchar != NUL) { + AppendToRedobuff(mkstr(nchar)); + AppendToUndoUndobuff(mkstr(nchar)); + } + dochange(); + + last_command = 'c'; + break; + + default: + beep(); + } + operator = NOP; + } +} + +/* + * tabinout(shift_type, num) + * + * If shift_type == RSHIFT, add a tab to the begining of the next num lines; + * otherwise delete a tab from the beginning of the next num lines. + */ +static void +tabinout(shift_type, num) + int shift_type; + int num; +{ + LPtr *p; + + beginline(FALSE); + while (num-- > 0) { + beginline(FALSE); + if (shift_type == RSHIFT) + inschar(TAB); + else { + if (gchar(Curschar) == TAB) + delchar(TRUE, FALSE); + } + if (num > 0) { + if ((p = nextline(Curschar)) != NULL) + *Curschar = *p; + else + break; + } + } +} + +/* + * doshift - handle a shift operation + */ +static void +doshift(op) + int op; +{ + LPtr top, bot; + int nlines; + + top = startop; + bot = *Curschar; + + if (lt(&bot, &top)) + pswap(top, bot); + + nlines = cntllines(&top, &bot); + *Curschar = top; + tabinout(op, nlines); + + /* + * The cursor position afterward is the prior of the two positions. + */ + *Curschar = top; + + /* + * If we were on the last char of a line that got shifted left, then move + * left one so we aren't beyond the end of the line + */ + if (gchar(Curschar) == NUL && Curschar->index > 0) + Curschar->index--; + + if (op == RSHIFT) + oneright(); + else + oneleft(); + + S_NOT_VALID; + + if (nlines > P(P_RP)) + smsg("%d lines %ced", nlines, (op == RSHIFT) ? '>' : '<'); +} + +/* + * dodelete - handle a delete operation + */ + +static void +dodelete(redraw, setup_for_undo, try_to_yank) + bool_t redraw; + bool_t setup_for_undo; + bool_t try_to_yank; +{ + LPtr top, bot; + int nlines; + int n; + + /* + * Do a yank of whatever we're about to delete. If there's too much stuff + * to fit in the yank buffer, then get a confirmation before doing the + * delete. This is crude, but simple. And it avoids doing a delete of + * something we can't put back if we want. + */ + if (try_to_yank) { + if (!doyank()) { + msg("yank buffer exceeded: press to confirm"); + if (vgetc() != 'y') { + emsg("delete aborted"); + *Curschar = startop; + return; + } + } + } + top = startop; + bot = *Curschar; + + if (lt(&bot, &top)) + pswap(top, bot); + + *Curschar = top; + nlines = cntllines(&top, &bot); + + if (mtype == MLINE) { + if (operator == CHANGE) { + last_command_char = 'a'; + delline(nlines - 1); + Curschar->index = 0; + while (delchar(TRUE, FALSE)); + } else { + if ((Filetop->linep->next == top.linep) && + (bot.linep->next == Fileend->linep)) + last_command_char = 'a'; + else if (bot.linep->next == Fileend->linep) + last_command_char = 'o'; + else + last_command_char = 'O'; + if (setup_for_undo) + AppendToUndobuff(mkstr(last_command_char)); + delline(nlines); + } + } else if (top.linep == bot.linep) { /* del. within line */ + if (!mincl) + dec(&bot); + + if (endofline(&bot)) + last_command_char = 'a'; + else + last_command_char = 'i'; + if (setup_for_undo) + AppendToUndobuff(mkstr(last_command_char)); + n = bot.index - top.index + 1; + while (n--) + if (!delchar(TRUE, FALSE)) + break; + } else { /* del. between lines */ + if (endofline(&top)) { + if (nextline(&top)) { + if (lineempty(nextline(&top))) + last_command_char = 'a'; + else + last_command_char = 'i'; + } else { + last_command_char = 'a'; + } + } else { + last_command_char = 'i'; + } + if (setup_for_undo) + AppendToUndobuff(mkstr(last_command_char)); + + n = Curschar->index; + while (Curschar->index >= n) + if (!delchar(TRUE, FALSE)) + break; + + top = *Curschar; + *Curschar = *nextline(Curschar); + delline(nlines - 2); + Curschar->index = 0; + n = bot.index; + if (!mincl) + n--; + + while (n-- >= 0) + if (!delchar(TRUE, FALSE)) + break; + *Curschar = top; + dojoin(FALSE, FALSE); + } + + if (mtype == MCHAR && nlines == 1 && redraw && P(P_NU) == FALSE) { + S_LINE_NOT_VALID; + } else { + S_NOT_VALID; + } + + if (nlines > P(P_RP)) + smsg("%d fewer lines", nlines); + + if (setup_for_undo) { + AppendToUndobuff(Yankbuff); + AppendToUndobuff(ESC_STR); + } +} + + +/* + * dochange - handle a change operation + */ +static void +dochange() +{ + LPtr l; + + if (lt(Curschar, &startop)) + l = *Curschar; + else + l = startop; + + dodelete(FALSE, FALSE, !UndoInProgress); + + if ((l.index > Curschar->index) && !lineempty(Curschar)) + inc(Curschar); + + startinsert(FALSE); +} + +static bool_t +doyank() +{ + LPtr top, bot; + char *ybend = &Yankbuff[YANKSIZE - 1]; + int nlines; + + Yankbuffptr = Yankbuff; + + top = startop; + bot = *Curschar; + + if (lt(&bot, &top)) + pswap(top, bot); + + nlines = cntllines(&top, &bot); + + ybtype = mtype; /* set the yank buffer type */ + ybcrossline = FALSE; + if (LINEOF(&top) != LINEOF(&bot)) + ybcrossline = TRUE; + + if (mtype == MLINE) { + ybcrossline = TRUE; + top.index = 0; + bot.index = strlen(bot.linep->s); + /* + * The following statement checks for the special case of yanking a + * blank line at the beginning of the file. If not handled right, we + * yank an extra char (a newline). + */ + if (dec(&bot) == -1) { + *Yankbuff = NUL; + Yankbuffptr = NULL; + return TRUE; + } + } else { + if (!mincl) + if (!equal(&top, &bot)) + dec(&bot); + } + + for (; ltoreq(&top, &bot); inc(&top)) { + *Yankbuffptr = (gchar(&top) != NUL) ? gchar(&top) : NL; + Yankbuffptr++; + if (Yankbuffptr >= ybend) { + *Yankbuffptr = NUL; + msg("yank too big for buffer"); + ybtype = MBAD; + return FALSE; + } + } + + *Yankbuffptr = NUL; + + if (operator == YANK) + if (nlines > P(P_RP)) + smsg("%d lines yanked", nlines); + + return TRUE; +} + +static void +doput(dir) + int dir; +{ + bool_t type; + + if (ybtype == MBAD) { + beep(); + return; + } + type = (ybtype == MCHAR); + if (dir == FORWARD) + stuffReadbuff(type ? "a" : "o"); + else + stuffReadbuff(type ? "i" : "O"); + + stuffReadbuff(Yankbuff); + stuffReadbuff(ESC_STR); + + if (ybtype != MCHAR) + stuffReadbuff("^"); +} + +static void +startinsert(startln) + int startln; /* if set, insert at start of line */ +{ +extern void nosuspend(void); +extern void dosuspend(void); + + nosuspend(); /* turn off the suspend character in insert mode */ + *Insstart = *Curschar; + if (startln) { + Insstart->index = 0; + } + *Insbuff = NUL; + Insbuffptr = NULL; + + State = INSERT; + if (P(P_MO)) { + if (last_command == 'R') + msg("Replace Mode"); + else + msg("Insert Mode"); + } +} + +void +ResetBuffers() +{ + if (UndoInProgress) + return; + + *Redobuff = NUL; + Redobuffptr = NULL; + + *Undobuff = NUL; + Undobuffptr = NULL; + + *UndoUndobuff = NUL; + UndoUndobuffptr = NULL; +} + +void +AppendToInsbuff(s) + char *s; +{ + if (UndoInProgress) + return; + + if (Insbuffptr == NULL) { + if ((strlen(s) + 1) < INSERT_SIZE) { + strcpy(Insbuff, s); + Insbuffptr = Insbuff; + return; + } + } else if ((strlen(Insbuff) + strlen(s) + 1) < INSERT_SIZE) { + strcat(Insbuff, s); + return; + } + emsg("Couldn't AppendToInsbuff() - clearing Insbuff\n"); + *Insbuff = NUL; + Insbuffptr = NULL; +} + +void +AppendToRedobuff(s) + char *s; +{ + if (UndoInProgress) + return; + + if (Redobuffptr == (char *) (-2)) { + return; + } + if (Redobuffptr == (char *) (-1)) { + Redobuffptr = (char *) (-2); + emsg("Couldn't AppendToRedobuff() - Redobuff corrupt"); + return; + } + if (Redobuffptr == NULL) { + if ((strlen(s) + 1) < REDO_UNDO_SIZE) { + strcpy(Redobuff, s); + Redobuffptr = Redobuff; + return; + } + } else if ((strlen(Redobuff) + strlen(s) + 1) < REDO_UNDO_SIZE) { + strcat(Redobuff, s); + return; + } + emsg("Couldn't AppendToRedobuff() - clearing Redobuff"); + *Redobuff = NUL; + Redobuffptr = (char *) (-1); +} + +void +AppendNumberToRedobuff(n) + int n; +{ + char buf[32]; + + if (UndoInProgress) + return; + + sprintf(buf, "%d", n); + AppendToRedobuff(buf); +} + +void +AppendToUndobuff(s) + char *s; +{ + if (UndoInProgress) + return; + + if (Undobuffptr == (char *) (-2)) { + return; + } + if (Undobuffptr == (char *) (-1)) { + Undobuffptr = (char *) (-2); + emsg("Couldn't AppendToUndobuff() - Undobuff corrupt"); + return; + } + if (Undobuffptr == NULL) { + if ((strlen(s) + 1) < REDO_UNDO_SIZE) { + strcpy(Undobuff, s); + Undobuffptr = Undobuff; + return; + } + } else if ((strlen(Undobuff) + strlen(s) + 1) < REDO_UNDO_SIZE) { + strcat(Undobuff, s); + return; + } + emsg("Couldn't AppendToUndobuff() - clearing Undobuff"); + *Undobuff = NUL; + Undobuffptr = (char *) (-1); +} + +void +AppendNumberToUndobuff(n) + int n; +{ + char buf[32]; + + if (UndoInProgress) + return; + + sprintf(buf, "%d", n); + AppendToUndobuff(buf); +} + +static bool_t +dojoin(leading_space, strip_leading_spaces) + bool_t leading_space; + bool_t strip_leading_spaces; +{ +} diff --git a/bin/vi/bugstevie.h b/bin/vi/bugstevie.h new file mode 100644 index 0000000..e824cdc --- /dev/null +++ b/bin/vi/bugstevie.h @@ -0,0 +1,346 @@ +/* + * STEVIE - Simply Try this Editor for VI Enthusiasts + * + * Code Contributions By : Tim Thompson twitch!tjt + * Tony Andrews onecom!wldrdg!tony + * G. R. (Fred) Walter watmath!grwalter + */ + +#ifdef __ORCAC__ +#ifndef MAINSEG +#pragma noroot +#endif +#endif + +#include "env.h" + +#include +#ifndef ATARI +# ifndef UNIX +# include +# endif +#endif +#include +#ifndef MWC +# include +#endif + +#include "ascii.h" +#include "keymap.h" +#include "param.h" +/*#include "term.h"*/ +#include "macros.h" + +#ifdef AMIGA +/* + * This is used to disable break signal handling. + */ +#include +#endif + +extern char *strchr(); + +#define NORMAL 0 +#define CMDLINE 1 +#define INSERT 2 +#define REPLACE 3 +#define APPEND 4 +#define UNDO 5 +#define REDO 6 +#define PUT 7 +#define FORWARD 8 +#define BACKWARD 9 +#define VALID 10 +#define NOT_VALID 11 +#define VALID_TO_CURSCHAR 12 +#define UPDATE_CURSOR 13 +#define UPDATE_ALL 14 + +/* + * Boolean type definition and constants + */ +typedef int bool_t; + +#ifndef TRUE +# define FALSE (0) +# define TRUE (1) +#endif +#define SORTOF (2) +#define YES TRUE +#define NO FALSE +#define MAYBE SORTOF + +/* + * Maximum screen dimensions + */ +#define MAX_COLUMNS 140L + +/* + * Buffer sizes + */ +#define CMDBUFFSIZE MAX_COLUMNS /* size of the command processing buffer */ + +#define LSIZE 512 /* max. size of a line in the tags file */ + +#define IOSIZE (1024+1) /* file i/o and sprintf buffer size */ + +#define YANKSIZE 5200 /* yank buffer size */ +#define INSERT_SIZE 5300 /* insert, redo and undo buffer size must be + * bigger than YANKSIZE */ +#define REDO_UNDO_SIZE 5400 /* redo, undo and (undo an undo) buffer size + * must be bigger than INSERT_SIZE */ +#define READSIZE 5500 /* read buffer size must be bigger than + * YANKSIZE and REDO_UNDO_SIZE */ + +/* + * SLOP is the amount of extra space we get for text on a line during editing + * operations that need more space. This keeps us from calling alloc every + * time we get a character during insert mode. No extra space is allocated + * when the file is initially read. + */ +#define SLOP 10 + +/* + * LINEINC is the gap we leave between the artificial line numbers. This + * helps to avoid renumbering all the lines every time a new line is + * inserted. + * + * Since line numbers are stored in longs (32 bits), a LINEINC of 10000 + * lets us have > 200,000 lines and we won't have to renumber very often. + */ +#define LINEINC 10000 + +#define CHANGED Changed = TRUE +#define UNCHANGED Changed = FALSE + +#define S_NOT_VALID NumLineSizes = -1 + +#define S_LINE_NOT_VALID LineNotValid = TRUE + +#define S_CHECK_TOPCHAR_AND_BOTCHAR CheckTopcharAndBotchar = TRUE + +#define S_MUST_UPDATE_BOTCHAR MustUpdateBotchar = TRUE + +#define S_VALID_TO_CURSCHAR ValidToCurschar = TRUE + +struct line { + struct line *next; /* next line */ + struct line *prev; /* previous line */ + char *s; /* text for this line */ + int size; /* actual size of space at 's' */ + unsigned long num; /* line "number" */ +}; + +#define LINEOF(x) ((x)->linep->num) + +struct lptr { + struct line *linep; /* line we're referencing */ + int index; /* position within that line */ +}; + +typedef struct line LINE; +typedef struct lptr LPtr; + +struct charinfo { + char ch_size; + char *ch_str; +}; + +extern struct charinfo chars[]; + +#ifdef AMIGA +extern int Aux_Device; +#endif +extern int State; +extern int Rows; +extern int Columns; +extern int CheckTopcharAndBotchar; +extern int MustUpdateBotchar; +extern int ValidToCurschar; +extern int LineNotValid; +extern int NumLineSizes; +extern LINE **LinePointers; +extern char *LineSizes; +extern char *Filename; +extern LPtr *Filemem; +extern LPtr *Filetop; +extern LPtr *Fileend; +extern LPtr *Topchar; +extern LPtr *Botchar; +extern LPtr *Curschar; +extern LPtr *Insstart; +extern int Cursrow; +extern int Curscol; +extern int Cursvcol; +extern int Curswant; +extern bool_t set_want_col; +extern int Prenum; +extern bool_t Changed; +extern bool_t RedrawingDisabled; +extern bool_t UndoInProgress; +extern char *IObuff; +extern char *Insbuffptr; +extern char *Insbuff; +extern char *Readbuffptr; +extern char *Readbuff; +extern char *Redobuffptr; +extern char *Redobuff; +extern char *Undobuffptr; +extern char *Undobuff; +extern char *UndoUndobuffptr; +extern char *UndoUndobuff; +extern char *Yankbuffptr; +extern char *Yankbuff; +extern char last_command; +extern char last_command_char; + +extern char *strcpy(); + +/* alloc.c */ +char *alloc(); +char *strsave(); +void screenalloc(); +void filealloc(); +void freeall(); +LINE *newline(); +bool_t canincrease(); + +/* cmdline.c */ +void readcmdline(); +void dotag(); +void msg(); +void emsg(); +void smsg(); +void gotocmdline(); +void wait_return(); + +/* dec.c */ +int dec(); + +/* edit.c */ +void edit(); +void insertchar(); +void getout(); +void scrollup(); +void scrolldown(); +void beginline(); +bool_t oneright(); +bool_t oneleft(); +bool_t oneup(); +bool_t onedown(); + +/* fileio.c */ +void filemess(); +void renum(); +bool_t readfile(); +bool_t writeit(); + +/* s_io.c */ +void s_cursor_off(); +void s_cursor_on(); +void s_clear(); +void s_refresh(); +void NotValidFromCurschar(); +void Update_Botchar(); + +/* help.c */ +bool_t help(); + +/* inc.c */ +int inc(); + +/* linefunc.c */ +LPtr *nextline(); +LPtr *prevline(); +void coladvance(); + +/* main.c */ +void stuffReadbuff(); +void stuffnumReadbuff(); +char vgetc(); +char vpeekc(); + +/* mark.c */ +void setpcmark(); +void clrall(); +void clrmark(); +bool_t setmark(); +LPtr *getmark(); + +/* misccmds.c */ +bool_t OpenForward(); +bool_t OpenBackward(); +void fileinfo(); +void inschar(); +void insstr(); +void delline(); +bool_t delchar(); +int cntllines(); +int plines(); +LPtr *gotoline(); + +/* normal.c */ +void normal(); +void ResetBuffers(); +void AppendToInsbuff(); +void AppendToRedobuff(); +void AppendNumberToRedobuff(); +void AppendToUndobuff(); +void AppendNumberToUndobuff(); +void AppendPositionToUndobuff(); +void AppendToUndoUndobuff(); +void AppendNumberToUndoUndobuff(); +void AppendPositionToUndoUndobuff(); +bool_t linewhite(); + +/* mk.c */ +char *mkstr(); +char *mkline(); + +/* param.c */ +void doset(); + +/* screen.c */ +void cursupdate(); + +/* search.c */ +void doglob(); +void dosub(); +void searchagain(); +bool_t dosearch(); +bool_t repsearch(); +bool_t searchc(); +bool_t crepsearch(); +bool_t findfunc(); +LPtr *showmatch(); +LPtr *fwd_word(); +LPtr *bck_word(); +LPtr *end_word(); + +/* format_l.c */ +char *format_line(); + +/* + * Machine-dependent routines. + */ +#ifdef AMIGA +# include "amiga.h" +#endif +#ifdef BSD +# include "bsd.h" +#endif +#ifdef UNIX +# include "unix.h" +#endif +#ifdef TOS +# include "tos.h" +#endif +#ifdef OS2 +# include "os2.h" +#endif +#ifdef DOS +# include "dos.h" +#endif +#ifdef GSOS +# include "gsos.h" +#endif diff --git a/bin/vi/charset.c b/bin/vi/charset.c new file mode 100644 index 0000000..33e5b89 --- /dev/null +++ b/bin/vi/charset.c @@ -0,0 +1,374 @@ +/* + * STEVIE - Simply Try this Editor for VI Enthusiasts + * + * Code Contributions By : Tim Thompson twitch!tjt + * Tony Andrews onecom!wldrdg!tony + * G. R. (Fred) Walter watmath!grwalter + */ + +#include "stevie.h" + +/* + * This file shows how to display characters on the screen. This is approach + * is something of an overkill. It's a remnant from the original code that + * isn't worth messing with for now. TABS are special-cased depending on the + * value of the "list" parameter. + */ + +struct charinfo chars[] = { + /* 0 */ 0, 0, /* both must be zero */ + /* 1 */ 2, "^A", + /* 2 */ 2, "^B", + /* 3 */ 2, "^C", + /* 4 */ 2, "^D", + /* 5 */ 2, "^E", + /* 6 */ 2, "^F", + /* 7 */ 2, "^G", + /* 8 */ 2, "^H", + /* 9 */ 2, "^I", + /* 10 */ 7, "[ERROR]", /* shouldn't happen */ + /* 11 */ 2, "^K", + /* 12 */ 2, "^L", + /* 13 */ 2, "^M", + /* 14 */ 2, "^N", + /* 15 */ 2, "^O", + /* 16 */ 2, "^P", + /* 17 */ 2, "^Q", + /* 18 */ 2, "^R", + /* 19 */ 2, "^S", + /* 20 */ 2, "^T", + /* 21 */ 2, "^U", + /* 22 */ 2, "^V", + /* 23 */ 2, "^W", + /* 24 */ 2, "^X", + /* 25 */ 2, "^Y", + /* 26 */ 2, "^Z", + /* 27 */ 2, "^[", + /* 28 */ 2, "^\\", + /* 29 */ 2, "^]", + /* 30 */ 2, "^^", + /* 31 */ 2, "^_", + /* 32 */ 1, " ", + /* 33 */ 1, "!", + /* 34 */ 1, "\"", + /* 35 */ 1, "#", + /* 36 */ 1, "$", + /* 37 */ 1, "%", + /* 38 */ 1, "&", + /* 39 */ 1, "'", + /* 40 */ 1, "(", + /* 41 */ 1, ")", + /* 42 */ 1, "*", + /* 43 */ 1, "+", + /* 44 */ 1, ",", + /* 45 */ 1, "-", + /* 46 */ 1, ".", + /* 47 */ 1, "/", + /* 48 */ 1, "0", + /* 49 */ 1, "1", + /* 50 */ 1, "2", + /* 51 */ 1, "3", + /* 52 */ 1, "4", + /* 53 */ 1, "5", + /* 54 */ 1, "6", + /* 55 */ 1, "7", + /* 56 */ 1, "8", + /* 57 */ 1, "9", + /* 58 */ 1, ":", + /* 59 */ 1, ";", + /* 60 */ 1, "<", + /* 61 */ 1, "=", + /* 62 */ 1, ">", + /* 63 */ 1, "?", + /* 64 */ 1, "@", + /* 65 */ 1, "A", + /* 66 */ 1, "B", + /* 67 */ 1, "C", + /* 68 */ 1, "D", + /* 69 */ 1, "E", + /* 70 */ 1, "F", + /* 71 */ 1, "G", + /* 72 */ 1, "H", + /* 73 */ 1, "I", + /* 74 */ 1, "J", + /* 75 */ 1, "K", + /* 76 */ 1, "L", + /* 77 */ 1, "M", + /* 78 */ 1, "N", + /* 79 */ 1, "O", + /* 80 */ 1, "P", + /* 81 */ 1, "Q", + /* 82 */ 1, "R", + /* 83 */ 1, "S", + /* 84 */ 1, "T", + /* 85 */ 1, "U", + /* 86 */ 1, "V", + /* 87 */ 1, "W", + /* 88 */ 1, "X", + /* 89 */ 1, "Y", + /* 90 */ 1, "Z", + /* 91 */ 1, "[", + /* 92 */ 1, "\\", + /* 93 */ 1, "]", + /* 94 */ 1, "^", + /* 95 */ 1, "_", + /* 96 */ 1, "`", + /* 97 */ 1, "a", + /* 98 */ 1, "b", + /* 99 */ 1, "c", + /* 100 */ 1, "d", + /* 101 */ 1, "e", + /* 102 */ 1, "f", + /* 103 */ 1, "g", + /* 104 */ 1, "h", + /* 105 */ 1, "i", + /* 106 */ 1, "j", + /* 107 */ 1, "k", + /* 108 */ 1, "l", + /* 109 */ 1, "m", + /* 110 */ 1, "n", + /* 111 */ 1, "o", + /* 112 */ 1, "p", + /* 113 */ 1, "q", + /* 114 */ 1, "r", + /* 115 */ 1, "s", + /* 116 */ 1, "t", + /* 117 */ 1, "u", + /* 118 */ 1, "v", + /* 119 */ 1, "w", + /* 120 */ 1, "x", + /* 121 */ 1, "y", + /* 122 */ 1, "z", + /* 123 */ 1, "{", + /* 124 */ 1, "|", + /* 125 */ 1, "}", + /* 126 */ 1, "~", + /* 127 */ 2, "^?", + /* 128 */ 5, "[128]", + /* 129 */ 5, "[129]", + /* 130 */ 5, "[130]", + /* 131 */ 5, "[131]", + /* 132 */ 5, "[132]", + /* 133 */ 5, "[133]", + /* 134 */ 5, "[134]", + /* 135 */ 5, "[135]", + /* 136 */ 5, "[136]", + /* 137 */ 5, "[137]", + /* 138 */ 5, "[138]", + /* 139 */ 5, "[139]", + /* 140 */ 5, "[140]", + /* 141 */ 5, "[141]", + /* 142 */ 5, "[142]", + /* 143 */ 5, "[143]", + /* 144 */ 5, "[144]", + /* 145 */ 5, "[145]", + /* 146 */ 5, "[146]", + /* 147 */ 5, "[147]", + /* 148 */ 5, "[148]", + /* 149 */ 5, "[149]", + /* 150 */ 5, "[150]", + /* 151 */ 5, "[151]", + /* 152 */ 5, "[152]", + /* 153 */ 5, "[153]", + /* 154 */ 5, "[154]", + /* 155 */ 5, "[155]", + /* 156 */ 5, "[156]", + /* 157 */ 5, "[157]", + /* 158 */ 5, "[158]", + /* 159 */ 5, "[159]", +#ifdef AMIGA + /* 160 */ 1, "\240", + /* 161 */ 1, "\241", + /* 162 */ 1, "\242", + /* 163 */ 1, "\243", + /* 164 */ 1, "\244", + /* 165 */ 1, "\245", + /* 166 */ 1, "\246", + /* 167 */ 1, "\247", + /* 168 */ 1, "\250", + /* 169 */ 1, "\251", + /* 170 */ 1, "\252", + /* 171 */ 1, "\253", + /* 172 */ 1, "\254", + /* 173 */ 1, "\255", + /* 174 */ 1, "\256", + /* 175 */ 1, "\257", + /* 176 */ 1, "\260", + /* 177 */ 1, "\261", + /* 178 */ 1, "\262", + /* 179 */ 1, "\263", + /* 180 */ 1, "\264", + /* 181 */ 1, "\265", + /* 182 */ 1, "\266", + /* 183 */ 1, "\267", + /* 184 */ 1, "\270", + /* 185 */ 1, "\271", + /* 186 */ 1, "\272", + /* 187 */ 1, "\273", + /* 188 */ 1, "\274", + /* 189 */ 1, "\275", + /* 190 */ 1, "\276", + /* 191 */ 1, "\277", + /* 192 */ 1, "\300", + /* 193 */ 1, "\301", + /* 194 */ 1, "\302", + /* 195 */ 1, "\303", + /* 196 */ 1, "\304", + /* 197 */ 1, "\305", + /* 198 */ 1, "\306", + /* 199 */ 1, "\307", + /* 200 */ 1, "\310", + /* 201 */ 1, "\311", + /* 202 */ 1, "\312", + /* 203 */ 1, "\313", + /* 204 */ 1, "\314", + /* 205 */ 1, "\315", + /* 206 */ 1, "\316", + /* 207 */ 1, "\317", + /* 208 */ 1, "\320", + /* 209 */ 1, "\321", + /* 210 */ 1, "\322", + /* 211 */ 1, "\323", + /* 212 */ 1, "\324", + /* 213 */ 1, "\325", + /* 214 */ 1, "\326", + /* 215 */ 1, "\327", + /* 216 */ 1, "\330", + /* 217 */ 1, "\331", + /* 218 */ 1, "\332", + /* 219 */ 1, "\333", + /* 220 */ 1, "\334", + /* 221 */ 1, "\335", + /* 222 */ 1, "\336", + /* 223 */ 1, "\337", + /* 224 */ 1, "\340", + /* 225 */ 1, "\341", + /* 226 */ 1, "\342", + /* 227 */ 1, "\343", + /* 228 */ 1, "\344", + /* 229 */ 1, "\345", + /* 230 */ 1, "\346", + /* 231 */ 1, "\347", + /* 232 */ 1, "\350", + /* 233 */ 1, "\351", + /* 234 */ 1, "\352", + /* 235 */ 1, "\353", + /* 236 */ 1, "\354", + /* 237 */ 1, "\355", + /* 238 */ 1, "\356", + /* 239 */ 1, "\357", + /* 240 */ 1, "\360", + /* 241 */ 1, "\361", + /* 242 */ 1, "\362", + /* 243 */ 1, "\363", + /* 244 */ 1, "\364", + /* 245 */ 1, "\365", + /* 246 */ 1, "\366", + /* 247 */ 1, "\367", + /* 248 */ 1, "\370", + /* 249 */ 1, "\371", + /* 250 */ 1, "\372", + /* 251 */ 1, "\373", + /* 252 */ 1, "\374", + /* 253 */ 1, "\375", + /* 254 */ 1, "\376", + /* 255 */ 1, "\377" +#else + /* 160 */ 5, "[160]", + /* 161 */ 5, "[161]", + /* 162 */ 5, "[162]", + /* 163 */ 5, "[163]", + /* 164 */ 5, "[164]", + /* 165 */ 5, "[165]", + /* 166 */ 5, "[166]", + /* 167 */ 5, "[167]", + /* 168 */ 5, "[168]", + /* 169 */ 5, "[169]", + /* 170 */ 5, "[170]", + /* 171 */ 5, "[171]", + /* 172 */ 5, "[172]", + /* 173 */ 5, "[173]", + /* 174 */ 5, "[174]", + /* 175 */ 5, "[175]", + /* 176 */ 5, "[176]", + /* 177 */ 5, "[177]", + /* 178 */ 5, "[178]", + /* 179 */ 5, "[179]", + /* 180 */ 5, "[180]", + /* 181 */ 5, "[181]", + /* 182 */ 5, "[182]", + /* 183 */ 5, "[183]", + /* 184 */ 5, "[184]", + /* 185 */ 5, "[185]", + /* 186 */ 5, "[186]", + /* 187 */ 5, "[187]", + /* 188 */ 5, "[188]", + /* 189 */ 5, "[189]", + /* 190 */ 5, "[190]", + /* 191 */ 5, "[191]", + /* 192 */ 5, "[192]", + /* 193 */ 5, "[193]", + /* 194 */ 5, "[194]", + /* 195 */ 5, "[195]", + /* 196 */ 5, "[196]", + /* 197 */ 5, "[197]", + /* 198 */ 5, "[198]", + /* 199 */ 5, "[199]", + /* 200 */ 5, "[200]", + /* 201 */ 5, "[201]", + /* 202 */ 5, "[202]", + /* 203 */ 5, "[203]", + /* 204 */ 5, "[204]", + /* 205 */ 5, "[205]", + /* 206 */ 5, "[206]", + /* 207 */ 5, "[207]", + /* 208 */ 5, "[208]", + /* 209 */ 5, "[209]", + /* 210 */ 5, "[210]", + /* 211 */ 5, "[211]", + /* 212 */ 5, "[212]", + /* 213 */ 5, "[213]", + /* 214 */ 5, "[214]", + /* 215 */ 5, "[215]", + /* 216 */ 5, "[216]", + /* 217 */ 5, "[217]", + /* 218 */ 5, "[218]", + /* 219 */ 5, "[219]", + /* 220 */ 5, "[220]", + /* 221 */ 5, "[221]", + /* 222 */ 5, "[222]", + /* 223 */ 5, "[223]", + /* 224 */ 5, "[224]", + /* 225 */ 5, "[225]", + /* 226 */ 5, "[226]", + /* 227 */ 5, "[227]", + /* 228 */ 5, "[228]", + /* 229 */ 5, "[229]", + /* 230 */ 5, "[230]", + /* 231 */ 5, "[231]", + /* 232 */ 5, "[232]", + /* 233 */ 5, "[233]", + /* 234 */ 5, "[234]", + /* 235 */ 5, "[235]", + /* 236 */ 5, "[236]", + /* 237 */ 5, "[237]", + /* 238 */ 5, "[238]", + /* 239 */ 5, "[239]", + /* 240 */ 5, "[240]", + /* 241 */ 5, "[241]", + /* 242 */ 5, "[242]", + /* 243 */ 5, "[243]", + /* 244 */ 5, "[244]", + /* 245 */ 5, "[245]", + /* 246 */ 5, "[246]", + /* 247 */ 5, "[247]", + /* 248 */ 5, "[248]", + /* 249 */ 5, "[249]", + /* 250 */ 5, "[250]", + /* 251 */ 5, "[251]", + /* 252 */ 5, "[252]", + /* 253 */ 5, "[253]", + /* 254 */ 5, "[254]", + /* 255 */ 5, "[255]" +#endif +}; diff --git a/bin/vi/cmdline.c b/bin/vi/cmdline.c new file mode 100644 index 0000000..6092239 --- /dev/null +++ b/bin/vi/cmdline.c @@ -0,0 +1,829 @@ +/* + * STEVIE - Simply Try this Editor for VI Enthusiasts + * + * Code Contributions By : Tim Thompson twitch!tjt + * Tony Andrews onecom!wldrdg!tony + * G. R. (Fred) Walter watmath!grwalter + */ + +#ifdef __ORCAC__ +segment "seg2"; +#include +#endif +#include "stevie.h" + +static char *altfile = NULL; /* alternate file */ +static int altline; /* line # in alternate file */ + +static char *nowrtmsg = "No write since last change (use ! to override)"; + +extern char **files; /* used for "n" and "rew" */ +extern int curfile; +extern int numfiles; + +#ifdef WILD_CARDS +char **cmd_files = NULL; /* list of input files */ +int cmd_numfiles = 0; /* number of input files */ +#endif + +/* + * The next two variables contain the bounds of any range given in a command. + * If no range was given, both contain null line pointers. If only a single + * line was given, u_pos will contain a null line pointer. + */ +static LPtr l_pos, u_pos; + +static bool_t interactive; /* TRUE if we're reading a real command line */ + +static bool_t doecmd(char *); +static void badcmd(void); +static void doshell(void); +static void get_range(char **); +static LPtr *get_line(char **); + +#ifdef MEGAMAX +overlay "cmdline" +#endif + +/* + * readcmdline() - accept a command line starting with ':', '/', or '?' + * + * readcmdline() accepts and processes colon commands and searches. If + * 'cmdline' is null, the command line is read here. Otherwise, cmdline + * points to a complete command line that should be used. This is used in + * main() to handle initialization commands in the environment variable + * "EXINIT". + */ +void +readcmdline(char firstc, char *cmdline) + /*char firstc; /* either ':', '/', or '?' */ + /*char *cmdline; /* optional command string */ +{ + char c; + char buff[CMDBUFFSIZE]; + char cmdbuf[CMDBUFFSIZE]; + char argbuf[CMDBUFFSIZE]; + char *p, *q, *cmd, *arg; + bool_t literal_next_flag = FALSE; + + /* + * Clear the range variables. + */ + l_pos.linep = (LINE *) NULL; + u_pos.linep = (LINE *) NULL; + + interactive = (cmdline == NULL); + + if (interactive) + gotocmdline(YES, firstc); + p = buff; + if (firstc != ':') + *p++ = firstc; + + if (interactive) { + /* collect the command string, handling '\b' and @ */ + for (;;) { + c = vgetc(); + if (c == CTRL('V') && !literal_next_flag) { + literal_next_flag = TRUE; + outchar('^'); + continue; + } + if (c == '\n' || ((c == '\r' || c == ESC) && (!literal_next_flag))) + break; + if ((c == '\b') && (!literal_next_flag)) { + if (p > buff + (firstc != ':')) { + p--; + /* + * this is gross, but it relies only on 'gotocmdline' + */ + gotocmdline(YES, firstc == ':' ? ':' : NUL); + for (q = buff; q < p; q++) + outstr(chars[*q].ch_str); + } else { + msg(""); + return; /* back to cmd mode */ + } + continue; + } + if ((c == '@') && (!literal_next_flag)) { + p = buff; + if (firstc != ':') + *p++ = firstc; + gotocmdline(YES, firstc); + continue; + } + if (literal_next_flag) { + literal_next_flag = FALSE; + outchar('\b'); + } + outstr(chars[c].ch_str); + *p++ = c; + } + *p = '\0'; + } else { + if (strlen(cmdline) > CMDBUFFSIZE - 2) /* should really do something + * better here... */ + return; + strcpy(p, cmdline); + } + + /* skip any initial white space */ + for (cmd = buff; *cmd != NUL && isspace(*cmd); cmd++); + + /* search commands */ + c = *cmd; + if (c == '/' || c == '?') { + cmd++; + /* was the command was '//' or '??' (I.E. repeat last search) */ + if ((*cmd == c) || (*cmd == NUL)) { + if (c == '/') + searchagain(FORWARD); + else + searchagain(BACKWARD); + return; + } + /* If there is a matching '/' or '?' at the end, toss it */ + p = strchr(cmd, NUL); + if (*(p - 1) == c && *(p - 2) != '\\') + *(p - 1) = NUL; + dosearch((c == '/') ? FORWARD : BACKWARD, cmd); + return; + } + /* + * Parse a range, if present (and update the cmd pointer). + */ + get_range(&cmd); + if (l_pos.linep != NULL) { + if (LINEOF(&l_pos) > LINEOF(&u_pos)) { + emsg("Invalid range"); + return; + } + } + strcpy(cmdbuf, cmd); /* save the unmodified command */ + + /* isolate the command and find any argument */ + for (p = cmd; *p != NUL && !isspace(*p); p++); + if (*p == NUL) + arg = NULL; + else { + *p = NUL; + for (p++; *p != NUL && isspace(*p); p++); + if (*p == NUL) { + arg = NULL; + } else { + strcpy(argbuf, p); + arg = argbuf; + } + } + + if (strcmp(cmd, "q!") == 0) { + getout(0); + } + if (strcmp(cmd, "q") == 0) { + if (Changed) { + emsg(nowrtmsg); + } else { + getout(0); + } + return; + } + if (strcmp(cmd, "w") == 0) { + if (arg == NULL) { + if (Filename != NULL) { + if (!writeit(Filename, &l_pos, &u_pos)) { + emsg("Problems occured while writing output file"); + } + } else { + emsg("No output file"); + } + } else { + (void) writeit(arg, &l_pos, &u_pos); + } + return; + } + if (strcmp(cmd, "wq") == 0) { + if (Filename != NULL) { + if (writeit(Filename, (LPtr *) NULL, (LPtr *) NULL)) { + getout(0); + } + } else { + emsg("No output file"); + } + return; + } + if (strcmp(cmd, "x") == 0) { + if (Changed) { + if (Filename != NULL) { + if (!writeit(Filename, (LPtr *) NULL, (LPtr *) NULL)) { + emsg("Problems occured while writing output file"); + return; + } + } else { + emsg("No output file"); + return; + } + } + getout(0); + } + if (strcmp(cmd, "f") == 0 && arg == NULL) { + fileinfo(); + return; + } + if (*cmd == 'n') { + if ((curfile + 1) < numfiles) { + /* + * stuff ":e[!] FILE\n" + */ + stuffReadbuff(":e"); + if (cmd[1] == '!') + stuffReadbuff("!"); + stuffReadbuff(" "); + stuffReadbuff(files[++curfile]); + stuffReadbuff("\n"); + } else + emsg("No more files!"); + return; + } + if (*cmd == 'p') { + if (curfile > 0) { + /* + * stuff ":e[!] FILE\n" + */ + stuffReadbuff(":e"); + if (cmd[1] == '!') + stuffReadbuff("!"); + stuffReadbuff(" "); + stuffReadbuff(files[--curfile]); + stuffReadbuff("\n"); + } else + emsg("No more files!"); + return; + } + if (strncmp(cmd, "rew", 3) == 0) { + if (numfiles <= 1) /* nothing to rewind */ + return; + curfile = 0; + /* + * stuff ":e[!] FILE\n" + */ + stuffReadbuff(":e"); + if (cmd[3] == '!') + stuffReadbuff("!"); + stuffReadbuff(" "); + stuffReadbuff(files[0]); + stuffReadbuff("\n"); + return; + } + if (strcmp(cmd, "e") == 0) { + if (Changed) { + emsg(nowrtmsg); + } else { + if (strcmp(arg, "%") == 0) { + (void) doecmd(NULL); + return; + } +#ifdef WILD_CARDS + if (strcmp(arg, "#") != 0) { + ExpandWildCards(1, &arg, &cmd_numfiles, &cmd_files); + if (cmd_numfiles == 0) { + emsg("Can't open file"); + return; + } else if (cmd_numfiles == 1) { + arg = cmd_files[0]; + } else { + emsg("Too many file names"); + } + } +#endif + (void) doecmd(arg); + } + return; + } + if (strcmp(cmd, "e!") == 0) { + if (strcmp(arg, "%") == 0) { + if (!doecmd(NULL)) + ResetBuffers(); + return; + } +#ifdef WILD_CARDS + if (strcmp(arg, "#") != 0) { + ExpandWildCards(1, &arg, &cmd_numfiles, &cmd_files); + if (cmd_numfiles == 0) { + emsg("Can't open file"); + return; + } else if (cmd_numfiles == 1) { + arg = cmd_files[0]; + } else { + emsg("Too many file names"); + } + } +#endif + if (!doecmd(arg)) + ResetBuffers(); + return; + } + if (strcmp(cmd, "f") == 0) { + Filename = strsave(arg); + filemess(""); + return; + } + if (strcmp(cmd, "r") == 0 || strcmp(cmd, ".r") == 0) { + if (arg == NULL) { + badcmd(); + return; + } +#ifdef WILD_CARDS + if (strcmp(arg, "#") != 0) { + ExpandWildCards(1, &arg, &cmd_numfiles, &cmd_files); + if (cmd_numfiles == 0) { + emsg("Can't open file"); + return; + } else if (cmd_numfiles == 1) { + arg = cmd_files[0]; + } else { + emsg("Too many file names"); + } + } +#endif + if (readfile(arg, Curschar, 1)) { + emsg("Can't open file"); + return; + } + ResetBuffers(); + CHANGED; + return; + } + if (strcmp(cmd, ".=") == 0) { + smsg("line %d", cntllines(Filemem, Curschar)); + return; + } + if (strcmp(cmd, "$=") == 0) { + smsg("%d", cntllines(Filemem, Fileend) - 1); + return; + } + if (strncmp(cmd, "ta", 2) == 0) { + dotag(arg, cmd[2] == '!'); + return; + } + if (strcmp(cmd, "set") == 0) { + doset(arg, interactive); + return; + } + if (strcmp(cmd, "help") == 0) { + if (help()) + s_clear(); + return; + } + if (strcmp(cmd, "version") == 0) { + extern char *Version; + + msg(Version); + return; + } + if (strcmp(cmd, "sh") == 0) { + doshell(); + return; + } + if (strncmp(cmd, "d", 1) == 0) { + LINE *cp; + int n; + + if (l_pos.linep == NULL) + l_pos = *Curschar; + if (u_pos.linep == NULL) + u_pos = l_pos; + + ResetBuffers(); + n = RowNumber(&l_pos); + AppendPositionToUndoUndobuff(0, n); + AppendPositionToUndobuff(0, n); + if ((Filetop->linep->next == l_pos.linep) && + (u_pos.linep->next == Fileend->linep)) + AppendToUndobuff("a"); + else if (u_pos.linep->next == Fileend->linep) + AppendToUndobuff("o"); + else + AppendToUndobuff("O"); + + n = 0; + cp = l_pos.linep; + for (; cp != NULL && cp != Fileend->linep; cp = cp->next) { + AppendToUndobuff(cp->s); + n++; + if (cp == u_pos.linep) + break; + AppendToUndobuff(NL_STR); + } + AppendToUndobuff(ESC_STR); + + if (n > 1) + AppendNumberToUndoUndobuff(n); + AppendToUndoUndobuff("dd"); + + *Curschar = l_pos; + delline(n); + S_NOT_VALID; + return; + } + if (strncmp(cmd, "s/", 2) == 0) { + dosub(&l_pos, &u_pos, cmdbuf + 1); + return; + } + if (strncmp(cmd, "g/", 2) == 0) { + doglob(&l_pos, &u_pos, cmdbuf + 1); + return; + } + if (cmd[0] == '!') { + if (cmd[1] == '\0') { + emsg("Incomplete shell escape command"); + return; + } + outstr("\n"); + flushbuf(); +#ifdef BSD + set_ostate(); +#endif +#ifdef UNIX + set_ostate(); +#endif + (void) system(&cmd[1]); +#ifdef BSD + set_nstate(); +#endif +#ifdef UNIX + set_nstate(); +#endif + wait_return(); + return; + } + /* + * If we got a line, but no command, then go to the line. + */ + if (*cmd == NUL && l_pos.linep != NULL) { + if (u_pos.linep != NULL) + *Curschar = u_pos; + else + *Curschar = l_pos; + + S_CHECK_TOPCHAR_AND_BOTCHAR; + + return; + } + badcmd(); +} + +/* + * get_range - parse a range specifier + * + * Ranges are of the form: + * + * addr[,addr] + * + * where 'addr' is: + * + * % (entire file) + * $ [+-NUM] + * 'x [+-NUM] (where x denotes a currently defined mark) + * . [+-NUM] + * NUM + * + * The pointer *cp is updated to point to the first character following the + * range spec. If an initial address is found, but no second, the upper bound + * is equal to the lower. + */ +static void +get_range(char **cp) +{ + LPtr *l; + char *p; + + if (**cp == '%') { + l_pos.index = 0; + l_pos.linep = Filetop->linep->next; + u_pos.index = 0; + u_pos.linep = Fileend->linep->prev; + (*cp)++; + return; + } + if ((l = get_line(cp)) == NULL) + return; + + l_pos = *l; + + for (p = *cp; *p != NUL && isspace(*p); p++); + + *cp = p; + + if (*p != ',') { /* is there another line spec ? */ + u_pos = l_pos; + return; + } + *cp = ++p; + + if ((l = get_line(cp)) == NULL) { + u_pos = l_pos; + return; + } + u_pos = *l; +} + +static LPtr * +get_line(char **cp) +{ + static LPtr pos; + LPtr *lp; + char *p, c; + int lnum; + + pos.index = 0; /* shouldn't matter... check back later */ + + p = *cp; + /* + * Determine the basic form, if present. + */ + switch (c = *p++) { + + case '$': + pos.linep = Fileend->linep->prev; + break; + + case '.': + pos.linep = Curschar->linep; + break; + + case '\'': + if ((lp = getmark(*p++)) == NULL) { + emsg("Unknown mark"); + return (LPtr *) NULL; + } + pos = *lp; + break; + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + for (lnum = c - '0'; isdigit(*p); p++) + lnum = (lnum * 10) + (*p - '0'); + + if (lnum == 0) + lnum = 1; + + pos = *gotoline(lnum); + break; + + default: + return (LPtr *) NULL; + } + + while (*p != NUL && isspace(*p)) + p++; + + if (*p == '-' || *p == '+') { + bool_t neg = (*p++ == '-'); + + for (lnum = 0; isdigit(*p); p++) + lnum = (lnum * 10) + (*p - '0'); + + if (neg) + lnum = -lnum; + + pos = *gotoline(cntllines(Filemem, &pos) + lnum); + } + *cp = p; + return &pos; +} + +static void +badcmd(void) +{ + if (interactive) + emsg("Unrecognized command"); +} + +/* + * dotag(tag, force) - goto tag + */ +void +dotag(char *tag, bool_t force) + /*char *tag; + bool_t force;*/ +{ + FILE *tp, *fopen(); + char lbuf[LSIZE]; + char *fname, *str; + + if ((tp = fopen("tags", "r")) == NULL) { + emsg("Can't open tags file"); + return; + } + while (fgets(lbuf, LSIZE, tp) != NULL) { + if ((fname = strchr(lbuf, TAB)) == NULL) { + emsg("Format error in tags file"); + return; + } + *fname++ = '\0'; + if ((str = strchr(fname, TAB)) == NULL) { + emsg("Format error in tags file"); + return; + } + *str++ = '\0'; + + if (strcmp(lbuf, tag) == 0) { + if (!force && Changed) { + emsg(nowrtmsg); + return; + } + if (doecmd(fname)) { + stuffReadbuff(str); /* str has \n at end */ + stuffReadbuff("\007"); /* CTRL('G') */ + fclose(tp); + return; + } + } + } + emsg("tag not found"); + fclose(tp); +} + +static bool_t +doecmd(char *arg) +{ + int line = 1; /* line # to go to in new file */ + + if (arg != NULL) { + /* + * First detect a ":e" on the current file. This is mainly for ":ta" + * commands where the destination is within the current file. + */ + if (Filename != NULL) { + if (strcmp(arg, Filename) == 0) { + if (!Changed) { + altfile = Filename; + altline = cntllines(Filemem, Curschar); + return TRUE; + } + } + } + if (strcmp(arg, "#") == 0) { /* alternate */ + char *s = Filename; + + if (altfile == NULL) { + emsg("No alternate file"); + return FALSE; + } + if (strcmp(altfile, Filename) == 0) { + if (!Changed) { + line = altline; + altline = cntllines(Filemem, Curschar); + goto DO_THE_STUFF_THING; + } + } + Filename = altfile; + altfile = s; + line = altline; + altline = cntllines(Filemem, Curschar); + } else { + altfile = Filename; + altline = cntllines(Filemem, Curschar); + Filename = strsave(arg); + } + } + if (Filename == NULL) { + emsg("No filename"); + return FALSE; + } + /* clear mem and read file */ + freeall(); + filealloc(); + UNCHANGED; + + if (readfile(Filename, Filemem, 0)) { + emsg("Can't open file"); + return FALSE; + } + *Topchar = *Curschar; + if (line != 1) { +DO_THE_STUFF_THING: + stuffnumReadbuff(line); + stuffReadbuff("G"); + } + setpcmark(); + + return TRUE; +} + +static void +doshell(void) +{ + char *sh, *getenv(); + + sh = getenv("SHELL"); + if (sh == NULL) { + emsg("Shell variable not set"); + return; + } + gotocmdline(YES, NUL); + + if (system(sh) < 0) { + emsg("Exec failed"); + return; + } + wait_return(); +} + +void +gotocmdline(bool_t clr, char firstc) +{ + windgoto(Rows - 1, 0); + if (clr) + toutstr(T_EL); /* clear the bottom line */ + if (firstc) + outchar(firstc); +} + +/* + * msg(s) - displays the string 's' on the status line + */ +void +msg(char *s) +{ + gotocmdline(YES, NUL); + outstr(s); +#ifdef AMIGA + flushbuf(); +#endif +#ifdef BSD + flushbuf(); +#endif +} + +/* VARARGS */ +#ifdef __ORCAC__ +void smsg(char *s, ...) +{ +static char sbuf[MAX_COLUMNS+1]; +va_list ap; + + + va_start(ap,s); + vsprintf(sbuf,s,ap); + msg(sbuf); + va_end(ap); +} +#else +void +smsg(s, a1, a2, a3, a4, a5, a6, a7, a8, a9) + char *s; + int a1, a2, a3, a4, a5, a6, a7, a8, a9; +{ + char sbuf[MAX_COLUMNS + 1]; + + sprintf(sbuf, s, a1, a2, a3, a4, a5, a6, a7, a8, a9); + msg(sbuf); +} +#endif + +/* + * emsg() - display an error message + * + * Rings the bell, if appropriate, and calls message() to do the real work + */ +void +emsg(char *s) +{ + UndoInProgress = FALSE; + RedrawingDisabled = FALSE; + + if (P(P_EB)) + beep(); + toutstr(T_TI); + msg(s); + toutstr(T_TP); +#ifdef AMIGA + flushbuf(); +#endif +#ifdef BSD + flushbuf(); +#endif +} + +void +wait_return(void) +{ + char c; + + outstr("Press RETURN to continue"); + do { + c = vgetc(); + } while (c != '\r' && c != '\n'); + + s_clear(); +} diff --git a/bin/vi/dec.c b/bin/vi/dec.c new file mode 100644 index 0000000..4bafe62 --- /dev/null +++ b/bin/vi/dec.c @@ -0,0 +1,31 @@ +/* + * STEVIE - Simply Try this Editor for VI Enthusiasts + * + * Code Contributions By : Tim Thompson twitch!tjt + * Tony Andrews onecom!wldrdg!tony + * G. R. (Fred) Walter watmath!grwalter + */ + +#include "stevie.h" + +/* + * dec(p) + * + * Decrement the line pointer 'p' crossing line boundaries as necessary. Return + * 1 when crossing a line, -1 when at start of file, 0 otherwise. + */ +int +dec(LPtr *lp) +{ + if (lp->index > 0) { /* still within line */ + lp->index--; + return 0; + } + if (lp->linep->prev != Filetop->linep) { /* there is a prior line */ + lp->linep = lp->linep->prev; + lp->index = strlen(lp->linep->s); + return 1; + } + lp->index = 0; /* stick at first char */ + return -1; /* at start of file */ +} diff --git a/bin/vi/edit.c b/bin/vi/edit.c new file mode 100644 index 0000000..fbbb7c5 --- /dev/null +++ b/bin/vi/edit.c @@ -0,0 +1,412 @@ +/* + * STEVIE - Simply Try this Editor for VI Enthusiasts + * + * Code Contributions By : Tim Thompson twitch!tjt + * Tony Andrews onecom!wldrdg!tony + * G. R. (Fred) Walter watmath!grwalter + */ + +#include "stevie.h" + +/* + * This flag is used to make auto-indent work right on lines where only a + * or is typed. It is set when an auto-indent is done, and + * reset when any other editting is done on the line. If an or + * is received, and did_ai is TRUE, the line is truncated. + */ +bool_t did_ai = FALSE; + +static int replace_num; + +void +edit(void) +{ + char c; + bool_t literal_next_flag = FALSE; + char *replace_line; + char *ptr; + int len; + void dosuspend(void); + + Prenum = 0; + + /* position the display and the cursor at the top of the file. */ + *Topchar = *Filemem; + *Curschar = *Filemem; + Cursrow = Curscol = 0; + + for (;;) { + + if (!RedrawingDisabled) { + /* Figure out where the cursor is based on Curschar. */ + cursupdate(UPDATE_CURSOR); + windgoto(Cursrow, Curscol); + } + c = vgetc(); + + if (State == NORMAL) { + /* We're in the normal (non-insert) mode. */ + + /* Pick up any leading digits and compute 'Prenum' */ + if (isascii(c)) { /* must disallow special chars from "ascii.h" */ + if ((Prenum > 0 && isdigit(c)) || (isdigit(c) && c != '0')) { + Prenum = Prenum * 10 + (c - '0'); + continue; + } + } + /* execute the command */ + normal(c); + if (State == INSERT && last_command == 'R') { + ptr = Curschar->linep->s + Curschar->index; + len = strlen(ptr) + 1; + replace_line = (char *) NULL; + replace_num = 0; + if (len > 1) { + replace_line = alloc((unsigned) len); + if (replace_line != (char *) NULL) + strcpy(replace_line, ptr); + } + } + Prenum = 0; + } else { + if (c == CTRL('V') && !literal_next_flag) { + literal_next_flag = TRUE; + outchar('^'); + continue; + } + if (literal_next_flag) { + literal_next_flag = FALSE; + outchar('\b'); + if (c != NL) { + did_ai = FALSE; + insertchar(c); + continue; + } + } + switch (c) { /* We're in insert mode */ + + case ESC: /* an escape ends input mode */ + doESCkey: + dosuspend(); + /* + * If we just did an auto-indent, truncate the line, and put + * the cursor back. + */ + if (did_ai) { + Curschar->linep->s[0] = NUL; + Curschar->index = 0; + did_ai = FALSE; + } + set_want_col = TRUE; + + /* + * The cursor should end up on the last inserted character. + * This is an attempt to match the real 'vi', but it may not + * be quite right yet. + */ + if (Curschar->index != 0) { + if (gchar(Curschar) == NUL) + dec(Curschar); + else if (Insbuffptr != NULL) + dec(Curschar); + } + State = NORMAL; + msg(""); + + if (!UndoInProgress) { + int n; + char *p; + + if (last_command == 'o') + AppendToUndobuff(UNDO_SHIFTJ_STR); + + if (Insbuffptr != NULL) { + if (last_command == 'O') + AppendToUndobuff("0"); + AppendToRedobuff(Insbuff); + AppendToUndoUndobuff(Insbuff); + n = 0; + for (p = Insbuff; *p != NUL; p++) { + if (*p == NL) { + if (n) { + AppendNumberToUndobuff(n); + AppendToUndobuff("dl"); + n = 0; + } + AppendToUndobuff(UNDO_SHIFTJ_STR); + } else + n++; + } + if (n) { + AppendNumberToUndobuff(n); + AppendToUndobuff("dl"); + } + } + if (last_command == 'c') { + AppendToUndobuff(mkstr(last_command_char)); + AppendToUndobuff(Yankbuff); + AppendToUndobuff(ESC_STR); + } + AppendToRedobuff(ESC_STR); + AppendToUndoUndobuff(ESC_STR); + if (last_command == 'O') + AppendToUndobuff(UNDO_SHIFTJ_STR); + + if (last_command == 'R' && replace_line != (char *) NULL) { + if (replace_num > 0) { + if (replace_num < len) { + AppendToUndobuff("i"); + replace_line[replace_num] = '\0'; + } else { + AppendToUndobuff("a"); + } + AppendToUndobuff(replace_line); + AppendToUndobuff(ESC_STR); + free(replace_line); + } + } + } + break; + + case CTRL('D'): + /* + * Control-D is treated as a backspace in insert mode to make + * auto-indent easier. This isn't completely compatible with + * vi, but it's a lot easier than doing it exactly right, and + * the difference isn't very noticeable. + */ + case BS: + /* can't backup past starting point */ + if (Curschar->linep == Insstart->linep && + Curschar->index <= Insstart->index) { + beep(); + break; + } + /* can't backup to a previous line */ + if (Curschar->linep != Insstart->linep && + Curschar->index <= 0) { + beep(); + break; + } + did_ai = FALSE; + dec(Curschar); + delchar(TRUE, FALSE); + /* + * It's a little strange to put backspaces into the redo + * buffer, but it makes auto-indent a lot easier to deal + * with. + */ + AppendToInsbuff(BS_STR); + if (!RedrawingDisabled) /* screen will be fixed later */ + S_LINE_NOT_VALID; + break; + + case CR: + case NL: + AppendToInsbuff(NL_STR); + if (!OpenForward(!RedrawingDisabled)) + goto doESCkey; /* out of memory */ + break; + + default: + did_ai = FALSE; + insertchar(c); + break; + } + } + } +} + +/* + * Special characters in this context are those that need processing other + * than the simple insertion that can be performed here. This includes ESC + * which terminates the insert, and CR/NL which need special processing to + * open up a new line. This routine tries to optimize insertions performed by + * the "redo", "undo" or "put" commands, so it needs to know when it should + * stop and defer processing to the "normal" mechanism. + */ +#define ISSPECIAL(c) ((c) == BS || (c) == NL || (c) == CR || (c) == ESC) + +void +insertchar(char c) +{ + /* + * If there's any pending input, grab up to MAX_COLUMNS at once. + */ + if (anyinput() && (last_command != 'R' || (gchar(Curschar) == NUL))) { + char p[MAX_COLUMNS + 1]; + int i; + + p[0] = c; + i = 1; + c = vpeekc(); + while (!ISSPECIAL(c) && anyinput() && (i < MAX_COLUMNS)) { + p[i++] = vgetc(); + c = vpeekc(); + } + p[i] = '\0'; + insstr(p); + replace_num += i; + AppendToInsbuff(p); + } else { + inschar(c); + replace_num++; + AppendToInsbuff(mkstr(c)); + } + + if (!RedrawingDisabled) /* screen will be fixed later */ + S_LINE_NOT_VALID; +} + +void +getout(int r) +{ + windgoto(Rows - 1, 0); + outchar('\n'); + windexit(r); +} + +void +scrolldown(int nlines) +{ + register LPtr *p; + + S_MUST_UPDATE_BOTCHAR; + S_CHECK_TOPCHAR_AND_BOTCHAR; + + /* Scroll up 'nlines' lines. */ + while (nlines--) { + p = prevline(Topchar); + if (p == NULL) + break; + Topchar->linep = p->linep; + } + /* + * The calling routine must make sure that Curschar is in the correct + * place with relation to Botchar. + */ +} + +void +scrollup(int nlines) +{ + register LPtr *p; + + S_MUST_UPDATE_BOTCHAR; + S_CHECK_TOPCHAR_AND_BOTCHAR; + + /* Scroll down 'nlines' lines. */ + while (nlines--) { + p = nextline(Topchar); + if (p == NULL) + break; + Topchar->linep = p->linep; + } + /* + * The calling routine must make sure that Curschar is in the correct + * place with relation to Topchar. + */ +} + +/* + * oneright oneleft onedown oneup + * + * Move one char {right,left,down,up}. Return TRUE when sucessful, FALSE when + * we hit a boundary (of a line, or the file). + */ + +bool_t +oneright(void) +{ + set_want_col = TRUE; + + switch (inc(Curschar)) { + + case 0: + return TRUE; + + case 1: + dec(Curschar); /* crossed a line, so back up */ + /* FALLTHROUGH */ + case -1: + return FALSE; + } + + return FALSE; /* PARANOIA: should never reach here */ +} + +bool_t +oneleft(void) +{ + set_want_col = TRUE; + + switch (dec(Curschar)) { + + case 0: + return TRUE; + + case 1: + inc(Curschar); /* crossed a line, so back up */ + /* FALLTHROUGH */ + case -1: + return FALSE; + } + + return FALSE; /* PARANOIA: should never reach here */ +} + +void +beginline(bool_t flag) +{ + while (oneleft()); + if (flag) { + while (isspace(gchar(Curschar)) && oneright()); + } + set_want_col = TRUE; +} + +bool_t +oneup(int n) +{ + register int k; + + S_CHECK_TOPCHAR_AND_BOTCHAR; + + for (k = 0; k < n; k++) { + if (Curschar->linep->prev == Filetop->linep) { + if (k > 0) + break; + else + return FALSE; + } + Curschar->linep = Curschar->linep->prev; + } + + /* try to advance to the column we want to be at */ + Curschar->index = 0; + coladvance(Curschar, Curswant); + return TRUE; +} + +bool_t +onedown(int n) +{ + register int k; + + S_CHECK_TOPCHAR_AND_BOTCHAR; + + for (k = 0; k < n; k++) { + if (Curschar->linep->next == Fileend->linep) { + if (k > 0) + break; + else + return FALSE; + } + Curschar->linep = Curschar->linep->next; + } + + /* try to advance to the column we want to be at */ + Curschar->index = 0; + coladvance(Curschar, Curswant); + return TRUE; +} diff --git a/bin/vi/env.h b/bin/vi/env.h new file mode 100644 index 0000000..032a46b --- /dev/null +++ b/bin/vi/env.h @@ -0,0 +1,70 @@ +/* + * STEVIE - Simply Try this Editor for VI Enthusiasts + * + * Code Contributions By : Tim Thompson twitch!tjt + * Tony Andrews onecom!wldrdg!tony + * G. R. (Fred) Walter watmath!grwalter + */ + +/* + * The defines in this file establish the environment we're compiling + * in. Set these appropriately before compiling the editor. + */ + +/* + * One (and only 1) of the following defines should be uncommented. Most of + * the code is pretty machine-independent. Machine dependent code goes in a + * file like tos.c or unix.c. The only other place where machine dependent + * code goes is term.h for escape sequences. + */ + +#ifndef AMIGA +# ifndef BSD +# ifndef UNIX +/* Defined in makefile : AMIGA Amiga */ +/* Defined in makefile : BSD BSD 4.3 */ +/* Defined in makefile : UNIX System V */ +/* #define ATARI Atari ST */ +/* #define OS2 Microsoft OS/2 */ +/* #define DOS MS DOS 3.3 */ +#define GSOS Apple //GS GS/OS 5.02 +# endif +# endif +#endif + +#ifdef AMIGA +# define WILD_CARDS +#endif + +/* + * If ATARI is defined, one of the following compilers must be selected. + */ +#ifdef ATARI +/* #define MWC Mark William's C 3.0.9 */ +/* #define MEGAMAX Megamax Compiler */ +/* #define ALCYON Alcyon C compiler */ + +# ifdef MWC +# define AppendNumberToUndoUndobuff XX1 +# define AppendPositionToUndoUndobuff XX2 +# define FOPENB +# endif + +# ifdef MEGAMAX +# define FOPENB +# endif +#endif + +/* + * If HELP is defined, the :help command shows a vi command summary. + */ +#define HELP /* enable help command */ + +/* + * STRCSPN should be defined if the target system doesn't have the + * routine strcspn() available. See regexp.c for details. + */ + +#ifdef ATARI +#define STRCSPN +#endif diff --git a/bin/vi/fileio.c b/bin/vi/fileio.c new file mode 100644 index 0000000..6bae16e --- /dev/null +++ b/bin/vi/fileio.c @@ -0,0 +1,381 @@ +/* + * STEVIE - Simply Try this Editor for VI Enthusiasts + * + * + * Code Contributions By : Jawaid Bayzar bazyar@cs.uiuc.edu + * Tim Thompson twitch!tjt + * Tony Andrews onecom!wldrdg!tony + * G. R. (Fred) Walter watmath!grwalter + */ + +#include "stevie.h" + +segment "s_io"; + +#ifdef GSOS +#include +#include +#include +#include +FileInfoRecGS finfo = {4, 0l, 0xC3, 0xB0, 0l}; +#endif + +void +filemess(char *s) +{ + sprintf(IObuff, "\"%s\" %s", ((Filename == NULL) ? "" : Filename), s); + msg(IObuff); +} + +void +renum(void) +{ + LPtr *p; + unsigned long l = 0; + + for (p = Filemem; p != NULL; p = nextline(p), l += LINEINC) + p->linep->num = l; + + Fileend->linep->num = 0xffffffffL; +} + +#ifdef MEGAMAX +overlay "fileio" +#endif + +#ifdef GSOS +#define BUFSIZE1 4096 +char *savebuf; +int outind; +int fd; + +void flushout() +{ + write(fd,savebuf,outind); + outind = 0; +} +#endif + +/* made a bunch of these things unsigned for speed- 816 be dumb */ + +bool_t +readfile(char *fname, LPtr *fromp, bool_t nochangename) + /*char *fname; + LPtr *fromp; + bool_t nochangename; /* if TRUE, don't change the Filename */ +{ + FILE *f, *fopen(); + LINE *curr; + char buf2[80]; + int c,hitEOF = 0; + unsigned short IObuffsize = 0; + unsigned long nchars = 0; + unsigned int linecnt = 0; + bool_t wasempty = bufempty(); + unsigned nonascii = 0; /* count garbage characters */ + unsigned nulls = 0; /* count nulls */ + bool_t incomplete = FALSE; /* was the last line incomplete? */ + bool_t toolong = FALSE; /* a line was too long */ + char *saveb1,*locIObuff; + unsigned inind,insize; + + curr = fromp->linep; + locIObuff = IObuff; + + if (!nochangename) + Filename = strsave(fname); + + f = fopen(fname, "r"); + if (f == NULL) { + s_refresh(NOT_VALID); + filemess(""); + return TRUE; + } +#ifdef GSOS + { + GSString255Ptr fn; + fn = malloc(strlen(fname)+2); + fn->length = strlen(fname); + strncpy(fn->text,fname,fn->length); + finfo.pCount = 4; + finfo.pathname = fn; + GetFileInfoGS(&finfo); + free(fn); + } +#endif + S_NOT_VALID; +#ifdef GSOS + savebuf = malloc(BUFSIZE1+1); + saveb1 = savebuf; + insize = inind = 0; +#endif + do { + if (inind == insize) { + insize = read(fileno(f), savebuf, BUFSIZE1); + if (!insize) hitEOF = 1; + inind = 0; + } + /*c = getc(f);*/ + c = saveb1[inind++]; + if (c == '\r') c = NL; + + if (hitEOF) { + if (IObuffsize == 0)/* normal loop termination */ + break; + + /* + * If we get EOF in the middle of a line, note the fact and + * complete the line ourselves. + */ + incomplete = TRUE; + c = NL; + } + if (c & 0x80) { /* much faster check for hi bit set */ + c &= 0x7f; /* this is faster, jesus */ + nonascii++; + } + /* + * If we reached the end of the line, OR we ran out of space for it, + * then process the complete line. + */ + if (c == NL || IObuffsize == (IOSIZE - 1)) { + LINE *lp; + + if (c != NL) + toolong = TRUE; + + locIObuff[IObuffsize++] = NUL; + lp = newline(IObuffsize); + if (lp == NULL) { + fprintf(stderr, "not enough memory - should never happen"); + getout(1); + } + strcpy(lp->s, locIObuff); + + curr->next->prev = lp; /* new line to next one */ + lp->next = curr->next; + + curr->next = lp; /* new line to prior one */ + lp->prev = curr; + + curr = lp; /* new line becomes current */ + IObuffsize = 0; + linecnt++; + } else if (c == NUL) { + nulls++; /* count and ignore nulls */ + } else { + locIObuff[IObuffsize++] = (char) c; /* normal character */ + } + + nchars++; + } while (!incomplete && !toolong); + + free(savebuf); + fclose(f); + + /* + * If the buffer was empty when we started, we have to go back and remove + * the "dummy" line at Filemem and patch up the ptrs. + */ + if (wasempty && linecnt != 0) { + LINE *dummy = Filemem->linep; /* dummy line ptr */ + + Filemem->linep = Filemem->linep->next; + Filemem->linep->prev = Filetop->linep; + Filetop->linep->next = Filemem->linep; + + Curschar->linep = Filemem->linep; + Topchar->linep = Filemem->linep; + + free(dummy->s); /* free string space */ + free((char *) dummy); /* free LINE struct */ + } + renum(); + + if (toolong) { + s_refresh(NOT_VALID); + + sprintf(IObuff, "\"%s\" Line too long", fname); + msg(IObuff); + return FALSE; + } + s_refresh(NOT_VALID); + + sprintf(IObuff, "\"%s\" %s%d line%s, %ld character%s", + fname, + incomplete ? "[Incomplete last line] " : "", + linecnt, (linecnt > 1) ? "s" : "", + nchars, (nchars > 1) ? "s" : ""); + + buf2[0] = NUL; + + if (nonascii || nulls) { + if (nonascii) { + if (nulls) + sprintf(buf2, " (%d null, %d non-ASCII)", + nulls, nonascii); + else + sprintf(buf2, " (%d non-ASCII)", nonascii); + } else + sprintf(buf2, " (%d null)", nulls); + } + strcat(IObuff, buf2); + msg(IObuff); + + return FALSE; +} + +/* + * writeit - write to file 'fname' lines 'start' through 'end' + * + * If either 'start' or 'end' contain null line pointers, the default is to use + * the start or end of the file respectively. + */ +bool_t +writeit(char *fname, LPtr *start, LPtr *end) +{ + FILE *f; + FILE *fopen(); + FILE *fopenb(); /* open in binary mode, where needed */ + char *s; + long nchars; + int lines; + LPtr *p; +#ifdef GSOS + int linesize,inind; + char *line_ptr,*saveb1; +#endif + sprintf(IObuff, "\"%s\"", fname); + msg(IObuff); + +#ifdef GSOS + signal(SIGTSTP,SIG_IGN); +#endif + + /* + * Form the backup file name - change foo.* to foo.bak - use IObuff to + * hold the backup file name + */ + strcpy(IObuff, fname); + for (s = IObuff; *s && *s != '.'; s++); + *s = NUL; + strcat(IObuff, ".bak"); + + /* + * Delete any existing backup and move the current version to the backup. + * For safety, we don't remove the backup until the write has finished + * successfully. And if the 'backup' option is set, leave it around. + */ + rename(fname, IObuff); + + f = P(P_CR) ? fopen(fname, "w") : fopenb(fname, "w"); + if (f == NULL) { + emsg("Can't open file for writing!"); + return FALSE; + } +#ifdef GSOS + savebuf = malloc(BUFSIZE1+1); /* allocate our big memory-save buffer */ + saveb1 = savebuf; +#endif + /* + * If we were given a bound, start there. Otherwise just start at the + * beginning of the file. + */ + if (start == NULL || start->linep == NULL) + p = Filemem; + else + p = start; + + lines = 0; + nchars = 0; +#ifdef GSOS + outind = 0; + fd = fileno(f); +#endif + + do { +#ifndef GSOS + fprintf(f, "%s\n", p->linep->s); +#else + line_ptr = p->linep->s; + asm { + stz inind +agin: ldy inind + lda [line_ptr],y + and #0xff + beq done + + ldy outind + cpy #BUFSIZE1 + bcc otaydude + pha + jsl flushout + pla + ldy #0 +otaydude: sta [saveb1],y + iny + sty outind + inc inind + jmp agin + +done: sty linesize + lda #13 + ldy outind + sta [saveb1],y + iny + sty outind + + lda linesize + clc + adc nchars + sta nchars + } +#endif + lines++; +#ifndef GSOS + nchars += strlen(p->linep->s) + 1; +#endif + /* + * If we were given an upper bound, and we just did that line, then + * bag it now. + */ + if (end != NULL && end->linep != NULL) { + if (end->linep == p->linep) + break; + } + } while ((p = nextline(p)) != NULL); +#ifdef GSOS + flushout(); + free(savebuf); +#endif + fclose(f); + + /* + * Remove the backup unless they want it left around + */ + if (!P(P_BK)) + remove(IObuff); + + sprintf(IObuff, "\"%s\" %d line%s, %ld character%s", fname, + lines, (lines > 1) ? "s" : "", + nchars, (nchars > 1) ? "s" : ""); + msg(IObuff); + UNCHANGED; +#ifdef GSOS + { + GSString255Ptr fn; + extern void stopHandler(int,int); + + fn = malloc(strlen(fname)+2); + fn->length = strlen(fname); + strncpy(fn->text,fname,fn->length); + finfo.pCount = 4; + finfo.pathname = fn; + SetFileInfoGS(&finfo); + free(fn); + } + signal(SIGTSTP, stopHandler); /* handle ^Z with a message */ +#endif + + return TRUE; +} diff --git a/bin/vi/format.l.c b/bin/vi/format.l.c new file mode 100644 index 0000000..361595b --- /dev/null +++ b/bin/vi/format.l.c @@ -0,0 +1,81 @@ +/* + * format_line() + * + * Return a pointer to a string buffer containing a formated screen line. + * + * By G. R. (Fred) Walter watmath!grwalter + */ + +#include "stevie.h" + +char *tab_expand = " "; + +char * +format_line(char *ptr, int *len) +{ + register char *dest; + register char c; + register int col; + char *p_extra; + int n_extra; + int coff; /* column offset */ + + dest = IObuff; + col = 0; + + coff = P(P_NU) ? 8 : 0; + + n_extra = 0; + p_extra = NULL; + + for (;;) { + if (n_extra > 0) { + c = *p_extra++; + n_extra--; + } else { + c = *ptr++; + while (c >= 32 && c < 127) { + *dest++ = c; + col++; + if (col >= IOSIZE) + goto DONE_FORMAT_LINE; + c = *ptr++; + } + if (!P(P_LS)) { + if (c == TAB) { + /* tab amount depends on current column */ + p_extra = tab_expand; + n_extra = (P(P_TS) - 1) - (col - coff) % P(P_TS); + c = ' '; + goto I_HATE_GOTOS; + } else if (c == NUL) { + break; + } + } else if (c == NUL) { + *dest++ = '$'; + col++; + break; + } + if ((n_extra = chars[c].ch_size - 1) > 0) { + p_extra = chars[c].ch_str; + c = *p_extra++; + } + } +I_HATE_GOTOS: + *dest++ = c; + col++; + if (col >= IOSIZE) + break; + } +DONE_FORMAT_LINE: + if (col >= IOSIZE) { + dest--; + col--; + } + *dest = NUL; + + if (len != NULL) + *len = col + coff; + + return (IObuff); +} diff --git a/bin/vi/gsos.c b/bin/vi/gsos.c new file mode 100644 index 0000000..abcf82d --- /dev/null +++ b/bin/vi/gsos.c @@ -0,0 +1,287 @@ +/* + * Apple //gs GSOS system-dependent code + */ + +#include +#include +#include +#include +#include +/* 2/orcacdefs/gsos.h, not OUR include file */ +#include "stevie.h" +#include +#include +#include +#include "gsos.h" +#include +#include + +#ifdef __ORCAC__ +#pragma optimize 8 +#endif + +char *tc_ED; +char *tc_EL; +char *tc_IL; +char *tc_DL; +char *tc_CI; +char *tc_CV; +char *tc_TP; +char *tc_TI; +char *tc_END_D; +char *tc_END_L; + +typedef struct { + int flag; + char *comm; + } execPB; +typedef struct { + char *var_name; + char *value; + } readPB; + +/* + * These routines only work by the grace of God (and because I + * track the old IIe cursor locations) + * GNO 1.1 has insert-line and delete-line chars in it's console + * driver. If present in the termcap file, stevie uses these. + */ +void InsertLine(void) +{ +byte *WindTop = (byte *) 0x0022, + *cy = (byte *) 0x0025; + + *WindTop = *cy; + WriteChar(22); + *WindTop = 0; +} + +void DeleteLine(void) +{ +byte *WindTop = (byte *) 0x0022, + *cy = (byte *) 0x0025; + + *WindTop = *cy; + WriteChar(23); + *WindTop = 0; +} + +char outbuf[1024]; +int indbuf = 0; + + +void +flushbuf(void) +{ + write(STDOUT_FILENO, outbuf, indbuf); + indbuf = 0; +} + +int +outchar(char c) +{ + if (c == '\n') c = '\r'; + outbuf[indbuf++] = c; + if (indbuf == 1024) flushbuf(); /* GOD DAMN, JAWAID!!! */ +} + +int +toutchar(char c) +{ + outbuf[indbuf++] = c; + if (indbuf == 1024) flushbuf(); /* GOD DAMN, JAWAID!!! */ +} + +void +outstr(char *s) +{ +char c; + + while (*s) { + if ((c = *s++) == '\n') c = '\r'; + outbuf[indbuf++] = c; + if (indbuf == 1024) flushbuf(); /* GOD DAMN, JAWAID!!! */ + } +} + +void +toutstr(char *s) +{ + /* this pretty much ignores how the delay characters need to work, + but I don't think delays really matter all that much */ + tputs(s,1,toutchar); +} + +int inchar(void) +{ +int c; + + flushbuf(); + c = ReadChar(0); + if (!(c & 0x0200)) /* make sure control is not down */ + switch (c & 0xFF) { + case 0x0a: + return K_DARROW; + case 0x0b: + return K_UARROW; + case 0x08: + return K_LARROW; + case 0x15: + return K_RARROW; + } + c &= 0x7F; + if (c == 0x7f) return 0x8; + return c; +} + +void beep(void) +{ + outchar(7); + flushbuf(); +} + +char *CM; +char *mp; +char tcb[100]; +struct sgttyb ss; +int oldFlags; +struct ltchars ltch; + +char oldsuspchar; +void nosuspend(void) +{ + ioctl(STDOUT_FILENO, TIOCGLTC, <ch); + ltch.t_suspc = -1; + ioctl(STDOUT_FILENO, TIOCSLTC, <ch); +} + +void dosuspend(void) +{ + ioctl(STDOUT_FILENO, TIOCGLTC, <ch); + ltch.t_suspc = oldsuspchar; + /*smsg("Old Suspend Char: %02X\n",oldsuspchar);*/ + ioctl(STDOUT_FILENO, TIOCSLTC, <ch); +} + +/* startup termcap, and initialize the characters */ +void +windinit(void) +{ +char *term,*tcp; + + Columns = 80; + P(P_LI) = Rows = 24; + SetInGlobals(0xFFFF,0x00); +/* TextStartUp(); */ + ioctl(STDOUT_FILENO,TIOCGETP,&ss); + oldFlags = ss.sg_flags; + ss.sg_flags &= ~ECHO; + ss.sg_flags |= CBREAK; /* for benefit of terminals */ + ioctl(STDOUT_FILENO,TIOCSETP,&ss); /* make sure echo is off for terms */ + ioctl(STDOUT_FILENO,TIOCGLTC,<ch); + oldsuspchar = ltch.t_suspc; + + if (!(term = getenv("TERM"))) { + fprintf(stderr, "vi: TERM: parameter not set\n"); + exit(1); + } + if (!(mp = malloc((size_t)1024))) { + fprintf(stderr, "vi: out of termcap space.\n"); + exit(1); + } + if (tgetent(mp, term) <= 0) { + fprintf(stderr, "vi: %s: unknown terminal type\n", term); + exit(1); + } + tcp = tcb; + if (!(CM = tgetstr("cm", &tcp))) { + fprintf(stderr, "vi: terminal not capable of cursor motion\n"); + exit(1); + } + tc_END_D = tgetstr("cd", &tcp); + tc_END_L = tgetstr("ce", &tcp); + tc_ED = tgetstr("cl", &tcp); + tc_TP = tgetstr("se", &tcp); + tc_TI = tgetstr("so", &tcp); + tc_CI = tgetstr("vi", &tcp); + tc_CV = tgetstr("vs", &tcp); + tc_EL = tgetstr("ce", &tcp); + tc_IL = tgetstr("al", &tcp); + tc_DL = tgetstr("dl", &tcp); +} + +void +windexit(int r) +{ + flushbuf(); + ss.sg_flags = oldFlags; + ioctl(STDOUT_FILENO,TIOCSETP,&ss); + exit(r); +} + +void +windgoto(int r, int c) +{ + tputs(tgoto(CM,c,r),1,toutchar); + + /*WriteChar(0x1e); + WriteChar(32+c); + WriteChar(32+r); */ +} + + /* + * Should do something reasonable here. + */ +/*void +sleep(n) + int n; +{ +}*/ + +void +delay(void) +{ + long l; + + flushbuf(); + /* + * Should do something better here... + */ + + sleep(1); +} + +FILE * +fopenb(fname, mode) + char *fname; + char *mode; +{ + char modestr[10]; + + sprintf(modestr, "b%s", mode); + + return fopen(fname, modestr); +} + +#pragma optimize 8 +/* we _must_ have this in here or we won't be in the right databank! */ +#pragma databank 1 +void stopHandler(int sig, int code) +{ +void *oldHndl; +int redrawChar = 12; + + windgoto(23,0); + flushbuf(); + printf("\nType 'fg' to restart vi...\n"); + oldHndl = signal(SIGTSTP,SIG_DFL); + ss.sg_flags = oldFlags; + ioctl(STDOUT_FILENO,TIOCSETP,&ss); + kill(getpid(),SIGSTOP); + signal(SIGTSTP,oldHndl); + /* push a control-L into the input buffer- kinda goofy, but it works */ + ioctl(STDOUT_FILENO,TIOCSTI,&redrawChar); + ss.sg_flags &= ~ECHO; + ss.sg_flags |= CBREAK; + ioctl(STDOUT_FILENO,TIOCSETP,&ss); +} diff --git a/bin/vi/gsos.h b/bin/vi/gsos.h new file mode 100644 index 0000000..ddacb34 --- /dev/null +++ b/bin/vi/gsos.h @@ -0,0 +1,16 @@ +/* + * Apple //gs GSOS Machine-dependent routines. + */ + +void flushbuf(void); + +int inchar(void); +int outchar(char); +void outstr(char *); +void toutstr(char *); +void beep(void); +void windinit(void); +void windexit(int); +void windgoto(int,int); +void delay(void); +unsigned int sleep(unsigned int); diff --git a/bin/vi/help.c b/bin/vi/help.c new file mode 100644 index 0000000..8da7eda --- /dev/null +++ b/bin/vi/help.c @@ -0,0 +1,321 @@ +/* + * STEVIE - Simply Try this Editor for VI Enthusiasts + * + * Code Contributions By : Tim Thompson twitch!tjt + * Tony Andrews onecom!wldrdg!tony + * G. R. (Fred) Walter watmath!grwalter + */ + +#include "stevie.h" +#include +extern char *Version; + +static int helprow; + +#ifdef HELP + +#ifdef MEGAMAX +overlay "help" +#endif + +static void longline(char *); + +bool_t +help(void) +{ +char s; + toutstr(T_ED); + windgoto(helprow = 0, 0); + + longline("\ +Positioning within file\n\ +=======================\n\ +^F\t\tForward screenfull Worked on by:\n\ +^B\t\tBackward screenfull Tim Thompson\n"); + longline("\ +^D\t\tscroll down half screen Tony Andrews\n\ +^U\t\tscroll up half screen G.R. (Fred) Walter\n"); + longline("\ +G\t\tGoto line (end default)\n\ +]]\t\tnext function\n\ +[[\t\tprevious function\n\ +/re\t\tnext occurence of regular expression 're'\n"); + longline("\ +?re\t\tprior occurence of regular expression 're'\n\ +n\t\trepeat last / or ?\n\ +N\t\treverse last / or ?\n\ +%\t\tfind matching (, ), {, }, [, or ]\n"); + longline("\ +\n\ +Adjusting the screen\n\ +====================\n\ +^L\t\tRedraw the screen\n\ +^E\t\tscroll window down 1 line\n\ +^Y\t\tscroll window up 1 line\n"); + longline("\ +z\tredraw, current line at top\n\ +z-\t\t... at bottom\n\ +z.\t\t... at center\n"); +/* s = ReadChar(0); + + windgoto(0, 32); + longline(Version); */ +#ifdef AMIGA + longline(" "); + longline(__DATE__); + longline(" "); + longline(__TIME__); +#endif + + windgoto(helprow = Rows - 2, 47); + longline("\n"); + windgoto(helprow = Rows - 1, 47); + longline(""); + + if (vgetc() != ' ') + return TRUE; + + toutstr(T_ED); + windgoto(helprow = 0, 0); + + longline("\ +Character Positioning\n\ +=====================\n\ +^\t\tfirst non-white\n\ +0\t\tbeginning of line\n\ +$\t\tend of line\n\ +h\t\tbackward\n"); + longline("\ +l\t\tforward\n\ +^H\t\tsame as h\n\ +space\t\tsame as l\n\ +fx\t\tfind 'x' forward\n"); + longline("\ +Fx\t\tfind 'x' backward\n\ +tx\t\tupto 'x' forward\n\ +Tx\t\tupto 'x' backward\n\ +;\t\tRepeat last f, F, t, or T\n"); + longline("\ +,\t\tinverse of ;\n\ +|\t\tto specified column\n\ +%\t\tfind matching (, ), {, }, [, or ]\n"); + + windgoto(helprow = Rows - 2, 47); + longline("\n"); + windgoto(helprow = Rows - 1, 47); + longline(""); + + if (vgetc() != ' ') + return TRUE; + + toutstr(T_ED); + windgoto(helprow = 0, 0); + + longline("\ +Line Positioning\n\ +================\n\ +H\t\thome window line\n\ +L\t\tlast window line\n\ +M\t\tmiddle window line\n"); + longline("\ ++\t\tnext line, at first non-white\n\ +-\t\tprevious line, at first non-white\n\ +CR\t\treturn, same as +\n\ +j\t\tnext line, same column\n\ +k\t\tprevious line, same column\n"); + + longline("\ +\n\ +Marking and Returning\n\ +=====================\n\ +``\t\tprevious context\n\ +''\t\t... at first non-white in line\n"); + longline("\ +mx\t\tmark position with letter 'x'\n\ +`x\t\tto mark 'x'\n\ +'x\t\t... at first non-white in line\n"); + + windgoto(helprow = Rows - 2, 47); + longline("\n"); + windgoto(helprow = Rows - 1, 47); + longline(""); + + if (vgetc() != ' ') + return TRUE; + + toutstr(T_ED); + windgoto(helprow = 0, 0); + + longline("\ +Insert and Replace\n\ +==================\n\ +a\t\tappend after cursor\n\ +i\t\tinsert before cursor\n\ +A\t\tappend at end of line\n\ +I\t\tinsert before first non-blank\n"); + longline("\ +o\t\topen line below\n\ +O\t\topen line above\n\ +rx\t\treplace single char with 'x'\n\ +R\t\treplace characters (not yet)\n\ +~\t\treplace character under cursor with other case\n"); + + longline("\ +\n\ +Words, sentences, paragraphs\n\ +============================\n\ +w\t\tword forward\n\ +b\t\tback word\n\ +e\t\tend of word\n\ +)\t\tto next sentence (not yet)\n\ +}\t\tto next paragraph (not yet)\n"); + longline("\ +(\t\tback sentence (not yet)\n\ +{\t\tback paragraph (not yet)\n\ +W\t\tblank delimited word\n\ +B\t\tback W\n\ +E\t\tto end of W"); + + windgoto(helprow = Rows - 2, 47); + longline("\n"); + windgoto(helprow = Rows - 1, 47); + longline(""); + + if (vgetc() != ' ') + return TRUE; + + toutstr(T_ED); + windgoto(helprow = 0, 0); + + longline("\ +Undo & Redo\n\ +=============\n\ +u\t\tundo last change\n\ +U\t\trestore current line (not yet)\n\ +.\t\trepeat last change\n"); + + longline("\ +\n\ +File manipulation\n\ +=================\n"); + longline("\ +:w\t\twrite back changes\n\ +:wq\t\twrite and quit\n\ +:x\t\twrite if modified, and quit\n\ +:q\t\tquit\n\ +:q!\t\tquit, discard changes\n\ +:e name\t\tedit file 'name'\n"); + longline("\ +:e!\t\tre-edit, discard changes\n\ +:e #\t\tedit alternate file\n\ +:w name\t\twrite file 'name'\n"); + longline("\ +:n\t\tedit next file in arglist\n\ +:n args\t\tspecify new arglist (not yet)\n\ +:rew\t\trewind arglist\n\ +:f\t\tshow current file and lines\n"); + longline("\ +:f file\t\tchange current file name\n\ +:ta tag\t\tto tag file entry 'tag'\n\ +^]\t\t:ta, current word is tag"); + + windgoto(helprow = Rows - 2, 47); + longline("\n"); + windgoto(helprow = Rows - 1, 47); + longline(""); + + if (vgetc() != ' ') + return TRUE; + + toutstr(T_ED); + windgoto(helprow = 0, 0); + + longline("\ +Operators (double to affect lines)\n\ +==================================\n\ +d\t\tdelete\n\ +c\t\tchange\n"); + longline("\ +<\t\tleft shift\n\ +>\t\tright shift\n\ +y\t\tyank to buffer\n"); + + longline("\n\ +Yank and Put\n\ +============\n\ +p\t\tput back text\n\ +P\t\tput before\n\ +Y\t\tyank lines"); + + windgoto(helprow = Rows - 2, 47); + longline("\n"); + windgoto(helprow = Rows - 1, 47); + longline(""); + + if (vgetc() != ' ') + return TRUE; + + toutstr(T_ED); + windgoto(helprow = 0, 0); + + longline("\n\ +Miscellaneous operations\n\ +========================\n\ +C\t\tchange rest of line\n\ +D\t\tdelete rest of line\n\ +s\t\tsubstitute chars\n"); + longline("\ +S\t\tsubstitute lines (not yet)\n\ +J\t\tjoin lines\n\ +x\t\tdelete characters\n\ +X\t\t... before cursor\n\ +:!cmd\t\tsystem(\"cmd\")\n\ +:[range]s/search/replace/[g]\n\ +:[range]g/search[/p|/d]\n\ +:[range]d\tdelete range of lines\n"); + + windgoto(helprow = Rows - 1, 47); + longline(""); + + vgetc(); + + return TRUE; +} + +static void +longline(char *p) +{ +# ifdef AMIGA + outstr(p); +# else + char *s; + + for (s = p; *s; s++) { + if (*s == '\n') + windgoto(++helprow, 0); + else + outchar(*s); + } +# endif +} +#else + +bool_t +help(void) +{ + toutstr(T_ED); + windgoto(0, 0); + + outstr(Version); + outstr("\n\nWorked on by:\n"); + outstr("\tTim Thompson\n"); + outstr("\tTony Andrews\n"); + outstr("\tG.R. (Fred) Walter\n"); + outstr("\nSorry, help not configured\n"); + outstr("\n"); + + vgetc(); + + return TRUE; +} +#endif diff --git a/bin/vi/inc.c b/bin/vi/inc.c new file mode 100644 index 0000000..ce38917 --- /dev/null +++ b/bin/vi/inc.c @@ -0,0 +1,32 @@ +/* + * STEVIE - Simply Try this Editor for VI Enthusiasts + * + * Code Contributions By : Tim Thompson twitch!tjt + * Tony Andrews onecom!wldrdg!tony + * G. R. (Fred) Walter watmath!grwalter + */ + +#include "stevie.h" + +/* + * inc(p) + * + * Increment the line pointer 'p' crossing line boundaries as necessary. Return + * 1 when crossing a line, -1 when at end of file, 0 otherwise. + */ +int +inc(LPtr *lp) +{ + register char *p = &(lp->linep->s[lp->index]); + + if (*p != NUL) { /* still within line */ + lp->index++; + return ((p[1] != NUL) ? 0 : 1); + } + if (lp->linep->next != Fileend->linep) { /* there is a next line */ + lp->index = 0; + lp->linep = lp->linep->next; + return 1; + } + return -1; +} diff --git a/bin/vi/keymap.h b/bin/vi/keymap.h new file mode 100644 index 0000000..6f088c4 --- /dev/null +++ b/bin/vi/keymap.h @@ -0,0 +1,51 @@ +/* + * STEVIE - Simply Try this Editor for VI Enthusiasts + * + * Code Contributions By : Tim Thompson twitch!tjt + * Tony Andrews onecom!wldrdg!tony + * G. R. (Fred) Walter watmath!grwalter + */ + +/* + * Keycode definitions for special keys + * + * On systems that have any of these keys, the routine 'inchar' in the + * machine-dependent code should return one of the codes here. + */ + +#define K_CGRAVE 0x1e /* control grave accent */ + +#define K_HELP 0x80 +#define K_UNDO 0x81 +#define K_INSERT 0x82 +#define K_HOME 0x83 +#define K_UARROW 0x84 +#define K_DARROW 0x85 +#define K_LARROW 0x86 +#define K_RARROW 0x87 +#define K_SUARROW 0x88 +#define K_SDARROW 0x89 +#define K_SLARROW 0x8a +#define K_SRARROW 0x8b + +#define K_F1 0x8c /* function keys */ +#define K_F2 0x8d +#define K_F3 0x8e +#define K_F4 0x8f +#define K_F5 0x90 +#define K_F6 0x91 +#define K_F7 0x92 +#define K_F8 0x93 +#define K_F9 0x94 +#define K_F10 0x95 + +#define K_SF1 0x96 /* shifted function keys */ +#define K_SF2 0x97 +#define K_SF3 0x98 +#define K_SF4 0x99 +#define K_SF5 0x9a +#define K_SF6 0x9b +#define K_SF7 0x9c +#define K_SF8 0x9d +#define K_SF9 0x9e +#define K_SF10 0x9f diff --git a/bin/vi/linefunc.c b/bin/vi/linefunc.c new file mode 100644 index 0000000..fd3d220 --- /dev/null +++ b/bin/vi/linefunc.c @@ -0,0 +1,85 @@ +/* + * STEVIE - Simply Try this Editor for VI Enthusiasts + * + * Code Contributions By : Tim Thompson twitch!tjt + * Tony Andrews onecom!wldrdg!tony + * G. R. (Fred) Walter watmath!grwalter + */ + +#include "stevie.h" + +/* + * nextline(curr) + * + * Return a pointer to the beginning of the next line after the one referenced + * by 'curr'. Return NULL if there is no next line (at EOF). + */ + +LPtr * +nextline(LPtr *curr) +{ + static LPtr next; + + if (curr != NULL) { + if (curr->linep->next != Fileend->linep) { + next.index = 0; + next.linep = curr->linep->next; + return &next; + } + } + return (LPtr *) NULL; +} + +/* + * prevline(curr) + * + * Return a pointer to the beginning of the line before the one referenced by + * 'curr'. Return NULL if there is no prior line. + */ + +LPtr * +prevline(LPtr *curr) +{ + static LPtr prev; + + if (curr != NULL) { + if (curr->linep->prev != Filetop->linep) { + prev.index = 0; + prev.linep = curr->linep->prev; + return &prev; + } + } + return (LPtr *) NULL; +} + +/* + * coladvance(p,col) + * + * Try to advance to the specified column, starting at p. + */ + +void +coladvance(LPtr *p, int want_col) +{ + register char c; + register int col; + register int incr; + + if (gchar(p) != NUL) { /* already at the end of line */ + for (col = 0; want_col > 0;) { + c = gchar(p); + if (c == TAB && !P(P_LS)) + incr = (P(P_TS) - col % P(P_TS)); + else + incr = chars[c].ch_size; + want_col -= incr; + col += incr; + + /* Don't go past the end of the file or the line. */ + if (inc(p)) { + dec(p); + break; + } + } + } +} diff --git a/bin/vi/linkscr b/bin/vi/linkscr new file mode 100755 index 0000000..3688e72 --- /dev/null +++ b/bin/vi/linkscr @@ -0,0 +1,24 @@ +o/MAIN +o/EDIT +o/LINEFUNC +o/CMDLINE +o/CHARSET +o/MK +o/FORMAT.L +o/NORMAL +o/REGEXP +o/REGSUB +o/VERSION +o/MISCCMDS +o/HELP +o/DEC +o/INC +o/SEARCH +o/ALLOC +o/S.IO +o/MARK +o/SCREEN +o/FILEIO +o/PARAM +o/GSOS + diff --git a/bin/vi/macros.h b/bin/vi/macros.h new file mode 100644 index 0000000..105498c --- /dev/null +++ b/bin/vi/macros.h @@ -0,0 +1,82 @@ +/* + * STEVIE - Simply Try this Editor for VI Enthusiasts + * + * Code Contributions By : Tim Thompson twitch!tjt + * Tony Andrews onecom!wldrdg!tony + * G. R. (Fred) Walter watmath!grwalter + */ + +/* + * gchar(lp) - get the character at position "lp" + */ +#define gchar(lp) ((lp)->linep->s[(lp)->index]) + +/* + * pchar(lp, c) - put character 'c' at position 'lp' + */ +#define pchar(lp, c) ((lp)->linep->s[(lp)->index] = (c)) + +/* + * pswap(a, b) - swap two position pointers + */ + +#define pswap(a, b) { LPtr \ + pswaptmp; pswaptmp = a; a = b; b = pswaptmp; } + +/* + * Position comparisons + */ +#define lt(a, b) ((LINEOF(a) != LINEOF(b)) \ + ? (LINEOF(a) < LINEOF(b)) : ((a)->index < (b)->index)) + +#define ltoreq(a, b) ((LINEOF(a) != LINEOF(b)) \ + ? (LINEOF(a) < LINEOF(b)) : ((a)->index <= (b)->index)) + +#define gt(a, b) ((LINEOF(a) != LINEOF(b)) \ + ? (LINEOF(a) > LINEOF(b)) : ((a)->index > (b)->index)) + +#define gtoreq(a, b) ((LINEOF(a) != LINEOF(b)) \ + ? (LINEOF(a) > LINEOF(b)) : ((a)->index >= (b)->index)) + +#define equal(a, b) (((a)->linep == (b)->linep) && ((a)->index == (b)->index)) + +/* + * anyinput + * + * Return non-zero if input is pending. + */ +#define anyinput() (Readbuffptr != NULL) + +/* + * buf1line() - return TRUE if there is only one line in file buffer + */ +#define buf1line() (Filemem->linep->next == Fileend->linep) + +/* + * bufempty() - return TRUE if the file buffer is empty + */ +#define bufempty() (buf1line() && Filemem->linep->s[0] == NUL) + +/* + * lineempty() - return TRUE if the line is empty + */ +#define lineempty(p) ((p)->linep->s[0] == NUL) + +/* + * startofline() - return TRUE if the given position is at start of line + */ +#define startofline(p) ((p)->index == 0) + +/* + * endofline() - return TRUE if the given position is at end of line + * + * This routine will probably never be called with a position resting on the NUL + * byte, but handle it correctly in case it happens. + */ +#define endofline(p) \ + ((p)->linep->s[(p)->index] == NUL || (p)->linep->s[(p)->index + 1] == NUL) + +/* + * RowNumber() - return the row number (if no UndoInProgress) + */ +#define RowNumber(p) (UndoInProgress ? 0 : cntllines(Filemem, (p))) diff --git a/bin/vi/main.c b/bin/vi/main.c new file mode 100644 index 0000000..7932b52 --- /dev/null +++ b/bin/vi/main.c @@ -0,0 +1,375 @@ +/* + * STEVIE - Simply Try this Editor for VI Enthusiasts + * + * Code Contributions By : Tim Thompson twitch!tjt + * Tony Andrews onecom!wldrdg!tony + * G. R. (Fred) Walter watmath!grwalter + */ + +#ifdef __ORCAC__ +#pragma stacksize 2048 +#define MAINSEG +#include +#include +#include +#endif + +#ifdef AMIGA +# include +#endif + +#include "stevie.h" + +#ifdef AMIGA +int Aux_Device = FALSE; +#endif + +int Rows; /* Number of Rows and Columns */ +int Columns; /* in the current window. */ + +int CheckTopcharAndBotchar = FALSE; +int MustUpdateBotchar = FALSE; +int ValidToCurschar = FALSE; +int LineNotValid = FALSE; + +int NumLineSizes = -1; /* # of active LineSizes */ +LINE **LinePointers = NULL; /* Pointer to the line for LineSizes */ +char *LineSizes = NULL; /* Size of a line (pline output) */ + +char *Filename = NULL;/* Current file name */ + +LPtr *Filemem; /* The contents of the file, as a single + * array. */ +LPtr *Filetop; /* Line 'above' the start of the file */ + +LPtr *Fileend; /* Pointer to the end of the file in Filemem. + * (It points to the byte AFTER the last + * byte.) */ + +LPtr *Topchar; /* Pointer to the byte in Filemem which is in + * the upper left corner of the screen. */ + +LPtr *Botchar; /* Pointer to the byte in Filemem which is + * just off the bottom of the screen. */ + +LPtr *Curschar; /* Pointer to byte in Filemem at which the + * cursor is currently placed. */ + +int Curscol; /* Current position of cursor (column) */ +int Cursrow; /* Current position of cursor (row) */ + +int Cursvcol; /* Current virtual column, the column number + * of the file's actual line, as opposed to + * the column number we're at on the screen. + * This makes a difference on lines that span + * more than one screen line. */ + +int Curswant = 0; /* The column we'd like to be at. This is + * used try to stay in the same column + * through up/down cursor motions. */ + +bool_t set_want_col; /* If set, then update Curswant the next time + * through cursupdate() to the current + * virtual column. */ + +int State = NORMAL; /* This is the current state of the command + * interpreter. */ + +int Prenum = 0; /* The (optional) number before a command. */ + +LPtr *Insstart; /* This is where the latest insert/append + * mode started. */ + +bool_t Changed = FALSE;/* Set to TRUE if something in the file has + * been changed and not written out. */ + +char *IObuff; /* file reads are done, one line at a time, + * into this buffer; as well as sprintf's */ + +char *Insbuffptr = NULL; +char *Insbuff; /* Each insertion gets stuffed into this + * buffer. */ + +char *Readbuffptr = NULL; +char *Readbuff; /* Having this buffer allows STEVIE to easily + * make itself do commands */ + +char *Redobuffptr = NULL; +char *Redobuff; /* Each command should stuff characters into + * this buffer that will re-execute itself. */ + +bool_t UndoInProgress = FALSE; /* Set to TRUE if undo'ing */ +char *Undobuffptr = NULL; +char *Undobuff; /* Each command should stuff characters into + * this buffer that will undo its effects. */ + +char *UndoUndobuffptr = NULL; +char *UndoUndobuff; /* Each command should stuff characters into + * this buffer that will undo its undo. */ + +char *Yankbuffptr = NULL; +char *Yankbuff; /* Yank buffer */ + +char last_command = NUL; /* last command */ +char last_command_char = NUL; /* character needed to undo + * last command */ + +bool_t RedrawingDisabled = FALSE; /* Set to TRUE if undo'ing or + * put'ing */ + +char **files = NULL; /* list of input files */ +int numfiles = 0; /* number of input files */ +int curfile; /* number of the current file */ + +static void +usage(void) +{ + fprintf(stderr, "usage: stevie [file ...]\n"); + fprintf(stderr, " stevie -t tag\n"); + fprintf(stderr, " stevie +[num] file\n"); + fprintf(stderr, " stevie +/pat file\n"); + exit(1); +} + +#ifdef AMIGA +void +#else +int +#endif +main(int argc, char **argv) +{ + char *initstr; /* init string from the environment */ + char *tag = NULL; /* tag from command line */ + char *pat = NULL; /* pattern from command line */ + int line = -1; /* line number from command line */ + + int atoi(char *); + char *getenv(char *); + +#ifdef __ORCAC__ +{ +extern void stopHandler(int,int); + signal(SIGINT, SIG_IGN); /* vi should ignore ^C requests */ + signal(SIGTSTP, stopHandler); /* handle ^Z with a message */ +} +#endif + +#ifdef AMIGA + { + struct Library *DosBase;/* Used for checking version */ + + DosBase = OpenLibrary("dos.library", 33); + if (!DosBase) { + fprintf(stderr, + "\nSTEVIE requires Version 33 or later of dos.library.\n"); + exit(2); + } else { + CloseLibrary(DosBase); + } + +/* + * I don't think STEVIE should be exited with a break. + */ + (void) signal(SIGINT, SIG_IGN); + } +#endif + + /* + * Process the command line arguments. + */ + if (argc > 1) { + switch (argv[1][0]) { + + case '-': /* -t tag */ + if (argv[1][1] != 't') + usage(); + + if (argv[2] == NULL) + usage(); + + tag = argv[2]; + numfiles = 1; + break; + + case '+': /* +n or +/pat */ + if (argv[1][1] == '/') { + if (argv[2] == NULL) + usage(); + Filename = strsave(argv[2]); + pat = &(argv[1][1]); + numfiles = 1; + + } else if (isdigit(argv[1][1]) || argv[1][1] == NUL) { + if (argv[2] == NULL) + usage(); + Filename = strsave(argv[2]); + numfiles = 1; + + line = (isdigit(argv[1][1])) ? + atoi(&(argv[1][1])) : 0; + } else + usage(); + + break; + + default: /* must be a file name */ +#ifdef WILD_CARDS + ExpandWildCards(argc - 1, &(argv[1]), &numfiles, &files); + if (numfiles == 0) + numfiles = 1; + else + Filename = strsave(files[0]); +#else + Filename = strsave(argv[1]); + files = &(argv[1]); + numfiles = argc - 1; +#endif + if (numfiles > 1) + printf("%d files to edit\n", numfiles); + break; + } + } else { + numfiles = 1; + } + curfile = 0; + + windinit(); + + /* + * Allocate LPtr structures for all the various position pointers and for + * the many buffers. + */ + Filetop = (LPtr *) alloc((unsigned) sizeof(LPtr)); + Filemem = (LPtr *) alloc((unsigned) sizeof(LPtr)); + Fileend = (LPtr *) alloc((unsigned) sizeof(LPtr)); + Topchar = (LPtr *) alloc((unsigned) sizeof(LPtr)); + Curschar = (LPtr *) alloc((unsigned) sizeof(LPtr)); + Botchar = (LPtr *) alloc((unsigned) sizeof(LPtr)); + Insstart = (LPtr *) alloc((unsigned) sizeof(LPtr)); + IObuff = alloc(IOSIZE); + Insbuff = alloc(INSERT_SIZE); + Readbuff = alloc(READSIZE); + Redobuff = alloc(REDO_UNDO_SIZE); + Undobuff = alloc(REDO_UNDO_SIZE); + UndoUndobuff = alloc(REDO_UNDO_SIZE); + Yankbuff = alloc(YANKSIZE); + if (Filetop == NULL || + Filemem == NULL || + Fileend == NULL || + Topchar == NULL || + Curschar == NULL || + Botchar == NULL || + Insstart == NULL || + IObuff == NULL || + Insbuff == NULL || + Readbuff == NULL || + Redobuff == NULL || + Undobuff == NULL || + UndoUndobuff == NULL || + Yankbuff == NULL) { + fprintf(stderr, "Can't allocate data structures\n"); + windexit(0); + } + screenalloc(); + filealloc(); /* Initialize Filemem, Filetop & Fileend */ + + s_clear(); + + initstr = getenv("EXINIT"); + if (initstr != NULL) { + char *lp, buf[128]; + + lp = getenv("LINES"); + if (lp != NULL) { + sprintf(buf, "%s lines=%s", initstr, lp); + readcmdline(':', buf); + } else + readcmdline(':', initstr); + } + if (Filename != NULL) { + if (readfile(Filename, Filemem, FALSE)) + filemess("[New File]"); + } else { + s_refresh(NOT_VALID); + msg("Empty Buffer"); + } + + setpcmark(); + + if (tag) { + stuffReadbuff(":ta "); + stuffReadbuff(tag); + stuffReadbuff("\n"); + } else if (pat) { + stuffReadbuff(pat); + stuffReadbuff("\n"); + } else if (line >= 0) { + if (line > 0) + stuffnumReadbuff(line); + stuffReadbuff("G"); + } + edit(); + /* NOTREACHED */ + /* windexit(0); */ +} + +void +stuffReadbuff(char *s) +{ + if (Readbuffptr == NULL) { + if ((strlen(s) + 1) < READSIZE) { + strcpy(Readbuff, s); + Readbuffptr = Readbuff; + return; + } + } else if ((strlen(Readbuff) + (strlen(s) + 1)) < READSIZE) { + strcat(Readbuff, s); + return; + } + emsg("Couldn't stuffReadbuff() - clearing Readbuff\n"); + *Readbuff = NUL; + Readbuffptr = NULL; +} + +void +stuffnumReadbuff(int n) +{ + char buf[32]; + + sprintf(buf, "%d", n); + stuffReadbuff(buf); +} + +/* OPTRESULT */ +char +vgetc(void) +{ + int c; + + /* + * inchar() may map special keys by using stuffReadbuff(). If it does so, + * it returns -1 so we know to loop here to get a real char. + */ + do { + if (Readbuffptr != NULL) { + char nextc = *Readbuffptr++; + + if (*Readbuffptr == NUL) { + *Readbuff = NUL; + Readbuffptr = NULL; + } + return (nextc); + } + c = inchar(); + } while (c == -1); + + return (char) c; +} + +char +vpeekc(void) +{ + if (Readbuffptr != NULL) + return (*Readbuffptr); + return (NUL); +} diff --git a/bin/vi/make.tags b/bin/vi/make.tags new file mode 100755 index 0000000..f7f467c --- /dev/null +++ b/bin/vi/make.tags @@ -0,0 +1,8 @@ +ctags -t alloc.c charset.c cmdline.c dec.c edit.c +ctags -t -a fileio.c help.c inc.c linefunc.c main.c mark.c +ctags -t -a misccmds.c normal.c param.c screen.c +ctags -t -a search.c format_l.c +ctags -t -a s_io.c ascii.h keymap.h +ctags -t -a macros.h param.h stevie.h term.h mk.c + +ctags -t -a amiga.h amiga.c diff --git a/bin/vi/mark.c b/bin/vi/mark.c new file mode 100644 index 0000000..ba4d2dc --- /dev/null +++ b/bin/vi/mark.c @@ -0,0 +1,129 @@ +/* + * STEVIE - Simply Try this Editor for VI Enthusiasts + * + * Code Contributions By : Tim Thompson twitch!tjt + * Tony Andrews onecom!wldrdg!tony + * G. R. (Fred) Walter watmath!grwalter + */ + +#include "stevie.h" + +#ifdef MEGAMAX +overlay "mark" +#endif + +/* + * This file contains routines to maintain and manipulate marks. + */ + +#define NMARKS 10 /* max. # of marks that can be saved */ + +struct mark { + char name; + LPtr pos; +}; + +static struct mark mlist[NMARKS]; +static struct mark pcmark; /* previous context mark */ +static bool_t pcvalid = FALSE;/* true if pcmark is valid */ + +/* + * setmark(c) - set mark 'c' at current cursor position + * + * Returns TRUE on success, FALSE if no room for mark or bad name given. + */ +bool_t +setmark(char c) +{ + int i; + + if (!isalpha(c)) + return FALSE; + + /* + * If there is already a mark of this name, then just use the existing + * mark entry. + */ + for (i = 0; i < NMARKS; i++) { + if (mlist[i].name == c) { + mlist[i].pos = *Curschar; + return TRUE; + } + } + + /* + * There wasn't a mark of the given name, so find a free slot + */ + for (i = 0; i < NMARKS; i++) { + if (mlist[i].name == NUL) { /* got a free one */ + mlist[i].name = c; + mlist[i].pos = *Curschar; + return TRUE; + } + } + return FALSE; +} + +/* + * setpcmark() - set the previous context mark to the current position + */ +void +setpcmark(void) +{ + pcmark.pos = *Curschar; + pcvalid = TRUE; +} + +/* + * getmark(c) - find mark for char 'c' + * + * Return pointer to LPtr or NULL if no such mark. + */ +LPtr * +getmark(char c) +{ + int i; + + if (c == '\'' || c == '`') /* previous context mark */ + return pcvalid ? &(pcmark.pos) : (LPtr *) NULL; + + for (i = 0; i < NMARKS; i++) { + if (mlist[i].name == c) + return &(mlist[i].pos); + } + return (LPtr *) NULL; +} + +/* + * clrall() - clear all marks + * + * Used mainly when trashing the entire buffer during ":e" type commands + */ +void +clrall(void) +{ + int i; + + for (i = 0; i < NMARKS; i++) + mlist[i].name = NUL; + pcvalid = FALSE; +} + +/* + * clrmark(line) - clear any marks for 'line' + * + * Used any time a line is deleted so we don't have marks pointing to + * non-existent lines. + */ +void +clrmark(LINE *line) +{ + int i; + + for (i = 0; i < NMARKS; i++) { + if (mlist[i].pos.linep == line) + mlist[i].name = NUL; + } + if (pcvalid && (pcmark.pos.linep == line)) + pcvalid = FALSE; +} diff --git a/bin/vi/misccmds.c b/bin/vi/misccmds.c new file mode 100644 index 0000000..5d33527 --- /dev/null +++ b/bin/vi/misccmds.c @@ -0,0 +1,425 @@ +/* + * STEVIE - Simply Try this Editor for VI Enthusiasts + * + * Code Contributions By : Tim Thompson twitch!tjt + * Tony Andrews onecom!wldrdg!tony + * G. R. (Fred) Walter watmath!grwalter + */ + +#ifdef __ORCAC__ +segment "seg2"; +#endif + +#include "stevie.h" + +extern int did_ai; + +/* + * OpenForward + * + * Add a blank line below the current line. + */ + +bool_t +OpenForward(int can_ai) +{ + LINE *l; + LPtr *next; + char *s; /* string to be moved to new line, if any */ + int newindex = 0; /* index of the cursor on the new + * line */ + + /* + * If we're in insert mode, we need to move the remainder of the current + * line onto the new line. Otherwise the new line is left blank. + */ + if (State == INSERT) + s = &Curschar->linep->s[Curschar->index]; + else + s = ""; + + if ((next = nextline(Curschar)) == NULL) /* open on last line */ + next = Fileend; + + /* + * By asking for as much space as the prior line had we make sure that + * we'll have enough space for any auto-indenting. + */ + l = newline(strlen(Curschar->linep->s) + SLOP); + if (l == NULL) { + emsg("out of memory"); + beep(); + sleep(2); + return (FALSE); + } + if (can_ai && P(P_AI)) { + char *p; + + /* + * Copy prior line, and truncate after white space + */ + strcpy(l->s, Curschar->linep->s); + + for (p = l->s; *p == ' ' || *p == TAB; p++); + *p = NUL; + newindex = p - l->s; + AppendToInsbuff(l->s); + if (*s != NUL) + strcat(l->s, s); + + /* + * If we just did an auto-indent, then we didn't type anything on the + * prior line, and it should be truncated. + */ + if (did_ai) + Curschar->linep->s[0] = NUL; + + did_ai = TRUE; + } else if (*s != NUL) { + strcpy(l->s, s); /* copy string to new line */ + } + if (State == INSERT) /* truncate current line at cursor */ + *s = NUL; + + Curschar->linep->next = l; /* link neighbors to new line */ + next->linep->prev = l; + + l->prev = Curschar->linep; /* link new line to neighbors */ + l->next = next->linep; + + if (next == Fileend) { /* new line at end */ + l->num = Curschar->linep->num + LINEINC; + } else if ((l->prev->num) + 1 == l->next->num) { /* no gap, renumber */ + renum(); + } else { /* stick it in the middle */ + long lnum; + + lnum = (l->prev->num + l->next->num) / 2; + l->num = lnum; + } + + *Curschar = *nextline(Curschar); /* cursor moves down */ + Curschar->index = newindex; + + S_NOT_VALID; + CHANGED; + + return (TRUE); +} + +/* + * OpenBackward + * + * Add a blank line above the current line. + */ + +bool_t +OpenBackward(int can_ai) +{ + LINE *l; + LINE *prev; + int newindex = 0; /* index of the cursor on the new + * line */ + + prev = Curschar->linep->prev; + + l = newline(strlen(Curschar->linep->s) + SLOP); + if (l == NULL) { + emsg("out of memory"); + beep(); + sleep(2); + return (FALSE); + } + Curschar->linep->prev = l; /* link neighbors to new line */ + prev->next = l; + + l->next = Curschar->linep; /* link new line to neighbors */ + l->prev = prev; + + if (can_ai && P(P_AI)) { + char *p; + + /* + * Copy current line, and truncate after white space + */ + strcpy(l->s, Curschar->linep->s); + + for (p = l->s; *p == ' ' || *p == TAB; p++); + *p = NUL; + newindex = p - l->s; + AppendToInsbuff(l->s); + + did_ai = TRUE; + } + Curschar->linep = Curschar->linep->prev; + Curschar->index = newindex; + + if (prev == Filetop->linep) { /* new start of file */ + Filemem->linep = l; + renum(); + } else if ((l->prev->num) + 1 == l->next->num) { /* no gap, renumber */ + renum(); + } else { /* stick it in the middle */ + long lnum; + + lnum = (l->prev->num + l->next->num) / 2; + l->num = lnum; + } + + S_NOT_VALID; + CHANGED; + + return (TRUE); +} + +int +cntllines(LPtr *pbegin, LPtr *pend) +{ + register LINE *lp; + register int lnum = 1; + + for (lp = pbegin->linep; lp != pend->linep; lp = lp->next) + lnum++; + + return (lnum); +} + +/* + * plines(s) - return the number of physical screen lines taken by the + * line pointed to by 's' + */ + +int +plines(char *s) +{ + register int col = 0; + + if (*s == NUL) /* empty line */ + return 1; + + for (; *s != NUL; s++) { + if (*s == TAB && !P(P_LS)) + col += P(P_TS) - (col % P(P_TS)); + else + col += chars[*s].ch_size; + } + + /* + * If list mode is on, then the '$' at the end of the line takes up one + * extra column. + */ + if (P(P_LS)) + col += 1; + + /* + * If 'number' mode is on, add another 8. + */ + if (P(P_NU)) + col += 8; + + return ((col + (Columns - 1)) / Columns); +} + +void +fileinfo(void) +{ + long l1, l2; + char buf[MAX_COLUMNS + 1]; + + l1 = cntllines(Filemem, Curschar); + l2 = cntllines(Filemem, Fileend) - 1; + sprintf(buf, "\"%s\"%s line %ld of %ld -- %ld %% --", + (Filename != NULL) ? Filename : "No File", + Changed ? " [Modified]" : "", + l1, l2, (l1 * 100) / l2); + msg(buf); +} + +/* + * gotoline(n) - return a pointer to line 'n' + * + * Returns a pointer to the last line of the file if n is zero, or beyond the + * end of the file. + */ +LPtr * +gotoline(int n) +{ + static LPtr l; + + l.index = 0; + + if (n == 0) + l = *prevline(Fileend); + else { + LPtr *p; + + for (l = *Filemem; --n > 0; l = *p) + if ((p = nextline(&l)) == NULL) + break; + } + return &l; +} + +void +inschar(char c) +{ + register char *p; + register char *pend; + + if (last_command == 'R' && (gchar(Curschar) != NUL)) { + pchar(Curschar, c); + } else { + /* make room for the new char. */ + if (!canincrease(1)) + return; + + p = &Curschar->linep->s[strlen(Curschar->linep->s) + 1]; + pend = &Curschar->linep->s[Curschar->index]; + + for (; p > pend; p--) + *p = *(p - 1); + + *p = c; + } + + if (RedrawingDisabled) { + Curschar->index++; + return; + } + /* + * If we're in insert mode and showmatch mode is set, then check for + * right parens and braces. If there isn't a match, then beep. If there + * is a match AND it's on the screen, then flash to it briefly. If it + * isn't on the screen, don't do anything. + */ + if (P(P_SM) && State == INSERT && (c == ')' || c == '}' || c == ']')) { + LPtr *lpos, csave; + + if ((lpos = showmatch()) == NULL) /* no match, so beep */ + beep(); + else if (LINEOF(lpos) >= LINEOF(Topchar)) { + /* show the new char first */ + s_refresh(VALID_TO_CURSCHAR); + csave = *Curschar; + *Curschar = *lpos; /* move to matching char */ + cursupdate(UPDATE_CURSOR); + windgoto(Cursrow, Curscol); + delay(); /* brief pause */ + *Curschar = csave; /* restore cursor position */ + cursupdate(UPDATE_ALL); + } + } + inc(Curschar); + + CHANGED; +} + +void +insstr(char *s) +{ + register char *p; + register char *pend; + register int n = strlen(s); + + /* Move everything in the file over to make */ + /* room for the new string. */ + if (!canincrease(n)) + return; + + p = &Curschar->linep->s[strlen(Curschar->linep->s) + n]; + pend = &Curschar->linep->s[Curschar->index]; + + for (; p > pend; p--) + *p = *(p - n); + + for (; n > 0; n--) { + *p++ = *s++; + Curschar->index++; + } + CHANGED; +} + +bool_t +delchar(bool_t fixpos, bool_t undo) +/* bool_t fixpos; /* if TRUE fix the cursor position when done */ +/* bool_t undo; /* if TRUE put char deleted into Undo buffer */ +{ + int i; + + /* Check for degenerate case; there's nothing in the file. */ + if (bufempty()) + return FALSE; + + if (lineempty(Curschar)) /* can't do anything */ + return FALSE; + + if (undo) + AppendToUndobuff(mkstr(gchar(Curschar))); + + /* Delete the char. at Curschar by shifting everything in the line down. */ + for (i = Curschar->index + 1; i < Curschar->linep->size; i++) + Curschar->linep->s[i - 1] = Curschar->linep->s[i]; + + /* + * If we just took off the last character of a non-blank line, we don't + * want to end up positioned at the newline. + */ + if (fixpos) { + if (gchar(Curschar) == NUL && Curschar->index > 0 && State != INSERT) + Curschar->index--; + } + CHANGED; + return TRUE; +} + +void +delline(int nlines) +{ + register LINE *p; + register LINE *q; + + while (nlines-- > 0) { + + if (bufempty()) /* nothing to delete */ + break; + + if (buf1line()) { /* just clear the line */ + Curschar->linep->s[0] = NUL; + Curschar->index = 0; + break; + } + p = Curschar->linep->prev; + q = Curschar->linep->next; + + if (p == Filetop->linep) { /* first line of file so... */ + Filemem->linep = q; /* adjust start of file */ + Topchar->linep = q; /* and screen */ + } + p->next = q; + q->prev = p; + + clrmark(Curschar->linep); /* clear marks for the line */ + + /* + * If deleting the top line on the screen, adjust Topchar + */ + if (Topchar->linep == Curschar->linep) + Topchar->linep = q; + + free(Curschar->linep->s); + free((char *) (Curschar->linep)); + + Curschar->linep = q; + Curschar->index = 0; /* is this right? */ + + S_NOT_VALID; + CHANGED; + + /* If we delete the last line in the file, back up */ + if (Curschar->linep == Fileend->linep) { + Curschar->linep = Curschar->linep->prev; + /* and don't try to delete any more lines */ + break; + } + } +} diff --git a/bin/vi/mk.c b/bin/vi/mk.c new file mode 100644 index 0000000..434ad28 --- /dev/null +++ b/bin/vi/mk.c @@ -0,0 +1,48 @@ +/* + * STEVIE - Simply Try this Editor for VI Enthusiasts + * + * Code Contributions By : Tim Thompson twitch!tjt + * Tony Andrews onecom!wldrdg!tony + * G. R. (Fred) Walter watmath!grwalter + */ + +#include "stevie.h" + +char * +mkline(int n) +{ + static char lbuf[9]; + int i = 6; + + strcpy(lbuf, " "); + + lbuf[i--] = (char) ((n % 10) + '0'); + n /= 10; + if (n != 0) { + lbuf[i--] = (char) ((n % 10) + '0'); + n /= 10; + } + if (n != 0) { + lbuf[i--] = (char) ((n % 10) + '0'); + n /= 10; + } + if (n != 0) { + lbuf[i--] = (char) ((n % 10) + '0'); + n /= 10; + } + if (n != 0) + lbuf[i] = (char) ((n % 10) + '0'); + + return lbuf; +} + +char * +mkstr(char c) +{ + static char s[2]; + + s[0] = c; + s[1] = NUL; + + return s; +} diff --git a/bin/vi/normal.c b/bin/vi/normal.c new file mode 100644 index 0000000..2ce9b76 --- /dev/null +++ b/bin/vi/normal.c @@ -0,0 +1,1804 @@ +/* + * STEVIE - Simply Try this Editor for VI Enthusiasts + * + * Code Contributions By : Tim Thompson twitch!tjt + * Tony Andrews onecom!wldrdg!tony + * G. R. (Fred) Walter watmath!grwalter + */ + +/* + * This file contains the main routine for processing characters in command + * mode as well as routines for handling the operators. + */ + +#ifdef __ORCAC__ +segment "normal"; +#endif + +#include "stevie.h" + +static void doshift(int); +static void dodelete(bool_t, bool_t, bool_t); +static void doput(int); +static void dochange(void); +static void startinsert(int); +static bool_t dojoin(bool_t,bool_t); +static bool_t doyank(void); + +/* + * Macro evaluates true if char 'c' is a valid identifier character + */ +#define IDCHAR(c) (isalpha(c) || isdigit(c) || (c) == '_') + +/* + * Operators + */ +#define NOP 0 /* no pending operation */ +#define DELETE 1 +#define YANK 2 +#define CHANGE 3 +#define LSHIFT 4 +#define RSHIFT 5 + +#define CLEAROP (operator = NOP)/* clear any pending operator */ + +static int operator = NOP; /* current pending operator */ + +/* + * When a cursor motion command is made, it is marked as being a character or + * line oriented motion. Then, if an operator is in effect, the operation + * becomes character or line oriented accordingly. + * + * Character motions are marked as being inclusive or not. Most char. motions + * are inclusive, but some (e.g. 'w') are not. + * + * Generally speaking, every command in normal() should either clear any pending + * operator (with CLEAROP), or set the motion type variable. + */ + +/* + * Motion types + */ +#define MBAD (-1) /* 'bad' motion type marks unusable yank buf */ +#define MCHAR 0 +#define MLINE 1 + +static int mtype; /* type of the current cursor motion */ +static bool_t mincl; /* true if char motion is inclusive */ +static int ybtype = MBAD; +static int ybcrossline = FALSE; + +static LPtr startop; /* cursor pos. at start of operator */ + +/* + * Operators can have counts either before the operator, or between the + * operator and the following cursor motion as in: + * + * d3w or 3dw + * + * If a count is given before the operator, it is saved in opnum. If normal() is + * called with a pending operator, the count in opnum (if present) overrides + * any count that came later. + */ +static int opnum = 0; + +#define DEFAULT1(x) (((x) == 0) ? 1 : (x)) + +/* + * normal + * + * Execute a command in normal mode. + */ + +void +normal(char c) +{ + char *p; + int n; + int nn; + bool_t flag = FALSE; + int type = 0; /* used in some operations to modify type */ + int dir = FORWARD; /* search direction */ + char nchar = NUL; + bool_t finish_op; + LPtr temp_Curschar; + + last_command = NUL; + /* + * If there is an operator pending, then the command we take this time + * will terminate it. Finish_op tells us to finish the operation before + * returning this time (unless the operation was cancelled). + */ + finish_op = (operator != NOP); + + /* + * If we're in the middle of an operator AND we had a count before the + * operator, then that count overrides the current value of Prenum. What + * this means effectively, is that commands like "3dw" get turned into + * "d3w" which makes things fall into place pretty neatly. + */ + if (finish_op) { + if (opnum != 0) + Prenum = opnum; + } else + opnum = 0; + + switch (c) { + + case K_HELP: + CLEAROP; + if (help()) + s_clear(); + break; + + case CTRL('L'): + CLEAROP; + s_clear(); + break; + + case CTRL('D'): + CLEAROP; + if (Prenum) + P(P_SS) = (Prenum > Rows - 1) ? Rows - 1 : Prenum; + scrollup((P(P_SS) < Rows) ? P(P_SS) : Rows - 1); + onedown((P(P_SS) < Rows) ? P(P_SS) : Rows - 1); + break; + + case CTRL('U'): + CLEAROP; + if (Prenum) + P(P_SS) = (Prenum > Rows - 1) ? Rows - 1 : Prenum; + scrolldown((P(P_SS) < Rows) ? P(P_SS) : Rows - 1); + oneup((P(P_SS) < Rows) ? P(P_SS) : Rows - 1); + break; + + case CTRL('F'): + CLEAROP; + if (nextline(Topchar) == NULL) { + beep(); + break; + } + Prenum = DEFAULT1(Prenum); + while (Prenum > 0) { + *Curschar = *prevline(Botchar); + *Topchar = *Curschar; + Topchar->index = 0; + Update_Botchar(); + Prenum--; + } + beginline(TRUE); + s_clear(); + break; + + case CTRL('B'): + CLEAROP; + if (prevline(Topchar) == NULL) { + beep(); + break; + } + Prenum = DEFAULT1(Prenum); + while (Prenum > 0) { + *Curschar = *Topchar; + n = Rows - 1; + { + LPtr *lp = Curschar; + int l = 0; + + while ((l < n) && (lp != NULL)) { + l += plines(lp->linep->s); + *Topchar = *lp; + lp = prevline(lp); + } + } + Topchar->index = 0; + Prenum--; + } + beginline(TRUE); + s_clear(); + break; + + case CTRL('E'): + CLEAROP; + scrollup(DEFAULT1(Prenum)); + if (LINEOF(Curschar) < LINEOF(Topchar)) + Curschar->linep = Topchar->linep; + break; + + case CTRL('Y'): + CLEAROP; + scrolldown(DEFAULT1(Prenum)); + Update_Botchar(); + if (LINEOF(Curschar) >= LINEOF(Botchar)) { + LPtr *lp; + + lp = prevline(Botchar); + if (lp == NULL) + lp = Topchar; + Curschar->linep = lp->linep; + } + break; + + case 'z': + CLEAROP; + S_CHECK_TOPCHAR_AND_BOTCHAR; + switch (vgetc()) { + case NL: /* put Curschar at top of screen */ + case CR: + *Topchar = *Curschar; + Topchar->index = 0; + break; + + case '.': /* put Curschar in middle of screen */ + n = Rows / 2; + goto dozcmd; + + case '-': /* put Curschar at bottom of screen */ + n = Rows - 1; + /* FALLTHROUGH */ + + dozcmd: + { + register LPtr *lp = Curschar; + register int l = 0; + + while ((l < n) && (lp != NULL)) { + l += plines(lp->linep->s); + *Topchar = *lp; + lp = prevline(lp); + } + } + Topchar->index = 0; + break; + + default: + beep(); + } + break; + + case CTRL('G'): + CLEAROP; + fileinfo(); + break; + + case 'G': + mtype = MLINE; + *Curschar = *gotoline(Prenum); + if (!UndoInProgress) { + beginline(TRUE); + S_CHECK_TOPCHAR_AND_BOTCHAR; + } + break; + + case 'H': + mtype = MLINE; + *Curschar = *Topchar; + for (n = Prenum; n && onedown(1); n--); + beginline(TRUE); + break; + + case 'M': + mtype = MLINE; + *Curschar = *Topchar; + for (n = 0; n < Rows / 2 && onedown(1); n++); + beginline(TRUE); + break; + + case 'L': + mtype = MLINE; + *Curschar = *prevline(Botchar); + for (n = Prenum; n && oneup(1); n--); + beginline(TRUE); + break; + + case 'l': + case K_RARROW: + case ' ': + mtype = MCHAR; + mincl = FALSE; + n = DEFAULT1(Prenum); + while (n--) { + if (!oneright()) { + if (operator != DELETE && operator != CHANGE) { + beep(); + } else { + if (lineempty(Curschar)) { + CLEAROP; + beep(); + } else { + mincl = TRUE; + } + } + break; + } + } + set_want_col = TRUE; + break; + + case 'h': + case K_LARROW: + case CTRL('H'): + mtype = MCHAR; + mincl = FALSE; + Prenum = DEFAULT1(Prenum); + n = Prenum; + while (n--) { + if (!oneleft()) { + if (operator != DELETE && operator != CHANGE) { + beep(); + } else if (Prenum == 1) { + CLEAROP; + beep(); + } + break; + } + } + set_want_col = TRUE; + break; + + case '-': + flag = TRUE; + /* FALLTHROUGH */ + + case 'k': + case K_UARROW: + case CTRL('P'): + mtype = MLINE; + if (!oneup(DEFAULT1(Prenum))) { + CLEAROP; + beep(); + } else if (flag) + beginline(TRUE); + break; + + case '+': + case CR: + case NL: + flag = TRUE; + /* FALLTHROUGH */ + + case 'j': + case K_DARROW: + case CTRL('N'): + mtype = MLINE; + if (!onedown(DEFAULT1(Prenum))) { + CLEAROP; + beep(); + } else if (flag) + beginline(TRUE); + break; + + /* + * This is a strange motion command that helps make operators more + * logical. It is actually implemented, but not documented in the + * real 'vi'. This motion command actually refers to "the current + * line". Commands like "dd" and "yy" are really an alternate form of + * "d_" and "y_". It does accept a count, so "d3_" works to delete 3 + * lines. + */ + case '_': +lineop: + mtype = MLINE; + if (!onedown(DEFAULT1(Prenum) - 1)) { + CLEAROP; + beep(); + } else + beginline(TRUE); + break; + + case '|': + mtype = MCHAR; + mincl = TRUE; + beginline(FALSE); + if (Prenum > 0) + coladvance(Curschar, Prenum - 1); + Curswant = Prenum - 1; + break; + + case CTRL(']'): /* :ta to current identifier */ + CLEAROP; + { + char ch; + LPtr save; + + save = *Curschar; + /* + * First back up to start of identifier. This doesn't match the + * real vi but I like it a little better and it shouldn't bother + * anyone. + */ + ch = gchar(Curschar); + while (IDCHAR(ch)) { + if (!oneleft()) + break; + ch = gchar(Curschar); + } + if (!IDCHAR(ch)) + oneright(); + + stuffReadbuff(":ta "); + /* + * Now grab the chars in the identifier + */ + ch = gchar(Curschar); + while (IDCHAR(ch)) { + stuffReadbuff(mkstr(ch)); + if (!oneright()) + break; + ch = gchar(Curschar); + } + stuffReadbuff("\n"); + + *Curschar = save; /* restore, in case of error */ + } + break; + + case '%': + S_CHECK_TOPCHAR_AND_BOTCHAR; + mtype = MCHAR; + mincl = TRUE; + { + LPtr *pos; + + if ((pos = showmatch()) == NULL) { + CLEAROP; + beep(); + } else { + setpcmark(); + *Curschar = *pos; + set_want_col = TRUE; + } + } + break; + + /* + * Word Motions + */ + + case 'B': + type = 1; + /* FALLTHROUGH */ + + case 'b': + mtype = MCHAR; + mincl = FALSE; + set_want_col = TRUE; + for (n = DEFAULT1(Prenum); n > 0; n--) { + LPtr *pos; + + if ((Curschar->linep->prev == Filetop->linep) + && (Curschar->index == 0)) { + CLEAROP; + beep(); + break; + } + pos = bck_word(Curschar, type); + if (pos == NULL) { + CLEAROP; + beep(); + *Curschar = *gotoline(1); /* goto top of file */ + } else + *Curschar = *pos; + } + break; + + case 'W': + type = 1; + /* FALLTHROUGH */ + + case 'w': + /* + * This is a little strange. To match what the real vi does, we + * effectively map 'cw' to 'ce', and 'cW' to 'cE'. This seems + * impolite at first, but it's really more what we mean when we say + * 'cw'. + */ + if (operator == CHANGE) + goto do_e_cmd; + + mtype = MCHAR; + mincl = FALSE; + set_want_col = TRUE; + for (n = DEFAULT1(Prenum); n > 0; n--) { + LPtr *pos; + + if ((pos = fwd_word(Curschar, type)) == NULL) { + CLEAROP; + beep(); + break; + } else + *Curschar = *pos; + } + if (operator == DELETE && DEFAULT1(Prenum) == 1) { + if (LINEOF(&startop) != LINEOF(Curschar)) { + *Curschar = startop; + while (oneright()); + mincl = TRUE; + } + } + break; + + case 'E': + type = 1; + /* FALLTHROUGH */ + + case 'e': +do_e_cmd: + mtype = MCHAR; + mincl = TRUE; + set_want_col = TRUE; + if (c == 'e' || c == 'E') { + if (inc(Curschar) == -1) { + CLEAROP; + beep(); + break; + } + } + for (n = DEFAULT1(Prenum); n > 0; n--) { + LPtr *pos; + + if ((pos = end_word(Curschar, type)) == NULL) { + CLEAROP; + beep(); + break; + } else + *Curschar = *pos; + } + break; + + case '$': + mtype = MCHAR; + mincl = TRUE; + while (oneright()); + Curswant = 999; /* so we stay at the end */ + break; + + case '^': + flag = TRUE; + /* FALLTHROUGH */ + + case '0': + mtype = MCHAR; + mincl = TRUE; + beginline(flag); + break; + + case 'R': + ResetBuffers(); + AppendToRedobuff("R"); + CLEAROP; + n = RowNumber(Curschar); + AppendPositionToUndobuff(Curschar->index, n); + AppendPositionToUndoUndobuff(Curschar->index, n); + AppendToUndoUndobuff("R"); + + last_command = 'R'; + startinsert(FALSE); + break; + + case 'A': + set_want_col = TRUE; + while (oneright()); + ResetBuffers(); + AppendToRedobuff("A"); + goto doAPPENDcmd; + + case 'a': + ResetBuffers(); + AppendToRedobuff("a"); + +doAPPENDcmd: + CLEAROP; + /* Works just like an 'i'nsert on the next character. */ + n = RowNumber(Curschar); + AppendPositionToUndoUndobuff(Curschar->index, n); + AppendToUndoUndobuff("a"); + + if (!lineempty(Curschar)) + inc(Curschar); + + n = RowNumber(Curschar); + AppendPositionToUndobuff(Curschar->index, n); + + startinsert(FALSE); + break; + + case 'I': + beginline(TRUE); + ResetBuffers(); + AppendToRedobuff("I"); + goto doINSERTcmd; + /* FALLTHROUGH */ + + case 'i': + case K_INSERT: + ResetBuffers(); + AppendToRedobuff("i"); + +doINSERTcmd: + CLEAROP; + + n = RowNumber(Curschar); + AppendPositionToUndobuff(Curschar->index, n); + AppendPositionToUndoUndobuff(Curschar->index, n); + AppendToUndoUndobuff("i"); + + startinsert(FALSE); + break; + + case 'o': + CLEAROP; + ResetBuffers(); + + n = RowNumber(Curschar); + AppendToRedobuff("o"); + AppendPositionToUndobuff(Curschar->index, n); + AppendPositionToUndoUndobuff(Curschar->index, n); + AppendToUndoUndobuff("o"); + + if (OpenForward(!RedrawingDisabled)) + startinsert(TRUE); + + last_command = 'o'; + break; + + case 'O': + CLEAROP; + ResetBuffers(); + + n = RowNumber(Curschar); + AppendToRedobuff("O"); + AppendPositionToUndobuff(Curschar->index, n); + AppendPositionToUndoUndobuff(Curschar->index, n); + AppendToUndoUndobuff("O"); + + if (OpenBackward(!RedrawingDisabled)) + startinsert(TRUE); + + last_command = 'O'; + break; + + case 'd': + if (operator == DELETE) /* handle 'dd' */ + goto lineop; + if (Prenum != 0) + opnum = Prenum; + startop = *Curschar; + operator = DELETE; + break; + + /* + * Some convenient abbreviations... + */ + + case 'x': + if (Prenum) + stuffnumReadbuff(Prenum); + stuffReadbuff("dl"); + break; + + case 'X': + if (Prenum) + stuffnumReadbuff(Prenum); + stuffReadbuff("dh"); + break; + + case 'D': + stuffReadbuff("d$"); + break; + + case 'Y': + if (Prenum) + stuffnumReadbuff(Prenum); + stuffReadbuff("yy"); + break; + + case 'C': + stuffReadbuff("c$"); + break; + + case 'c': + if (operator == CHANGE) { /* handle 'cc' */ + CLEAROP; + stuffReadbuff("0c$"); + break; + } + if (Prenum != 0) + opnum = Prenum; + startop = *Curschar; + operator = CHANGE; + break; + + case 'y': + if (operator == YANK) /* handle 'yy' */ + goto lineop; + if (Prenum != 0) + opnum = Prenum; + startop = *Curschar; + operator = YANK; + break; + + case ENABLE_REDRAWING: + RedrawingDisabled = FALSE; + S_NOT_VALID; + break; + + case 'p': + if (Yankbuffptr != NULL) { + doput(FORWARD); + + stuffReadbuff(ENABLE_REDRAWING_STR); + RedrawingDisabled = TRUE; + } else + beep(); + break; + + case 'P': + if (Yankbuffptr != NULL) { + doput(BACKWARD); + + stuffReadbuff(ENABLE_REDRAWING_STR); + RedrawingDisabled = TRUE; + } else + beep(); + break; + + case '>': + if (operator == RSHIFT) /* handle >> */ + goto lineop; + if (operator == LSHIFT) { + CLEAROP; + beep(); + break; + } + if (Prenum != 0) + opnum = Prenum; + startop = *Curschar; /* save current position */ + operator = RSHIFT; + break; + + case '<': + if (operator == LSHIFT) /* handle << */ + goto lineop; + if (operator == RSHIFT) { + CLEAROP; + beep(); + break; + } + if (Prenum != 0) + opnum = Prenum; + startop = *Curschar; /* save current position */ + operator = LSHIFT; + break; + + case 's': /* substitute characters */ + if (Prenum) + stuffnumReadbuff(Prenum); + stuffReadbuff("cl"); + break; + + case '?': + case '/': + case ':': + CLEAROP; + readcmdline(c, (char *) NULL); + break; + + case 'n': + mtype = MCHAR; + mincl = FALSE; + set_want_col = TRUE; + if (!repsearch(0)) { + CLEAROP; + beep(); + } + break; + + case 'N': + mtype = MCHAR; + mincl = FALSE; + set_want_col = TRUE; + if (!repsearch(1)) { + CLEAROP; + beep(); + } + break; + + /* + * Character searches + */ + case 'T': + dir = BACKWARD; + /* FALLTHROUGH */ + + case 't': + type = 1; + goto docsearch; + + case 'F': + dir = BACKWARD; + /* FALLTHROUGH */ + + case 'f': +docsearch: + mtype = MCHAR; + mincl = TRUE; + set_want_col = TRUE; + if ((nchar = vgetc()) == ESC) /* search char */ + break; + if (!searchc(nchar, dir, type)) { + CLEAROP; + beep(); + } + break; + + case ',': + flag = 1; + /* FALLTHROUGH */ + + case ';': + mtype = MCHAR; + mincl = TRUE; + set_want_col = TRUE; + if (!crepsearch(flag)) { + CLEAROP; + beep(); + } + break; + + /* + * Function searches + */ + + case '[': + dir = BACKWARD; + /* FALLTHROUGH */ + + case ']': + mtype = MLINE; + set_want_col = TRUE; + if (vgetc() != c) { + CLEAROP; + beep(); + break; + } + if (!findfunc(dir)) { + CLEAROP; + beep(); + } + break; + + /* + * Marks + */ + + case 'm': + CLEAROP; + if (!setmark(vgetc())) + beep(); + break; + + case '\'': + flag = TRUE; + /* FALLTHROUGH */ + + case '`': + S_CHECK_TOPCHAR_AND_BOTCHAR; + { + LPtr mtmp; + LPtr *mark = getmark(vgetc()); + + if (mark == NULL) { + CLEAROP; + beep(); + } else { + mtmp = *mark; + setpcmark(); + *Curschar = mtmp; + if (flag) + beginline(TRUE); + } + mtype = flag ? MLINE : MCHAR; + mincl = TRUE; /* ignored if not MCHAR */ + set_want_col = TRUE; + } + break; + + case 'r': + CLEAROP; + if (lineempty(Curschar)) { /* Nothing to replace */ + beep(); + break; + } + nosuspend(); + nchar = vgetc(); + dosuspend(); + if (nchar == ESC) break; + + Prenum = DEFAULT1(Prenum); + n = strlen(Curschar->linep->s) - Curschar->index; + if (n < Prenum) { + beep(); + break; + } + ResetBuffers(); + + nn = RowNumber(Curschar); + AppendPositionToUndobuff(Curschar->index, nn); + AppendPositionToUndoUndobuff(Curschar->index, nn); + + while (Prenum > 0) { + AppendToRedobuff("r"); + AppendToRedobuff(mkstr(nchar)); + + AppendToUndobuff("r"); + AppendToUndobuff(mkstr(gchar(Curschar))); + + AppendToUndoUndobuff("r"); + AppendToUndoUndobuff(mkstr(nchar)); + + pchar(Curschar, nchar); /* Change current character. */ + + if (Prenum > 1) { + oneright(); + AppendToRedobuff("l"); + AppendToUndobuff("l"); + AppendToUndoUndobuff("l"); + } + Prenum--; + } + + CHANGED; + S_LINE_NOT_VALID; + break; + + case '~': /* swap case */ + CLEAROP; + if (lineempty(Curschar)) { + beep(); + break; + } + ResetBuffers(); + + n = RowNumber(Curschar); + AppendPositionToUndobuff(Curschar->index, n); + AppendPositionToUndoUndobuff(Curschar->index, n); + + Prenum = DEFAULT1(Prenum); + if (Prenum > 0) { + AppendNumberToRedobuff(Prenum); + AppendNumberToUndobuff(Prenum); + AppendNumberToUndoUndobuff(Prenum); + } + AppendToRedobuff("~"); + AppendToUndobuff("~"); + AppendToUndoUndobuff("~"); + + while (Prenum > 0) { + c = gchar(Curschar); + if (isalpha(c)) { + if (islower(c)) + pchar(Curschar, toupper(c)); + else + pchar(Curschar, tolower(c)); + } + if (!oneright()) + break; + Prenum--; + } + + CHANGED; + S_LINE_NOT_VALID; + break; + + case UNDO_SHIFTJ: + CLEAROP; + if (UndoInProgress) { + (void) dojoin(FALSE, FALSE); + break; + } + goto doSHIFTJcommand; + + case 'J': + CLEAROP; +doSHIFTJcommand: + if (nextline(Curschar) == NULL) { /* on last line */ + beep(); + break; + } + ResetBuffers(); + + temp_Curschar = *Curschar; + nn = strlen(Curschar->linep->s); + if (nn < 0) + nn = 0; + n = RowNumber(&temp_Curschar); + + AppendToRedobuff("J"); + + AppendPositionToUndobuff(nn, n); + + AppendPositionToUndoUndobuff(0, n); + AppendToUndoUndobuff("J"); + + if (linewhite(nextline(Curschar))) { + AppendToUndobuff("a\n"); + if (!dojoin(FALSE, TRUE)) { + beep(); + break; + } + } else if (lineempty(Curschar)) { + AppendToUndobuff("i\n"); + if (!dojoin(FALSE, TRUE)) { + beep(); + break; + } + } else { + AppendToUndobuff("dli\n"); + if (!dojoin(TRUE, TRUE)) { + beep(); + break; + } + } + + AppendToUndobuff(ESC_STR); + AppendPositionToUndobuff(nn, n); + break; + + case K_CGRAVE: /* shorthand command */ + CLEAROP; + stuffReadbuff(":e #\n"); + break; + + case 'Z': /* write, if changed, and exit */ + if (vgetc() != 'Z') { + beep(); + break; + } + if (Changed) { + if (Filename != NULL) { + if (!writeit(Filename, (LPtr *) NULL, (LPtr *) NULL)) + return; + } else { + emsg("No output file"); + return; + } + } + getout(0); + break; + + case '.': + CLEAROP; + if (Redobuffptr != NULL) { + stuffReadbuff(Redobuff); + + stuffReadbuff(ENABLE_REDRAWING_STR); + RedrawingDisabled = TRUE; + } else + beep(); + break; + + case 'u': + case K_UNDO: + CLEAROP; + if (UndoInProgress) { + p = UndoUndobuff; + UndoUndobuff = Undobuff; + Undobuff = p; + p = UndoUndobuffptr; + UndoUndobuffptr = Undobuffptr; + Undobuffptr = p; + + UndoInProgress = FALSE; + RedrawingDisabled = FALSE; + S_NOT_VALID; + } else if (Undobuffptr != NULL) { + stuffReadbuff(Undobuff); + stuffReadbuff("u"); + UndoInProgress = TRUE; + RedrawingDisabled = TRUE; + } else { + beep(); + } + break; + + default: + CLEAROP; + beep(); + break; + } + + /* + * If an operation is pending, handle it... + */ + if (finish_op) { /* we just finished an operator */ + if (operator == NOP) /* ... but it was cancelled */ + return; + + switch (operator) { + + case LSHIFT: + case RSHIFT: + ResetBuffers(); + + n = RowNumber(&startop); + AppendPositionToUndobuff(startop.index, n); + AppendPositionToUndoUndobuff(startop.index, n); + if (Prenum != 0) { + AppendNumberToRedobuff(Prenum); + AppendNumberToUndobuff(Prenum); + AppendNumberToUndoUndobuff(Prenum); + } + AppendToRedobuff((operator == LSHIFT) ? "<" : ">"); + AppendToUndobuff((operator == LSHIFT) ? ">" : "<"); + AppendToUndoUndobuff((operator == LSHIFT) ? "<" : ">"); + AppendToRedobuff(mkstr(c)); + if (c == '>') + AppendToUndobuff("<"); + else if (c == '<') + AppendToUndobuff(">"); + else + AppendToUndobuff(mkstr(c)); + AppendToUndoUndobuff(mkstr(c)); + + doshift(operator); + break; + + case DELETE: + ResetBuffers(); + + n = RowNumber(&startop); + AppendPositionToUndoUndobuff(startop.index, n); + + if (lt(&startop, Curschar)) + temp_Curschar = startop; + else + temp_Curschar = *Curschar; + n = RowNumber(&temp_Curschar); + if (Prenum != 0) { + AppendNumberToRedobuff(Prenum); + AppendNumberToUndoUndobuff(Prenum); + } + AppendToRedobuff("d"); + AppendToUndoUndobuff("d"); + AppendToRedobuff(mkstr(c)); + AppendToUndoUndobuff(mkstr(c)); + if (nchar != NUL) { + AppendToRedobuff(mkstr(nchar)); + AppendToUndoUndobuff(mkstr(nchar)); + } + AppendPositionToUndobuff(temp_Curschar.index, n); + + dodelete(!UndoInProgress, !UndoInProgress, !UndoInProgress); + + AppendPositionToUndobuff(temp_Curschar.index, n); + break; + + case YANK: + ResetBuffers(); /* no redo/undo/(undo of undo) on yank... */ + if (!doyank()) + msg("yank buffer exceeded"); + if (!ybcrossline) + *Curschar = startop; + else if (lt(&startop, Curschar)) + *Curschar = startop; + break; + + case CHANGE: + ResetBuffers(); + + n = RowNumber(&startop); + AppendPositionToUndoUndobuff(startop.index, n); + + if (lt(&startop, Curschar)) + temp_Curschar = startop; + else + temp_Curschar = *Curschar; + n = RowNumber(&temp_Curschar); + if (mtype == MLINE) + AppendPositionToUndobuff(0, n); + else + AppendPositionToUndobuff(temp_Curschar.index, n); + + if (Prenum != 0) { + AppendNumberToRedobuff(Prenum); + AppendNumberToUndoUndobuff(Prenum); + } + AppendToRedobuff("c"); + AppendToUndoUndobuff("c"); + AppendToRedobuff(mkstr(c)); + AppendToUndoUndobuff(mkstr(c)); + if (nchar != NUL) { + AppendToRedobuff(mkstr(nchar)); + AppendToUndoUndobuff(mkstr(nchar)); + } + dochange(); + + last_command = 'c'; + break; + + default: + beep(); + } + operator = NOP; + } +} + +/* + * tabinout(shift_type, num) + * + * If shift_type == RSHIFT, add a tab to the begining of the next num lines; + * otherwise delete a tab from the beginning of the next num lines. + */ +static void +tabinout(int shift_type, int num) +{ + LPtr *p; + + beginline(FALSE); + while (num-- > 0) { + beginline(FALSE); + if (shift_type == RSHIFT) + inschar(TAB); + else { + if (gchar(Curschar) == TAB) + delchar(TRUE, FALSE); + } + if (num > 0) { + if ((p = nextline(Curschar)) != NULL) + *Curschar = *p; + else + break; + } + } +} + +/* + * doshift - handle a shift operation + */ +static void +doshift(int op) +{ + LPtr top, bot; + int nlines; + + top = startop; + bot = *Curschar; + + if (lt(&bot, &top)) + pswap(top, bot); + + nlines = cntllines(&top, &bot); + *Curschar = top; + tabinout(op, nlines); + + /* + * The cursor position afterward is the prior of the two positions. + */ + *Curschar = top; + + /* + * If we were on the last char of a line that got shifted left, then move + * left one so we aren't beyond the end of the line + */ + if (gchar(Curschar) == NUL && Curschar->index > 0) + Curschar->index--; + + if (op == RSHIFT) + oneright(); + else + oneleft(); + + S_NOT_VALID; + + if (nlines > P(P_RP)) + smsg("%d lines %ced", nlines, (op == RSHIFT) ? '>' : '<'); +} + +/* + * dodelete - handle a delete operation + */ +static void +dodelete(bool_t redraw, bool_t setup_for_undo, bool_t try_to_yank) +{ + LPtr top, bot; + int nlines; + int n; + + /* + * Do a yank of whatever we're about to delete. If there's too much stuff + * to fit in the yank buffer, then get a confirmation before doing the + * delete. This is crude, but simple. And it avoids doing a delete of + * something we can't put back if we want. + */ + if (try_to_yank) { + if (!doyank()) { + msg("yank buffer exceeded: press to confirm"); + if (vgetc() != 'y') { + emsg("delete aborted"); + *Curschar = startop; + return; + } + } + } + top = startop; + bot = *Curschar; + + if (lt(&bot, &top)) + pswap(top, bot); + + *Curschar = top; + nlines = cntllines(&top, &bot); + + if (mtype == MLINE) { + if (operator == CHANGE) { + last_command_char = 'a'; + delline(nlines - 1); + Curschar->index = 0; + while (delchar(TRUE, FALSE)); + } else { + if ((Filetop->linep->next == top.linep) && + (bot.linep->next == Fileend->linep)) + last_command_char = 'a'; + else if (bot.linep->next == Fileend->linep) + last_command_char = 'o'; + else + last_command_char = 'O'; + if (setup_for_undo) + AppendToUndobuff(mkstr(last_command_char)); + delline(nlines); + } + } else if (top.linep == bot.linep) { /* del. within line */ + if (!mincl) + dec(&bot); + + if (endofline(&bot)) + last_command_char = 'a'; + else + last_command_char = 'i'; + if (setup_for_undo) + AppendToUndobuff(mkstr(last_command_char)); + n = bot.index - top.index + 1; + while (n--) + if (!delchar(TRUE, FALSE)) + break; + } else { /* del. between lines */ + if (endofline(&top)) { + if (nextline(&top)) { + if (lineempty(nextline(&top))) + last_command_char = 'a'; + else + last_command_char = 'i'; + } else { + last_command_char = 'a'; + } + } else { + last_command_char = 'i'; + } + if (setup_for_undo) + AppendToUndobuff(mkstr(last_command_char)); + + n = Curschar->index; + while (Curschar->index >= n) + if (!delchar(TRUE, FALSE)) + break; + + top = *Curschar; + *Curschar = *nextline(Curschar); + delline(nlines - 2); + Curschar->index = 0; + n = bot.index; + if (!mincl) + n--; + + while (n-- >= 0) + if (!delchar(TRUE, FALSE)) + break; + *Curschar = top; + dojoin(FALSE, FALSE); + } + + if (mtype == MCHAR && nlines == 1 && redraw && P(P_NU) == FALSE) { + S_LINE_NOT_VALID; + } else { + S_NOT_VALID; + } + + if (nlines > P(P_RP)) + smsg("%d fewer lines", nlines); + + if (setup_for_undo) { + AppendToUndobuff(Yankbuff); + AppendToUndobuff(ESC_STR); + } +} + +/* + * dochange - handle a change operation + */ +static void +dochange(void) +{ + LPtr l; + + if (lt(Curschar, &startop)) + l = *Curschar; + else + l = startop; + + dodelete(FALSE, FALSE, !UndoInProgress); + + if ((l.index > Curschar->index) && !lineempty(Curschar)) + inc(Curschar); + + startinsert(FALSE); +} + +static bool_t +doyank(void) +{ + LPtr top, bot; + char *ybend = &Yankbuff[YANKSIZE - 1]; + int nlines; + + Yankbuffptr = Yankbuff; + + top = startop; + bot = *Curschar; + + if (lt(&bot, &top)) + pswap(top, bot); + + nlines = cntllines(&top, &bot); + + ybtype = mtype; /* set the yank buffer type */ + ybcrossline = FALSE; + if (LINEOF(&top) != LINEOF(&bot)) + ybcrossline = TRUE; + + if (mtype == MLINE) { + ybcrossline = TRUE; + top.index = 0; + bot.index = strlen(bot.linep->s); + /* + * The following statement checks for the special case of yanking a + * blank line at the beginning of the file. If not handled right, we + * yank an extra char (a newline). + */ + if (dec(&bot) == -1) { + *Yankbuff = NUL; + Yankbuffptr = NULL; + return TRUE; + } + } else { + if (!mincl) + if (!equal(&top, &bot)) + dec(&bot); + } + + for (; ltoreq(&top, &bot); inc(&top)) { + *Yankbuffptr = (gchar(&top) != NUL) ? gchar(&top) : NL; + Yankbuffptr++; + if (Yankbuffptr >= ybend) { + *Yankbuffptr = NUL; + msg("yank too big for buffer"); + ybtype = MBAD; + return FALSE; + } + } + + *Yankbuffptr = NUL; + + if (operator == YANK) + if (nlines > P(P_RP)) + smsg("%d lines yanked", nlines); + + return TRUE; +} + +static void +doput(int dir) +{ + bool_t type; + + if (ybtype == MBAD) { + beep(); + return; + } + type = (ybtype == MCHAR); + if (dir == FORWARD) + stuffReadbuff(type ? "a" : "o"); + else + stuffReadbuff(type ? "i" : "O"); + + stuffReadbuff(Yankbuff); + stuffReadbuff(ESC_STR); + + if (ybtype != MCHAR) + stuffReadbuff("^"); +} + +static void +startinsert(int startln) +/* int startln; /* if set, insert at start of line */ +{ +extern void nosuspend(void); +extern void dosuspend(void); + + nosuspend(); /* turn off the suspend character in insert mode */ + *Insstart = *Curschar; + if (startln) { + Insstart->index = 0; + } + *Insbuff = NUL; + Insbuffptr = NULL; + + State = INSERT; + if (P(P_MO)) { + if (last_command == 'R') + msg("Replace Mode"); + else + msg("Insert Mode"); + } +} + +void +ResetBuffers(void) +{ + if (UndoInProgress) + return; + + *Redobuff = NUL; + Redobuffptr = NULL; + + *Undobuff = NUL; + Undobuffptr = NULL; + + *UndoUndobuff = NUL; + UndoUndobuffptr = NULL; +} + +void +AppendToInsbuff(char *s) +{ + if (UndoInProgress) + return; + + if (Insbuffptr == NULL) { + if ((strlen(s) + 1) < INSERT_SIZE) { + strcpy(Insbuff, s); + Insbuffptr = Insbuff; + return; + } + } else if ((strlen(Insbuff) + strlen(s) + 1) < INSERT_SIZE) { + strcat(Insbuff, s); + return; + } + emsg("Couldn't AppendToInsbuff() - clearing Insbuff\n"); + *Insbuff = NUL; + Insbuffptr = NULL; +} + +void +AppendToRedobuff(char *s) +{ + if (UndoInProgress) + return; + + if (Redobuffptr == (char *) (-2)) { + return; + } + if (Redobuffptr == (char *) (-1)) { + Redobuffptr = (char *) (-2); + emsg("Couldn't AppendToRedobuff() - Redobuff corrupt"); + return; + } + if (Redobuffptr == NULL) { + if ((strlen(s) + 1) < REDO_UNDO_SIZE) { + strcpy(Redobuff, s); + Redobuffptr = Redobuff; + return; + } + } else if ((strlen(Redobuff) + strlen(s) + 1) < REDO_UNDO_SIZE) { + strcat(Redobuff, s); + return; + } + emsg("Couldn't AppendToRedobuff() - clearing Redobuff"); + *Redobuff = NUL; + Redobuffptr = (char *) (-1); +} + +void +AppendNumberToRedobuff(int n) +{ + char buf[32]; + + if (UndoInProgress) + return; + + sprintf(buf, "%d", n); + AppendToRedobuff(buf); +} + +void +AppendToUndobuff(char *s) +{ + if (UndoInProgress) + return; + + if (Undobuffptr == (char *) (-2)) { + return; + } + if (Undobuffptr == (char *) (-1)) { + Undobuffptr = (char *) (-2); + emsg("Couldn't AppendToUndobuff() - Undobuff corrupt"); + return; + } + if (Undobuffptr == NULL) { + if ((strlen(s) + 1) < REDO_UNDO_SIZE) { + strcpy(Undobuff, s); + Undobuffptr = Undobuff; + return; + } + } else if ((strlen(Undobuff) + strlen(s) + 1) < REDO_UNDO_SIZE) { + strcat(Undobuff, s); + return; + } + emsg("Couldn't AppendToUndobuff() - clearing Undobuff"); + *Undobuff = NUL; + Undobuffptr = (char *) (-1); +} + +void +AppendNumberToUndobuff(int n) +{ + char buf[32]; + + if (UndoInProgress) + return; + + sprintf(buf, "%d", n); + AppendToUndobuff(buf); +} + +void +AppendPositionToUndobuff(int column, int row) +{ + if (UndoInProgress) + return; + + AppendNumberToUndobuff(row); + AppendToUndobuff("G"); + AppendNumberToUndobuff(column); + if (column) + AppendToUndobuff("l"); +} + +void +AppendToUndoUndobuff(char *s) +{ + if (UndoInProgress) + return; + + if (UndoUndobuffptr == (char *) (-2)) { + return; + } + if (UndoUndobuffptr == (char *) (-1)) { + UndoUndobuffptr = (char *) (-2); + emsg("Couldn't AppendToUndoUndobuff() - UndoUndobuff corrupt"); + return; + } + if (UndoUndobuffptr == NULL) { + if ((strlen(s) + 1) < REDO_UNDO_SIZE) { + strcpy(UndoUndobuff, s); + UndoUndobuffptr = Undobuff; + return; + } + } else if ((strlen(UndoUndobuff) + strlen(s) + 1) < REDO_UNDO_SIZE) { + strcat(UndoUndobuff, s); + return; + } + emsg("Couldn't AppendToUndoUndobuff() - clearing UndoUndobuff"); + *UndoUndobuff = NUL; + UndoUndobuffptr = (char *) (-1); +} + +void +AppendNumberToUndoUndobuff(int n) +{ + char buf[32]; + + if (UndoInProgress) + return; + + sprintf(buf, "%d", n); + AppendToUndoUndobuff(buf); +} + +void +AppendPositionToUndoUndobuff(int column, int row) +{ + if (UndoInProgress) + return; + + AppendNumberToUndoUndobuff(row); + AppendToUndoUndobuff("G"); + AppendNumberToUndoUndobuff(column); + if (column) + AppendToUndoUndobuff("l"); +} + +static bool_t +dojoin(bool_t leading_space, bool_t strip_leading_spaces) +{ + int scol; /* save cursor column */ + int currsize; /* size of the current line */ + int nextsize; /* size of the next line */ + + if (nextline(Curschar) == NULL) /* on last line */ + return FALSE; + + nextsize = strlen(Curschar->linep->next->s); + if (!canincrease(nextsize)) + return FALSE; + + currsize = strlen(Curschar->linep->s); + + while (oneright()); /* to end of line */ + + strcat(Curschar->linep->s, Curschar->linep->next->s); + + /* + * Delete the following line. To do this we move the cursor there + * briefly, and then move it back. Don't back up if the delete made us + * the last line. + */ + Curschar->linep = Curschar->linep->next; + scol = Curschar->index; + + if (nextline(Curschar) != NULL) { + delline(1); + Curschar->linep = Curschar->linep->prev; + } else + delline(1); + + Curschar->index = scol; + + if (currsize) + oneright(); /* go to first char. of joined line */ + + if (nextsize != 0 && strip_leading_spaces) { + /* + * Delete leading white space on the joined line and insert a single + * space. + */ + while (gchar(Curschar) == ' ' || gchar(Curschar) == TAB) { + delchar(TRUE, TRUE); + } + if (leading_space) + inschar(' '); + } + CHANGED; + + return TRUE; +} + +/* + * linewhite() - returns TRUE if the line consists only of white space + */ + +bool_t +linewhite(LPtr *p) +{ + register int i; + register char c; + + i = 1; + c = p->linep->s[0]; + while (c != NUL) { + if (c != ' ' && c != '\t') + return (FALSE); + c = p->linep->s[i++]; + } + + return (TRUE); +} diff --git a/bin/vi/param.c b/bin/vi/param.c new file mode 100644 index 0000000..5b17206 --- /dev/null +++ b/bin/vi/param.c @@ -0,0 +1,161 @@ +/* + * STEVIE - Simply Try this Editor for VI Enthusiasts + * + * Code Contributions By : Tim Thompson twitch!tjt + * Tony Andrews onecom!wldrdg!tony + * G. R. (Fred) Walter watmath!grwalter + */ + +/* + * Code to handle user-settable parameters. This is all pretty much table- + * driven. To add a new parameter, put it in the params array, and add a + * macro for it in param.h. If it's a numeric parameter, add any necessary + * bounds checks to doset(). String parameters aren't currently supported. + */ + +#include "stevie.h" + +struct param params[] = { + + {"tabstop", "ts", 8, P_NUM}, + {"scroll", "scroll", 12, P_NUM}, + {"report", "report", 5, P_NUM}, + {"lines", "lines", 25, P_NUM}, + + {"vbell", "vb", TRUE, P_BOOL}, + {"showmatch", "sm", FALSE, P_BOOL}, + {"wrapscan", "ws", TRUE, P_BOOL}, + {"errorbells", "eb", FALSE, P_BOOL}, + {"showmode", "mo", FALSE, P_BOOL}, + {"backup", "bk", FALSE, P_BOOL}, + {"return", "cr", TRUE, P_BOOL}, + {"list", "list", FALSE, P_BOOL}, + {"ignorecase", "ic", FALSE, P_BOOL}, + {"autoindent", "ai", FALSE, P_BOOL}, + {"number", "nu", FALSE, P_BOOL}, + + {"", "", 0, 0} /* end marker */ +}; + +static void showparms(bool_t); + +void +doset(char *arg, bool_t inter) +/* char *arg; /* parameter string */ +/* bool_t inter; /* TRUE if called interactively */ +{ + int i; + char *s; + bool_t did_lines = FALSE; + + bool_t state = TRUE; /* new state of boolean parms. */ + + if (arg == NULL) { + showparms(FALSE); + return; + } + if (strncmp(arg, "all", 3) == 0) { + showparms(TRUE); + return; + } + if (strncmp(arg, "no", 2) == 0) { + state = FALSE; + arg += 2; + } + for (i = 0; params[i].fullname[0] != NUL; i++) { + s = params[i].fullname; + if (strncmp(arg, s, strlen(s)) == 0) /* matched full name */ + break; + s = params[i].shortname; + if (strncmp(arg, s, strlen(s)) == 0) /* matched short name */ + break; + } + + if (params[i].fullname[0] != NUL) { /* found a match */ + if (params[i].flags & P_NUM) { + did_lines = (i == P_LI); + if (inter && (arg[strlen(s)] != '=' || state == FALSE)) + emsg("Invalid set of numeric parameter"); + else { + params[i].value = atoi(arg + strlen(s) + 1); + params[i].flags |= P_CHANGED; + } + } else { /* boolean */ + if (inter && (arg[strlen(s)] == '=')) + emsg("Invalid set of boolean parameter"); + else { + params[i].value = state; + params[i].flags |= P_CHANGED; + } + } + } else { + if (inter) + emsg("Unrecognized 'set' option"); + } + + if (did_lines) { + Rows = P(P_LI); + screenalloc(); /* allocate new screen buffers */ + s_clear(); + } + /* + * Mark the screen contents as not valid in case we changed something + * like "tabstop" or "list" that will change its appearance. + */ + if (inter) + S_NOT_VALID; + + /* + * Check the bounds for numeric parameters here + */ + if (P(P_TS) <= 0 || P(P_TS) > 32) { + if (inter) + emsg("Invalid tab size specified"); + P(P_TS) = 8; + return; + } + if (P(P_SS) <= 0 || P(P_SS) > Rows) { + if (inter) + emsg("Invalid scroll size specified"); + P(P_SS) = 12; + return; + } + /* + * Check for another argument, and call doset() recursively, if found. If + * any argument results in an error, no further parameters are processed. + */ + while (*arg != ' ' && *arg != '\t') { /* skip to next white space */ + if (*arg == NUL) + return; /* end of parameter list */ + arg++; + } + while (*arg == ' ' || *arg == '\t') /* skip to next non-white */ + arg++; + + if (*arg) + doset(arg, TRUE); /* recurse on next parameter, if present */ +} + +static void +showparms(bool_t all) +/* bool_t all; /* show ALL parameters */ +{ + struct param *p; + char buf[64]; + + gotocmdline(YES, NUL); + outstr("Parameters:\r\n"); + + for (p = ¶ms[0]; p->fullname[0] != NUL; p++) { + if (!all && ((p->flags & P_CHANGED) == 0)) + continue; + if (p->flags & P_BOOL) + sprintf(buf, "\t%s%s\r\n", + (p->value ? "" : "no"), p->fullname); + else + sprintf(buf, "\t%s=%d\r\n", p->fullname, p->value); + + outstr(buf); + } + wait_return(); +} diff --git a/bin/vi/param.h b/bin/vi/param.h new file mode 100644 index 0000000..0b0c879 --- /dev/null +++ b/bin/vi/param.h @@ -0,0 +1,59 @@ +/* + * STEVIE - Simply Try this Editor for VI Enthusiasts + * + * Code Contributions By : Tim Thompson twitch!tjt + * Tony Andrews onecom!wldrdg!tony + * G. R. (Fred) Walter watmath!grwalter + */ + +/* + * Settable parameters + */ + +struct param { + char *fullname; /* full parameter name */ + char *shortname; /* permissible abbreviation */ + int value; /* parameter value */ + int flags; +}; + +extern struct param params[]; + +/* + * Flags + */ +#define P_BOOL 0x01 /* the parameter is boolean */ +#define P_NUM 0x02 /* the parameter is numeric */ +#define P_CHANGED 0x04 /* the parameter has been changed */ + +/* + * The following are the indices in the params array for each parameter + */ + +/* + * Numeric parameters + */ +#define P_TS 0 /* tab size */ +#define P_SS 1 /* scroll size */ +#define P_RP 2 /* report */ +#define P_LI 3 /* lines */ + +/* + * Boolean parameters + */ +#define P_VB 4 /* visual bell */ +#define P_SM 5 /* showmatch */ +#define P_WS 6 /* wrap scan */ +#define P_EB 7 /* error bells */ +#define P_MO 8 /* show mode */ +#define P_BK 9 /* make backups when writing out files */ +#define P_CR 10 /* use cr-lf to terminate lines on writes */ +#define P_LS 11 /* show tabs and newlines graphically */ +#define P_IC 12 /* ignore case in searches */ +#define P_AI 13 /* auto-indent */ +#define P_NU 14 /* number lines on the screen */ + +/* + * Macro to get the value of a parameter + */ +#define P(n) (params[n].value) diff --git a/bin/vi/regexp.c b/bin/vi/regexp.c new file mode 100644 index 0000000..a83da2a --- /dev/null +++ b/bin/vi/regexp.c @@ -0,0 +1,1318 @@ +/* + * NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE + * + * This is NOT the original regular expression code as written by + * Henry Spencer. This code has been modified specifically for use + * with the STEVIE editor, and should not be used apart from compiling + * STEVIE. If you want a good regular expression library, get the + * original code. The copyright notice that follows is from the + * original. + * + * NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE + * + * + * regcomp and regexec -- regsub and regerror are elsewhere + * + * Copyright (c) 1986 by University of Toronto. + * Written by Henry Spencer. Not derived from licensed software. + * + * Permission is granted to anyone to use this software for any + * purpose on any computer system, and to redistribute it freely, + * subject to the following restrictions: + * + * 1. The author is not responsible for the consequences of use of + * this software, no matter how awful, even if they arise + * from defects in it. + * + * 2. The origin of this software must not be misrepresented, either + * by explicit claim or by omission. + * + * 3. Altered versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * Beware that some of this code is subtly aware of the way operator + * precedence is structured in regular expressions. Serious changes in + * regular-expression syntax might require a total rethink. + * + * $Log: regexp.c,v $ + * Revision 1.1 1998/03/09 08:29:35 gdr-ftp + * Initial checkin of aroff, binprint, center, less, ls, make, makemake, + * passwd, ps, purge, shutdown, stty, upper, and vi. These sources are + * for the versions of the utils shipped with GNO v2.0.4. + * + * Revision 1.2 88/04/28 08:09:45 tony + * First modification of the regexp library. Added an external variable + * 'reg_ic' which can be set to indicate that case should be ignored. + * Added a new parameter to regexec() to indicate that the given string + * comes from the beginning of a line and is thus eligible to match + * 'beginning-of-line'. + * + */ +#ifdef __ORCAC__ +segment "regexp"; +#endif +#include "env.h" + +#ifdef MEGAMAX +overlay "regexp" +#endif + +#include +#ifdef __ORCAC__ +#include +#include +#include +#endif +#include "regexp.h" +#include "regmagic.h" + +int +cstrncmp(char *, char *,int ); + + +/* + * The "internal use only" fields in regexp.h are present to pass info from + * compile to execute that permits the execute phase to run lots faster on + * simple cases. They are: + * + * regstart char that must begin a match; '\0' if none obvious + * reganch is the match anchored (at beginning-of-line only)? + * regmust string (pointer into program) that match must include, or NULL + * regmlen length of regmust string + * + * Regstart and reganch permit very fast decisions on suitable starting points + * for a match, cutting down the work a lot. Regmust permits fast rejection + * of lines that cannot possibly match. The regmust tests are costly enough + * that regcomp() supplies a regmust only if the r.e. contains something + * potentially expensive (at present, the only such thing detected is * or + + * at the start of the r.e., which can involve a lot of backup). Regmlen is + * supplied because the test in regexec() needs it and regcomp() is computing + * it anyway. + */ + +/* + * Structure for regexp "program". This is essentially a linear encoding + * of a nondeterministic finite-state machine (aka syntax charts or + * "railroad normal form" in parsing technology). Each node is an opcode + * plus a "next" pointer, possibly plus an operand. "Next" pointers of + * all nodes except BRANCH implement concatenation; a "next" pointer with + * a BRANCH on both ends of it is connecting two alternatives. (Here we + * have one of the subtle syntax dependencies: an individual BRANCH (as + * opposed to a collection of them) is never concatenated with anything + * because of operator precedence.) The operand of some types of node is + * a literal string; for others, it is a node leading into a sub-FSM. In + * particular, the operand of a BRANCH node is the first node of the branch. + * (NB this is *not* a tree structure: the tail of the branch connects + * to the thing following the set of BRANCHes.) The opcodes are: + */ + +/* definition number opnd? meaning */ +#define END 0 /* no End of program. */ +#define BOL 1 /* no Match "" at beginning of line. */ +#define EOL 2 /* no Match "" at end of line. */ +#define ANY 3 /* no Match any one character. */ +#define ANYOF 4 /* str Match any character in this string. */ +#define ANYBUT 5 /* str Match any character not in this + * string. */ +#define BRANCH 6 /* node Match this alternative, or the + * next... */ +#define BACK 7 /* no Match "", "next" ptr points backward. */ +#define EXACTLY 8 /* str Match this string. */ +#define NOTHING 9 /* no Match empty string. */ +#define STAR 10 /* node Match this (simple) thing 0 or more + * times. */ +#define PLUS 11 /* node Match this (simple) thing 1 or more + * times. */ +#define OPEN 20 /* no Mark this point in input as start of + * #n. */ + /* OPEN+1 is number 1, etc. */ +#define CLOSE 30 /* no Analogous to OPEN. */ + +/* + * Opcode notes: + * + * BRANCH The set of branches constituting a single choice are hooked + * together with their "next" pointers, since precedence prevents + * anything being concatenated to any individual branch. The + * "next" pointer of the last BRANCH in a choice points to the + * thing following the whole choice. This is also where the + * final "next" pointer of each individual branch points; each + * branch starts with the operand node of a BRANCH node. + * + * BACK Normal "next" pointers all implicitly point forward; BACK + * exists to make loop structures possible. + * + * STAR,PLUS '?', and complex '*' and '+', are implemented as circular + * BRANCH structures using BACK. Simple cases (one character + * per match) are implemented with STAR and PLUS for speed + * and to minimize recursive plunges. + * + * OPEN,CLOSE ...are numbered at compile time. + */ + +/* + * A node is one char of opcode followed by two chars of "next" pointer. + * "Next" pointers are stored as two 8-bit pieces, high order first. The + * value is a positive offset from the opcode of the node containing it. + * An operand, if any, simply follows the node. (Note that much of the + * code generation knows about this implicit relationship.) + * + * Using two bytes for the "next" pointer is vast overkill for most things, + * but allows patterns to get big without disasters. + */ +#define OP(p) (*(p)) +#define NEXT(p) (((*((p)+1)&0377)<<8) + (*((p)+2)&0377)) +#define OPERAND(p) ((p) + 3) + +/* + * See regmagic.h for one further detail of program structure. + */ + + +/* + * Utility definitions. + */ +#ifndef CHARBITS +#define UCHARAT(p) ((int)*(unsigned char *)(p)) +#else +#define UCHARAT(p) ((int)*(p)&CHARBITS) +#endif + +#define FAIL(m) { regerror(m); return(NULL); } +#define ISMULT(c) ((c) == '*' || (c) == '+' || (c) == '?') +#define META "^$.[()|?+*\\" + +/* + * Flags to be passed up and down. + */ +#define HASWIDTH 01 /* Known never to match null string. */ +#define SIMPLE 02 /* Simple enough to be STAR/PLUS operand. */ +#define SPSTART 04 /* Starts with * or +. */ +#define WORST 0 /* Worst case. */ + +#ifndef ORIGINAL +/* + * The following supports the ability to ignore case in searches. + */ + +#include + +int reg_ic = 0; /* set by callers to ignore case */ + +/* + * mkup - convert to upper case IF we're doing caseless compares + */ +#define mkup(c) ((islower(c) && reg_ic) ? toupper(c) : (c)) + +#endif + +/* + * Global work variables for regcomp(). + */ +static char *regparse; /* Input-scan pointer. */ +static int regnpar; /* () count. */ +static char regdummy; +static char *regcode; /* Code-emit pointer; ®dummy = don't. */ +static long regsize; /* Code size. */ + +/* + * Forward declarations for regcomp()'s friends. + */ +#ifndef STATIC +#define STATIC static +#endif +STATIC char *reg(); +STATIC char *regbranch(); +STATIC char *regpiece(); +STATIC char *regatom(); +STATIC char *regnode(); +STATIC char *regnext(); +STATIC void regc(); +STATIC void reginsert(); +STATIC void regtail(); +STATIC void regoptail(); +#ifdef STRCSPN +STATIC int strcspn(); +#endif + +/* + - regcomp - compile a regular expression into internal code + * + * We can't allocate space until we know how big the compiled form will be, + * but we can't compile it (and thus know how big it is) until we've got a + * place to put the code. So we cheat: we compile it twice, once with code + * generation turned off and size counting turned on, and once "for real". + * This also means that we don't allocate space until we are sure that the + * thing really will compile successfully, and we never have to move the + * code and thus invalidate pointers into it. (Note that it has to be in + * one piece because free() must be able to free it all.) + * + * Beware that the optimization-preparation code in here knows about some + * of the structure of the compiled regexp. + */ +regexp * +regcomp(exp) + char *exp; +{ + register regexp *r; + register char *scan; + register char *longest; + register int len; + int flags; +/* extern char *malloc();*/ + extern char *alloc(); + + if (exp == NULL) + FAIL("NULL argument"); + + /* First pass: determine size, legality. */ + regparse = exp; + regnpar = 1; + regsize = 0L; + regcode = ®dummy; + regc(MAGIC); + if (reg(0, &flags) == NULL) + return (NULL); + + /* Small enough for pointer-storage convention? */ + if (regsize >= 32767L) /* Probably could be 65535L. */ + FAIL("regexp too big"); + + /* Allocate space. */ +/* r = (regexp *) malloc((unsigned) (sizeof(regexp) + regsize));*/ + r = (regexp *) alloc((unsigned) (sizeof(regexp) + regsize)); + if (r == NULL) + FAIL("out of space"); + + /* Second pass: emit code. */ + regparse = exp; + regnpar = 1; + regcode = r->program; + regc(MAGIC); + if (reg(0, &flags) == NULL) + return (NULL); + + /* Dig out information for optimizations. */ + r->regstart = '\0'; /* Worst-case defaults. */ + r->reganch = 0; + r->regmust = NULL; + r->regmlen = 0; + scan = r->program + 1; /* First BRANCH. */ + if (OP(regnext(scan)) == END) { /* Only one top-level choice. */ + scan = OPERAND(scan); + + /* Starting-point info. */ + if (OP(scan) == EXACTLY) + r->regstart = *OPERAND(scan); + else if (OP(scan) == BOL) + r->reganch++; + + /* + * If there's something expensive in the r.e., find the longest + * literal string that must appear and make it the regmust. Resolve + * ties in favor of later strings, since the regstart check works + * with the beginning of the r.e. and avoiding duplication + * strengthens checking. Not a strong reason, but sufficient in the + * absence of others. + */ + if (flags & SPSTART) { + longest = NULL; + len = 0; + for (; scan != NULL; scan = regnext(scan)) + if (OP(scan) == EXACTLY && strlen(OPERAND(scan)) >= len) { + longest = OPERAND(scan); + len = strlen(OPERAND(scan)); + } + r->regmust = longest; + r->regmlen = len; + } + } + return (r); +} + +/* + - reg - regular expression, i.e. main body or parenthesized thing + * + * Caller must absorb opening parenthesis. + * + * Combining parenthesis handling with the base level of regular expression + * is a trifle forced, but the need to tie the tails of the branches to what + * follows makes it hard to avoid. + */ +static char * +reg(paren, flagp) + int paren; /* Parenthesized? */ + int *flagp; +{ + register char *ret; + register char *br; + register char *ender; + register int parno; + int flags; + + *flagp = HASWIDTH; /* Tentatively. */ + + /* Make an OPEN node, if parenthesized. */ + if (paren) { + if (regnpar >= NSUBEXP) + FAIL("too many ()"); + parno = regnpar; + regnpar++; + ret = regnode(OPEN + parno); + } else + ret = NULL; + + /* Pick up the branches, linking them together. */ + br = regbranch(&flags); + if (br == NULL) + return (NULL); + if (ret != NULL) + regtail(ret, br); /* OPEN -> first. */ + else + ret = br; + if (!(flags & HASWIDTH)) + *flagp &= ~HASWIDTH; + *flagp |= flags & SPSTART; + while (*regparse == '|') { + regparse++; + br = regbranch(&flags); + if (br == NULL) + return (NULL); + regtail(ret, br); /* BRANCH -> BRANCH. */ + if (!(flags & HASWIDTH)) + *flagp &= ~HASWIDTH; + *flagp |= flags & SPSTART; + } + + /* Make a closing node, and hook it on the end. */ + ender = regnode((paren) ? CLOSE + parno : END); + regtail(ret, ender); + + /* Hook the tails of the branches to the closing node. */ + for (br = ret; br != NULL; br = regnext(br)) + regoptail(br, ender); + + /* Check for proper termination. */ + if (paren && *regparse++ != ')') { + FAIL("unmatched ()"); + } else if (!paren && *regparse != '\0') { + if (*regparse == ')') { + FAIL("unmatched ()"); + } else + FAIL("junk on end");/* "Can't happen". */ + /* NOTREACHED */ + } + return (ret); +} + +/* + - regbranch - one alternative of an | operator + * + * Implements the concatenation operator. + */ +static char * +regbranch(flagp) + int *flagp; +{ + register char *ret; + register char *chain; + register char *latest; + int flags; + + *flagp = WORST; /* Tentatively. */ + + ret = regnode(BRANCH); + chain = NULL; + while (*regparse != '\0' && *regparse != '|' && *regparse != ')') { + latest = regpiece(&flags); + if (latest == NULL) + return (NULL); + *flagp |= flags & HASWIDTH; + if (chain == NULL) /* First piece. */ + *flagp |= flags & SPSTART; + else + regtail(chain, latest); + chain = latest; + } + if (chain == NULL) /* Loop ran zero times. */ + (void) regnode(NOTHING); + + return (ret); +} + +/* + - regpiece - something followed by possible [*+?] + * + * Note that the branching code sequences used for ? and the general cases + * of * and + are somewhat optimized: they use the same NOTHING node as + * both the endmarker for their branch list and the body of the last branch. + * It might seem that this node could be dispensed with entirely, but the + * endmarker role is not redundant. + */ +static char * +regpiece(flagp) + int *flagp; +{ + register char *ret; + register char op; + register char *next; + int flags; + + ret = regatom(&flags); + if (ret == NULL) + return (NULL); + + op = *regparse; + if (!ISMULT(op)) { + *flagp = flags; + return (ret); + } + if (!(flags & HASWIDTH) && op != '?') + FAIL("*+ operand could be empty"); + *flagp = (op != '+') ? (WORST | SPSTART) : (WORST | HASWIDTH); + + if (op == '*' && (flags & SIMPLE)) + reginsert(STAR, ret); + else if (op == '*') { + /* Emit x* as (x&|), where & means "self". */ + reginsert(BRANCH, ret); /* Either x */ + regoptail(ret, regnode(BACK)); /* and loop */ + regoptail(ret, ret); /* back */ + regtail(ret, regnode(BRANCH)); /* or */ + regtail(ret, regnode(NOTHING)); /* null. */ + } else if (op == '+' && (flags & SIMPLE)) + reginsert(PLUS, ret); + else if (op == '+') { + /* Emit x+ as x(&|), where & means "self". */ + next = regnode(BRANCH); /* Either */ + regtail(ret, next); + regtail(regnode(BACK), ret); /* loop back */ + regtail(next, regnode(BRANCH)); /* or */ + regtail(ret, regnode(NOTHING)); /* null. */ + } else if (op == '?') { + /* Emit x? as (x|) */ + reginsert(BRANCH, ret); /* Either x */ + regtail(ret, regnode(BRANCH)); /* or */ + next = regnode(NOTHING);/* null. */ + regtail(ret, next); + regoptail(ret, next); + } + regparse++; + if (ISMULT(*regparse)) + FAIL("nested *?+"); + + return (ret); +} + +/* + - regatom - the lowest level + * + * Optimization: gobbles an entire sequence of ordinary characters so that + * it can turn them into a single node, which is smaller to store and + * faster to run. Backslashed characters are exceptions, each becoming a + * separate node; the code is simpler that way and it's not worth fixing. + */ +static char * +regatom(flagp) + int *flagp; +{ + register char *ret; + int flags; + + *flagp = WORST; /* Tentatively. */ + + switch (*regparse++) { + case '^': + ret = regnode(BOL); + break; + case '$': + ret = regnode(EOL); + break; + case '.': + ret = regnode(ANY); + *flagp |= HASWIDTH | SIMPLE; + break; + case '[':{ + register int class; + register int classend; + + if (*regparse == '^') { /* Complement of range. */ + ret = regnode(ANYBUT); + regparse++; + } else + ret = regnode(ANYOF); + if (*regparse == ']' || *regparse == '-') + regc(*regparse++); + while (*regparse != '\0' && *regparse != ']') { + if (*regparse == '-') { + regparse++; + if (*regparse == ']' || *regparse == '\0') + regc('-'); + else { + class = UCHARAT(regparse - 2) + 1; + classend = UCHARAT(regparse); + if (class > classend + 1) + FAIL("invalid [] range"); + for (; class <= classend; class++) + regc(class); + regparse++; + } + } else + regc(*regparse++); + } + regc('\0'); + if (*regparse != ']') + FAIL("unmatched []"); + regparse++; + *flagp |= HASWIDTH | SIMPLE; + } + break; + case '(': + ret = reg(1, &flags); + if (ret == NULL) + return (NULL); + *flagp |= flags & (HASWIDTH | SPSTART); + break; + case '\0': + case '|': + case ')': + FAIL("internal urp"); /* Supposed to be caught earlier. */ + /* break; Not Reached */ + case '?': + case '+': + case '*': + FAIL("?+* follows nothing"); + /* break; Not Reached */ + case '\\': + if (*regparse == '\0') + FAIL("trailing \\"); + ret = regnode(EXACTLY); + regc(*regparse++); + regc('\0'); + *flagp |= HASWIDTH | SIMPLE; + break; + default:{ + register int len; + register char ender; + + regparse--; + len = strcspn(regparse, META); + if (len <= 0) + FAIL("internal disaster"); + ender = *(regparse + len); + if (len > 1 && ISMULT(ender)) + len--; /* Back off clear of ?+* operand. */ + *flagp |= HASWIDTH; + if (len == 1) + *flagp |= SIMPLE; + ret = regnode(EXACTLY); + while (len > 0) { + regc(*regparse++); + len--; + } + regc('\0'); + } + break; + } + + return (ret); +} + +/* + - regnode - emit a node + */ +static char * /* Location. */ +regnode(op) + char op; +{ + register char *ret; + register char *ptr; + + ret = regcode; + if (ret == ®dummy) { + regsize += 3; + return (ret); + } + ptr = ret; + *ptr++ = op; + *ptr++ = '\0'; /* Null "next" pointer. */ + *ptr++ = '\0'; + regcode = ptr; + + return (ret); +} + +/* + - regc - emit (if appropriate) a byte of code + */ +static void +regc(b) + char b; +{ + if (regcode != ®dummy) + *regcode++ = b; + else + regsize++; +} + +/* + - reginsert - insert an operator in front of already-emitted operand + * + * Means relocating the operand. + */ +static void +reginsert(op, opnd) + char op; + char *opnd; +{ + register char *src; + register char *dst; + register char *place; + + if (regcode == ®dummy) { + regsize += 3; + return; + } + src = regcode; + regcode += 3; + dst = regcode; + while (src > opnd) + *--dst = *--src; + + place = opnd; /* Op node, where operand used to be. */ + *place++ = op; + *place++ = '\0'; + *place = '\0'; +} + +/* + - regtail - set the next-pointer at the end of a node chain + */ +static void +regtail(p, val) + char *p; + char *val; +{ + register char *scan; + register char *temp; + register int offset; + + if (p == ®dummy) + return; + + /* Find last node. */ + scan = p; + for (;;) { + temp = regnext(scan); + if (temp == NULL) + break; + scan = temp; + } + + if (OP(scan) == BACK) + offset = scan - val; + else + offset = val - scan; + *(scan + 1) = (char) ((offset >> 8) & 0377); + *(scan + 2) = (char) (offset & 0377); +} + +/* + - regoptail - regtail on operand of first argument; nop if operandless + */ +static void +regoptail(p, val) + char *p; + char *val; +{ + /* "Operandless" and "op != BRANCH" are synonymous in practice. */ + if (p == NULL || p == ®dummy || OP(p) != BRANCH) + return; + regtail(OPERAND(p), val); +} + +/* + * regexec and friends + */ + +/* + * Global work variables for regexec(). + */ +static char *reginput; /* String-input pointer. */ +static char *regbol; /* Beginning of input, for ^ check. */ +static char **regstartp; /* Pointer to startp array. */ +static char **regendp; /* Ditto for endp. */ + +/* + * Forwards. + */ +STATIC int regtry(); +STATIC int regmatch(); +STATIC int regrepeat(); + +#ifdef DEBUG +int regnarrate = 0; +void regdump(); +STATIC char *regprop(); +#endif + +/* + - regexec - match a regexp against a string + */ +int +regexec(prog, string, at_bol) + register regexp *prog; + register char *string; + int at_bol; +{ + register char *s; + extern char *cstrchr(); + + /* Be paranoid... */ + if (prog == NULL || string == NULL) { + regerror("NULL parameter"); + return (0); + } + /* Check validity of program. */ + if (UCHARAT(prog->program) != MAGIC) { + regerror("corrupted program"); + return (0); + } + /* If there is a "must appear" string, look for it. */ + if (prog->regmust != NULL) { + s = string; + while ((s = cstrchr(s, prog->regmust[0])) != NULL) { + if (cstrncmp(s, prog->regmust, prog->regmlen) == 0) + break; /* Found it. */ + s++; + } + if (s == NULL) /* Not present. */ + return (0); + } + /* Mark beginning of line for ^ . */ + if (at_bol) + regbol = string; /* is possible to match bol */ + else + regbol = NULL; /* we aren't there, so don't match it */ + + /* Simplest case: anchored match need be tried only once. */ + if (prog->reganch) + return (regtry(prog, string)); + + /* Messy cases: unanchored match. */ + s = string; + if (prog->regstart != '\0') + /* We know what char it must start with. */ + while ((s = cstrchr(s, prog->regstart)) != NULL) { + if (regtry(prog, s)) + return (1); + s++; + } + else + /* We don't -- general case. */ + do { + if (regtry(prog, s)) + return (1); + } while (*s++ != '\0'); + + /* Failure. */ + return (0); +} + +/* + - regtry - try match at specific point + */ +static int /* 0 failure, 1 success */ +regtry(prog, string) + regexp *prog; + char *string; +{ + register int i; + register char **sp; + register char **ep; + + reginput = string; + regstartp = prog->startp; + regendp = prog->endp; + + sp = prog->startp; + ep = prog->endp; + for (i = NSUBEXP; i > 0; i--) { + *sp++ = NULL; + *ep++ = NULL; + } + if (regmatch(prog->program + 1)) { + prog->startp[0] = string; + prog->endp[0] = reginput; + return (1); + } else + return (0); +} + +/* + - regmatch - main matching routine + * + * Conceptually the strategy is simple: check to see whether the current + * node matches, call self recursively to see whether the rest matches, + * and then act accordingly. In practice we make some effort to avoid + * recursion, in particular by going through "ordinary" nodes (that don't + * need to know whether the rest of the match failed) by a loop instead of + * by recursion. + */ +static int /* 0 failure, 1 success */ +regmatch(prog) + char *prog; +{ + register char *scan; /* Current node. */ + char *next; /* Next node. */ + extern char *strchr(); + + scan = prog; +#ifdef DEBUG + if (scan != NULL && regnarrate) + fprintf(stderr, "%s(\n", regprop(scan)); +#endif + while (scan != NULL) { +#ifdef DEBUG + if (regnarrate) + fprintf(stderr, "%s...\n", regprop(scan)); +#endif + next = regnext(scan); + + switch (OP(scan)) { + case BOL: + if (reginput != regbol) + return (0); + break; + case EOL: + if (*reginput != '\0') + return (0); + break; + case ANY: + if (*reginput == '\0') + return (0); + reginput++; + break; + case EXACTLY:{ + register int len; + register char *opnd; + + opnd = OPERAND(scan); + /* Inline the first character, for speed. */ + if (mkup(*opnd) != mkup(*reginput)) + return (0); + len = strlen(opnd); + if (len > 1 && cstrncmp(opnd, reginput, len) != 0) + return (0); + reginput += len; + } + break; + case ANYOF: + if (*reginput == '\0' || strchr(OPERAND(scan), *reginput) == NULL) + return (0); + reginput++; + break; + case ANYBUT: + if (*reginput == '\0' || strchr(OPERAND(scan), *reginput) != NULL) + return (0); + reginput++; + break; + case NOTHING: + break; + case BACK: + break; + case OPEN + 1: + case OPEN + 2: + case OPEN + 3: + case OPEN + 4: + case OPEN + 5: + case OPEN + 6: + case OPEN + 7: + case OPEN + 8: + case OPEN + 9:{ + register int no; + register char *save; + + no = OP(scan) - OPEN; + save = reginput; + + if (regmatch(next)) { + /* + * Don't set startp if some later invocation of the same + * parentheses already has. + */ + if (regstartp[no] == NULL) + regstartp[no] = save; + return (1); + } else + return (0); + } + /* break; Not Reached */ + case CLOSE + 1: + case CLOSE + 2: + case CLOSE + 3: + case CLOSE + 4: + case CLOSE + 5: + case CLOSE + 6: + case CLOSE + 7: + case CLOSE + 8: + case CLOSE + 9:{ + register int no; + register char *save; + + no = OP(scan) - CLOSE; + save = reginput; + + if (regmatch(next)) { + /* + * Don't set endp if some later invocation of the same + * parentheses already has. + */ + if (regendp[no] == NULL) + regendp[no] = save; + return (1); + } else + return (0); + } + /* break; Not Reached */ + case BRANCH:{ + register char *save; + + if (OP(next) != BRANCH) /* No choice. */ + next = OPERAND(scan); /* Avoid recursion. */ + else { + do { + save = reginput; + if (regmatch(OPERAND(scan))) + return (1); + reginput = save; + scan = regnext(scan); + } while (scan != NULL && OP(scan) == BRANCH); + return (0); + /* NOTREACHED */ + } + } + break; + case STAR: + case PLUS:{ + register char nextch; + register int no; + register char *save; + register int min; + + /* + * Lookahead to avoid useless match attempts when we know + * what character comes next. + */ + nextch = '\0'; + if (OP(next) == EXACTLY) + nextch = *OPERAND(next); + min = (OP(scan) == STAR) ? 0 : 1; + save = reginput; + no = regrepeat(OPERAND(scan)); + while (no >= min) { + /* If it could work, try it. */ + if (nextch == '\0' || *reginput == nextch) + if (regmatch(next)) + return (1); + /* Couldn't or didn't -- back up. */ + no--; + reginput = save + no; + } + return (0); + } + /* break; Not Reached */ + case END: + return (1); /* Success! */ + /* break; Not Reached */ + default: + regerror("memory corruption"); + return (0); + /* break; Not Reached */ + } + + scan = next; + } + + /* + * We get here only if there's trouble -- normally "case END" is the + * terminating point. + */ + regerror("corrupted pointers"); + return (0); +} + +/* + - regrepeat - repeatedly match something simple, report how many + */ +static int +regrepeat(p) + char *p; +{ + register int count = 0; + register char *scan; + register char *opnd; + + scan = reginput; + opnd = OPERAND(p); + switch (OP(p)) { + case ANY: + count = strlen(scan); + scan += count; + break; + case EXACTLY: + while (mkup(*opnd) == mkup(*scan)) { + count++; + scan++; + } + break; + case ANYOF: + while (*scan != '\0' && strchr(opnd, *scan) != NULL) { + count++; + scan++; + } + break; + case ANYBUT: + while (*scan != '\0' && strchr(opnd, *scan) == NULL) { + count++; + scan++; + } + break; + default: /* Oh dear. Called inappropriately. */ + regerror("internal foulup"); + count = 0; /* Best compromise. */ + break; + } + reginput = scan; + + return (count); +} + +/* + - regnext - dig the "next" pointer out of a node + */ +static char * +regnext(p) + register char *p; +{ + register int offset; + + if (p == ®dummy) + return (NULL); + + offset = NEXT(p); + if (offset == 0) + return (NULL); + + if (OP(p) == BACK) + return (p - offset); + else + return (p + offset); +} + +#ifdef DEBUG + +STATIC char *regprop(); + +/* + - regdump - dump a regexp onto stdout in vaguely comprehensible form + */ +void +regdump(r) + regexp *r; +{ + register char *s; + register char op = EXACTLY; /* Arbitrary non-END op. */ + register char *next; + extern char *strchr(); + + + s = r->program + 1; + while (op != END) { /* While that wasn't END last time... */ + op = OP(s); + printf("%2d%s", s - r->program, regprop(s)); /* Where, what. */ + next = regnext(s); + if (next == NULL) /* Next ptr. */ + printf("(0)"); + else + printf("(%d)", (s - r->program) + (next - s)); + s += 3; + if (op == ANYOF || op == ANYBUT || op == EXACTLY) { + /* Literal string, where present. */ + while (*s != '\0') { + putchar(*s); + s++; + } + s++; + } + putchar('\n'); + } + + /* Header fields of interest. */ + if (r->regstart != '\0') + printf("start `%c' ", r->regstart); + if (r->reganch) + printf("anchored "); + if (r->regmust != NULL) + printf("must have \"%s\"", r->regmust); + printf("\n"); +} + +/* + - regprop - printable representation of opcode + */ +static char * +regprop(op) + char *op; +{ + register char *p; + static char buf[50]; + + (void) strcpy(buf, ":"); + + switch (OP(op)) { + case BOL: + p = "BOL"; + break; + case EOL: + p = "EOL"; + break; + case ANY: + p = "ANY"; + break; + case ANYOF: + p = "ANYOF"; + break; + case ANYBUT: + p = "ANYBUT"; + break; + case BRANCH: + p = "BRANCH"; + break; + case EXACTLY: + p = "EXACTLY"; + break; + case NOTHING: + p = "NOTHING"; + break; + case BACK: + p = "BACK"; + break; + case END: + p = "END"; + break; + case OPEN + 1: + case OPEN + 2: + case OPEN + 3: + case OPEN + 4: + case OPEN + 5: + case OPEN + 6: + case OPEN + 7: + case OPEN + 8: + case OPEN + 9: + sprintf(buf + strlen(buf), "OPEN%d", OP(op) - OPEN); + p = NULL; + break; + case CLOSE + 1: + case CLOSE + 2: + case CLOSE + 3: + case CLOSE + 4: + case CLOSE + 5: + case CLOSE + 6: + case CLOSE + 7: + case CLOSE + 8: + case CLOSE + 9: + sprintf(buf + strlen(buf), "CLOSE%d", OP(op) - CLOSE); + p = NULL; + break; + case STAR: + p = "STAR"; + break; + case PLUS: + p = "PLUS"; + break; + default: + regerror("corrupted opcode"); + break; + } + if (p != NULL) + (void) strcat(buf, p); + return (buf); +} +#endif + +/* + * The following is provided for those people who do not have strcspn() in + * their C libraries. They should get off their butts and do something + * about it; at least one public-domain implementation of those (highly + * useful) string routines has been published on Usenet. + */ +#ifdef STRCSPN +/* + * strcspn - find length of initial segment of s1 consisting entirely + * of characters not from s2 + */ + +static int +strcspn(s1, s2) + char *s1; + char *s2; +{ + register char *scan1; + register char *scan2; + register int count; + + count = 0; + for (scan1 = s1; *scan1 != '\0'; scan1++) { + for (scan2 = s2; *scan2 != '\0';) /* ++ moved down. */ + if (*scan1 == *scan2++) + return (count); + count++; + } + return (count); +} +#endif + +int +cstrncmp(char *s1, char *s2,int n) + /*char *s1, *s2; + int n;*/ +{ + char *p, *S1, *S2, *strsave(); + int rval; + + if (!reg_ic) + return (strncmp(s1, s2, n)); + + S1 = strsave(s1); + S2 = strsave(s2); + + for (p = S1; *p; p++) + if (islower(*p)) + *p = toupper(*p); + + for (p = S2; *p; p++) + if (islower(*p)) + *p = toupper(*p); + + rval = strncmp(S1, S2, n); + + free(S1); + free(S2); + + return rval; +} + +char * +cstrchr(s, c) + char *s; + char c; +{ + char *p; + + for (p = s; *p; p++) { + if (mkup(*p) == mkup(c)) + return p; + } + return NULL; +} diff --git a/bin/vi/regexp.h b/bin/vi/regexp.h new file mode 100644 index 0000000..8bd5773 --- /dev/null +++ b/bin/vi/regexp.h @@ -0,0 +1,36 @@ +/* + * NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE + * + * This is NOT the original regular expression code as written by + * Henry Spencer. This code has been modified specifically for use + * with the STEVIE editor, and should not be used apart from compiling + * STEVIE. If you want a good regular expression library, get the + * original code. The copyright notice that follows is from the + * original. + * + * NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE + * + * Definitions etc. for regexp(3) routines. + * + * Caveat: this is V8 regexp(3) [actually, a reimplementation thereof], + * not the System V one. + */ +#define NSUBEXP 10 +typedef struct regexp { + char *startp[NSUBEXP]; + char *endp[NSUBEXP]; + char regstart; /* Internal use only. */ + char reganch; /* Internal use only. */ + char *regmust; /* Internal use only. */ + int regmlen; /* Internal use only. */ + char program[1]; /* Unwarranted chumminess with compiler. */ +} regexp; + +extern regexp *regcomp(); +extern int regexec(); +extern void regsub(); +extern void regerror(); + +#ifndef ORIGINAL +extern int reg_ic; /* set non-zero to ignore case in searches */ +#endif diff --git a/bin/vi/regmagic.h b/bin/vi/regmagic.h new file mode 100644 index 0000000..b6b579e --- /dev/null +++ b/bin/vi/regmagic.h @@ -0,0 +1,16 @@ +/* + * NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE + * + * This is NOT the original regular expression code as written by + * Henry Spencer. This code has been modified specifically for use + * with the STEVIE editor, and should not be used apart from compiling + * STEVIE. If you want a good regular expression library, get the + * original code. The copyright notice that follows is from the + * original. + * + * NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE + * + * The first byte of the regexp internal "program" is actually this magic + * number; the start node begins in the second byte. + */ +#define MAGIC 0234 diff --git a/bin/vi/regsub.c b/bin/vi/regsub.c new file mode 100644 index 0000000..d769225 --- /dev/null +++ b/bin/vi/regsub.c @@ -0,0 +1,111 @@ +/* + * NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE + * + * This is NOT the original regular expression code as written by + * Henry Spencer. This code has been modified specifically for use + * with the STEVIE editor, and should not be used apart from compiling + * STEVIE. If you want a good regular expression library, get the + * original code. The copyright notice that follows is from the + * original. + * + * NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE + * + * regsub + * + * Copyright (c) 1986 by University of Toronto. + * Written by Henry Spencer. Not derived from licensed software. + * + * Permission is granted to anyone to use this software for any + * purpose on any computer system, and to redistribute it freely, + * subject to the following restrictions: + * + * 1. The author is not responsible for the consequences of use of + * this software, no matter how awful, even if they arise + * from defects in it. + * + * 2. The origin of this software must not be misrepresented, either + * by explicit claim or by omission. + * + * 3. Altered versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * $Log: regsub.c,v $ + * Revision 1.1 1998/03/09 08:29:50 gdr-ftp + * Initial checkin of aroff, binprint, center, less, ls, make, makemake, + * passwd, ps, purge, shutdown, stty, upper, and vi. These sources are + * for the versions of the utils shipped with GNO v2.0.4. + * + * Revision 1.2 88/04/28 08:11:25 tony + * First modification of the regexp library. Added an external variable + * 'reg_ic' which can be set to indicate that case should be ignored. + * Added a new parameter to regexec() to indicate that the given string + * comes from the beginning of a line and is thus eligible to match + * 'beginning-of-line'. + * + */ + +#include "env.h" + +#ifdef MEGAMAX +overlay "regexp" +#endif + +#include +#include "regexp.h" +#include "regmagic.h" + +#ifndef CHARBITS +#define UCHARAT(p) ((int)*(unsigned char *)(p)) +#else +#define UCHARAT(p) ((int)*(p)&CHARBITS) +#endif + +/* + - regsub - perform substitutions after a regexp match + */ +void +regsub(prog, source, dest) + regexp *prog; + char *source; + char *dest; +{ + register char *src; + register char *dst; + register char c; + register int no; + register int len; + extern char *strncpy(); + + if (prog == NULL || source == NULL || dest == NULL) { + regerror("NULL parm to regsub"); + return; + } + if (UCHARAT(prog->program) != MAGIC) { + regerror("damaged regexp fed to regsub"); + return; + } + src = source; + dst = dest; + while ((c = *src++) != '\0') { + if (c == '&') + no = 0; + else if (c == '\\' && '0' <= *src && *src <= '9') + no = *src++ - '0'; + else + no = -1; + if (no < 0) { /* Ordinary character. */ + if (c == '\\' && (*src == '\\' || *src == '&')) + c = *src++; + *dst++ = c; + } else if (prog->startp[no] != NULL && prog->endp[no] != NULL) { + len = prog->endp[no] - prog->startp[no]; + (void) strncpy(dst, prog->startp[no], len); + dst += len; + if (len != 0 && *(dst - 1) == '\0') { /* strncpy hit NUL. */ + regerror("damaged match string"); + return; + } + } + } + *dst = '\0'; +} diff --git a/bin/vi/s.io.c b/bin/vi/s.io.c new file mode 100644 index 0000000..56ba3a6 --- /dev/null +++ b/bin/vi/s.io.c @@ -0,0 +1,485 @@ +/* + * s_io() - routines that do screen I/O or effect what we think is + * on the screen. + * + * By G. R. (Fred) Walter watmath!grwalter + */ + +#include "stevie.h" + +segment "s_io"; + +/* + * s_cursor_off() - turn off the cursor (if it is appropriate) + */ + +void +s_cursor_off(void) +{ +#ifdef AMIGA + if (!Aux_Device) + outstr(T_CI); +#else + toutstr(T_CI); +#endif +} + +/* + * s_cursor_on() - turn on the cursor (if it is appropriate) + */ + +void +s_cursor_on(void) +{ +#ifdef AMIGA + if (!Aux_Device) + outstr(T_CV); +#else + toutstr(T_CV); +#endif +} + +/* + * screen_ins(row, nlines, total_rows) - insert 'nlines' lines at 'row' + * + * NOTE: this routine assumes it is called with valid arguments. + */ + +static void +screen_ins(int row, int nlines, int total_rows) +{ + if (nlines < 1 || (row + nlines) > total_rows) + return; + +#ifndef T_IL_B + { + int i; + + for (i = 0; i < nlines; i++) { + windgoto(row, 0); + if (T_IL != NULL) + toutstr(T_IL); + else InsertLine(); + } + } +#else + windgoto(row, 0); + toutstr(T_IL); + + if (nlines >= 10) + outchar((char) (nlines / 10 + '0')); + outchar((char) (nlines % 10 + '0')); + toutstr(T_IL_B); +#endif + + /* delete any garbage that may have been shifted to the status line */ + windgoto(total_rows - 1, 0); + toutstr(T_EL); +} + +/* + * screen_del(row, nlines, total_rows) - delete 'nlines' lines at 'row' + * + * NOTE: this routine assumes it is called with valid arguments. + */ + +static void +screen_del(int row, int nlines, int total_rows) +{ + if (nlines < 1 || (row + nlines) > total_rows) + return; + + /* delete any garbage that may have been on the status line */ + windgoto(total_rows - 1, 0); + toutstr(T_EL); + +#ifndef T_DL_B + { + int i; + + for (i = 0; i < nlines; i++) { + windgoto(row, 0); + if (T_DL != NULL) + toutstr(T_DL); /* delete a line */ + else DeleteLine(); + } + } +#else + windgoto(row, 0); + toutstr(T_DL); + if (nlines >= 10) + outchar((char) (nlines / 10 + '0')); + outchar((char) (nlines % 10 + '0')); + toutstr(T_DL_B); +#endif +} + +/* + * screen_refresh() + * + * Based on the current value of Topchar, refresh the contents of the screen + * and update Botchar. + */ + +static void +screen_refresh(int type) +{ + char *ptr; + int total_rows; + int row; + int col; + LINE *memp; + LPtr start; + bool_t off_top; + bool_t done; /* if TRUE, we hit the end of the file */ + bool_t didline; /* if TRUE, we finished the last line */ + int lno; /* number of the line we're doing */ + int idx; + int i; + int j; + + if (NumLineSizes <= 0) + type = NOT_VALID; + + if (!RedrawingDisabled) + s_cursor_off(); + + off_top = FALSE; + idx = 0; + row = 0; + total_rows = Rows - 1; + memp = Topchar->linep; + + if ((type == VALID) || (type == VALID_TO_CURSCHAR)) { + j = -1; + for (i = 0; i < NumLineSizes; i++) { + if (LinePointers[i] == memp) { + j = i; + break; + } + row += LineSizes[i]; + } + if (j == -1) { + /* Are we off the top of the screen by one line ? */ + if (memp->next == LinePointers[0]) { + i = plines(Topchar->linep->s); + if (i < (total_rows)) { + off_top = TRUE; + for (idx = NumLineSizes; idx > 0; idx--) { + LinePointers[idx] = LinePointers[idx - 1]; + LineSizes[idx] = LineSizes[idx - 1]; + } + LineSizes[idx] = (char) i; + if (!RedrawingDisabled) + screen_ins(0, i, Rows); + } + } + row = 0; + } else if (j == 0 && type == VALID) { + if (!RedrawingDisabled) + s_cursor_on(); + return; + } else { + if (!RedrawingDisabled) + screen_del(0, row, Rows); + row = 0; + for (;;) { + LineSizes[idx] = LineSizes[j]; + LinePointers[idx] = LinePointers[j]; + + if (type == VALID_TO_CURSCHAR) { + if (LinePointers[idx] == Curschar->linep) { + memp = LinePointers[idx]; + break; + } + } + j++; + if (j >= NumLineSizes) { + memp = LinePointers[idx]; + if (memp->next != Fileend->linep) { + row += LineSizes[idx]; + idx++; + memp = memp->next; + } + break; + } + row += LineSizes[idx]; + idx++; + } + } + } + if (P(P_NU)) { + start.linep = memp; + lno = cntllines(Filemem, &start); + } + didline = TRUE; + done = FALSE; + + for (;;) { + ptr = format_line(memp->s, &col); + i = 1 + ((col - 1) / Columns); + if ((row + i) <= total_rows) { + LinePointers[idx] = memp; + LineSizes[idx++] = (char) i; + if (!RedrawingDisabled) { + windgoto(row, 0); + + if (P(P_NU)) + outstr(mkline(lno++)); + outstr(ptr); + + j = col; + col %= Columns; + if ((col != 0) || (j == 0)) { +#ifdef T_END_L + windgoto(row + i - 1, col); + toutstr(T_END_L); +#else + for (; col < Columns; col++) + outchar(' '); +#endif + } + } + row += i; + if (memp->next != Fileend->linep) { + memp = memp->next; + } else { + done = TRUE; + break; + } + if (off_top) + break; + } else { + didline = FALSE; + break; + } + } + + /* Do we have to do 'off the top of the screen' processing ? */ + if (off_top && !done) { + row = 0; + for (idx = 0; idx <= NumLineSizes && row < total_rows; idx++) { + row += LineSizes[idx]; + } + + idx--; + + if (row < total_rows) { + if (LinePointers[idx]->next == Fileend->linep) + done = TRUE; + idx++; + } else if (row > total_rows) { + row -= LineSizes[idx]; + didline = FALSE; + memp = LinePointers[idx]; + } else { + didline = TRUE; + memp = LinePointers[idx]->next; + idx++; + } + } + NumLineSizes = idx; + + if (done && didline) { + ptr = "~\n"; + } else { + ptr = "@\n"; + } + + if (!RedrawingDisabled) { + if (row < total_rows) { + /* Clear the rest of the screen. */ +#ifdef T_END_D + windgoto(row, 0); + toutstr(T_END_D); +#else + screen_del(row, total_rows - row, Rows); + windgoto(row, 0); +#endif + } + /* put '@'s or '~'s on the remaining rows */ + for (; row < total_rows; row++) + outstr(ptr); + + s_cursor_on(); + } + if (done) + *Botchar = *Fileend; /* we hit the end of the file */ + else + Botchar->linep = memp; +} + +/* + * s_refresh() + * + * Based on the current value of Curschar, (if necessary) update Topchar and + * Botchar and refresh the screen contensts. + */ + +void +s_refresh(int type) +{ + LPtr *p; + LPtr *pp; + int i; + int nlines; + int refreshed; + + refreshed = FALSE; + + if (bufempty()) { /* special case - file is empty */ + *Topchar = *Filemem; + *Curschar = *Filemem; + screen_refresh(NOT_VALID); + return; + } + if (NumLineSizes < 0) { + type = NOT_VALID; + } + if (type != VALID) { + screen_refresh(type); + refreshed = TRUE; + type = VALID; + } + if (LINEOF(Curschar) < LINEOF(Topchar)) { + nlines = cntllines(Curschar, Topchar); + /* + * if the cursor is above the top of the screen, put it at the top of + * the screen.. + */ + *Topchar = *Curschar; + Topchar->index = 0; + /* + * ... and, if we weren't very close to begin with, we scroll so that + * the line is close to the middle. + */ + if (nlines > Rows / 3) { + p = Topchar; + for (i = 0; i < Rows / 3; i += plines(p->linep->s)) { + pp = prevline(p); + if (pp == NULL) + break; + p = pp; + } + *Topchar = *p; + } + screen_refresh(VALID); + } else if (LINEOF(Curschar) >= LINEOF(Botchar)) { + nlines = cntllines(Botchar, Curschar); + /* + * If the cursor is off the bottom of the screen, put it at the top + * of the screen.. ... and back up + */ + if (nlines > Rows / 3) { + p = Curschar; + for (i = 0; i < (2 * Rows) / 3; i += plines(p->linep->s)) { + pp = prevline(p); + if (pp == NULL) + break; + p = pp; + } + *Topchar = *p; + } else { + scrollup(nlines); + } + screen_refresh(VALID); + } else if (refreshed == FALSE) { + screen_refresh(type); + } + /* Check if we are below Botchar (this can occur). */ + if (LINEOF(Curschar) == LINEOF(Botchar)) { + pp = nextline(Topchar); + if (pp != NULL) { + Topchar->linep = pp->linep; + screen_refresh(VALID); + } + } else if (LINEOF(Curschar) >= LINEOF(Botchar)) { + nlines = cntllines(Botchar, Curschar); + /* + * If the cursor is off the bottom of the screen, put it at the top + * of the screen.. ... and back up + */ + if (nlines > Rows / 3) { + p = Curschar; + for (i = 0; i < (2 * Rows) / 3; i += plines(p->linep->s)) { + pp = prevline(p); + if (pp == NULL) + break; + p = pp; + } + *Topchar = *p; + } else { + scrollup(nlines); + } + screen_refresh(VALID); + } +} + +/* + * s_clear() - clear the screen and mark the stored information as invalid. + */ +void +s_clear(void) +{ + toutstr(T_ED); /* clear the display */ + S_NOT_VALID; +} + +/* + * Update_Botchar() + * + * Based on the current value of Topchar update Botchar. + */ + +void +Update_Botchar(void) +{ + int row; + LINE *memp; + int total_rows; + int i; + + row = 0; + total_rows = Rows - 1; + memp = Topchar->linep; + + for (;;) { + i = plines(memp->s); + if ((row + i) <= total_rows) { + row += i; + memp = memp->next; + if (memp == Fileend->linep) + break; + } else { + break; + } + } + Botchar->linep = memp; + + MustUpdateBotchar = FALSE; +} + +#ifdef DONTINCLUDEANYMORE +/* + * NotValidFromCurschar() + * + * Mark the lines in NumLinePointers and NumLineSizes from Curschar on as + * not valid. + */ + +void +NotValidFromCurschar(void) +{ + register int idx; + register unsigned long num; + + S_VALID_TO_CURSCHAR; + + num = LINEOF(Curschar); + for (idx = 0; idx < NumLineSizes; idx++) { + if (LinePointers[idx]->num >= num) + break; + } + NumLineSizes = idx; +} +#endif diff --git a/bin/vi/screen.c b/bin/vi/screen.c new file mode 100644 index 0000000..8980f24 --- /dev/null +++ b/bin/vi/screen.c @@ -0,0 +1,148 @@ +/* + * STEVIE - Simply Try this Editor for VI Enthusiasts + * + * Code Contributions By : Tim Thompson twitch!tjt + * Tony Andrews onecom!wldrdg!tony + * G. R. (Fred) Walter watmath!grwalter + */ + +#include "stevie.h" + +/* + * The following variable is set (in cursupdate) to the number of physical + * lines taken by the line the cursor is on. We use this to avoid extra calls + * to plines(). The optimized routine updateline() makes sure that the size of + * the cursor line hasn't changed. If so, lines below the cursor will move up + * or down and we need to call the routine s_refresh() to examine the + * entire screen. + */ +static int Cline_size; /* size (in rows) of the cursor line */ +static int Cline_row; /* starting row of the cursor line */ + +/* + * updateline() - like s_refresh() but only for cursor line + * + * This determines whether or not we need to call s_refresh() to examine + * the entire screen for changes. This occurs if the size of the cursor line + * (in rows) has changed. + */ +static void +updateline(void) +{ + char *ptr; + int col; + int size; + int j; + + if (RedrawingDisabled) /* Is this the correct action ? */ + return; + + ptr = format_line(Curschar->linep->s, &col); + + size = 1 + ((col - 1) / Columns); + if (Cline_size == size) { + s_cursor_off(); + windgoto(Cline_row, 0); + if (P(P_NU)) { + /* + * This should be done more efficiently. + */ + outstr(mkline(cntllines(Filemem, Curschar))); + } + outstr(ptr); + + j = col; + col %= Columns; + if ((col != 0) || (j == 0)) { +#ifdef T_END_L + windgoto(Cline_row + size - 1, col); + toutstr(T_END_L); +#else + for (; col < Columns; col++) + outchar(' '); +#endif + } + s_cursor_on(); + } else { + s_refresh(VALID_TO_CURSCHAR); + } +} + +void +cursupdate(int type) +{ + register char c; + register int incr; + register int i; + register int didincr; + + if (MustUpdateBotchar == TRUE) + Update_Botchar(); + + if (NumLineSizes < 0) { + s_refresh(NOT_VALID); + } else { + if (LineNotValid == TRUE) + updateline(); + + if (type != UPDATE_CURSOR) { + s_refresh(type); + } else if (ValidToCurschar == TRUE) { + s_refresh(VALID_TO_CURSCHAR); + } else if (CheckTopcharAndBotchar == TRUE) { + s_refresh(VALID); + } + } + + CheckTopcharAndBotchar = FALSE; + MustUpdateBotchar = FALSE; + ValidToCurschar = FALSE; + LineNotValid = FALSE; + + Cursrow = Curscol = Cursvcol = i = 0; + for (i = 0; i < NumLineSizes; i++) { + if (LinePointers[i] == Curschar->linep) + break; + Cursrow += LineSizes[i]; + } + + if (P(P_NU)) + Curscol = 8; + + Cline_row = Cursrow; + Cline_size = LineSizes[i]; + + for (i = 0; i <= Curschar->index; i++) { + c = Curschar->linep->s[i]; + /* A tab gets expanded, depending on the current column */ + if (c == TAB && !P(P_LS)) + incr = P(P_TS) - (Cursvcol % P(P_TS)); + else + incr = chars[c].ch_size; + Curscol += incr; + Cursvcol += incr; + if (Curscol >= Columns) { + Curscol -= Columns; + Cursrow++; + didincr = TRUE; + } else + didincr = FALSE; + } + if (didincr) + Cursrow--; + + if (c == TAB && State == NORMAL && !P(P_LS)) { + Curscol--; + Cursvcol--; + } else { + Curscol -= incr; + Cursvcol -= incr; + } + if (Curscol < 0) + Curscol += Columns; + + if (set_want_col) { + Curswant = Cursvcol; + set_want_col = FALSE; + } +} diff --git a/bin/vi/search.c b/bin/vi/search.c new file mode 100644 index 0000000..7396c03 --- /dev/null +++ b/bin/vi/search.c @@ -0,0 +1,1028 @@ +/* + * STEVIE - Simply Try this Editor for VI Enthusiasts + * + * Code Contributions By : Tim Thompson twitch!tjt + * Tony Andrews onecom!wldrdg!tony + * G. R. (Fred) Walter watmath!grwalter + */ + +#ifdef __ORCAC__ +segment "search"; +#endif + +#include "stevie.h" +/* modified Henry Spencer's regular expression routines */ +#include "regexp.h" + +#ifdef MEGAMAX +overlay "search" +#endif + +/* + * This file contains various searching-related routines. These fall into + * three groups: string searches (for /, ?, n, and N), character searches + * within a single line (for f, F, t, T, etc), and "other" kinds of searches + * like the '%' command, and 'word' searches. + */ + +/* + * String searches + * + * The actual searches are done using Henry Spencer's regular expression + * library. + */ + +#define BEGWORD "([^a-zA-Z0-9_]|^)" /* replaces "\<" in search strings */ +#define ENDWORD "([^a-zA-Z0-9_]|$)" /* likewise replaces "\>" */ + +bool_t begword; /* does the search include a 'begin word' + * match */ + +/* + * mapstring(s) - map special backslash sequences + */ +static char * +mapstring(char *s) +{ + static char ns[MAX_COLUMNS + 1]; + register char *p; + + begword = FALSE; + + for (p = ns; *s; s++) { + if ((*s == '(') || (*s == ')')) { + *p++ = '\\'; + *p++ = *s; + continue; + } + if (*s != '\\') { /* not an escape */ + *p++ = *s; + continue; + } + switch (*++s) { + case '/': + *p++ = '/'; + break; + + case '<': + strcpy(p, BEGWORD); + p += strlen(BEGWORD); + begword = TRUE; + break; + + case '>': + strcpy(p, ENDWORD); + p += strlen(ENDWORD); + break; + + default: + *p++ = '\\'; + *p++ = *s; + break; + } + } + *p = NUL; + + return ns; +} + +static LPtr * +bcksearch(char *str) +{ + static LPtr infile; + register LPtr *p; + regexp *prog; + register char *s; + register int i; + bool_t want_start = (*str == '^'); /* looking for start of line? */ + register char *match; + + /* make sure str isn't empty */ + if (str == NULL || *str == NUL) + return NULL; + + prog = regcomp(str); + if (prog == NULL) { + emsg("Invalid search string"); + return NULL; + } + p = Curschar; + dec(p); + + if (begword) /* so we don't get stuck on one match */ + dec(p); + + i = (want_start) ? 0 : p->index; + + do { + s = p->linep->s; + + if (regexec(prog, s, TRUE)) { /* match somewhere on line */ + + if (want_start) { /* could only have been one */ + infile.linep = p->linep; + infile.index = (int) (prog->startp[0] - s); + free((char *) prog); + return (&infile); + } + /* + * Now, if there are multiple matches on this line, we have to + * get the last one. Or the last one before the cursor, if we're + * on that line. + */ + + match = prog->startp[0]; + + while (regexec(prog, prog->endp[0], FALSE)) { + if ((i >= 0) && ((prog->startp[0] - s) > i)) + break; + match = prog->startp[0]; + } + + if ((i >= 0) && ((match - s) > i)) { + i = -1; + continue; + } + infile.linep = p->linep; + infile.index = (int) (match - s); + free((char *) prog); + return (&infile); + } + i = -1; + + } while ((p = prevline(p)) != NULL); + + /* + * If wrapscan isn't set, bag the search now + */ + if (!P(P_WS)) { + free((char *) prog); + return NULL; + } + /* search backward from the end of the file */ + p = prevline(Fileend); + do { + s = p->linep->s; + + if (regexec(prog, s, TRUE)) { /* match somewhere on line */ + + if (want_start) { /* could only have been one */ + infile.linep = p->linep; + infile.index = (int) (prog->startp[0] - s); + free((char *) prog); + return (&infile); + } + /* + * Now, if there are multiple matches on this line, we have to + * get the last one. + */ + + match = prog->startp[0]; + + while (regexec(prog, prog->endp[0], FALSE)) + match = prog->startp[0]; + + infile.linep = p->linep; + infile.index = (int) (match - s); + free((char *) prog); + return (&infile); + } + if (p->linep == Curschar->linep) + break; + + } while ((p = prevline(p)) != NULL); + + free((char *) prog); + return NULL; +} + +static LPtr * +fwdsearch(char *str) +{ + static LPtr infile; + LPtr *p; + regexp *prog; + bool_t want_start = (*str == '^'); /* looking for start of line? */ + + char *s; + int i; + + prog = regcomp(str); + if (prog == NULL) { + emsg("Invalid search string"); + return NULL; + } + p = Curschar; + i = Curschar->index + 1; + do { + s = p->linep->s + i; + i = 0; + + if (regexec(prog, s, i == 0)) { /* got a match */ + /* + * If we wanted the start of a line and we aren't really there, + * then a match doesn't count. + */ + if (want_start && (s != p->linep->s)) + continue; + + infile.linep = p->linep; + infile.index = (int) (prog->startp[0] - p->linep->s); + free((char *) prog); + return (&infile); + } + } while ((p = nextline(p)) != NULL); + + /* + * If wrapscan isn't set, then don't scan from the beginning of the file. + * Just return failure here. + */ + if (!P(P_WS)) { + free((char *) prog); + return NULL; + } + /* search from the beginning of the file to Curschar */ + for (p = Filemem; p != NULL; p = nextline(p)) { + s = p->linep->s; + + if (regexec(prog, s, TRUE)) { /* got a match */ + infile.linep = p->linep; + infile.index = (int) (prog->startp[0] - s); + free((char *) prog); + return (&infile); + } + if (p->linep == Curschar->linep) + break; + } + + free((char *) prog); + return (NULL); +} + +static char *laststr = NULL; +static int lastsdir; + +static LPtr * +ssearch(int dir, char *str) +/* int dir; /* FORWARD or BACKWARD */ +/* char *str; */ +{ + LPtr *pos; + + reg_ic = P(P_IC); /* tell the regexp routines how to search */ + + if (laststr != str) { + if (laststr != NULL) + free(laststr); + laststr = strsave(str); + } + lastsdir = dir; + + if (dir == BACKWARD) + pos = bcksearch(mapstring(str)); + else + pos = fwdsearch(mapstring(str)); + + /* + * This is kind of a kludge, but its needed to make 'beginning of word' + * searches land on the right place. + */ + if (pos != NULL && begword) { + if (pos->index != 0) + pos->index += 1; + } + return pos; +} + +bool_t +dosearch(int dir, char *str) +{ + LPtr *p; + + S_CHECK_TOPCHAR_AND_BOTCHAR; + + if ((p = ssearch(dir, str)) == NULL) { + msg("Pattern not found"); + return (FALSE); + } else { + LPtr savep; + + /* if we're backing up, we make sure the line we're on */ + /* is on the screen. */ + setpcmark(); + *Curschar = savep = *p; + + return (TRUE); + } +} + +void +searchagain(int dir) +{ + if (laststr == NULL) + beep(); + else + dosearch(dir, laststr); + + lastsdir = dir; +} + +#define OTHERDIR(x) (((x) == FORWARD) ? BACKWARD : FORWARD) + +bool_t +repsearch(bool_t flag) +{ + int dir = lastsdir; + bool_t found; + + if (laststr == NULL) { + beep(); + return FALSE; + } + found = dosearch(flag ? OTHERDIR(lastsdir) : lastsdir, laststr); + + /* + * We have to save and restore 'lastsdir' because it gets munged by + * ssearch() and winds up saving the wrong direction from here if 'flag' + * is true. + */ + lastsdir = dir; + + return (found); +} + +/* + * regerror - called by regexp routines when errors are detected. + */ +void +regerror(char *s) +{ + emsg(s); +} + +/* + * dosub(lp, up, cmd) + * + * Perform a substitution from line 'lp' to line 'up' using the + * command pointed to by 'cmd' which should be of the form: + * + * /pattern/substitution/g + * + * The trailing 'g' is optional and, if present, indicates that multiple + * substitutions should be performed on each line, if applicable. + * The usual escapes are supported as described in the regexp docs. + */ + +void +dosub(LPtr *lp, LPtr *up, char *cmd) +{ + LINE *cp; + char *pat, *sub; + regexp *prog; + int nsubs; + bool_t do_all; /* do multiple substitutions per line */ + int n; + + /* + * If no range was given, do the current line. If only one line was + * given, just do that one. + */ + if (lp->linep == NULL) + *up = *lp = *Curschar; + else { + if (up->linep == NULL) + *up = *lp; + } + + pat = ++cmd; /* skip the initial '/' */ + + while (*cmd) { + if (cmd[0] == '/' && cmd[-1] != '\\') { + *cmd++ = NUL; + break; + } + cmd++; + } + + if (*pat == NUL) { + emsg("NULL pattern specified"); + return; + } + sub = cmd; + + do_all = FALSE; + + while (*cmd) { + if (cmd[0] == '/' && cmd[-1] != '\\') { + do_all = (cmd[1] == 'g'); + *cmd = NUL; + break; + } + cmd++; + } + + reg_ic = P(P_IC); /* set "ignore case" flag appropriately */ + + prog = regcomp(pat); + if (prog == NULL) { + emsg("Invalid search string"); + return; + } + nsubs = 0; + + ResetBuffers(); + n = RowNumber(lp); + + cp = lp->linep; + for (; cp != Fileend->linep && cp != NULL; cp = cp->next, n++) { + if (regexec(prog, cp->s, TRUE)) { /* a match on this line */ + char *ns, *sns, *p; + + /* + * Save the line that was last changed for the final cursor + * position (just like the real vi). + */ + Curschar->linep = cp; + + /* + * Get some space for a temporary buffer to do the substitution + * into. + */ + sns = ns = alloc(2048); + if (ns == NULL) + break; + + *sns = NUL; + + p = cp->s; + + do { + for (ns = sns; *ns; ns++); + /* + * copy up to the part that matched + */ + while (p < prog->startp[0]) + *ns++ = *p++; + + regsub(prog, sub, ns); + + /* + * continue searching after the match + */ + p = prog->endp[0]; + + } while (regexec(prog, p, FALSE) && do_all); + + for (ns = sns; *ns; ns++); + + /* + * copy the rest of the line, that didn't match + */ + while (*p) + *ns++ = *p++; + + *ns = NUL; + + AppendPositionToUndoUndobuff(0, n); + AppendPositionToUndobuff(0, n); + AppendToUndoUndobuff("c$"); + AppendToUndobuff("c$"); + AppendToUndoUndobuff(sns); + AppendToUndobuff(cp->s); + AppendToUndoUndobuff(ESC_STR); + AppendToUndobuff(ESC_STR); + + free(cp->s); /* free the original line */ + cp->s = strsave(sns); /* and save the modified str */ + cp->size = strlen(cp->s) + 1; + free(sns); /* free the temp buffer */ + nsubs++; + } + if (cp == up->linep) + break; + } + + if (nsubs) { + CHANGED; + S_NOT_VALID; + AppendPositionToUndoUndobuff(0, 1); + AppendPositionToUndobuff(0, 1); + beginline(TRUE); + if (nsubs >= P(P_RP)) + smsg("%d substitution%c", nsubs, (nsubs > 1) ? 's' : ' '); + } else + msg("No match"); + + free((char *) prog); +} + +/* + * doglob(cmd) + * + * Execute a global command of the form: + * + * g/pattern/X + * + * where 'x' is a command character, currently one of the following: + * + * d Delete all matching lines + * p Print all matching lines + * + * The command character (as well as the trailing slash) is optional, and + * is assumed to be 'p' if missing. + */ + +void +doglob(LPtr *lp, LPtr *up, char *cmd) +{ + LINE *cp; + + char *pat; + regexp *prog; + int ndone; + char cmdchar = NUL; /* what to do with matching lines */ + int nu; + int nuu = 0; + + /* + * If no range was given, do every line. If only one line was given, just + * do that one. + */ + if (lp->linep == NULL) { + *lp = *Filemem; + *up = *Fileend; + } else { + if (up->linep == NULL) + *up = *lp; + } + + pat = ++cmd; /* skip the initial '/' */ + + while (*cmd) { + if (cmd[0] == '/' && cmd[-1] != '\\') { + cmdchar = cmd[1]; + *cmd = NUL; + break; + } + cmd++; + } + if (cmdchar == NUL) + cmdchar = 'p'; + + reg_ic = P(P_IC); /* set "ignore case" flag appropriately */ + + if (cmdchar != 'd' && cmdchar != 'p') { + emsg("Invalid command character"); + return; + } + prog = regcomp(pat); + if (prog == NULL) { + emsg("Invalid search string"); + return; + } + msg(""); + ndone = 0; + + nu = RowNumber(lp); + if (cmdchar == 'd') { + ResetBuffers(); + nuu = nu; + } + cp = lp->linep; + for (; cp != Fileend->linep && cp != NULL; cp = cp->next, nu++) { + if (regexec(prog, cp->s, TRUE)) { /* a match on this line */ + Curschar->linep = cp; + Curschar->index = 0; + + switch (cmdchar) { + + case 'd': /* delete the line */ + AppendPositionToUndoUndobuff(0, nuu); + AppendToUndoUndobuff("dd"); + if (buf1line() && (ndone == 0)) { + AppendToUndobuff("a"); + } else if (buf1line()) { + AppendToUndobuff("j"); + AppendToUndobuff("I"); + } else if (cp->next == Fileend->linep) { + AppendPositionToUndobuff(0, nu); + AppendToUndobuff("o"); + } else { + AppendPositionToUndobuff(0, nu); + AppendToUndobuff("O"); + } + AppendToUndobuff(cp->s); + AppendToUndobuff(ESC_STR); + + delline(1); + break; + + case 'p': /* print the line */ + if (P(P_NU)) { + outstr(mkline(nu)); + } + s_cursor_off(); + outstr(format_line(cp->s, (int *) NULL)); + s_cursor_on(); + outstr("\r\n"); + break; + } + ndone++; + } else if (cmdchar == 'd') { + nuu++; + } + if (cp == up->linep) + break; + } + + if (ndone) { + switch (cmdchar) { + + case 'd': + S_NOT_VALID; + AppendPositionToUndobuff(0, 1); + if (ndone >= P(P_RP)) + smsg("%d fewer line%c", ndone, + (ndone > 1) ? 's' : ' '); + break; + + case 'p': + wait_return(); + break; + } + stuffReadbuff("^"); + } else + msg("No match"); + + free((char *) prog); +} + +/* + * Character Searches + */ + +static char lastc = NUL; /* last character searched for */ +static int lastcdir; /* last direction of character search */ +static int lastctype; /* last type of search ("find" or "to") */ + +/* + * searchc(c, dir, type) + * + * Search for character 'c', in direction 'dir'. If type is 0, move to the + * position of the character, otherwise move to just before the char. + */ +bool_t +searchc(char c, int dir, int type) +{ + LPtr save; + + save = *Curschar; /* save position in case we fail */ + lastc = c; + lastcdir = dir; + lastctype = type; + + /* + * On 'to' searches, skip one to start with so we can repeat searches in + * the same direction and have it work right. + */ + if (type) + (dir == FORWARD) ? oneright() : oneleft(); + + while ((dir == FORWARD) ? oneright() : oneleft()) { + if (gchar(Curschar) == c) { + if (type) + (dir == FORWARD) ? oneleft() : oneright(); + return TRUE; + } + } + *Curschar = save; + return FALSE; +} + +bool_t +crepsearch(int flag) +{ + int dir = lastcdir; + int rval; + + if (lastc == NUL) + return FALSE; + + rval = searchc(lastc, flag ? OTHERDIR(lastcdir) : lastcdir, lastctype); + + lastcdir = dir; /* restore dir., since it may have changed */ + + return rval; +} + +/* + * "Other" Searches + */ + +/* + * showmatch - move the cursor to the matching paren or brace + */ +LPtr * +showmatch(void) +{ + static LPtr pos; + int (*move) (), inc(), dec(); + char initc = gchar(Curschar); /* initial char */ + char findc; /* terminating char */ + char c; + int count = 0; + + pos = *Curschar; /* set starting point */ + + switch (initc) { + + case '(': + findc = ')'; + move = inc; + break; + case ')': + findc = '('; + move = dec; + break; + case '{': + findc = '}'; + move = inc; + break; + case '}': + findc = '{'; + move = dec; + break; + case '[': + findc = ']'; + move = inc; + break; + case ']': + findc = '['; + move = dec; + break; + default: + return (LPtr *) NULL; + } + + while ((*move) (&pos) != -1) { /* until end of file */ + c = gchar(&pos); + if (c == initc) + count++; + else if (c == findc) { + if (count == 0) + return &pos; + count--; + } + } + return (LPtr *) NULL; /* never found it */ +} + +/* + * findfunc(dir) - Find the next function in direction 'dir' + * + * Return TRUE if a function was found. + */ +bool_t +findfunc(int dir) +{ + LPtr *curr; + + S_CHECK_TOPCHAR_AND_BOTCHAR; + + curr = Curschar; + + do { + curr = (dir == FORWARD) ? nextline(curr) : prevline(curr); + + if (curr != NULL && curr->linep->s[0] == '{') { + setpcmark(); + *Curschar = *curr; + return TRUE; + } + } while (curr != NULL); + + return FALSE; +} + +/* + * The following routines do the word searches performed by the 'w', 'W', + * 'b', 'B', 'e', and 'E' commands. + */ + +/* + * To perform these searches, characters are placed into one of three + * classes, and transitions between classes determine word boundaries. + * + * The classes are: + * + * 0 - white space 1 - letters, digits, and underscore 2 - everything else + */ + +static int stype; /* type of the word motion being performed */ + +#define C0(c) (((c) == ' ') || ((c) == '\t') || ((c) == NUL)) +#define C1(c) (isalpha(c) || isdigit(c) || ((c) == '_')) + +/* + * cls(c) - returns the class of character 'c' + * + * The 'type' of the current search modifies the classes of characters if a 'W', + * 'B', or 'E' motion is being done. In this case, chars. from class 2 are + * reported as class 1 since only white space boundaries are of interest. + */ +static int +cls(char c) +{ + if (C0(c)) + return 0; + + if (C1(c)) + return 1; + + /* + * If stype is non-zero, report these as class 1. + */ + return (stype == 0) ? 2 : 1; +} + + +/* + * fwd_word(pos, type) - move forward one word + * + * Returns the resulting position, or NULL if EOF was reached. + */ +LPtr * +fwd_word(LPtr *p, int type) +{ + static LPtr pos; + int sclass = cls(gchar(p)); /* starting class */ + + S_CHECK_TOPCHAR_AND_BOTCHAR; + + pos = *p; + + stype = type; + + /* + * We always move at least one character. + */ + if (inc(&pos) == -1) + return NULL; + + if (sclass != 0) { + while (cls(gchar(&pos)) == sclass) { + if (inc(&pos) == -1) + return NULL; + } + /* + * If we went from 1 -> 2 or 2 -> 1, return here. + */ + if (cls(gchar(&pos)) != 0) + return &pos; + } + /* We're in white space; go to next non-white */ + + while (cls(gchar(&pos)) == 0) { + /* + * We'll stop if we land on a blank line + */ + if (pos.index == 0 && pos.linep->s[0] == NUL) + break; + + if (inc(&pos) == -1) + return NULL; + } + + return &pos; +} + +/* + * bck_word(pos, type) - move backward one word + * + * Returns the resulting position, or NULL if top-of-file was reached. + */ +LPtr * +bck_word(LPtr *p, int type) +{ + static LPtr pos; + int sclass = cls(gchar(p)); /* starting class */ + + S_CHECK_TOPCHAR_AND_BOTCHAR; + + pos = *p; + + stype = type; + + if (dec(&pos) == -1) + return NULL; + + /* + * If we're in the middle of a word, we just have to back up to the start + * of it. + */ + if (cls(gchar(&pos)) == sclass && sclass != 0) { + /* + * Move backward to start of the current word + */ + while (cls(gchar(&pos)) == sclass) { + if (dec(&pos) == -1) + return NULL; + } + inc(&pos); /* overshot - forward one */ + return &pos; + } + /* + * We were at the start of a word. Go back to the start of the prior + * word. + */ + + while (cls(gchar(&pos)) == 0) { /* skip any white space */ + /* + * We'll stop if we land on a blank line + */ + if (pos.index == 0 && pos.linep->s[0] == NUL) + return &pos; + + if (dec(&pos) == -1) + return NULL; + } + + sclass = cls(gchar(&pos)); + + /* + * Move backward to start of this word. + */ + while (cls(gchar(&pos)) == sclass) { + if (dec(&pos) == -1) + return NULL; + } + inc(&pos); /* overshot - forward one */ + + return &pos; +} + +/* + * end_word(pos, type) - move to the end of the word + * + * There is an apparent bug in the 'e' motion of the real vi. At least on the + * System V Release 3 version for the 80386. Unlike 'b' and 'w', the 'e' + * motion crosses blank lines. When the real vi crosses a blank line in an + * 'e' motion, the cursor is placed on the FIRST character of the next + * non-blank line. The 'E' command, however, works correctly. Since this + * appears to be a bug, I have not duplicated it here. + * + * Returns the resulting position, or NULL if EOF was reached. + */ +LPtr * +end_word(LPtr *p, int type) +{ + static LPtr pos; + int sclass = cls(gchar(p)); /* starting class */ + + S_CHECK_TOPCHAR_AND_BOTCHAR; + + pos = *p; + + stype = type; + + if (inc(&pos) == -1) + return NULL; + dec(&pos); + + /* + * If we're in the middle of a word, we just have to move to the end of + * it. + */ + if (cls(gchar(&pos)) == sclass && sclass != 0) { + /* + * Move forward to end of the current word + */ + while (cls(gchar(&pos)) == sclass) { + if (inc(&pos) == -1) + return NULL; + } + dec(&pos); /* overshot - forward one */ + return &pos; + } + /* + * We were at the end of a word. Go to the end of the next word. + */ + + while (cls(gchar(&pos)) == 0) { /* skip any white space */ + if (inc(&pos) == -1) + return NULL; + } + + sclass = cls(gchar(&pos)); + + /* + * Move forward to end of this word. + */ + while (cls(gchar(&pos)) == sclass) { + if (inc(&pos) == -1) + return NULL; + } + dec(&pos); /* overshot - forward one */ + + return &pos; +} diff --git a/bin/vi/source.doc b/bin/vi/source.doc new file mode 100644 index 0000000..80b6d1c --- /dev/null +++ b/bin/vi/source.doc @@ -0,0 +1,114 @@ + + Release Notes for STEVIE - Version 3.10a + + Source Notes + + Tony Andrews - March 6, 1988 + +Overview +-------- + + This file provides a brief description of the source code for +Stevie. The data structures are described later as well. For information +specific to porting the editor, see the file 'porting.doc'. This document +is more relevant to people who want to hack on the editor apart from doing +a simple port. + + Most of this document was written some time ago so a lot of the +discussion centers on problems related to the Atari ST environment and +compilers. Most of this can be ignored for other systems. + +Things You Need - ATARI +----------------------- + + Stevie has been compiled with both the Alcyon (4.14A) and the +Megamax C compilers. For the posted binary, Megamax was used because +it's less buggy and provides a reasonable malloc(). Ports to other +compilers should be pretty simple. The current ifdef's for ALCYON and +MEGAMAX should point to the potential trouble areas. (See 'porting.doc' +for more information.) + + The search code depends on Henry Spencer's regular expression +code. I used a version I got from the net recently (as part of a 'grep' +posting) and had absolutely no trouble compiling it on the ST. Thanks, +Henry! + + The file 'getenv.c' contains a getenv routine that may or may +not be needed with your compiler. My version works with Alcyon and +Megamax, under either the Beckemeyer or Gulam shells. + + Lastly, you need a decent malloc. Lots of stuff in stevie is +allocated dynamically. The malloc with Alcyon is problematic because +it allocates from the heap between the end of data and the top of stack. +If you make the stack big enough to edit large files, you wind up +wasting space when working with small files. Mallocs that get their memory +from GEMDOS (in fairly large chunks) are much better. + +Things You Need - AMIGA +----------------------- + + Lattice C version 5.0 or later. + +Data Structures +--------------- + + A brief discussion of the evolution of the data structures will +do much to clarify the code, and explain some of the strangeness you may +see. + + In the original version, the file was maintained in memory as a +simple contiguous buffer. References to positions in the file were simply +character pointers. Due to the obvious performance problems inherent in +this approach, the following changes were made. + + The file is now represented by a doubly linked list of 'line' +structures defined as follows: + +struct line { + struct line *next; /* next line */ + struct line *prev; /* previous line */ + char *s; /* text for this line */ + int size; /* actual size of space at 's' */ + unsigned long num; /* line "number" */ +}; + +The members of the line structure are described more completely here: + +prev - pointer to the structure for the prior line, or NULL for the + first line of the file + +next - like 'prev' but points to the next line + +s - points to the contents of the line (null terminated) + +size - contains the size of the chunk of space pointed to by s. This + is used so we know when we can add text to a line without getting + more space. When we DO need more space, we always get a little + extra so we don't make so many calls to malloc. + +num - This is a pseudo line number that makes it easy to compare + positions within the file. Otherwise, we'd have to traverse + all the links to tell which line came first. + + Since character pointers served to mark file positions in the +original, a similar data object was needed for the new data structures. +This purpose is served by the 'lptr' structure which is defined as: + +struct lptr { + struct line *linep; /* line we're referencing */ + int index; /* position within that line */ +}; + +The member 'linep' points to the 'line' structure for the line containing +the location of interest. The integer 'index' is the offset into the line +data (member 's') of the character to be referenced. + +The following typedef's are more commonly used: + +typedef struct line LINE; +typedef struct lptr LPtr; + +Many operations that were trivial with character pointers had to be +implemented by functions or macros to manipulate LPtr's. Most of these are in +the files 'ptrfunc.c' and 'macros.h'. There you'll find functions to increment, +decrement, and compare LPtr's. diff --git a/bin/vi/stevie.doc b/bin/vi/stevie.doc new file mode 100644 index 0000000..8bcb9e9 --- /dev/null +++ b/bin/vi/stevie.doc @@ -0,0 +1,532 @@ + + STEVIE - Simply Try this Editor for VI Enthusiasts + + Quick Reference Card + + by + + Tony Andrews And G. R. (Fred) Walter + +STEVIE may be freely distributed. The source isn't copyrighted or +restricted in any way. If you pass the program along, please include all +the documentation and, if practical, the source as well. + +STEVIE used to stand for 'ST Editor for VI Enthusiasts', however since this +editor is used on more machines than just ST's the acronym was changed. + +Starting the Editor +------------------- + +The following command line forms are supported: + + vi [file ...] Edit the specified file(s) + + vi -t tag Start at location of the given tag + + vi + file Edit file starting at end + + vi +n file Edit file starting a line number 'n' + + vi +/pat file Edit file starting at pattern 'pat' + +If multiple files are given on the command line (using the first form), +the ":n" command goes to the next file, ":p" goes backward in the list, +and ":rew" can be used to rewind back to the start of the file list. + +Set Command Options +------------------- + +The ":set" command works as usual to set parameters. Each parameter has +a long and an abbreviated name, either of which may be used. Boolean +parameters are set as in: + + set showmatch + +or cleared by: + + set noshowmatch + +Numeric parameters are set as in: + + set scroll=5 + +Several parameters may be set with a single command: + + set novb sm report=1 + +To see the status of all parameters use ":set all". Typing ":set" with +no arguments will show only those parameters that have been changed. +The supported parameters, their names, defaults, and descriptions are +shown below: + +Full Name Short Default Description +------------------------------------------------------------------------------ +vbell vb vb Use visual bell (novb for audible bell) +showmatch sm nosm Showmatch mode +wrapscan ws ws Wrapscan (searches cross file start/end) +errorbells eb noeb Ring bell when error messages are shown +showmode mo nomo Show on status line when in insert mode +backup bk nobk Leave backup in *.bak on file writes +return cr cr End lines with cr-lf when writing +list list nolist Show tabs and newlines graphically +autoindent ai noai Start new line at same col as prior line +ignorecase ic noic Ignore case in search strings +number nu nonu Display lines with their line numbers + +scroll scroll 12 Number of lines to scroll for ^D and ^U +tabstop ts 8 Number of spaces in a tab +report report 5 Min # of lines to report operations on +lines lines 25 Number of lines on the screen + +The EXINIT environment variable can be used to modify the default values +on startup as in: + + setenv EXINIT="set sm ts=4" + +The 'backup' parameter, if set, causes the editor to retain a backup of any +files that are written. During file writes, a backup is always kept for +safety until the write is completed. At that point, the 'backup' parameter +determines whether the backup file is deleted. + +In environments (e.g. OS/2 or TOS) where lines are normally terminated by +CR-LF, the 'return' parameter allows files to be written with only a LF +terminator (if the parameter is cleared). + +The 'lines' parameter tells the editor how many lines there are on the screen. +This is useful on systems like the ST where various screen resolutions may be +used. By using the 'lines' parameter, different screen sizes can be easily +handled. On the Amiga system window resizes are atomatically detected and +acted upon. It is suggested that one's window be larger than 2 rows and 5 +columns. + +Colon Commands +-------------- + +Several of the normal 'vi' colon commands are supported by STEVIE. Some commands +may be preceded by a line range specification. For commands that accept a range +of lines, the following address forms are supported: + +addr +addr + number +addr - number + +where 'addr' may be one of the following: + a line number + a mark (as in 'a or 'b) + % (entire file) + . (the current line) + $ (the last line) + +The Global Command +------------------ + +A limited form of the global command is supported, accepting the following +command form: + +g/pattern/X + +where X may be either 'd' or 'p' to delete or print lines that match the given +pattern. If a line range is given, only those lines are checked for a match +with the pattern. If no range is given, all lines are checked. + +If the trailing command character is omitted, 'p' is assumed. In this case, the +trailing slash is also optional. The current version of the editor does not +support the undo operation following the deletion of lines with the global +command. + +The Substitute Command +---------------------- + +The substitute command provides a powerful mechanism for making more complex +substitutions than can be done directly from visual mode. The general form of +the command is: + +s/pattern/replacement/g + +Each line in the given range (or the current line, if no range was given) is +scanned for the given regular expression. When found, the string that matched +the pattern is replaced with the given replacement string. If the replacement +string is null, each matching pattern string is deleted. + +The trailing 'g' is optional and, if present, indicates that multiple +occurrences of 'pattern' on a line should all be replaced. + +Some special sequences are recognized in the replacement string. The +ampersand character is replaced by the entire pattern that was matched. +For example, the following command could be used to put all occurrences +of 'foo' or 'bar' within double quotes: + +1,$s/foo|bar/&/g + +The special sequence "\n" where 'n' is a digit from 1 to 9, is replaced +by the string the matched the corresponding parenthesized expression in +the pattern. The following command could be used to swap the first two +parameters in calls to the C function "foo": + +1,$s/foo\\(([^,]*),([^,]*),/foo(\\2,\\1,/g + +Like the global command, substitutions can't be undone with this version of +the editor. + +The Delete Command +------------------ + +:[range]d will delete the range of lines. + +File Manipulation Commands +-------------------------- + +:w write the current file +:wq write and quit +:x write (if necessary) and quit +ZZ same as ":x" + +:e file edit the named file +:e! re-edit the current file, discarding any changes +:e # edit the alternate file + +:w file write the buffer to the named file +:x,y w file write lines x through y to the named file +:r file read the named file into the buffer + +:n edit the next file +:p edit the previous file +:rew rewind the file list + +:f show the current file name +:f name change the current file name + +:ta tag go to the named tag +^] like ":ta" using the current word as the tag + +:help display a command summary + +:!cmd execute the 'cmd' via a system() call + +The ":help" command can also be invoke with the key on the Atari ST +or the Amiga. This actually displays a pretty complete summary of the real vi +with unsupported features indicated appropriately. + +The commands above work pretty much like they do in 'vi'. Most of the +commands support a '!' suffix (if appropriate) to discard any pending +changes. + +String Searches +--------------- + +String searches are supported, as in vi, accepting the usual regular +expression syntax. This was done using Henry Spencer's regular expression +library without modification. Tony Andrews added code outside the library to +support the '\<' and '\>' extensions and code inside the library to support +the ignorecase option. + +Operators +--------- + +The vi operators (d, c, y, <, and >) work as true operators. + +Tags +---- + +Tags are implemented. + +System-Specific Comments +------------------------ + +The following sections provide additional relevant information for the +systems to which STEVIE has been ported. + +Atari ST +-------- + +The editor has been tested in all three resolutions, although low and +high res. are less tested than medium. The 50-line high res. mode can +be used by setting the 'lines' parameter to 50. Alternatively, the +environment variable 'LINES' can be set. The editor doesn't actively +set the number of lines on the screen. It just operates using the number +of lines it was told. + +The arrow keys, as well as the , , and keys are +all mapped appropriately. + +UNIX +---- + +The editor has been ported to UNIX System V release 3. It's hard-coded for +ansi-style escape sequences and doesn't use the termcap/terminfo routines at +all. + +OS9 +--- + +The editor has been ported to OS9 version 2.2. + +OS/2 +---- + +Make sure 'ansi' mode is on (using the 'ansi' command). +The OS/2 console driver doesn't support insert/delete line, so STEVIE +bypasses the driver and makes the appropriate system calls directly. +This is all done in the system-specific part of the editor so the kludge +is at least localized. + +The arrow keys, page up/down and home/end all do what you'd expect. The function +keys are hard-coded to some useful macros until I can get true support for +macros into the editor. The current mappings are: + +F1 :p +F2 :n +F3 :e # +F4 :rew +F5 [[ +F6 ]] +F7 << +F8 >> +F9 :x +F10 :help + +S-F1 :p! +S-F2 :n! + +MSDOS +----- + +STEVIE has been ported to MSDOS 3.3 on an AT using the Microsoft C compiler, +version 5.10. The keyboard mappings are the same as for OS/2. +The only problem with the PC version is that the inefficiency of +the screen update code becomes painfully apparent on slower machines. + +BSD 4.3 +------- + +This port was done so it could be worked on in a main-frame enviroment. + +Amiga +----- + +The arrow keys and the help key are supported, as is window re-sizing. +It is strongly suggested that you not try to type in console commands +(alt-esc in some keymaps, plus the appropriate other keys) since STEVIE +captures all console input. If you do type alt-esc then typing '|' will +return you to STEVIE. +If you have ARP installed, then you can use wildcards on the command line, +in the :e command and in the :r command. +If you 'run stevie' it will first attempt to open a window that is 640x200; +if this doesn't work then it tries to open a window that is 480x200; +if this fails it gives up. +NOTE: that you can't use :!cmd on BCPL programs. + +Missing Features +---------------- + +1. Macros with support for function keys. + +2. More "set" options. + +3. Many others... + +Known Bugs and Problems +----------------------- + +1. The yank buffer uses statically allocated memory, so yanks of more than + 5K of text will fail. If a delete spans more than 5K, the program asks + for confirmation before proceeding. That way, if you were moving text, + you don't get screwed by the limited yank buffer. You just have to move + smaller chunks at a time. All the internal buffers (yank, redo, etc.) + need to be reworked to allocate memory dynamically. + +2. If you stay in insert mode for a long time (around 5K's worth of + characters, including newlines) the insert buffer can overflow. + When this happens you lose your ability to automatically undo the text just + inserted and the redo/undo/(undo of undo) buffers are reset to the + current position. + +3. Several other less bothersome glitches... + +Character Function Summary +-------------------------- + +The following list describes the meaning of each character that's used +by the editor. In some cases characters have meaning in both command and +insert mode; these are all described. + +^@ The null character. Not used in any mode. This character may not + be present in the file, as is the case with vi. + +^B Backward one screen. + +^D Scroll the window down one half screen. + +^E Scroll the screen up one line. + +^F Forward one screen. + +^G Same as ":f" command. Displays file information. + +^H (BS) Moves cursor left one space in command mode. In insert mode, erases + the last character typed. + +^J Move the cursor down one line. + +^L Clear and redraw the screen. + +^M (CR) Move to the first non-white character in the next line. In insert + mode, a carriage return opens a new line for input. + +^N Move the cursor down a line. + +^P Move the cursor up a line. + +^U Scroll the window up one half screen. + +^V Indicates that the next character is should be treated as entered + and not modified (used to enter control characters, etc.). + +^Y Scroll the screen down one line. + +^[ Escape cancels a pending command in command mode, and is used to + terminate insert mode. + +^] Moves to the tag whose name is given by the word in which the cursor + resides. + +^` Same as ":e #" if supported (system-dependent). + +SPACE Move the cursor right on column. + +$ Move to the end of the current line. + +% If the cursor rests on a paren '()', brace '{}', or bracket '[]', + move to the matching one. + +' Used to move the cursor to a previously marked position, as in + 'a or 'b. The cursor moves to the start of the marked line. The + special mark '' refers to the "previous context". + ++ Same as carriage return, in command mode. + +, Reverse of the last t, T, f, or F command. + +- Move to the first non-white character in the previous line. + +. Repeat the last edit command. + +/ Start of a forward string search command. String searches may be + optionally terminated with a closing slash. To search for a slash + use '\/' in the search string. + +0 Move to the start of the current line. Also used within counts. + +1-9 Used to add 'count' prefixes to commands. + +: Prefix character for "ex" commands. + +; Repeat last t, T, f, or F command. + +< The 'left shift' operator. + +> The 'right shift' operator. + +? Same as '/', but search backward. + +A Append at the end of the current line. + +B Backward one blank-delimited word. + +C Change the rest of the current line. + +D Delete the rest of the current line. + +E End of the end of a blank-delimited word. + +F Find a character backward on the current line. + +G Go to the given line number (end of file, by default). + +H Move to the first non-white char. on the top screen line. + +I Insert before the first non-white char. on the current line. + +J Join two lines. + +L Move to the first non-white char. on the bottom screen line. + +M Move to the first non-white char. on the middle screen line. + +N Reverse the last string search. + +O Open a new line above the current line, and start inserting. + +P Put the yank/delete buffer before the current cursor position. + +T Reverse search 'upto' the given character. + +W Move forward one blank-delimited word. + +X Delete one character before the cursor. + +Y Yank the current line. Same as 'yy'. + +ZZ Exit from the editor, saving changes if necessary. + +[[ Move backward one C function. + +]] Move forward one C function. + +^ Move to the first non-white on the current line. + +` Move to the given mark, as with '. The distinction between the two + commands is important when used with operators. I support the + difference correctly. If you don't know what I'm talking about, + don't worry, it won't matter to you. + +~ Switch case of character under cursor. + +a Append text after the cursor. + +b Back one word. + +c The change operator. + +d The delete operator. + +e Move to the end of a word. + +f Find a character on the current line. + +h Move left one column. + +i Insert text before the cursor. + +j Move down one line. + +k Move up one line. + +l Move right one column. + +m Set a mark at the current position (e.g. ma or mb). + +n Repeat the last string search. + +o Open a new line and start inserting text. + +p Put the yank/delete buffer after the cursor. + +r Replace a character. + +s Replace characters. + +t Move forward 'upto' the given character on the current line. + +u Undo the last edit. + +w Move forward one word. + +x Delete the character under the cursor. + +y The yank operator. + +z Redraw the screen with the current line at the top (zRETURN), + the middle (z.), or the bottom (z-). + +| Move to the column given by the preceding count. diff --git a/bin/vi/stevie.h b/bin/vi/stevie.h new file mode 100644 index 0000000..f999be8 --- /dev/null +++ b/bin/vi/stevie.h @@ -0,0 +1,345 @@ +/* + * STEVIE - Simply Try this Editor for VI Enthusiasts + * + * Code Contributions By : Tim Thompson twitch!tjt + * Tony Andrews onecom!wldrdg!tony + * G. R. (Fred) Walter watmath!grwalter + */ + +#ifdef __ORCAC__ +#ifndef MAINSEG +#pragma noroot +#endif +#pragma optimize 9 +#endif + +#include "env.h" + +#include +#ifndef ATARI +# ifndef UNIX +# include +# endif +#endif +#include +#ifndef MWC +# include +#endif + +#include "ascii.h" +#include "keymap.h" +#include "param.h" +#include "term.h" +#include "macros.h" + +#ifdef AMIGA +/* + * This is used to disable break signal handling. + */ +#include +#endif + +/*extern char *strchr();*/ + +#define NORMAL 0 +#define CMDLINE 1 +#define INSERT 2 +#define REPLACE 3 +#define APPEND 4 +#define UNDO 5 +#define REDO 6 +#define PUT 7 +#define FORWARD 8 +#define BACKWARD 9 +#define VALID 10 +#define NOT_VALID 11 +#define VALID_TO_CURSCHAR 12 +#define UPDATE_CURSOR 13 +#define UPDATE_ALL 14 + +/* + * Boolean type definition and constants + */ +typedef int bool_t; + +#ifndef TRUE +# define FALSE (0) +# define TRUE (1) +#endif +#define SORTOF (2) +#define YES TRUE +#define NO FALSE +#define MAYBE SORTOF + +/* + * Maximum screen dimensions + */ +#define MAX_COLUMNS 140L + +/* + * Buffer sizes + */ +#define CMDBUFFSIZE MAX_COLUMNS /* size of the command processing buffer */ + +#define LSIZE 512 /* max. size of a line in the tags file */ + +#define IOSIZE (1024+1) /* file i/o and sprintf buffer size */ + +#define YANKSIZE 5200 /* yank buffer size */ +#define INSERT_SIZE 5300 /* insert, redo and undo buffer size must be + * bigger than YANKSIZE */ +#define REDO_UNDO_SIZE 5400 /* redo, undo and (undo an undo) buffer size + * must be bigger than INSERT_SIZE */ +#define READSIZE 5500 /* read buffer size must be bigger than + * YANKSIZE and REDO_UNDO_SIZE */ + +/* + * SLOP is the amount of extra space we get for text on a line during editing + * operations that need more space. This keeps us from calling alloc every + * time we get a character during insert mode. No extra space is allocated + * when the file is initially read. + */ +#define SLOP 10 + +/* + * LINEINC is the gap we leave between the artificial line numbers. This + * helps to avoid renumbering all the lines every time a new line is + * inserted. + * + * Since line numbers are stored in longs (32 bits), a LINEINC of 10000 + * lets us have > 200,000 lines and we won't have to renumber very often. + */ +#define LINEINC 10000 + +#define CHANGED Changed = TRUE +#define UNCHANGED Changed = FALSE + +#define S_NOT_VALID NumLineSizes = -1 + +#define S_LINE_NOT_VALID LineNotValid = TRUE + +#define S_CHECK_TOPCHAR_AND_BOTCHAR CheckTopcharAndBotchar = TRUE + +#define S_MUST_UPDATE_BOTCHAR MustUpdateBotchar = TRUE + +#define S_VALID_TO_CURSCHAR ValidToCurschar = TRUE + +struct line { + struct line *next; /* next line */ + struct line *prev; /* previous line */ + char *s; /* text for this line */ + int size; /* actual size of space at 's' */ + unsigned long num; /* line "number" */ +}; + +#define LINEOF(x) ((x)->linep->num) + +struct lptr { + struct line *linep; /* line we're referencing */ + int index; /* position within that line */ +}; + +typedef struct line LINE; +typedef struct lptr LPtr; + +struct charinfo { + char ch_size; + char *ch_str; +}; + +extern struct charinfo chars[]; + +#ifdef AMIGA +extern int Aux_Device; +#endif +extern int State; +extern int Rows; +extern int Columns; +extern int CheckTopcharAndBotchar; +extern int MustUpdateBotchar; +extern int ValidToCurschar; +extern int LineNotValid; +extern int NumLineSizes; +extern LINE **LinePointers; +extern char *LineSizes; +extern char *Filename; +extern LPtr *Filemem; +extern LPtr *Filetop; +extern LPtr *Fileend; +extern LPtr *Topchar; +extern LPtr *Botchar; +extern LPtr *Curschar; +extern LPtr *Insstart; +extern int Cursrow; +extern int Curscol; +extern int Cursvcol; +extern int Curswant; +extern bool_t set_want_col; +extern int Prenum; +extern bool_t Changed; +extern bool_t RedrawingDisabled; +extern bool_t UndoInProgress; +extern char *IObuff; +extern char *Insbuffptr; +extern char *Insbuff; +extern char *Readbuffptr; +extern char *Readbuff; +extern char *Redobuffptr; +extern char *Redobuff; +extern char *Undobuffptr; +extern char *Undobuff; +extern char *UndoUndobuffptr; +extern char *UndoUndobuff; +extern char *Yankbuffptr; +extern char *Yankbuff; +extern char last_command; +extern char last_command_char; + +/* alloc.c */ +char *alloc(unsigned size); +char *strsave(char *); +void screenalloc(void); +void filealloc(void); +void freeall(void); +LINE *newline(int); +bool_t canincrease(int); + +/* cmdline.c */ +void readcmdline(char,char *); +void dotag(char *, bool_t); +void msg(char *s); +void emsg(char *); +void smsg(char *s,...); +void gotocmdline(bool_t, char); +void wait_return(void); + +/* dec.c */ +int dec(LPtr *lp); + +/* edit.c */ +void edit(void); +void insertchar(char); +void getout(int); +void scrollup(int); +void scrolldown(int); +void beginline(bool_t); +bool_t oneright(void); +bool_t oneleft(void); +bool_t oneup(int); +bool_t onedown(n); + +/* fileio.c */ +void filemess(char *); +void renum(void); +bool_t readfile(char *,LPtr *,bool_t); +bool_t writeit(char *, LPtr *, LPtr *); + +/* s_io.c */ +void s_cursor_off(void); +void s_cursor_on(void); +void s_clear(void); +void s_refresh(int); +void NotValidFromCurschar(void); +void Update_Botchar(void); + +/* help.c */ +bool_t help(void); + +/* inc.c */ +int inc(LPtr *); + +/* linefunc.c */ +LPtr *nextline(LPtr *); +LPtr *prevline(LPtr *); +void coladvance(LPtr *,int); + +/* main.c */ +void stuffReadbuff(char *); +void stuffnumReadbuff(int); +char vgetc(void); +char vpeekc(void); + +/* mark.c */ +void setpcmark(void); +void clrall(void); +void clrmark(LINE *); +bool_t setmark(char); +LPtr *getmark(char); + +/* misccmds.c */ +bool_t OpenForward(int); +bool_t OpenBackward(int); +void fileinfo(void); +void inschar(char); +void insstr(char *); +void delline(int); +bool_t delchar(bool_t, bool_t); +int cntllines(LPtr *, LPtr *); +int plines(char *); +LPtr *gotoline(int); + +/* normal.c */ +void normal(char); +void ResetBuffers(void); +void AppendToInsbuff(char *); +void AppendToRedobuff(char *); +void AppendNumberToRedobuff(int); +void AppendToUndobuff(char *); +void AppendNumberToUndobuff(int); +void AppendPositionToUndobuff(int,int); +void AppendToUndoUndobuff(char *); +void AppendNumberToUndoUndobuff(int); +void AppendPositionToUndoUndobuff(int,int); +bool_t linewhite(LPtr *); + +/* mk.c */ +char *mkstr(char); +char *mkline(int); + +/* param.c */ +void doset(char *,bool_t); + +/* screen.c */ +void cursupdate(int); + +/* search.c */ +void doglob(LPtr *,LPtr *,char *); +void dosub(LPtr *,LPtr *,char *); +void searchagain(int); +bool_t dosearch(int,char *); +bool_t repsearch(bool_t); +bool_t searchc(char,int,int); +bool_t crepsearch(int); +bool_t findfunc(int); +LPtr *showmatch(void); +LPtr *fwd_word(LPtr *,int); +LPtr *bck_word(LPtr *,int); +LPtr *end_word(LPtr *,int); + +/* format_l.c */ +char *format_line(char *, int *); + +/* + * Machine-dependent routines. + */ +#ifdef AMIGA +# include "amiga.h" +#endif +#ifdef BSD +# include "bsd.h" +#endif +#ifdef UNIX +# include "unix.h" +#endif +#ifdef TOS +# include "tos.h" +#endif +#ifdef OS2 +# include "os2.h" +#endif +#ifdef DOS +# include "dos.h" +#endif +#ifdef GSOS +# include "gsos.h" +#endif diff --git a/bin/vi/term.h b/bin/vi/term.h new file mode 100644 index 0000000..595a358 --- /dev/null +++ b/bin/vi/term.h @@ -0,0 +1,170 @@ +/* + * STEVIE - Simply Try this Editor for VI Enthusiasts + * + * Code Contributions By : Tim Thompson twitch!tjt + * Tony Andrews onecom!wldrdg!tony + * G. R. (Fred) Walter watmath!grwalter + */ + +/* + * This file contains the machine dependent escape sequences that the editor + * needs to perform various operations. Some of the sequences here are + * optional. Anything not available should be indicated by a null string. + * + * Insert/delete line sequences are necessary. + */ + +#ifdef GSOS +#include +#endif + +/* + * The macro names here correspond (more or less) to the actual ANSI names + */ + +#ifdef ATARI +#define T_ED "\033E" /* erase display (may optionally home cursor) */ +#define T_EL "\033l" /* erase the entire current line */ +#define T_IL "\033L" /* insert one line */ +#define T_DL "\033M" /* delete one line */ +#define T_CI "\033f" /* invisible cursor (very optional) */ +#define T_CV "\033e" /* visible cursor (very optional) */ +#define T_TP "" /* plain text */ +#define T_TI "" /* inverse-video text */ +#endif + +#ifdef UNIX +/* The UNIX sequences are hard-wired for ansi-like terminals. */ +#define T_ED "\033[2J" /* erase display (may optionally home cursor) */ +#define T_END_D "\033[J" /* erase to end of display */ +#define T_EL "\033[2K" /* erase the entire current line */ +#define T_END_L "\033[K" /* erase to the end of the current line */ +#define T_IL "\033[L" /* insert one line */ +#define T_DL "\033[M" /* delete one line */ +#define T_CI "" /* invisible cursor (very optional) */ +#define T_CV "" /* visible cursor (very optional) */ +#define T_TP "" /* plain text */ +#define T_TI "" /* inverse-video text */ +#endif + +#ifdef BSD +/* The BSD 4.3 sequences are hard-wired for ansi-like terminals. */ +#define T_ED "\033[2J" /* erase display (may optionally home cursor) */ +#define T_END_D "\033[J" /* erase to end of display */ +#define T_EL "\033[2K" /* erase the entire current line */ +#define T_END_L "\033[K" /* erase to the end of the current line */ +#define T_IL "\033[L" /* insert line */ +#define T_DL "\033[M" /* delete line */ +#define T_CI "" /* invisible cursor */ +#define T_CV "" /* visible cursor */ +#define T_TP "\033[m" /* plain text */ +#define T_TI "\033[7m" /* inverse-video text */ +#endif + +#ifdef OS2 +/* + * The OS/2 ansi console driver is pretty deficient. No insert or delete line + * sequences. The erase line sequence only erases from the cursor to the end + * of the line. For our purposes that works out okay, since the only time + * T_EL is used is when the cursor is in column 0. + * + * The insert/delete line sequences marked here are actually implemented in + * the file os2.c using direct OS/2 system calls. This makes the capability + * available for the rest of the editor via appropriate escape sequences + * passed to outstr(). + */ +#define T_ED "\033[2J" /* erase display (may optionally home cursor) */ +#define T_EL "\033[K" /* erase the entire current line */ +#define T_END_L "\033[K" /* erase to the end of the current line */ +#define T_IL "\033[L" /* insert one line - fake (see os2.c) */ +#define T_DL "\033[M" /* delete one line - fake (see os2.c) */ +#define T_CI "" /* invisible cursor (very optional) */ +#define T_CV "" /* visible cursor (very optional) */ +#define T_TP "" /* plain text */ +#define T_TI "" /* inverse-video text */ +#endif + +#ifdef AMIGA +/* + * The erase line sequence only erases from the cursor to the end of the + * line. For our purposes that works out okay, since the only time T_EL is + * used is when the cursor is in column 0. + */ +#define T_ED "\014" /* erase display (may optionally home cursor) */ +#define T_END_D "\033[J" /* erase to end of display */ +#define T_EL "\033[K" /* erase the entire current line */ +#define T_END_L "\033[K" /* erase to the end of the current line */ +#define T_IL "\033[" /* insert line */ +#define T_IL_B "L" +#define T_DL "\033[" /* delete line */ +#define T_DL_B "M" +#define T_CI "\033[0 p" /* invisible cursor (very optional) */ +#define T_CV "\033[1 p" /* visible cursor (very optional) */ +#define T_TP "\033[0m" /* plain text */ +#define T_TI "\033[7m" /* inverse-video text */ +#endif + +#ifdef DOS +/* + * DOS sequences + * + * Some of the following sequences require the use of the "nansi.sys" + * console driver. The standard "ansi.sys" driver doesn't support + * sequences for insert/delete line. + */ +#define T_ED "\033[2J" /* erase display (may optionally home cursor) */ +#define T_EL "\033[K" /* erase the entire current line */ +#define T_IL "\033[L" /* insert line (requires nansi.sys driver) */ +#define T_DL "\033[M" /* delete line (requires nansi.sys driver) */ +#define T_CI "" /* invisible cursor (very optional) */ +#define T_CV "" /* visible cursor (very optional) */ +#define T_TP "" /* plain text */ +#define T_TI "" /* inverse-video text */ +#endif + +#ifdef GSOS +/* + * Apple //gs sequences + * + * the insert and delete line sequences, since they are not supported by + * the display firmware :-(, are kluged. We set a window and scroll either + * up or down depending. But it works. + */ + +extern char *tc_ED; +extern char *tc_EL; +extern char *tc_IL; +extern char *tc_DL; +extern char *tc_CI; +extern char *tc_CV; +extern char *tc_TP; +extern char *tc_TI; +extern char *tc_END_D; +extern char *tc_END_L; + +#define T_ED tc_ED +#define T_EL tc_EL +#define T_IL tc_IL +#define T_DL tc_DL +#define T_CI tc_CI +#define T_CV tc_CV +#define T_TP tc_TP +#define T_TI tc_TI +#define T_END_D tc_END_D +#define T_END_L tc_END_L + +#ifdef NOTDEFINED + +#define T_ED "\014" /* erase display */ +#define T_EL "\032" /* erase the current line */ +#define T_IL "\033L" /* our own user-defined code */ +#define T_DL "\033M" /* our own user-defined code */ +#define T_CI "\006" /* 006 make cursor invisible */ +#define T_CV "\005" /* 005 make cursor visible */ +#define T_TP "\016" /* normal display format */ +#define T_TI "\017" /* inverse-video */ +#define T_END_D "\013" /* clear to end of screen */ +#define T_END_L "\035" /* erase to the end of the current line */ +#endif /* notdefined */ + +#endif diff --git a/bin/vi/test b/bin/vi/test new file mode 100644 index 0000000..5bfb92b --- /dev/null +++ b/bin/vi/test @@ -0,0 +1,71 @@ +/* + * STEVIE - Simply Try this Editor for VI Enthusiasts + * + * Code Contributions By : Tim Thompson twitch!tjt + * Tony Andrews onecom!wldrdg!tony + * G. R. (Fred) Walter watmath!grwalter + */ + +/* + * The defines in this file establish the environment we're compiling + * in. Set these appropriately before compiling the editor. + */ + +/* + * One (and only 1) of the following defines should be uncommented. Most of + * the code is pretty machine-independent. Machine dependent code goes in a + * file like tos.c or unix.c. The only other place where machine dependent + * code goes is term.h for escape sequences. + */ + +#ifndef AMIGA +# ifndef BSD +# ifndef UNIX +/* Defined in makefile : AMIGA Amiga */ +/* Defined in makefile : BSD BSD 4.3 */ +/* Defined in makefile : UNIX System V */ +/* #define ATARI Atari ST */ +/* #define OS2 Microsoft OS/2 */ +/* #define DOS MS DOS 3.3 */ +#define GSOS Apple //GS GS/OS 5.02 +# endif +# endif +#endif + +#ifdef AMIGA +# define WILD_CARDS +#endif + +/* + * If ATARI is defined, one of the following compilers must be selected. + */ +#ifdef ATARI +/* #define MWC Mark William's C 3.0.9 */ +/* #define MEGAMAX Megamax Compiler */ +/* #define ALCYON Alcyon C compiler */ + +# ifdef MWC +# define AppendNumberToUndoUndobuff XX1 +# define AppendPositionToUndoUndobuff XX2 +# define FOPENB +# endif + +# ifdef MEGAMAX +# define FOPENB +# endif +#endif + +/* + * If HELP is defined, the :help command shows a vi command summary. + */ +#define HELP /* enable help command */ + +/* + * STRCSPN should be defined if the target system doesn't have the + * routine strcspn() available. See regexp.c for details. + */ + +#ifdef ATARI +#define STRCSPN +#endif + diff --git a/bin/vi/version.c b/bin/vi/version.c new file mode 100644 index 0000000..4d08964 --- /dev/null +++ b/bin/vi/version.c @@ -0,0 +1,186 @@ +/* + * STEVIE - Simply Try this Editor for VI Enthusiasts + * + * Code Contributions By : Tim Thompson twitch!tjt + * Tony Andrews onecom!wldrdg!tony + * G. R. (Fred) Walter watmath!grwalter + */ + +/* + * Version Changes (and person who did them) + * ------- --------------------------------- + * 3.10 - version that started it all. Found on comp.sources.unix + * Jun88 Volume 15 i037, i038, i039, i040, i042, and INF3 + * - Tim Thompson and Tony Andrews + * + * 3.10A - took version of STEVIE posted to usenet and added Amiga + * and BSD support; added undo and redo commands; sped up + * output to screen; sped up on-screen activities (such as + * cursoring); fixed miscellaneous small bugs and changed some + * commands so that they more closely resembled vi. + * - GRWalter (Fred) + * + * 3.11B - added the ability to be run in the background (STEVIE will + * attempt to use the current window, but if it can't then it + * will open its own window). Fixed some other miscellaneous + * bugs (some to do with re-sizing the screen, one to do with + * undo'ing changes on lines that start with whitespace). + * - GRWalter (Fred) + * + * 3.11C - fixed a bug that was causing the entire screen to be refreshed + * at the wrong times sometimes. Input mode was sped up as well + * as a bug involving lines that wrapped was fixed. Changed :ta + * a bit. Fixed bug triggered when files are > 6000 lines. + * - GRWalter (Fred) + * + * 3.31A - Tony Andrews put out a divergent version of STEVIE (version 3.31). + * I moved the important stuff over into my version. + * + * Here is a list of what was moved over : + * + ************************************************************************* + * Revision 3.29 88/06/26 14:53:19 tony + * Added support for a simple form of the "global" command. It supports + * commands of the form "g/pat/d" or "g/pat/p", to delete or print lines + * that match the given pattern. A range spec may be used to limit the + * lines to be searched. + * + * Revision 3.28 88/06/25 21:44:22 tony + * Fixed a problem in the processing of colon commands that caused + * substitutions of patterns containing white space to fail. + * + * Revision 3.26 88/06/10 13:44:06 tony + * Fixed a bug involving writing out files with long pathnames. A small + * fixed size buffer was being used. The space for the backup file name + * is now allocated dynamically. + * + * Revision 1.12 88/05/03 14:39:52 tony + * Also merged in support for DOS. + * + * Revision 1.11 88/05/02 21:38:21 tony + * The code that reads files now handles boundary/error conditions much + * better, and generates status/error messages that are compatible with + * the real vi. Also fixed a bug in repeated reverse searches that got + * inserted in the recent changes to search.c. + * + * Revision 1.10 88/05/02 07:35:41 tony + * Fixed a bug in the routine plines() that was introduced during changes + * made for the last version. + * + * Revision 1.9 88/05/01 20:10:19 tony + * Fixed some problems with auto-indent, and added support for the "number" + * parameter. + * + * Revision 1.8 88/04/30 20:00:49 tony + * Added support for the auto-indent feature. + * + * Revision 1.6 88/04/28 08:19:35 tony + * Modified Henry Spencer's regular expression library to support new + * features that couldn't be done easily with the existing interface. + * This code is now a direct part of the editor source code. The editor + * now supports the "ignorecase" parameter, and multiple substitutions + * per line, as in "1,$s/foo/bar/g". + * + * Revision 1.5 88/04/24 21:38:00 tony + * Added preliminary support for the substitute command. Full range specs. + * are supported, but only a single substitution is allowed on each line. + * + * Revision 1.4 88/04/23 20:41:01 tony + * Worked around a problem with adding lines to the end of the buffer when + * the cursor is at the bottom of the screen (in misccmds.c). Also fixed a + * bug that caused reverse searches from the start of the file to bomb. + * + * Revision 1.3 88/03/24 08:57:00 tony + * Fixed a bug in cmdline() that had to do with backspacing out of colon + * commands or searches. Searches were okay, but colon commands backed out + * one backspace too early. + * + * Revision 1.2 88/03/21 16:47:55 tony + * Fixed a bug in renum() causing problems with large files (>6400 lines). + ************************************************************************* + * - GRWalter (Fred) + * + * 3.32A - added the :[range]d command. Played with 'p' and 'P'. + * Added undo capability to :s and :g//d commands. + * Added '%' as a line range specifier (entire file). + * Fixed search so that tags file from real ctags could be used. + * Fixed undo after delete everything operation. + * Made prt_line work in nu mode (so :g//p works right). + * Fixed ai mode (when there was text after the cursor it didn't ai). + * Fixed 'J' (didn't handle next line just having whitespace). + * Fixed :[range] so it behaves like the real vi (goes to highest + * line number in the given range). + * Made it so that the cursor is on the last char inserted instead + * the one right after when there is exactly 1 char right after. + * Made change operator work properly when it ended on the + * end of the line. + * - GRWalter (Fred) + * + * 3.33A - no longer s_refresh when putting or undoing or + * redoing until I am done. 'p', 'u' and '.' thus sped up. + * - no longer recalculate line lengths when cursupdate() called, + * which speeds up lots'a things (like on-screen cursoring). + * - avoid redrawing (in s_refresh) as much as possible, which + * speeds up (among other things) cursoring (off screen), put, undo, + * redo, etc. + * - GRWalter (Fred) + * + * 3.34A - rewrote s_refresh and updatenextline so they won't do as + * much work. Sped up cursoring off-screen. Fixed bug in cursupdate + * (could index through NULL pointer). + * - GRWalter (Fred) + * + * 3.35A - Compiles with Lattice 5.0 now - needed miscellaneous changes. + * - Environment variables (EXINIT) finally used. + * - Stevie is now compiled so it's residentable. + * - Fixed bug in insstr() (caused problems with redo of inserts). + * - Fixed buffer overflow/corrupt messages. + * - Tweaked s_refresh routine some more. + * - Added __DATE__ and __TIME__ of compilation to help screen. + * - GRWalter (Fred) + * + * 3.35F - Removed Nextscreen, Realscreen, updateRealscreen and + * - redrawline. Re-wrote all screen I/O to directly update the + * screen when necessary, with a resulting memory and speed savings. + * - Fixed bug in amiga.c that caused Stevie to (occasionlly) lock-up + * upon startup until the window was resized. + * - Removed T_SC and T_RC from term.h file (no longer used). + * - Miscellaneous little changes. + * - GRWalter (Fred) + * + * 3.35H - Fixed all routines to check memory allocation return status + * (except for strsave() function). + * - Fixed bug with macro outone() in amiga.c and bsd.c. + * - GRWalter (Fred) + * + * 3.6 - Big jump in revision number since last version on a Fish Disk + * was descibed as version 3.5 (thus people will know this is + * a later version). + * - Miscellaneous little changes. + * - GRWalter (Fred) + * + * 3.6A - Added (Amiga) AUX: device support to Stevie. + * - GRWalter (Fred) + * + * 3.7 - Now use ARP wildcard expansion for the command line and for + * the :e and :r commands. If ARP isn't available, Stevie still + * works, but no wildcard expansion is done. + * - Added :!cmd support to Stevie. + * - Added 'R'eplace mode to Stevie. + * - Added ':e %' where %=current filename. + * - Fixed bug with 'cw' when on a single character word. + * - Fixed bug with 'cw' and 'dw' when at the end of a line. + * - Fixed bugs with ":set list" and ":set nu" modes. + * - Fixed bug with CTRL('^') (switch to alternate file) after + * a :ta was done that stayed within the same file. + * - Fixed bug with '@' when editting on the command line + * (I.E. with '/', '?', or ':'). + * - When run, Stevie tries to open a 640x200 window. + * - GRWalter (Fred) + * + * 3.7A - Fixed possible memory leakage in wildcard code. + * - Fixed filename loss if file didn't exist (bug in wildcard code). + * - GRWalter (Fred) + */ + +char *Version = "STEVIE - Version 3.7A";