#define TRUE 1 #define FALSE 0 #define NO 0 #define YES 1 #define NULL 0 /* null pointer */ #define MODE_READ 0 #define CR '\015' #define LF '\012' #define BS '\010' #define ESC '\033' #define TAB '\011' #define HOME -71 #define UPARO -72 #define PGUP -73 #define LTARO -75 #define RTARO -77 #define END -79 #define DNARO -80 #define PGDN -81 #define INS -82 #define DEL -83 #define CHOM -119 #define CEND -117 #define LF_PAREN '(' #define RT_PAREN ')' #define LF_CURLY '{' #define RT_CURLY '}' #define LF_SQUARE '[' #define RT_SQUARE ']' #define MIN(x,y) (((x) < (y)) ? (x):(y)) #define MAX(x,y) (((x) < (y)) ? (y):(x)) #define MEMMOVE(t,s,l) _move(l,s,t) #define PROMPTLINE 0 #define MSGLINE 1 #define FIRSTLINE 2 #define ENDLINE 24 #define MAX_COLS 80 #define MEMBUF_SIZE 50000 #define COPYBUF_SIZE 8000 #define NUM_HEADERS 1 #define NUM_TAGS 4 /* Now, on to the variables! */ unsigned int line_ptrs[ENDLINE+2]; /* start of line in memory */ /* Note: +2 so I can check start of line after last line on screen */ unsigned int startcol; /* starting column displayed on screen */ unsigned int numcols; /* number of columns on current screen */ unsigned int numrows; /* number of rows on current screen */ unsigned int startline; /* staring line displayed on screen */ unsigned int startpos; /* position in membuf of the staring line */ unsigned int buf_start, buf_end; unsigned int scr_row, scr_col; /* screen cursor position */ unsigned int changed; unsigned int tags[NUM_TAGS]; /* tags for the Jump command */ unsigned int videoseg; /* 0xb800 for CGA/EGA/VGA, 0xb000 for MDA/Hercules */ unsigned char home_hit; /* check for second home key */ unsigned char end_hit; /* check for second end key */ long filesize; unsigned char cmdbuf[128]; /* a long line */ unsigned char srchbuf[128]; unsigned char replacebuf[128]; unsigned char filename[128]; unsigned int case_significant; /* 0 = NO, 1 = YES */ int strncmp(), strncmpi(); /* Note: the library strncmp and strncmpi are not used, as the work badly when given a length of 0 */ int (*strcmp_fn[2])() = {strncmpi, strncmp}; /* indexed by case_significant */ int filein; /* The file handle. */ int again_cmd; int tab_spacing = 8; char membuf[MEMBUF_SIZE+1000]; /* I want SLOP! */ char copybuf[COPYBUF_SIZE+100]; unsigned int copybuf_len; /* len of stuff in copybuf */ int header_num; /* index of the header line displayed */ char header_msg[] = "Again Buffer Copy Delete Find -find Replace Insert Jump Quit Set Tag"; int main_cmds[] = { 'A','B','C','D','F','-','I','J','Q','R','S','T','(',')', '{','}','[',']',BS,DNARO,UPARO,PGDN,PGUP,HOME,END,LTARO, RTARO,DEL,CHOM,CEND,INS }; char buf_header[] = "Buffer: "; int buf_cmds[] = { 'B',UPARO,DNARO,LTARO,RTARO,PGUP,PGDN,HOME,END,CHOM,CEND }; char del_header[] = "Delete: "; int del_cmds[] = { 'D',UPARO,DNARO,LTARO,RTARO,PGUP,PGDN,HOME,END,CHOM,CEND }; char ins_header[] = "Insert: , Esc to exit"; char exch_header[] = "Exchange: , Esc to exit"; char quit_header[] = "Quit: Backup Exit Initalize Save-exit Update Write"; int quit_cmds[] = { 'B','E','I','S','U','W' }; char set_header[] = "Set: Tabs Case"; int set_cmds[] = { 'T','C' }; char tag_header[] = "Tag: A, B, C, D"; /* assume NUM_TAGS is 4 */ int tag_cmds[] = { 'A','B','C','D' }; char jump_header[] = "Jump tag: A, B, C, D"; /* assume NUM_TAGS is 4 */ int jump_cmds[] = { 'A','B','C','D' }; main(argc,argv) int argc; char *argv[]; { if (_peek(0x63,0x40) < 0xd0) /* check video I/O port base */ videoseg = 0xb000; /* MDA/Hercules */ else videoseg = 0xb800; /* CGA/EGA/VGA */ filename[0] = '\0'; if (argc > 1) strcpy(filename,argv[1]); edit_init(); edit_file(); msgout(0,"Bye!"); curpos(ENDLINE,0); } edit_init() { int i,j; long lseek(); numrows = ENDLINE; numcols = MAX_COLS; for (i = FIRSTLINE; i < ENDLINE+1; i++) line_ptrs[i] = -1; /* NULL */ for (i = 0; i < NUM_TAGS; i++) tags[i] = -1; for (i = 0; i <= numrows ; i++) /* initalize screen */ for (j = 0; j < numcols; j++) char_attr_out(i,j,' ',7); header_num = 0; promptout(header_msg); /* curpos(1,0); */ msgout(-6,"-----"); /* -6 to offset the 6 added in msgout! */ case_significant = NO; scr_row = FIRSTLINE; scr_col = 0; startcol = 0; startline = 0; startpos = 0; filesize = 0; home_hit = 0; end_hit = 0; if (filename[0]) { if ((filein = open(filename,MODE_READ)) == -1) { msgout(0,filename); msgout(strlen(filename),": file not found."); } else { if ((filesize = lseek(filein,0L,2)) > MEMBUF_SIZE) { msgout(0,filename); msgout(strlen(filename),": file too big."); } else { lseek(filein,0L,0); read(filein,membuf,(unsigned int)filesize); } close(filein); } } showscreen(FIRSTLINE); } edit_file() { unsigned int i,j,k,filepos(); int ch; char left, right; /* for brace/bracket/parenthesis matching */ changed = FALSE; while (1) /* FOREVER! */ { ch = get_cmd(main_cmds); if (ch != HOME) home_hit = FALSE; if (ch != END) end_hit = FALSE; switch (ch) { case 'A': /* Again! */ check_endline(); /* make sure cursor not past EOL */ switch (again_cmd) { case 'F': msgout(0,"Find: \""); msgout(7,srchbuf); find_text(); break; case '-': msgout(0,"Reverse find: \""); msgout(15,srchbuf); rev_find_text(); break; case 'R': msgout(0,"Replace: \""); msgout(10,srchbuf); msgout(10+strlen(srchbuf),"\" with \""); msgout(19+strlen(srchbuf),replacebuf); replace_text(); changed = TRUE; break; } break; case 'B': /* buffer */ check_endline(); /* make sure cursor not past EOL */ buf_start = filepos(); promptout(buf_header); /* show BUF command line */ do { ch = get_cmd(buf_cmds); move_cursor(ch); } while ((ch != 'B') && (ch != ESC)); check_endline(); /* make sure cursor not past EOL */ buf_end = filepos(); if (buf_end < buf_start) { k = buf_start; buf_start = buf_end; buf_end = k; } if (buf_start != buf_end) { if (buf_end-buf_start < COPYBUF_SIZE) { copybuf_len = buf_end - buf_start; MEMMOVE(copybuf,&membuf[buf_start],copybuf_len); } else msgout(0,"Block too big for buffer, not saved."); } promptout(header_msg); break; case 'C': /* copy buffer into memory */ check_endline(); /* make sure cursor not past EOL */ if (copybuf_len) { if (filesize + copybuf_len < MEMBUF_SIZE) { i = filepos(); MEMMOVE(&membuf[i+copybuf_len],&membuf[i],(unsigned int)filesize - i); MEMMOVE(&membuf[i],copybuf,copybuf_len); filesize += copybuf_len; for (k = 0; k < NUM_TAGS; k++) /* move tags, too */ if ((tags[k] != -1) && (tags[k] > i)) tags[k] += copybuf_len; showscreen(FIRSTLINE); } else msgout(0,"No room in file buffer."); } break; case 'D': check_endline(); /* make sure cursor not past EOL */ buf_start = filepos(); char_attr_out(scr_row,scr_col,membuf[buf_start],0x70); promptout(del_header); /* show DEL command line */ do { ch = get_cmd(buf_cmds); move_cursor(ch); } while ((ch != 'D') && (ch != ESC)); if (calc_row_col(buf_start,&i,&j)) char_attr_out(i,j,membuf[buf_start],7); check_endline(); /* make sure cursor not past EOL */ buf_end = filepos(); if (buf_end < buf_start) { k = buf_start; buf_start = buf_end; buf_end = k; } if (buf_start != buf_end) { copybuf_len = buf_end - buf_start; if (copybuf_len >= COPYBUF_SIZE) { msgout(0,"Block to big for buffer, delete anyway?"); ch = get_char(); if (ch != 'Y') ch = '\001'; /* just a flag value */ } else MEMMOVE(copybuf,&membuf[buf_start],copybuf_len); if (ch != '\001') { MEMMOVE(&membuf[buf_start],&membuf[buf_end],(unsigned int)filesize - buf_end); filesize -= copybuf_len; for (k = 0; k < NUM_TAGS; k++) /* move tags, too */ if (tags[k] != -1) if (((tags[k] >= buf_start) && (tags[k] <= buf_end)) || (tags[k] > (unsigned int)filesize)) tags[k] = -1; /* tag removed! */ else if (tags[k] > copybuf_len) tags[k] -= copybuf_len; findscreen(buf_start,YES); /* show screen, as appropriate */ changed = TRUE; } } promptout(header_msg); break; case 'F': /* find string */ check_endline(); /* make sure cursor not past EOL */ msgout(0,"Find: \""); getstr(7,srchbuf,cmdbuf); if (cmdbuf[0]) { again_cmd = 'F'; /* for Again */ strcpy(srchbuf,cmdbuf); find_text(); } break; case '-': check_endline(); /* make sure cursor not past EOL */ msgout(0,"Reverse Find: \""); getstr(15,srchbuf,cmdbuf); if (cmdbuf[0]) { again_cmd = '-'; /* for Again */ strcpy(srchbuf,cmdbuf); rev_find_text(); } break; case 'R': /* replace */ check_endline(); /* make sure cursor not past EOL */ msgout(0,"Replace: \""); getstr(10,srchbuf,cmdbuf); if (cmdbuf[0]) { strcpy(srchbuf,cmdbuf); msgout(10+strlen(srchbuf),"\" with \""); getstr(19+strlen(srchbuf),replacebuf,cmdbuf); if (cmdbuf[0]) { again_cmd = 'R'; strcpy(replacebuf,cmdbuf); replace_text(); changed = TRUE; } } break; case INS: case 'I': /* insert mode */ check_endline(); /* make sure cursor not past EOL */ if (filesize < MEMBUF_SIZE) /* if room left */ { promptout(ins_header); do { if ((ch = ci()) == 0) ch = (ci() * -1); if ((ch == BS) || (ch < 0)) move_cursor(ch); else if (ch != ESC) { check_endline(); /* make sure cursor OK */ if (filesize + 2 >= MEMBUF_SIZE) { msgout(0,"No room left in file."); break; } k = (ch == CR) ? 2:1; if (filesize) /* if not blank! */ i = filepos(); else { i = 0; line_ptrs[scr_row] = 0; /* set initial ptr */ } MEMMOVE(&membuf[i+k],&membuf[i],(unsigned int)filesize - i); for (j = scr_row+1; j <= numrows+1; j++) if (line_ptrs[j] != -1) line_ptrs[j] += k; /* move lines down */ filesize += k; /* update size of file */ for (j = 0; j < NUM_TAGS; j++) /* update tags */ if ((tags[j] != -1) && (tags[j] > i)) tags[j] += k; membuf[i] = ch; if (ch == CR) { membuf[i+1] = LF; scr_col = 0; showscreen(scr_row); move_cursor(DNARO); /* go down to next line */ } else if (ch == TAB) scr_col += tab_spacing - scr_col%tab_spacing; else scr_col++; showline(scr_row); curpos(scr_row,scr_col); /* set cursor, too */ changed = TRUE; } } while (ch != ESC); } else msgout(0,"No room left in file."); promptout(header_msg); break; case 'Q': /* quit the editor */ check_endline(); /* make sure cursor not past EOL */ do { promptout(quit_header); ch = get_cmd(quit_cmds); msgout(0,""); /* clear message line */ switch (ch) { case 'B': /* create Backup file */ promptout("Backup file"); if (!filename[0]) /* if no filename */ { msgout(0,"Filename? "); getstr(10,"",cmdbuf); /* get a string, no template */ if (cmdbuf[0]) strcpy(filename,cmdbuf); else break; /* if no name, can't back it up */ } strcpy(cmdbuf,filename); for (i = strlen(cmdbuf); i; i--) if (cmdbuf[i] == '.') break; if (cmdbuf[i] == '.') strcpy(&cmdbuf[i],".BAK"); else strcat(cmdbuf,".BAK"); unlink(cmdbuf); /* remove any old .BAK file */ rename(filename,cmdbuf); break; case 'E': /* Exit */ promptout("Exit"); if (changed) { msgout(0,"Ignore changes?"); i = get_char(); if (i != 'Y') break; } return; /* that's all! */ case 'I': /* initalize */ promptout("Initalize"); if (changed) { msgout(0,"Ignore changes?"); i = get_char(); if (i != 'Y') { ch = '\001'; /* anything but I or Esc */ break; } } msgout(0,"Filename? "); getstr(10,"",cmdbuf); /* get a line without template */ if (cmdbuf[0]) { strcpy(filename,cmdbuf); edit_init(); changed = FALSE; } else ch = '\001'; /* anything but I or Esc */ break; case 'S': /* Save and exit */ promptout("Save and Exit"); if (!filename[0]) /* if no filename */ { msgout(0,"Filename? "); getstr(10,"",cmdbuf); /* get a string, no template */ if (cmdbuf[0]) strcpy(filename,cmdbuf); else break; /* if no name, can't save it */ } i = filesize + 1; /* not equal to filesize! */ if ((filein = creat(filename)) != -1) { i = write(filein,membuf,(unsigned int)filesize); close(filein); } if (i == (unsigned int)filesize) return; /* and done */ msgout(0,"Unable to write file--please try again."); break; case 'U': /* update current file */ promptout("Update"); i = filesize + 1; /* not equal to filesize! */ if ((filein = creat(filename)) != -1) { i = write(filein,membuf,(unsigned int)filesize); close(filein); } if (i != (unsigned int)filesize) msgout(0,"Unable to write file--please try again."); else changed = FALSE; /* no longer changed */ break; case 'W': promptout("Write"); msgout(0,"Output file? "); getstr(14,"",cmdbuf); if (cmdbuf[0]) { strcpy(filename,cmdbuf); i = filesize + 1; /* not equal to filesize! */ if ((filein = creat(filename)) != -1) { i = write(filein,membuf,(unsigned int)filesize); close(filein); } if (i != (unsigned int)filesize) msgout(0,"Unable to write file--please try again."); else changed = FALSE; } break; } } while ((ch != ESC) && (ch != 'I')); promptout(header_msg); break; case LF_PAREN: /* match parenthesis/brace/brackets */ case RT_PAREN: case LF_CURLY: case RT_CURLY: case LF_SQUARE: case RT_SQUARE: check_endline(); /* make sure cursor not past EOL */ i = filepos(); ch = membuf[i]; if ((ch == LF_PAREN) || (ch == RT_PAREN) || (ch == LF_CURLY) || (ch == RT_CURLY) || (ch == LF_SQUARE) || (ch == RT_SQUARE)) { j = 0; /* level */ if ((ch == LF_PAREN) || (ch == LF_CURLY) || (ch == LF_SQUARE)) { left = ch; right = RT_PAREN; /* default */ if (left == LF_CURLY) right = RT_CURLY; if (left == LF_SQUARE) right = RT_SQUARE; do { if (membuf[i] == left) j++; else if (membuf[i] == right) j--; } while ((++i < (unsigned int)filesize) && (j)); i--; /* back to last char */ } else { right = ch; left = LF_PAREN; /* default */ if (right == RT_CURLY) left = LF_CURLY; if (right == RT_SQUARE) left = LF_SQUARE; do { if (membuf[i] == left) j--; else if (membuf[i] == right) j++; } while ((--i != -1) && (j)); i++; /* up to last char */ } if (j) msgout(0,"match not found"); else findscreen(i,NO); /* do not force screen redraw */ } break; case 'S': /* set stuff */ promptout(set_header); do { ch = get_cmd(set_cmds); switch (ch) { case 'T': /* set tab spacing */ msgout(0,"Select tab spacing: 1 .. 9"); ch = get_char(); if ((ch > '0') && (ch <= '9')) { tab_spacing = ch - '0'; /* set new spacing */ showscreen(FIRSTLINE); /* redraw with new spacing */ msgout(0,""); /* erase message line */ } else msgout(0,"Illegal tab spacing"); ch = ESC; /* time to break the outer loop */ break; case 'C': /* case ignore */ msgout(0,"Ignore case on searches (Y/N)?"); ch = get_char(); if (ch == 'Y') case_significant = NO; if (ch == 'N') case_significant = YES; ch = ESC; /* always quit */ msgout(0,""); /* and erase msg */ break; } } while (ch != ESC); promptout(header_msg); break; case 'T': promptout(tag_header); do { ch = get_char(); if ((ch >= 'A') && (ch < 'A'+NUM_TAGS)) { msgout(0,""); /* erase message line */ tags[ch-'A'] = filepos(); /* set appropriate tag */ ch = ESC; /* time to quit */ } else msgout(0,"Illegal tag"); } while (ch != ESC); promptout(header_msg); break; case 'J': /* Jmp to Tag */ promptout(jump_header); do { ch = get_char(); if ((ch >= 'A') && (ch < 'A'+NUM_TAGS)) { msgout(0,""); /* erase message line */ if (tags[ch-'A'] != -1) /* if tag exists */ findscreen(tags[ch-'A']); /* goto appropriate tag */ ch = ESC; /* time to quit */ } else msgout(0,"Illegal tag"); } while (ch != ESC); promptout(header_msg); break; case ESC: check_endline(); /* make sure cursor not past EOL */ msgout(0,""); /* clear message line */ break; case ' ': /* switch command header lines */ header_num = (header_num + 1) % NUM_HEADERS; promptout(header_msg); break; default: /* a cursor key */ move_cursor(ch); break; } } } find_text() { int i; i = findstr(); /* get location in membuf */ if (i != -1) findscreen(i+strlen(srchbuf),NO); /* back up a few lines and show screen, no force redraw */ else msgout(0,"Text not found"); curpos(scr_row,scr_col); } rev_find_text() { int i; i = rev_findstr(); /* get location in membuf */ if (i != -1) findscreen(i,NO); /* back up a few lines and show screen, no force redraw */ else msgout(0,"Text not found"); curpos(scr_row,scr_col); } replace_text() { int i,j,k; i = findstr(); if (i != -1) { j = strlen(srchbuf); MEMMOVE(&membuf[i],&membuf[i+j],(unsigned int)filesize - i - j); filesize -= j; for (k = 0; k < NUM_TAGS; k++) /* move tags, too */ if (tags[k] != -1) if (((tags[k] >= i) && (tags[k] <= i+j)) || (tags[k] > (unsigned int)filesize)) tags[k] = -1; /* tag removed! */ else if (tags[k] > i) tags[k] -= j; j = strlen(replacebuf); MEMMOVE(&membuf[i+j],&membuf[i],(unsigned int)filesize - i); MEMMOVE(&membuf[i],replacebuf,j); filesize += j; for (k = 0; k < NUM_TAGS; k++) if ((tags[k] != -1) && (tags[k] > i)) tags[k] += j; findscreen(i,YES); /* force redraw */ } else msgout(0,"Text not found"); curpos(scr_row,scr_col); } findstr() /* Find text in the file. */ { unsigned int i, j; char *p; char ch; if (((j = strlen(srchbuf)) == 0) || (j > (unsigned int)filesize)) return(-1); /* if blank string, not found */ i = filepos(); /* our location in the file */ p = &membuf[i]; /* ptr to the string */ ch = tolower(srchbuf[0]); for ( ; i < (unsigned int)filesize - j; i++, p++) if (ch == tolower(*p)) if ((*strcmp_fn[case_significant])(srchbuf,p,j) == 0) return(i); /* found, return location */ return(-1); /* else not found */ } rev_findstr() /* Reverse find text in the file. */ { unsigned int i, j; char *p; char ch; if (((j = strlen(srchbuf)) == 0) || (j > (unsigned int)filesize) || ((i = filepos()) == -1)) return(-1); /* if blank string, not found */ p = &membuf[--i]; /* ptr to the string */ ch = tolower(srchbuf[0]); for ( ; i != -1 ; i--, p--) if (ch == tolower(*p)) if ((*strcmp_fn[case_significant])(srchbuf,p,j) == 0) return(i); /* found, return location */ return(-1); /* else not found */ } strncmp(s1,s2,n) char *s1, *s2; int n; { while ((n--) && (*s1) && (*s2)) /* while count and not EOS */ { if (*s1 != *s2) return(*s1 - *s2); /* if different, say so */ s1++; /* else next char */ s2++; } if (n != -1) return(*s1 - *s2); /*if stopped by EOS, be sure both have EOS*/ return(0); } strncmpi(s1,s2,n) char *s1, *s2; int n; { while ((n--) && (*s1) && (*s2)) /* while count and not EOS */ { if (tolower(*s1) != tolower(*s2)) /* if different, say so */ return(tolower(*s1) - tolower(*s2)); s1++; /* else next char */ s2++; } if (n != -1) return(*s1 - *s2); /*if stopped by EOS, be sure both have EOS*/ return(0); } move_cursor(ch) int ch; { unsigned int i, p, n, c, prevline(); switch (ch) { case DNARO: if (line_ptrs[scr_row+1] != -1) /* if there is a next line */ { if (scr_row == numrows) screen_down(); /* move logical screen down 1 line */ else { scr_row++; curpos(scr_row,scr_col); } } else { scr_col = linepos(scr_row,(unsigned int)filesize-line_ptrs[scr_row]); curpos(scr_row,scr_col); } break; case UPARO: if (scr_row == FIRSTLINE) { if (!line_ptrs[FIRSTLINE]) curpos(scr_row,scr_col = 0); /* if top of screen */ else screen_up(); /* move logical screen up 1 line */ } else { scr_row--; curpos(scr_row,scr_col); } break; case PGDN: if (line_ptrs[numrows+1] == -1) move_cursor(CEND); else { line_ptrs[FIRSTLINE] = line_ptrs[FIRSTLINE+20]; showscreen(FIRSTLINE); curpos(scr_row,scr_col = 0); } break; case PGUP: if (!line_ptrs[FIRSTLINE]) move_cursor(CHOM); else { p = line_ptrs[FIRSTLINE]; for (i = 0; (i < 20) && (p); i++, p = prevline(p)) ; line_ptrs[FIRSTLINE] = p; showscreen(FIRSTLINE); scr_col = 0; curpos(scr_row,scr_col); } break; case HOME: scr_col = 0; if (home_hit) scr_row = FIRSTLINE; curpos(scr_row,scr_col); home_hit = 1; break; case END: if (end_hit) { if (line_ptrs[numrows + 1] == -1) { move_cursor(CEND); break; } scr_row = numrows; } if (line_ptrs[scr_row+1] == -1) /* if no more lines */ scr_col = linepos(scr_row,(unsigned int)filesize-line_ptrs[scr_row]); else scr_col = linepos(scr_row,line_ptrs[scr_row+1]-line_ptrs[scr_row]-2); curpos(scr_row,scr_col); end_hit = 1; break; case LTARO: check_endline(); /* make sure cursor not past EOL */ if (filepos()) /* if not at BOF */ { scr_col = posline(scr_row,scr_col) - 1; /* get physical line pos */ if (scr_col == -1) { if (scr_row == FIRSTLINE) screen_up(); /* move logical screen up 1 line */ else scr_row--; scr_col = linepos(scr_row,line_ptrs[scr_row+1]-line_ptrs[scr_row]-2); } else scr_col = linepos(scr_row,scr_col); } curpos(scr_row,scr_col); break; case RTARO: check_endline(); /* make sure cursor not past EOL */ scr_col = posline(scr_row,scr_col) + 1; /* get physical line pos */ if (scr_col + line_ptrs[scr_row] > (unsigned int)filesize) /* if EOF */ scr_col--; /* can't go there! */ else if (scr_col + line_ptrs[scr_row] + 1 == line_ptrs[scr_row+1]) { if (scr_row == numrows) screen_down(); /*move screen down 1 line*/ else scr_row++; scr_col = 0; } scr_col = linepos(scr_row,scr_col); curpos(scr_row,scr_col); break; case DEL: check_endline(); /* make sure cursor not past EOL */ i = filepos(); /* get posn of this character */ if (i < (unsigned int)filesize) { c = membuf[i]; /* and the char itself */ p = 1; /* size to delete */ if ((c == CR) && (i < (unsigned int)filesize) && (membuf[i+1] == LF)) p = 2; if ((c == LF) && (i) && (membuf[i-1] == CR)) { i--; p = 2; c = CR; } MEMMOVE(&membuf[i],&membuf[i+p],(unsigned int)filesize - i); filesize -= p; for (n = 0; n < NUM_TAGS; n++) /* move tags, too */ if (tags[n] != -1) if (((tags[n] >= i) && (tags[n] <= i+p)) || (tags[n] > (unsigned int)filesize)) tags[n] = -1; /* tag removed! */ else if (tags[n] > i) tags[n] -= p; changed = TRUE; /* we just modified the file */ if ((c == CR) && (p == 2)) showscreen(scr_row); else { showline(scr_row); for (i = scr_row+1; i <= numrows+1; i++) /* update ptrs, too */ if (line_ptrs[i] != -1) line_ptrs[i] -= p; } } break; case BS: if (filepos()) /* if not at BOF */ { move_cursor(LTARO); move_cursor(DEL); } break; case CHOM: if (filesize) line_ptrs[FIRSTLINE] = 0; /* top of file */ showscreen(FIRSTLINE); /* show the screen */ scr_row = FIRSTLINE; scr_col = 0; curpos(scr_row,scr_col); break; case CEND: if (filesize) { findscreen((unsigned int) filesize,NO); /* no need to force redraw */ scr_col = linepos(scr_row,(unsigned int)filesize-line_ptrs[scr_row]); curpos(scr_row,scr_col); } break; } } screen_down() /* Move logical screen down 1 line. */ { unsigned int i, nextline(); if (line_ptrs[numrows+1] != -1) /* if another line available */ { for (i = FIRSTLINE; i <= numrows; i++) line_ptrs[i] = line_ptrs[i+1]; /* move pointers up 1 */ line_ptrs[numrows+1] = nextline(line_ptrs[numrows+1]); /* add another line to the ptrs */ scroll_up(FIRSTLINE,numrows,0,numcols-1,1); showline(numrows); } } screen_up() /* Move logical screen up 1 line. */ { unsigned int i, prevline(); if (line_ptrs[FIRSTLINE]) /* if not at top of screen */ { for (i = numrows+1; i > FIRSTLINE; i--) line_ptrs[i] = line_ptrs[i-1]; /* move pointers down 1 */ line_ptrs[FIRSTLINE] = prevline(line_ptrs[FIRSTLINE]); scroll_down(FIRSTLINE,numrows,0,numcols-1,1); showline(FIRSTLINE); } } /* Back up a few lines before location p, put cursor on p. NOTE: P must be within the file! */ findscreen(p,force) unsigned int p, force; { unsigned int i,j,redraw; redraw = TRUE; i = (line_ptrs[numrows+1] == -1) ? (unsigned int)filesize:line_ptrs[numrows+1]; if ((p < line_ptrs[FIRSTLINE] || (p > i)) || (line_ptrs[numrows+1] == (unsigned int)filesize)) /* if not on current screen */ { if (p < line_ptrs[FIRSTLINE]) { j = line_ptrs[FIRSTLINE]; do { j = prevline(j); } while ((j > p) && (j)); /*back up until correct line is found*/ } else { j = i; /* get end location */ do { j = nextline(j); } while ((j < p) && (j != -1)); /*forward until correct line is found*/ if (j == -1) j = (unsigned int)filesize; } for (i = 0; (i < 10) && (j); i++) j = prevline(j); /* make this the 4th line on the screen */ line_ptrs[FIRSTLINE] = j; /* set new pointer */ showscreen(FIRSTLINE); /* show entire screen */ redraw = FALSE; /* redraw already occurred */ } for (i = numrows; (i > FIRSTLINE) && (line_ptrs[i] == -1); i--) ; /* get last available line */ for (j = i; j > FIRSTLINE; j--) /* get line cursor should be on */ if (p >= line_ptrs[j]) break; scr_row = j; scr_col = linepos(scr_row,p-line_ptrs[j]); /* find new column */ curpos(scr_row,scr_col); if ((redraw) && (force)) showscreen(FIRSTLINE); /* force screen update */ } getstr(col,template,str) int col; char *template, *str; { int i, p, row, ch; col += 6; /* skip the dashes */ row = MSGLINE; if (template[0]) /* if non-blank template */ for (i = 0; (template[i]) && (i+col < numcols); i++) charout(row,col+i,template[i]); curpos(row,col); ch = ci(); if (ch == CR) strcpy(str,template); /* if use template */ else { clr_to_eol(row,col); p = 0; /* position on line */ do { if (ch == BS) { if (p) { p--; charout(row,col+p,' '); curpos(row,col+p); } } else { str[p] = ch; if (col+p < numcols) { charout(row,col+p,ch); curpos(row,col+p+1); } p++; } ch = ci(); } while (ch != CR); str[p] = '\0'; } } get_char() /* get a char: Upper case if normal, lower case if curosr */ { int ch; if ((ch = toupper(ci())) == 0) ch = (ci() * -1); return(ch); } get_cmd(legal) int *legal; { int ch; do { ch = get_char(); } while ((ch != ESC) && (!charfind(legal,ch))); return(ch); } int charfind(legal,ch) /* see if a char occurs in a string */ int *legal; int ch; { while (*legal) if (*(legal++) == ch) return(TRUE); return(FALSE); } unsigned int nextline(p) /* Return ofs of next line, or -1 if none. */ unsigned int p; { unsigned int x,y; if (p != -1) { if (p == (unsigned int)filesize) p = -1; else { while ((p < (unsigned int)filesize) && (membuf[p] != LF)) p++; if ((p < (unsigned int)filesize) && (membuf[p] == LF)) p++; /* another line */ if ((p == (unsigned int)filesize) && (membuf[p-1] != LF)) p = -1; /* there IS no line after! */ } } return(p); } unsigned int prevline(p) /* Return ofs of previous line, 0 if start. */ unsigned int p; { if (p == -1) return(0); /* if nothing in membuf */ if (p) { while ((p != -1) && (membuf[p] != LF)) p--; /* back up to end of prev line */ if (membuf[p] == LF) p--; /* if another line back past end of line */ while ((p != -1) && (membuf[p] != LF)) p--; /* back up to line before that */ if (membuf[p] == LF) p++; /* if another line up one to start of line */ else p = 0; } return(p); } showscreen(r) /* First row to start at. */ int r; { unsigned int i,j,p; if (filesize) /* point to known lines */ p = (line_ptrs[r] == -1) ? 0:line_ptrs[r]; else p = -1; for (i = r; i < numrows+1; i++) { line_ptrs[i] = p; /* offset into membuf */ showline(i); /* show this line */ p = nextline(p); /* get pointer to next line */ } line_ptrs[i] = p; /* set ptr to line after lines on screen */ curpos(scr_row,scr_col); /* put cursor in place */ } showline(r) int r; { char *p; char ch, ch2; int i, j, n, pos; i = 0; /* index into line */ pos = 0; /* position on screen */ if (line_ptrs[r] != -1) /* if line exists */ { p = &membuf[n = line_ptrs[r]]; while ((i < numcols) && (n <= (unsigned int)filesize) && (*p != LF)) { ch = *(p++); n++; if (ch != CR) /* CR is a special case */ { if (ch == TAB) /* handle TAB special */ { j = tab_spacing - pos % tab_spacing; while (j--) { if (pos >= startcol) { charout(r,pos-startcol,' '); /* add spaces for tab */ i++; /* count this char */ } pos++; /* always update pos */ } } else { if (pos >= startcol) { charout(r,pos-startcol,ch); i++; /* count this char */ } pos++; } } } if (pos < startcol) clr_to_eol(r,0); /* if nothing shown */ else if (pos-startcol < numcols) /* if more line available */ clr_to_eol(r,pos-startcol - ((n > (unsigned int)filesize) ? 1:0)); } else clr_to_eol(r,0); /* clear entire line */ } /* Put a message on the message line. Actually, 6 is added to the column. */ msgout(col,msg) int col; char *msg; { col += 6; /* NOTE: If you ever make this LESS than 6, BEWARE of the call to this routine with a -6 above! */ clr_to_eol(MSGLINE,col); /* clear the message line */ while (*msg) charout(MSGLINE,col++,*(msg++)); } promptout(msg) /* Put a message on the prompt line. */ char *msg; { int col; col = 0; clr_to_eol(PROMPTLINE,0); /* clear the prompt line */ while (*msg) charout(PROMPTLINE,col++,*(msg++)); } check_endline() { unsigned int i, j; if (filesize) { i = line_ptrs[scr_row] + posline(scr_row,scr_col); if ((j = line_ptrs[scr_row+1]) == -1) j = (unsigned int)filesize+1; if (i >= j) { if ((j == (unsigned int)filesize + 1) && (membuf[j-1] == LF)) i = j; else i = j - 2; scr_col = linepos(scr_row,i-line_ptrs[scr_row]); } curpos(scr_row,scr_col); } } linepos(r,c) /* Find logical col on screen, given physical row and col. */ int r,c; { int pos; char *p; pos = 0; /* start at column 0 */ p = &membuf[line_ptrs[r]]; /* point into this row */ while (c--) pos = (*(p++) == TAB) ? pos - pos % tab_spacing + tab_spacing:pos + 1; return(pos); /* take shifted screen into account */ } posline(r,c) /* Find physical col on screen, given logical row and col. */ int r,c; { int pos; char *p; pos = 0; /* start a column 0 */ p = &membuf[line_ptrs[r]]; /* point into this row */ while (c > pos) pos = (*(p++) == TAB) ? pos - pos % tab_spacing + tab_spacing:pos + 1; return((unsigned int)(p-&membuf[line_ptrs[r]])); } unsigned filepos() /* Find place in file from screen location, taking tabs into account. */ { unsigned int i, j, pos; char *p; i = line_ptrs[scr_row]; j = scr_col; /* position on (logical) screen */ pos = 0; /* start of line */ p = &membuf[i]; while (pos < j) { pos = (*(p++) == TAB) ? pos - pos % tab_spacing + tab_spacing:pos + 1; i++; } return(i); } clr_to_eol(r,c) /* Clear a line. */ int r,c; { while (c < numcols) char_attr_out(r,c++,' ',7); } curpos(r,c) /* Set logical cursor position. */ int r,c; { if (r < FIRSTLINE) set_cursor(r,c); /* if not on editing screen */ else { if (c < startcol) /* if less than current starting col, scroll left */ { startcol = MAX(0,c-15); /* set new screen starting col */ showscreen(FIRSTLINE); /* reshow screen */ } else if (c-startcol >= numcols) /* if off screen, scroll right */ { startcol = MAX(0,c-65); /* set new screen starting col */ showscreen(FIRSTLINE); /* reshow screen */ } set_cursor(r,c-startcol); } } calc_row_col(o,r,c) unsigned int o; int *r, *c; { if ((o < line_ptrs[FIRSTLINE]) || (o >= line_ptrs[numrows+1])) return(0); for (*r = FIRSTLINE;(*r < numrows + 1) && (o > line_ptrs[*r]);(*r)++) ; if (o != line_ptrs[*r]) --(*r); *c = linepos(*c,o - line_ptrs[*r]); return(1); } charout(r,c,ch) int r,c; char ch; { char_attr_out(r,c,ch,7); } char_attr_out(r,c,ch,attr) int r,c; char ch; int attr; { #asm mov ax,word [bp+4] ;get row mov cl,5 ;for 5-bit shift shl ax,cl ;row * 32 mov di,ax ;save a copy shl ax,1 ;*64 shl ax,1 ;*128 add di,ax ;row * 160 mov es,word videoseg_ ;address the video segment mov ax,word [bp+6] ;column shl ax,1 ;*2 for word address add di,ax ;es:di = video screen mov al,byte [bp+8] ;get char mov ah,byte [bp+10] ;and attribute stosw ;put into video memory #endasm } scroll_up(start_row,end_row,start_col,end_col,num) int start_row,end_row,start_col,end_col,num; { #asm mov ch,byte [bp+4] ;start row mov cl,byte [bp+8] ;start col mov dh,byte [bp+6] ;end row mov dl,byte [bp+10] ;end col mov al,byte [bp+12] ;number of rows to scroll mov bh,7 ;normal fill attribute mov ah,6 ;fn = scroll up push bp ;save BP from BIOS int 10h ;call BIOS pop bp ;and restore BP #endasm } scroll_down(start_row,end_row,start_col,end_col,num) int start_row,end_row,start_col,end_col,num; { #asm mov ch,byte [bp+4] ;start row mov cl,byte [bp+8] ;start col mov dh,byte [bp+6] ;end row mov dl,byte [bp+10] ;end col mov al,byte [bp+12] ;number of rows to scroll mov bh,7 ;normal fill attribute mov ah,7 ;fn = scroll down push bp ;save BP from BIOS int 10h ;call BIOS pop bp ;and restore BP #endasm } set_cursor(r,c) int r,c; { #asm mov dh,byte [bp+4] ;get row mov dl,byte [bp+6] ;get col mov bh,0 ;this page mov ah,2 ;fn = set cursor position push bp ;save BP from BIOS int 10h ;call BIOS pop bp ;and restore BP #endasm }