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";