/********************************************************************** yace.cpp -> based on earlier work, page.cpp An interactive editor. July 19, 2004 Nathan J. Crawford. This file is distributed under the terms of the GNU general public license, available at: http://www.fsf.org/licensing/licenses/gpl.txt This file fills a buffer according to commands received from stdin: A space must separate a command from its operands. + Add a line after the current line. The added line then becomes the current line. - Remove the current line. -(m,n) removes lines m through n. -(m.x,n.y) removes everything from position x in line m through position y in line n. * position the current line and current position at the beginnning of the next occurrence of . Leading spaces and final cr-lf are ignored. To find a leading space, use an apostrophe immediately after the command. / Replace the first string with the second. The strings are separated by an apostrophe ' and only the current line is scanned. /(+-,*n) Replace as above, from the current position forward (+) or backward(-) to a count of n times, or (*) replaces all. >(x,y,n) Copy and insert n lines from line x to y. If in hex mode, copies and inserts n bytes from offset x to offset y. >(x,y,n,k) Hex mode only: copies n bytes from x to y, overwriting the destination, iff n and k are the same. Otherwise reports an error. r Read the given file into the buffer. Replaces the existing buffer contents. w Write the buffer contents to a file. The filename may be ommitted if a read has already filled the filename buffer. i Read a file, inserting the contents into the buffer after the current line. i(m,n) Insert lines m thru n from given file after current line. s(v,r) Loads the value v into a register r. v may be a register. Registers: ls line size ps page size (lines per screen) fs file size (read only) bs buffer size (read only) lc line count (read only) bk[0-31] bookmark register. Subscripts must be used. st[0-31] location stack. sp stack pointer. cp current position cl current line off current offset Note: setting any of the above 3 will cause a recalculation of the others. j Jump to the given location. j(m,n) sets the line and position registers to m and n respectively. j n sets the line to n and zeros the position. Jumps push the current location onto the stack and increment the stack pointer. l Zero out the current position and set the current line number. The difference between l and j is that l does not push the current position's offset onto the stack. b Go back. This pops the stack into the current location. f Go forward. If the value on top of the stack is not null, then this value is used and sp is incremented. g The register must be either a bookmark or stack register, and this sets the current location to the value in that register. d Displays one screen of the buffer, from the current line forward, respecting the line size and page size. n Next screen. Moves the current position forward one screen. p Previous Screen. Moves the current position backward one screen. m Set mode. "text" will display normal characters, "hex" will list in hexadecimal. Text mode is the default. ? Display help. . Execute a shell command. Improvements: Use a * or > to mark the current line when displaying text. Use the cp register to set the cursor position - that is, where the current line is displayed on the screen. A cp of 10 would display 9 lines before and 10 lines after the current line on the screen for a 20-line terminal. Allow a 1 character margin for displaying edit control characters - for example, use a | to mark selected text, and perhaps a + to mark register locations. Allow the display of line numbers on either the left or right margin. **********************************************************************/ #include /* tc201 workaround here follows */ //#include #include #include #include #include #include //#include /* don't try to read the whole file into memory if larger than: */ #define CACHE_LIMIT 32000 #define MAX_LINE_SIZE 4096 #define DEFAULT_LINE_SIZE 80 #define DEFAULT_PAGE_SIZE 23 #define DEFAULT_CURRENT_POSITION 5 #define CHARACTER_MODE 0 #define HEXADECIMAL_MODE 1 #define VERSION_MAJOR 1 #define VERSION_MINOR 2 #define INITIALIZED 37 /* redefine with long ints */ typedef struct editregisters{ /* positioning variables */ int offset; /* current offset within file */ int cp; /* current position from top line */ int cl; /* current line number */ int book[32]; /* bookmarks - offsets */ int stack[32];/* the location stack */ int sp; /* stack pointer */ /* display variables */ int ls; /* line size */ int ps; /* page size (lines per screen) */ int mode; /* 0 = character, 1 = hex */ /* other variables */ char * buffer; char * filename; /* if not null, the name on disk */ int bs; /* size of buffer */ int fs; /* size of file */ int lc; /* number of counted lines in file */ int * lines; /* array of indexes of lines */ int initialized; /* flag to determine when to initialize */ int ln; /* display line numbers? */ char last_search[80]; }edregs; typedef struct ed_stat{ int error_number; char * message; }edstat; /* messages to the user: */ edstat ed_success = { 0, "Successful"}; edstat ed_failure = { -1, "Failure"}; edstat ed_out_of_memory = { -1,"Out of memory"}; edstat ed_file_open_error = {-2, "Unable to open file"}; edstat ed_file_write_error = {-3, "Unable to write file"}; void print_stat(edstat st, edregs * buf){ int curline = buf->cl + 1; printf("-- at line %d of %d, status %d : %s\n",\ curline,buf->lc,st.error_number,st.message); } edstat init_edregs(edregs * buf); void cleanup_edregs(edregs * buf); edstat add_line(char * command, edregs * buf); edstat sub_line(char * command, edregs * buf); edstat replace_string(char * command, edregs * buf); edstat next_string(char * command, edregs * buf); edstat delete_line(char * command, edregs * buf); edstat copy_chars(char * command, edregs * buf); edstat read_file(char * command, edregs * buf); edstat write_file(char * command, edregs * buf); edstat insert_file(char * command, edregs * buf); edstat set_register(char * command, edregs * buf); edstat jump(char * command, edregs * buf); edstat to_line(char * command, edregs * buf); edstat backward(char * command, edregs * buf); edstat forward(char * command, edregs * buf); edstat go(char * command, edregs * buf); edstat display_buffer(char * command, edregs * buf); edstat next_screen(char * command, edregs * buf); edstat previous_screen(char * command, edregs * buf); edstat set_mode(char * command, edregs * buf); edstat display_help(char * command, edregs * buf); edstat run_command(char * command, edregs * buf); edstat quit(char * command, edregs * buf){ exit(0); return ed_success; } edstat get_filename(edregs * buf); edstat copy_lines(char * command, edregs * buf); edstat process_command(char * command, edregs * buf); int count_lines(edregs * buf); void index_lines(edregs * buf); char * command; edregs document; char message[80] = {0}; /* These two correspondend to one another */ char * actions = "+-/*>rwisjlbfgnpmd?q."; #define ACTION_COUNT 21 edstat (*functions[ACTION_COUNT])(char *, edregs *) = { add_line, sub_line, replace_string,next_string, copy_chars,read_file,write_file,insert_file, set_register,jump,to_line,backward, forward,go,next_screen,previous_screen, set_mode,display_buffer,display_help,quit, run_command }; int main(int ac, char ** av){ edstat result; FILE * infile = stdin; int close_done = 0; /* if (ac > 1){ infile = fopen(av[1],"rb"); if (!infile){ printf("Unable to open file\n"); } else { close_done = 1; } } */ command = (char *)malloc(MAX_LINE_SIZE); if (!command){ printf("Unable to allocate memory"); return -1; } init_edregs(&document); if (ac > 1){ strcpy(command,"r "); strncat(command,av[1],MAX_LINE_SIZE - 3); result = process_command(command,&document); print_stat(result,&document); } else{ display_help(command,&document); } while(fgets(command,MAX_LINE_SIZE - 1, infile)){ result = process_command(command,&document); print_stat(result,&document); } free(command); if (close_done){ fclose(infile); } cleanup_edregs(&document); return 0; } /* this returns the first non-whitespace character */ char first_char(char * cm){ char a; if (!cm)return 0; a = cm[0]; while( ( (a == ' ') || (a == '\t') || (a == '\n') || (a == '\r') ) && (a != '\0') ){ cm++; a = *cm; } return a; } /* this returns a pointer to the first non-whitespace character */ char * next_char(char * cm){ char a; if (!cm) return NULL; a = *cm; while ( ( (a == ' ') || (a == '\t') || (a == '\n') || (a == '\r') ) && (a != '\0') ){ ++cm; a = *cm; } return cm; } /* this returns a pointer to the first whitespace character */ char * next_whitespace(char * cm){ char a; if (!cm) return NULL; a = *cm; while ( (a != ' ') && (a != '\t') && (a != '\n') && (a != '\r') && (a != '\0') ){ ++cm; a = *cm; } return cm; } edstat process_command(char * command, edregs * buf){ int ndx; edstat result; char cmd; cmd = first_char(command); for (ndx = 0; ndx < ACTION_COUNT; ++ndx){ if (cmd == actions[ndx]){ result = functions[ndx](command,buf); display_buffer(command,buf); return result; } } return ed_success; } edstat init_edregs(edregs * buf){ int x; if (!buf)return ed_failure; if (buf->initialized == INITIALIZED){ if (buf->filename) free(buf->filename); if (buf->buffer) free(buf->buffer); if (buf->lines) free(buf->lines); } buf->initialized = 0; buf->cp = buf->cl = buf->lc = buf->fs = 0; buf->offset = 0; buf->sp = 0; buf->buffer = (char *)malloc(CACHE_LIMIT); if (!buf->buffer) return ed_out_of_memory; buf->buffer[0] = 0; buf->bs = CACHE_LIMIT; buf->filename = 0; buf->lines = 0; for (x = 0; x < 32; ++x){ buf->book[x] = buf->stack[x] = 0; } buf->ps = DEFAULT_PAGE_SIZE; buf->ls = DEFAULT_LINE_SIZE; buf->cp = DEFAULT_CURRENT_POSITION; buf->mode = CHARACTER_MODE; buf->ln = 0; buf->initialized = INITIALIZED; return ed_success; } void cleanup_edregs(edregs * buf){ if (!buf) return; if (buf->initialized){ if (buf->buffer) free(buf->buffer); if (buf->lines) free(buf->lines); if (buf->filename) free(buf->filename); } } edstat add_line(char * command, edregs * buf){ int sz = strlen(command); int x, newsize; char * newbuf; sz -= 2; if (buf->initialized != INITIALIZED) init_edregs(buf); if (buf->lines){ if (buf->cl >= buf->lc) buf->offset = buf->fs; else buf->offset = buf->lines[buf->cl]; } else buf->offset = 0; if (buf->bs < (sz + buf->fs)){ newsize = ((buf->bs * 2) < (sz + buf->fs)) ? (sz + buf->fs) : (buf->bs * 2); newbuf = (char *) malloc (newsize); if (!newbuf) return ed_out_of_memory; for (x = 0; x < buf->fs; ++x){ newbuf[x] = buf->buffer[x]; } free (buf->buffer); buf->buffer = newbuf; buf->bs = newsize; } for (x = buf->fs; x >= buf->offset; --x){ buf->buffer[x + sz] = buf->buffer[x]; } for (x = 0; x < sz; ++x){ buf->buffer[buf->offset++] = command[x + 2]; } buf->fs += sz; index_lines(buf); buf->cl++; return ed_success; } edstat sub_line(char * command, edregs * buf){ int start, stop, size, x; if (buf->lc == 0) return ed_failure; //for (x = 0; x < buf->lc; ++x){ // printf("Line: %d ->starts at: %d\n",x,buf->lines[x]); // } if (buf->cl >= (buf->lc - 1)){ buf->cl = buf->lc - 1; stop = buf->fs; } else { stop = buf->lines[buf->cl + 1]; } start = buf->lines[buf->cl]; size = stop - start; //printf("deleting from %d through %d\n",start, stop); for (x = start; x <= (buf->fs - size); ++x){ buf->buffer[x] = buf->buffer[x + size]; } buf->fs -= size; index_lines(buf); buf->offset = buf->lines[buf->cl]; return ed_success; } edstat replace_string(char * command, edregs * buf){ return ed_failure; } edstat next_string(char * command, edregs * buf){ char * search = &(command[2]); int len = strlen(search); int x, y, pos; search[len - 1] = 0; //strip the linefeed. --len; if (len > 0){ memset(buf->last_search,0,80); for (x = 0; (x < len) && (x < 79);x++){ buf->last_search[x] = command[x + 2]; } pos = buf->lines[buf->cl]; } else { search = buf->last_search; len = strlen(search); if (command[1] != '<'){ pos = (buf->cl < buf->lc) ? buf->lines[buf->cl + 1] : buf->lines[buf->cl]; } else { // backward search pos = (buf->cl > 0) ? buf->lines[buf->cl - 1] : buf->lines[buf->cl]; } } //printf("Searching for %s:\n",search); if (command[1] == '<'){ for (pos; pos >= 0 ; --pos){ for (x = pos, y = 0; (y < len) && (x < buf->fs); ++x, ++y){ if (buf->buffer[x] != search[y]) break; } if (y == len) // match found... break; } } else { for (pos; pos < buf->fs; ++pos){ for (x = pos, y = 0; (y < len) && (x < buf->fs); ++x, ++y){ if (buf->buffer[x] != search[y]) break; } if (y == len) // match found... break; } } if (y == len){ if (command[1] != '<'){ for (x = buf->cl; (x < buf->lc) && (pos > buf->lines[x]); ++x); --x; } else { for (x = buf->cl; (x > 0) && (pos < buf->lines[x]); --x); } if (x < 0) x = 0; /* save current location on the stack */ buf->stack[(buf->sp % 32)] = buf->cl; buf->sp++; /* now jump to location */ buf->cl = x; return ed_success; } else return ed_failure; } edstat delete_line(char * command, edregs * buf){ return ed_failure; } edstat copy_chars(char * command, edregs * buf){ return ed_failure; } edstat read_file(char * command, edregs * buf){ edstat good; FILE * in; struct stat st; int byte_count; char * endname; char * fname = next_whitespace(command); fname = next_char(fname); endname = next_whitespace(fname); *endname = '\0'; //printf("Attempting to open file: %s<--\n",fname); if (!fname)return ed_file_open_error; in = fopen(fname,"rb"); if (!in) return ed_file_open_error; stat(fname,&st); if (buf->filename)free(buf->filename); if (buf->buffer)free(buf->buffer); buf->filename = (char *) malloc(strlen(fname) + 1); strcpy(buf->filename,fname); buf->fs = st.st_size; buf->bs = st.st_size + (st.st_size / 3); buf->buffer = (char *) malloc(buf->fs + (st.st_size / 3)); for (byte_count = 0; byte_count < buf->fs; byte_count += 512){ fread(&(buf->buffer[byte_count]),1,512,in); } fclose(in); buf->buffer[buf->fs] = 0; buf->offset = 0; buf->ps = DEFAULT_PAGE_SIZE; buf->ls = DEFAULT_LINE_SIZE; buf->cl = 0; buf->cp = 5; index_lines(buf); good.message = message; good.error_number = 0; sprintf(message,"%d bytes, %d lines...",buf->fs,buf->lc); return good; } // change this to use the command for the filename! edstat write_file(char * command, edregs * buf){ edstat tmp; FILE * out; int pos,rem,result, non_existent_fname; char * fname, *name_end; fname = next_char(command); fname = next_whitespace(fname); if ((!fname) && (!buf->filename)){ return ed_failure; } fname = next_char(fname); non_existent_fname = 1; if ((strlen(fname)) <= 0){ if (!buf->filename) return ed_failure; else{ fname = buf->filename; non_existent_fname = 0; } } name_end = next_whitespace(fname); if (name_end)(*name_end) = '\0'; //truncate trailing chars... printf("Attempting to open %s**\n",fname); out = fopen(fname,"wb"); if (!out) return ed_file_write_error; else if (non_existent_fname) { buf->filename = (char *)malloc(strlen(fname) + 1); strcpy(buf->filename,fname); } for (pos = 0; pos < buf->fs; pos += 512){ rem = ((buf->fs - pos) > 512) ? 512 : (buf->fs - pos); result = fwrite(&(buf->buffer[pos]),1,rem,out); if (result != rem){ fclose(out); return ed_file_write_error; } } fclose(out); return ed_success; } edstat insert_file(char * command, edregs * buf){ return ed_failure; } edstat display_registers(edregs * buf){ int x; char a; printf("Registers:\n"); printf("offset..............:%d\n",buf->offset); printf("cp..................:%d\n",buf->cp); printf("cl..................:%d\n",buf->cl); printf("ls..................:%d\n",buf->ls); printf("ps..................:%d\n",buf->ps); printf("bs..................:%d\n",buf->bs); printf("fs..................:%d\n",buf->fs); printf("lc..................:%d\n",buf->lc); printf("ln..................:%d\n",buf->ln); printf("sp..................:%d\n",buf->sp); printf("filename............:%s\n",(buf->filename) ? buf->filename : "(NULL)"); printf("Stack:\n"); for (x = 0; x < buf->sp; x++){ printf("\t%04d\n",buf->stack[x]); } printf("Type any character and hit enter to continue\n"); scanf("%c",&a); return ed_success; } edstat set_register(char * command, edregs * buf){ int parm; if (strlen(&(command[2])) < 1){ return display_registers(buf); } if (!(strncmp(&(command[2]),"cp",2))){ sscanf(&(command[4])," %d",&parm); if (( parm < buf->ps) && (parm > 0)) buf->cp = parm; } if (!(strncmp(&(command[2]),"ps",2))){ sscanf(&(command[4])," %d",&parm); if (( parm > 0) && (parm < buf->lc)) buf->ps = parm; } if (!(strncmp(&(command[2]),"ln",2))){ sscanf(&(command[4])," %d",&parm); buf->ln = parm; } if (!(strncmp(&(command[2]),"ls",2))){ sscanf(&(command[4])," %d",&parm); if (parm > 0) buf->ls = parm; } return ed_success; } edstat jump(char * command, edregs * buf){ char dummy; /* the stack is circular, that is, we automatically wrap around */ buf->stack[(buf->sp % 32)] = buf->cl; buf->sp++; sscanf(command,"%c %d",&dummy,&(buf->cl)); if (buf->cl > (buf->lc + 1))buf->cl = buf->lc + 1; buf->cl--; if (buf->cl < 0)buf->cl = 0; //printf("Jumping to %d\n",buf->cl); return ed_success; } edstat to_line(char * command, edregs * buf){ char dummy; sscanf(command,"%c %d",&dummy,&(buf->cl)); if (buf->cl > (buf->lc + 1))buf->cl = buf->lc + 1; buf->cl--; if (buf->cl < 0)buf->cl = 0; return ed_success; } edstat backward(char * command, edregs * buf){ buf->sp--; buf->cl = buf->stack[(buf->sp % 32)]; return ed_success; } edstat forward(char * command, edregs * buf){ int x = buf->sp; ++x; x %= 32; if (buf->stack[x]){ buf->cl = buf->stack[x]; buf->sp = x; return ed_success; } return ed_failure; } edstat go(char * command, edregs * buf){ return ed_failure; } /* this needs to be refined to handle the various page and line size settings. */ edstat display_buffer(char * command, edregs * buf){ int x,y; int pos; int current_line; if (!buf)return ed_failure; if (buf->initialized != INITIALIZED) return ed_success; current_line = buf->cl; current_line -= buf->cp; --current_line; if (current_line < 0) current_line = 0; if (!(buf->lines)){return ed_success;} pos = buf->lines[current_line]; for (x = 0; x < buf->ps; ++x){ if (buf->ln){ printf("%04d",current_line + x); } if ((x + current_line) == buf->cl) printf(">"); else printf(" "); y = 0; while((buf->buffer[pos] != 0) && (buf->buffer[pos] != '\n') && (buf->buffer[pos] != '\r')){ if (y < buf->ls) printf("%c",buf->buffer[pos]); y++; pos++; } printf("\n"); /* account for cr & lf ended lines as well */ if ((buf->buffer[pos] == '\r') && (buf->buffer[pos+1] == '\n')){ pos ++; } pos++; if (pos >= buf->fs) break; } return ed_success; } edstat next_screen(char * command, edregs * buf){ if ((buf->cl + buf->ps) < buf->lc){ buf->cl += buf->ps; return ed_success; } else { return ed_failure; } } edstat previous_screen(char * command, edregs * buf){ if ((buf->cl - buf->ps) < 0){ return ed_failure; } else { buf->cl -= buf->ps; return ed_success; } } edstat set_mode(char * command, edregs * buf){ return ed_failure; } edstat display_help(char * command, edregs * buf){ char * date_time = "__DATE__ : __TIME__"; char a; //char * date_time = "October 15, 2004"; printf("YACE - Yet Another Coder's Editor...\n"); printf("Version %d.%02d, Compiled %s : %s\n",VERSION_MAJOR,VERSION_MINOR,__DATE__,__TIME__); printf("+ add a line\n"); printf("- delete current line\n"); printf("* search for string\n"); printf("j position to line number\n"); printf("r read file\n"); printf("w write file\n"); printf("n next screen\n"); printf("p previous screen\n"); printf("q quit\n"); printf("? help\n"); printf(". run shell command\n"); printf("The > character marks the current position;\n"); printf("insertions and deletions are done here.\n"); printf("Type any character and hit enter to continue\n"); scanf("%c",&a); return ed_success; } edstat copy_lines(char * command, edregs * buf){ return ed_failure; } edstat run_command(char * command, edregs * buf){ char * com = &(command[2]); char dummy; system(com); printf("Type a key and press enter to continue..."); scanf("%c",&dummy); return ed_success; } int count_lines(edregs * buf){ int linecount = 0; char * ptr; if (!buf) return -1; if (!buf->buffer)return -2; ptr = buf->buffer; while(*ptr != 0){ if (*ptr == '\n')++linecount; ++ptr; } return linecount; } void index_lines(edregs * buf){ int x; int pos; x = count_lines(buf); if (x < 0){ printf("Internal error"); exit(0); } if (x == 0)x = 1; buf->lc = x; if (buf->lines)free(buf->lines); buf->lines = (int *) malloc (sizeof(int) * buf->lc); for (x = pos = 0; x < buf->lc; ++x){ buf->lines[x] = pos; while ((buf->buffer[pos]) && (buf->buffer[pos] != '\n'))++pos; ++pos; } if (buf->cl >= buf->lc){ buf->cl = buf->lc - 1; } } edstat get_filename (edregs * buf){ char fname[256] = {0}; int fnsize; printf("Filename: "); fgets(fname,256,stdin); if (buf->filename) free(buf->filename); fnsize = strlen(fname); if (fnsize < 1) return ed_file_write_error; buf->filename = (char *) malloc(fnsize + 1); // strip trailing linefeed. strncpy(buf->filename,fname, fnsize); buf->filename[fnsize - 1] = 0; return ed_success; }