Category : Word Processors
Archive   : MEDIT.ZIP
Filename : MEDIT.C

 
Output of file : MEDIT.C contained in archive : MEDIT.ZIP
// medit.c Mikes Editor - Semester project
// Programmer: Michael Tyson
// Date: 12/17/91
//
// Medit is a full screen dos text editor, storing each line of text in a
// structure that is placed in a doubly linked list on the far heap. All
// line structures are allocated at run time and when compiled in the
// Borland C++ Compact memory model it provides the maximum amount of memory
// for text files ranging from 450k to 580k depending on the dos version
// used and memory resident programs.

#include
#include // memory allocation routines
#include
#include
#include
#include // describes bios information area
#include
#include
#include
#include

#define READWRITE 2 // file has read write capability
#define READONLY 1 // file is read only

typedef struct line { // type definition for line structure
char *dataptr; // pointer to actual text
struct line *prev_line; // pointer to previous line
struct line *next_line; // pointer to next line
} LINE;

LINE far *start, // pointer to start of list in the far heap
far *new, // used for allocation of new nodes and locating specific nodes
far *tptr, // pointer to node currently displayed at top of screen
far *prev; // use in load process to link to previously loaded line

char pathname[78], drive[MAXDRIVE], // strings used to hold filename
dir[MAXDIR], filename[MAXFILE], // information
ext[MAXEXT],
buffer[256]; // all actual input goes here first before being added

int row=0, // current row number the cursor is on
column=0, // current column number the cursor is on also index to buffer[]
lpos=0, // column number displayed at left of screen \ used for l/r
rpos=79, // column number displayed at right of screen / scrolling
tpos=0, // row number displayed at top of screen \ used for u/d
bpos=23, // row number displayed at bottom of screen/ scrolling
last=0, // highest numbered row
insert_flag=0, // insert mode (0=off, 1=on)
load=0, // load flag (0=not loading,1=loading current file)
attr, // current files attribute (readwrite,readonly)
changed=0; // file changed flag

FILE *fn; // file stream pointer
BIOSDATA far *bios; // pointer to bios data area
char origscrn[4000]; // hold area for screen contents before execution
char savearea[2000]; // general screen text hold area

void save_file(void);
void clear_text(void);
int menu(void);
char error_msg(int,int,int,char *,byte);
char get_charray(char *,int,int,int);
void screen_write(int,int,int,int,byte *);
void screen_writea(int,int,int,int,byte *,byte);
void box(int,int,int,int);
char pop_input(int,int, char *,int,char *);
int get_text(void);
void init_screen(void);
void uplc(void);
void mem(void);
void update_screen(void);
int add_line(int);
void fill_buffer(void);
void load_file(void);
int check_file(char *);
void delete_line(LINE far *);
int insert_line(LINE far *);
unsigned cursor_change(unsigned);


void main(int argc,char *argv[])
{
int option; // option number selected
unsigned old; // original cursor
char ch; // single character answer

bios=MK_FP(0x40,0x0); // point bios to actual bios data area
gettext(1,1,80,25,origscrn); // save dos screen
clrscr(); // clear screen
while(1) // main loop
{
while(1) // load file loop
{
if(argc<2) // if no file name given prompt for one
{
if(pop_input(0,0,pathname,78,"Enter A Filename:")==27)
{
puttext(1,1,80,25,origscrn); // write back orig screen if user pressed ESC
gotoxy(1,25); // reposition cursor
exit(0); // exit program
}
if(strlen(pathname)==0) // if no input continue load file loop
continue;
}
else
strcpy(pathname,argv[1]); // copy command line filename into pathname
if(check_file(pathname)>0) // if file is valid and exists load it
{
gotoxy(1,1);
load_file();
break; // exit load file loop
}
else
{
if(attr>0) // if filename is valid and does not exist
break; // exit load file loop
argc=1; // invalid file name or dir reset argc to
// first test into prompting for filename
continue; // continue to load file loop
}
}
init_screen(); // setup screen
while((option=get_text())) // begin text entry - option gets special cases
{
switch(option) // process special cases
{
// pop up menu
case 27: old=cursor_change(0xffff); // hide cursor
option=menu(); // show menu get choice
cursor_change(old); // restore cursor
break; // exit switch statement
// alt-x pressed exit program
case 45: if(changed) // if current file changed prompt are you sure?
{
ch=error_msg(22,11,22," Loose changes to the current file? (Y or N)",79);
if(toupper(ch)!='Y') // if ch is not Y drop through
break;
}
puttext(1,1,80,25,origscrn); // else restore orig screen
gotoxy(1,25); // and cursor and exit program
exit(0);
}
if(option==0) // menu option - load file
{
clear_text(); // erase the current list
argc=1; // reset argc to allow for prompt of filename
for(column=0;column<=255;column++) // clear text buffer
buffer[column]='\0';
start=new=tptr=prev=NULL; // reset pointers & variables to
row=column=lpos=tpos=last=load=0; // original values
rpos=79;
bpos=23;
attr=0;
break; // break out of text entry loop
}
else if(option==3) // menu option - quit
{
puttext(1,1,80,25,origscrn);
gotoxy(1,25);
exit(0);
}
}
}
}



// void clear_text(void)
//
// Description: Starting at the begining of the list the function traverses
// the list freeing the text string that dataptr points to
// and then freeing the structure that is being pointed to.
// This erases the list from memory to free the memory for use
// by further mallocs.
// Arguments: NONE
// Return: Nothing
//
void clear_text(void)
{
while(start!=NULL)
{
new=start->next_line;
farfree(start->dataptr);
farfree(start);
start=new;
}
}



// char pop_input(int x, int y, char *str,int len,char *msg)
//
// Description: Used to allow the input of a charater array of up to 78
// characters into the str field. Displays a boxed area at
// x,y that is 80 columns long and 4 row wide and prints
// the string in msg on the first line of the box and
// waits for user input on the second line, exits when enter
// or ESC is pressed.
// Arguments: x - zero based x position on screen
// y - zero based y position on screen
// str - the character array the user input will be placed in, it
// will be NULL terminated unless the use inputs len number
// of characters.
// len - maximum length of array
// msg - message to display at top of message box
// Returns: 0 if user pressed enter 27 if user pressed esc
// str - receives user inputed character array
//
char pop_input(int x, int y, char *str,int len,char *msg)
{
char ch; // return value
unsigned old; // old cursor shape

old=cursor_change(0x010f); // change to blinking block cursor
gettext(x+1,y+1,x+2+len,y+4,savearea); // save text area under input area
box(x,y,len,4); // draw box around area
screen_write(x+1,x+strlen(msg),y+1,y+1,msg); // display msg
ch=get_charray(str,len,x,y+2); // get user input
puttext(x+1,y+1,x+2+len,y+4,savearea); // restore text area
cursor_change(old); // restore cursor
return ch; // return result
}



// void box(int x,int y,int len,int width)
//
// Description: Draws a box around and fills a specified region of
// the screen.
// Arguments: x - zero based x position of screen
// y - zero based y position of screen
// len - maximun length of interior region; cannot exceed 78
// width - number of rows for box include top and bottom of box
// Return: nothing
//
void box(int x,int y,int len,int width)
{
int i,j;

screen_writea(x,x,y,y,"É",79); //-----------------
for(i=x+1;i screen_writea(i,i,y,y,"Í",79); // line
screen_writea(i,i,y,y,"»",79); //-----------------
for(j=y+1;j { // draw the
screen_writea(x,x,j,j,"º",79); // sides
for(i=x+1;i screen_writea(i,i,j,j," ",30);// box and
screen_writea(i,i,j,j,"º",79); // fill
} //-----------------
screen_writea(x,x,j,j,"È",79); //-----------------
for(i=x+1;i screen_writea(i,i,j,j,"Í",79); // line
screen_writea(i,i,j,j,"¼",79); //-----------------
}



//int menu(void)
//
// Description: When the user presses ESC while inputing text this menu is
// displayed with a list of functions that the user can select
// by moving the cursor up or down and pressing enter on the
// desired option or pressing ESC to go back to inputing text.
// Arguments: NONE
// Return: if enter is pressed then the option number selected or if
// ESC is pressed then 27 is returned
//
int menu(void)
{
char *itemlist[5]={" Load "," Save ", // menu option list
" Save as "," Quit "," Dos Shell "};

char ch, tempname[78],tdrive[MAXDRIVE], tdir[MAXDIR], tfilename[MAXFILE],
text[MAXEXT]; // temporary variables used with 'Save as' option
int pos=0, // position of cursor on menu
i, // return value of check_file()
oldattr=attr; // save current file attribute

gettext(1,2,13,8,savearea); // save text area under menu
box(0,1,11,7); // draw box for menu
while(1) // begin menu loop
{
for(i=0;i<5;i++) // display options highlighting the current pos
screen_writea(1,1+10,2+i,2+i,itemlist[i],(pos==i)?30:79);
ch=getch(); // get user input
if(ch==27) // ESC pressed ?
{
puttext(1,2,13,8,savearea); // restore screen
return 27; // exit function
}
if(ch=='\r') // pressed enter
{
puttext(1,2,13,8,savearea); // restore screen
break; // exit menu loop
}
if(ch=='\0') // extended code
{
ch=getch(); // get extended code
if(ch==72 && pos>0) // up arrow pressed
pos--; // move highlight up
else if(ch==80 && pos<4) // down arrow pressed
pos++; // move highlight down
}
} // end menu loop
if(pos==0) // user selected load file
{
if(changed) // did the user change the current file
{ // does the user wish to loose the changes to the current file ?
ch=error_msg(22,11,22," Loose changes to the current file? (Y or N)",79);
if(toupper(ch)!='Y')
return 27; // user does not want to loose changes - simulate ESC
changed=0; // reset changed flag
}
}
else if(pos==1) // user selected save file
{
if(attr==READONLY) // is file readonly ?
error_msg(22,11,34,"The file is READONLY - Cannot Save",79);
else
save_file(); // save current file
}
else if(pos==2) // user selected save as
{
while(1) // filename input loop
{
if(pop_input(0,0,tempname,78,"Enter A Filename:")==27)
return 27;
fnsplit(tempname,tdrive,tdir,tfilename,text); // split tempname into filename components
if(*tdrive==NULL) // if no drive specified use current
strcpy(tdrive,drive);
if(*tdir==NULL) // if no directory specified use current
strcpy(tdir,dir);
fnmerge(tempname,tdrive,tdir,tfilename,text); // merge new tempname
if((i=check_file(tempname))>=0) // check validity of new filename
{
if(attr==READONLY) // the new filename already exists and is readonly
{
error_msg(18,11,43,"The new file already exists and is READONLY",79);
attr=oldattr; // reset attr
fnsplit(pathname,drive,dir,filename,ext); // reset pathname
return 27; // simulate ESC
}
else if(i>0 && attr==READWRITE) // file already exists overwrite it
{
ch=error_msg(24,11,31,"Overlay existing file? (Y or N)",79);
if(toupper(ch)!='Y')
{
attr=oldattr; // reset to orig values
fnsplit(pathname,drive,dir,filename,ext);
continue; // allow for another filename to be entered
}
}
strcpy(pathname,tempname); // copy tempname to pathname
save_file(); // save new file
init_screen(); // reset screen
break; // exit loop
}
attr=oldattr; // invalid filename entered, reset to original values
fnsplit(pathname,drive,dir,filename,ext); // and allow for input of another filename
} // end file input loop
}
else if(pos==3) // quit selected
{
if(changed)
{
ch=error_msg(22,11,22," Loose changes to the current file? (Y or N)",79);
if(toupper(ch)!='Y')
return 27; // simulate ESC
}
}
else if(pos==4) // dos shell selected
{
clrscr(); // clear screen
system("command.com"); // load command.com
init_screen(); // reset screen upon re-entry into program
}
return pos; // return selected option
}



//void save_file(void)
//
// Description: opens the file currently in the pathname string as write only
// and uses the global variable new to traverse the list saving
// each dataptrs contents to disk with a '\n' at the end. When
// done the file is closed.
// Arguments: NONE
// Return: nothing
//
void save_file(void)
{
if((fn=fopen(pathname,"wt"))==NULL) // open file
error_msg(22,11,25,"Error opening output file",79); // display error if any
else
{
gettext(32,11,46,13,savearea);
screen_writea(31,45,10,12,"ÉÍÍÍÍÍÍÍÍÍÍÍÍÍ»º Saving File ºÈÍÍÍÍÍÍÍÍÍÍÍÍͼ",79);
new=start; // initialize new to start of list
while(new!=NULL) // while not at end
{
if(new->dataptr==NULL) // if blank line write only '\n'
fprintf(fn,"\n");
else // else write text and '\n'
fprintf(fn,"%s\n",new->dataptr);
new=new->next_line; // point to next_line
}
fclose(fn);
puttext(32,11,46,13,savearea); // restore screen
changed=0; // reset flag
}
}



//char get_charray(char *array,int size, int x, int y)
//
// Description: Processes user input and places it into array if array is
// less that size characters long it is NULL terminated otherwise
// it is not
//
// Arguments: array - character array that will receive all input
// size - maximum number of character to input into array
// x - x position of pop_input box
// y - y position of pop_input box +2
// Return: if user presses ESC then 27 else 0
//
char get_charray(char *array,int size, int x, int y) // read array of chars
{
int pos;
char ch;

for(pos=size-1;pos>=0;pos--) // clear the array
*(array+pos)='\0';
pos=x;
gotoxy(pos+2,y+1);
while((ch=getch()) != '\r')
{
if(ch==27)
return ch;
if(ch=='\0')
{
getch();
continue;
}
if(ch=='\b')
{
if(pos==0)
continue;
pos--;
*(array+pos)=' ';
screen_write(pos+1,pos+1,y,y," ");
gotoxy(pos+2,y+1);
continue;
}
if(pos>=size)
{
continue;
}
*(array+pos)=ch;
screen_write(pos+1,pos+1,y,y,&ch);
pos++;
if(pos *(array+pos)='\0';
gotoxy(pos+2,y+1);
continue;
}
for(pos=size-1;pos>=0;pos--)
if(*(array+pos)!=' ' && *(array+pos)!='\0')
break;
else
*(array+pos)='\0';
return 0;
}



//int check_file(char *name)
//
// Description: Check a filename and path to see if it is valid and to
// determine its read write access and whether it exists or not.
// Arguments: name - filename and path if any to be checked
// Return: -1 then invalid directory
// 0 newfile
// 1 readonly
// 2 readwrite
//
int check_file(char *name)
{
char ftemp[78];
int i; // counter
fnsplit(name,drive,dir,filename,ext); // split filename into components
if(*drive==NULL) // if all information was not given
{ // then build it from the current
drive[0]=(getdisk()+65); // drive and directory
drive[1]='\0';
strcat(drive,":");
}
if(*dir==NULL)
{
getcwd(ftemp,MAXDIR);
for(i=2;i dir[i-2]=ftemp[i];
if(strlen(dir)>1)
{
dir[i-2]='\\';
dir[i-1]='\0';
}
else
dir[i-2]='\0';
}
fnmerge(name,drive,dir,filename,ext); // merge components back into name
strcpy(ftemp,drive);
strncat(ftemp,dir,strlen(dir)-1); // test for existence of directory
if(access(ftemp,6)==-1 && strlen(dir) && strlen(dir)!=1)
{
error_msg(0,0,17,"Invalid directory",79);
attr=-1;
return -1;
}
if(access(name,0) == -1) // test for existence of filename
{
attr=READWRITE; // new file
return 0;
}
if(access(name,2)==-1) // test write capability of file
{
attr=READONLY; // read only file
return 1;
}
return (attr=READWRITE); // read write capable file

}



//char error_msg(int x,int y,int len,char *msg,byte attrib)
//
// Description: Display a boxed error message at a specified location and
// wait for a one key user input which is returned to the
// calling function
// Arguments: x - zero based x position of upper left corner of box
// y - zero based y position of upper left corner of box
// len - display width, if msg is longer than len it is wrapped
// to a second line in box. It has a variable width based
// on the length of msg
// msg - text to be displayed in error msg box
// attrib - color to display message in
// Return: char containing the key the user entered
//
char error_msg(int x,int y,int len,char *msg,byte attrib)
{
int width; // width of the msg box
char ch;

width=strlen(msg); // initialize with width of entire string
width=((width%len) > 0)? width/len+3:width/len+2; // mod it with len to determine
// the number of lines +2 for box top+bottom
gettext(x,y,x+len+2,y+width+1,savearea);
box(x,y,len,width);
screen_writea(x+1,x+len,y+1,y+width-2,msg,attrib); // write msg
ch=getch(); // get input
if(ch=='\0') // throw away any extra
getch();
puttext(x,y,x+len+2,y+width+1,savearea);
return ch; // return key pressed
}



//void load_file()
//
// Description: Loads file in pathname and creates places it into the list
// Arguments: NONE
// Return: nothing
//
void load_file()
{
int i, // counter
j, // number of bytes actually read from file
k, // curren position in line
mstat=0; // memory allocation error flag
byte fbuf[2048]; // buffer for file read
if((fn=fopen(pathname,"rt"))==NULL) // open input file if you can
{
printf("Error opening input file.\n");
exit(0);
}
gettext(32,11,47,13,savearea); // save screen text
screen_writea(31,46,10,12,"ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍ»º Loading File ºÈÍÍÍÍÍÍÍÍÍÍÍÍÍͼ",79);
tptr=start; // use tptr to point to first node, for load process it will act
// as if lines are contstantly being added at tptr
load=1; // set load flag
k=0; // reset k
while((j=fread(fbuf,1,2048,fn))!=0) // read into file buffer
{
i=0;
while(i {
for(;k<255 && i if(fbuf[i]!='\n') // upto but not including the newline
{ // or oup to 255 characters
if(fbuf[i]==9)
buffer[k]=' ';
else
buffer[k]=fbuf[i];
i++;
}
else
{
i++; // bump up i for newline
for(;k<255;k++) // clear buffer to end
buffer[k]='\0';
if((mstat=add_line(2))==-1) // add line using the same function used in get_text
break; // break on memory allocation error
tptr=tptr->next_line; // move tptr to next line should be NULL
tpos++; // move everything else to simulate user input
row++; // for the add_line function
k=0; // begin a new line
break;
}
if(mstat==-1) // did a memory allocation error occur
break;
if(k==255) // line filled buffer without newline
{
buffer[k]='\0'; // to ensure the ability to use string functions
if((mstat=add_line(2))==-1) // same as above
break;
tptr=tptr->next_line;
tpos++;
row++;
k=0;
}
if(mstat==-1) // error ?
break;
}
if(mstat==-1) // error ?
break;
}
fclose(fn);
row=0;
tpos=0;
tptr=start; // point to start for initial edit
puttext(32,11,47,13,savearea);
fill_buffer(); // fill buffer with current row data
load=0; // no longer in load
}



// unsigned cursor_change(unsigned value)
//
// Description: changes the cursor size, the value should be passed in hex
// so that it is easier to see exactly what is going on. A value
// of 0x0e0f starts the cursor on line 14->0x0e and ends the
// cursor on line 15->0x0f. 0xffff displays no cursor.
// Arguments: value - cursor size
// Return: current cursor size
//
unsigned cursor_change(unsigned value)
{
union REGS reg;

unsigned old=(bios->curstop<<8)| bios->cursbottom; // get old cursor shape from bios data area
reg.h.ah=0x01;
reg.h.ch=value>>8;
reg.h.cl=(value<<8)>>8;
int86(0x10,®,®); // generate interupt to change cursor
return old;
}



// void screen_writea(int xs,int xe,int ys,int ye,byte *screen,byte attrib)
//
// Description: Direct Screen Write with color attribute. Can be used to write
// on single line or multiple lines in any area of the screen
// the screen argument should contain the same number of bytes of
// information as calculated by ((xe-xs+1)*(ye-ys+1))
// Arguments: xs - zero based x starting position for write
// xe - ending x position of write
// ys - zero based y starting position for write
// ye - ending y position of write
// screen - character array of text to be displayed
// attrib - color of text to be printed
// Return: nothing
//
void screen_writea(int xs,int xe,int ys,int ye,byte *screen,byte attrib)
{
int x, // loop control for x position
y, // loop control for y position
pos=0; // position in screen array
for(y=ys;y<=ye;y++) // y loop
{
for(x=xs*2;x<=xe*2;x+=2) // x loop
{
poke(0xb800,(y*160)+x,((attrib<<8) | screen[pos])); // put two bytes directly to video memory
pos++;
}
}
}



//void screen_write(int xs,int xe,int ys,int ye,byte *screen)
//
// Description: Direct Screen Write of character using whatever attribute that
// currently displayed on the screen. Can be used to write
// on single line or multiple lines in any area of the screen
// the screen argument should contain the same number of bytes of
// information as calculated by ((xe-xs+1)*(ye-ys+1))
// Arguments: xs - zero based x starting position for write
// xe - ending x position of write
// ys - zero based y starting position for write
// ye - ending y position of write
// screen - character array of text to be displayed
// Return: nothing
//
void screen_write(int xs,int xe,int ys,int ye,byte *screen)
{
int x, // loop control for x position
y, // loop control for y position
pos=0; // position in screen array
for(y=ys;y<=ye;y++) // y loop
{
for(x=xs*2;x<=xe*2;x+=2) // x loop
{
pokeb(0xb800,(y*160)+x,screen[pos]); // write one byte to video memory
pos++;
}
}
}



//void attrib_fill(int xs,int xe,int ys,int ye,byte attrib)
//
// Description: Direct Screen Write of color changing the color of text that
// is already on the screen without rewriting it. Can be used to
// write on single line or multiple lines in any area of the screen
// Arguments: xs - zero based x starting position for write
// xe - ending x position of write
// ys - zero based y starting position for write
// ye - ending y position of write
// attrib - color to write to video memory
// Return: nothing
//
void attrib_fill(int xs,int xe,int ys,int ye,byte attrib)
{
int x, // loop control for x position
y; // loop control for y position
for(y=ys;y<=ye;y++)
{
for(x=xs*2+1;x<=xe*2;x+=2)
{
pokeb(0xb800,(y*160)+x,attrib); // write one byte to video memory
}
}
}



//void init_screen(void)
//
// Description: used to setup main editor screen
// Arguments: NONE
// Return: nothing
//
void init_screen(void)
{
clrscr(); // Clear the text screen
screen_writea(0,79,0,0, // write the top line L-for lines C-for columns
" L: C: ",
'p'); // color to display top line in
screen_write(24,24+strlen(pathname),0,0,pathname); // write out current filename
attrib_fill(0,80,1,24,30); // fill the rest of the screen with the attribute for yellow on blue
uplc(); // write out the current line and column
mem(); // write out the current number of kilobytes free
}



//void uplc(void)
//
// Description: displays the current line and column of the cursor. the values
// come from the global variables row and column.
// Arguments: NONE
// Return: nothing
//
void uplc(void)
{
char val[10], // used to hold the value returned by the itoa function
temp[15]; // used to hold the actual value to be displayed
strcpy(temp,itoa(row+1,val,10)); // convert the integer row and column values to
strcat(temp," "); // ascii concatenating with spaces to ensure that
screen_write(4,8,0,0,temp); // spaces are writen over any previous values
strcpy(temp,itoa(column+1,val,10)); // 1 is added to row and column because both values
strcat(temp," "); // are zero based
screen_write(12,16,0,0,temp);
}



//void mem(void)
//
// Description: displays the current memory available
// Arguments: NONE
// Return: nothing
//
void mem(void)
{
int i; // loop control
char temp[15]; // used for conversion of the value to ascii
ultoa((farcoreleft()/1024),temp,10); // convert unsigned long memory available in bytes
// to k and then to ascii
while(strlen(temp)<3) // if less than 100k left justify the string
{
for(i=strlen(temp);i>=0;i--)
temp[i+1]=temp[i];
temp[i+1]=' ';
}
strcat(temp,"k"); // add the k
screen_write(18,18+strlen(temp),0,0,temp); // display it
}



//int get_text(void)
//
// Description: gets all user input while editing the file and processes it accordingly
// Arguments: NONE
// Return: value to option in main() if special keys are pressed
//
int get_text(void)
{
int i,j;
char ch; // used to receive user input
update_screen(); // rewrite screen
while(1) // get_text loop
{
gotoxy(column-lpos+1,row-tpos+2); // position cursor using the global variables to determine
// the screen position
ch=getch(); // get a charater and put it in ch
if(ch==27) // ESC pressed
{
if(add_line(3)==-1) // add the current line before actually doing anything. 3 skips part of add_line
break; // exit get_text loop on memory allocation error
fill_buffer(); // fill the buffer with the current row of text
return(ch); // return the value 27 for main() to process
}
if(ch=='\r') // enter pressed
{
if(add_line(2)==-1) // add the current line before actually doing anything. 2 fills buffer with next_line
break; // exit get_text loop on memory allocation error
if(insert_flag && row {
for(i=0;i<=255;i++) // clear the buffer
buffer[i]='\0';
i=tpos; // set i = to the current row at the top of the screen
new=tptr; // point new pointer to the structure that is displayed at the top of the screen
while(i!=row && new!=NULL) // traverse list until new points to the current row position
{
new=new->next_line;
i++;
}
if(column>0) // if the cursor is sitting in any column but the first one
{
if(insert_line(new)==-1) // insert a line after the current line
break; // exit get_text loop on memory allocation error
}
else // cursor is in column 1
{
if(insert_line(new->prev_line)==-1) // insert line before the current line
break; // exit get_text loop on memory allocation error
row++; // increment the current row to properly fill
fill_buffer(); // the buffer then decrement so the rest
row--; // of the operation is unaffected
}
}
row++; // point to the next line
column=0; // position at the begining of the line
lpos=0; // move the screen all the way to the left
rpos=79; // " " " " " " " " "
if(row>bpos) // did this cause the screen to move down
{
bpos++; // icrement top & bottom row indicators
tpos++;
tptr=tptr->next_line; // move the pointer to the line displayed at the top to the next line
}
update_screen(); // update the screen with the new text
uplc(); // update the displayed line and column
continue; // to top of loop
}
else if(ch==25) // ctrl y delete line pressed
{
i=tpos; //***************************
new=tptr; //
while(i!=row && new!=NULL) //
{ // described above
new=new->next_line; //
i++; //
} //***************************
if(row {
delete_line(new); // delete current line
fill_buffer(); // fill the buffer with text of next line
last--; // one less line in file
}
else // special case lase line
{
if(new->dataptr!=NULL) // if current line is not already null
farfree(new->dataptr); // free the line currently pointed to by new
new->dataptr=NULL; // set it to NULL
for(i=0;i<=255;i++) // clear the buffer
buffer[i]='\0';
}
update_screen(); // update the screen
}
else if(ch==9) // tab key pressed
{
j=(254-column>8)?8:254-column; // if less than 8 characters left in line set j to whatever is left
if(j==0 || column>254) // if I can't do anything go to top of loop
continue;
if(!insert_flag) // if not insert mode just move cursor
{
column+=j;
}
else // insert mode
{
changed=1; // file changed
for(i=254;i>=column+j;i--) // begining at the end of the loop move the text j characters forward
buffer[i]=buffer[i-j];
for(i=column;i buffer[i]=' ';
column+=j; // move the cursor
if(column<=rpos) // if the screen didn't move
update_screen(); // update the screen
}
uplc(); // update row & column
if(column>rpos) // if the screen did move
{
while(column>rpos) // move left and right screen positions accordingly
{
rpos++;
lpos++;
}
update_screen(); // and update the screen
}
}
else if(ch=='\0') // extended keyboard input
{
ch=getch(); // get the second byte out of the buffer
if(ch==75 && column>0) // left arrow pressed
column--; // move column left
else if(ch==77 && column<254) // right arrow pressed
column++; // move column right
else if(ch==72 && row>0) // up arrow pressed
{
if(add_line(1)==-1) // add the current line
break; // exit loop on memory allocation error
row--; // move the row up one position
if(row {
tpos--; // move screen pointers and variables
bpos--;
tptr=tptr->prev_line;
}
fill_buffer(); // fill buffer with the current line
update_screen(); // update the screen
}
else if(ch==80 && row {
if(add_line(2)==-1) // works the same as up arrow
break;
row++;
if(row>bpos)
{
bpos++;
tpos++;
tptr=tptr->next_line;
}
fill_buffer();
update_screen();
uplc();
continue;
}
else if(ch==80 && row>=last && row==bpos) // down arrow at end of file
{
if(add_line(2)==-1) // at the end of the file move the
break; // add the line updating the text
bpos++; // move the screen up one line to display
tpos++; // the end of file marker
tptr=tptr->next_line;
fill_buffer();
update_screen();
}
else if(ch==83) // delete pressed
{
for(i=column;i<255;i++) // move all characters ahead of the cursor back one position
buffer[i]=buffer[i+1];
update_screen();
changed=1; // indicate the file has changed
continue; // top of loop
}
else if(ch==71) // home pressed
{
lpos=0; // just goto column 1
rpos=79;
column=0;
update_screen(); // and update screen
}
else if(ch==73 && row>0) // page up
{
if(add_line(3)==-1) // go ahead and add current line
break;
i=24; // maximun number of lines to page up
row=(tpos==0)?0:row; // keep current row unless at top of file
while(i>0 && row>0) // go until all 24 lines have been processed or at the top of file
{
if(tpos>0) // move every thing while not at the top
{ // of the file
tptr=tptr->prev_line;
tpos--;
bpos--;
row--;
}
i--;
}
fill_buffer(); // fill the buffer with the current lines text
update_screen(); // update the screen
uplc();
continue;
}
else if(ch==81 && row<=last+1) // page down
{
if(add_line(3)==-1) // same as above section other direction
break;
i=24;
row=(bpos>last)?last:row; // last is the last row in the file
while(i>0 && row<=last)
{
if(bpos<=last)
{
tptr=tptr->next_line;
bpos++;
tpos++;
if(row row++;
}
i--;
}
fill_buffer();
update_screen();
uplc();
continue;
}
else if(ch==79) // end pressed
{
for(i=254;i>=0;i--) // find the last actual character in the buffer
if(buffer[i]!='\0' && buffer[i]!=' ')
break; // exit for loop if it is found
if(i>=lpos && i<=rpos-1) // is the new column value within the current screen
column=i+1; // just change the cursor position
else if(i>=rpos)
{
rpos=(i<254)? i+1:i; // position cursor and rpos
lpos=rpos-79; // lpos always 79 characters less than rpos
column=(i<254)? i+1:i;
}
else if(i {
lpos=i+1; // position lpos and cursor
rpos=lpos+79; // rpos always 79 characters greater than lpos
column=lpos;
}
update_screen(); // rewrite that screen

}
else if(ch==82) // insert key pressed
{
if(insert_flag) // if it is already in insert mode
{
insert_flag=0; // turn insert mode off
cursor_change(0x0e0f); // change back to two lin cursor
screen_write(75,77,0,0," "); // get rid of insert indicator on top line
}
else
{
insert_flag=1; // turn insert mode on
cursor_change(0x010f); // change to block cursor
screen_write(75,77,0,0,"Ins"); // indicate insert mode is on
}
continue; // top of loop
}
else if(ch==45) // Alt-x to exit
return ch; // return the character to main to be processed
else continue; // any other key just loop again
uplc(); // some operations drop through to here
if(column>rpos) // to update the screen and column
{
while(column>rpos)
{
rpos++;
lpos++;
}
update_screen();
}
else if(column {
rpos--;
lpos--;
update_screen();
}
}
else // not an extened character
{
if(ch=='\b') // back space pressed
{
if(column>0) // if in the first column don't do anything
{
changed=1; // you changed the file
column--; // back up column
buffer[column]=' '; // erase the character that was there
if(insert_flag) // if in insert mode
{
for(i=column;i<255;i++) // bring all the characters in front of column with it
buffer[i]=buffer[i+1];
}
uplc(); // update the column and line #
if(column {
rpos--; // update it
lpos--;
}
update_screen();
continue; // top of loop
}
else
continue; // top of loop
}
changed=1; // you entered a valid character, indicate file change
screen_write(column-lpos,column-lpos,row-tpos+1,row-tpos+1,&ch); // write the single character out
if(column==254) // if its the last column just keep replacing the character
{
buffer[column]=ch;
continue;
}
if(insert_flag) // insert mode on ?
{
for(i=254;i>column;i--) // move everything ahead of column in the buffer forward one character
buffer[i]=buffer[i-1];
buffer[column]=ch; // insert character
update_screen(); // redisplay the character
}
else
buffer[column]=ch; // other wise just put in the character
column++; // move column
uplc();
if(column>rpos) // if the screen moved update it
{
rpos++;
lpos++;
update_screen();
}
}
}
return -1; // memory allocation error
}



//int insert_line(LINE far *ptr)
//
// Description: inserts a newly allocated structure after the structure passed
// to it
// Arguments: ptr - far pointer of type LINE insert is done after this line
// Return: -1 if memory allocation error occured
// 0 if ok
//
int insert_line(LINE far *ptr)
{
LINE far *ins_node; // pointer to newly allocated structure

if((ins_node=(LINE far *) malloc(sizeof(LINE)))==NULL) // allocate memory
{
error_msg(33,11,23,"Memory allocation error",79); // indcate any errors
return -1; // exit in error condition
}
ins_node->dataptr=NULL; // set the data pointer in structure to NULL

last++; // increase the total number of lines in the file
changed=1; // file has been changed
if(ptr==NULL) // if insert is at start of list
{
ins_node->next_line=start; // point the ins_node's next_line to the start of the list
ins_node->prev_line=NULL; // set its prev_line to NULL
start->prev_line=ins_node; // point starts previous line to newly allocated struct
start=ins_node; // ins_node is now the start of the list
tptr=start; // tptr points to new start of list
}
else // somewhere else in the list
{
ins_node->next_line=ptr->next_line; // point ins_node's next_line to ptr's next_line
ins_node->prev_line=ptr; // ins_lines previous line is ptr
ptr->next_line->prev_line=ins_node; // point the node after ptr back to ins_node
ptr->next_line=ins_node; // ptr's next_line is ins_node
if(row==tpos && column==0) // if at top of screen and insert was done before the current
tptr=ins_node; // row, set top of screen pointer to new node
}
mem(); // show memory use
return 0; // exit ok
}



//void fill_buffer(void)
//
// Description: fills the global string buffer with the text of the current row
// Arguments: NONE
// Return: nothing
//
//
void fill_buffer(void)
{
int i=tpos;
LINE far *ptr=tptr;

while(i!=row && ptr!=NULL) // find current line from top of screen
{
ptr=ptr->next_line;
i++;
}
for(i=0;idataptr);i++) // copy it into buffer
buffer[i]=ptr->dataptr[i];
while(i<=255) // clear rest of buffer
buffer[i++]='\0';
}



//void delete_line(LINE far *ptr)
//
// Description: deletes the structure passed in ptr
// Arguments: ptr - far pointer to the structure to be deleted
// Return: nothing
//
//
void delete_line(LINE far *ptr)
{
changed=1; // you changed the file
if(ptr==start) // delete begining of list
{
start=ptr->next_line; // reset start of list to the next line
start->prev_line=NULL; // next line now points back to nothing
tptr=start; // new top of screen
}
else
{
ptr->prev_line->next_line=ptr->next_line; // reroute structures on either side around
ptr->next_line->prev_line=ptr->prev_line; // ptr to point to each other
if(row==tpos) // deleted line at top of screen?
tptr=tptr->next_line; // new top of screen
}
if(ptr->dataptr!=NULL) // if there is any text associated with the structure
farfree(ptr->dataptr); // then free it
farfree(ptr); // free the structure
mem(); // show current memory
}



//void update_screen(void)
//
// Description: redisplay the screen printing out the text stored in the
// part of the list currently associated with the screen with
// the global variables row and tptr
// Arguments: NONE
// Return: nothing
//
//
void update_screen(void)
{
LINE far *ptr=tptr; // point to current node at top of screen
int ln=tpos; // row number at top of screen
int i, // loop control
len, // used to determine length of text to be displayed
y=1; // current row on actual screen
char show[80]; // used to hold the actual text to be displayed

while(ptr!=NULL || ln==row) // display list while not at end or displaying the current line
{
if((len=((ln!=row) ? strlen(ptr->dataptr)-lpos:255-lpos))>80) // determine length of current line
len=80; // if > 80 set to 80
if(len<0) // empty line
len=0;
if(len>0) // copy from dataptr if not displaying current line else copy from buffer
for(i=lpos;i show[i-lpos]=(ln!=row) ? ptr->dataptr[i]:buffer[i];
if(len<80) // if it didn't fill show pad with blanks
while(len<=80)
show[len++]=' ';
screen_writea(0,79,y,y,show,30); // write it to the screen
y++; // next screen line
ln++; // next row
if(y>24) // ran off the screen go back to editing
return;
if(ptr!=NULL) // point to next line and start loop again
ptr=ptr->next_line;
} // never got to the end of the screen ? write out the end of file marker
screen_writea(0,79,y,y," ******* End Of File ******* ",79);
y++; // account for it
while(y<=24) // still not it? then clear to end of screen
{
screen_writea(0,79,y,y," ",30);
y++;
}
}


//int add_line(int flag)
//
// Description: adds new structures to the end of the list and updates the
// text of existing structures
// Arguments: flag - a '2' indicates that buffer should be filled with the
// next lines text
// Return: nothing
//
int add_line(int flag)
{
int i, // loop control
ln=tpos; // row at top of screen
LINE far *ptr=tptr; // point to node at top of screen
if(start!=NULL || !load) // if the list is not empty or not in the middle of a load
while(ptr->next_line!=NULL && ln!=row) // traverse list till correct line is found
{
ptr=ptr->next_line;
ln++;
}
if(!load) // don't do if loading
{
for(i=255;i>=0;i--) // find the true end of the line
if(buffer[i]!='\0' && buffer[i]!=' ')
break;
else
buffer[i]='\0';
while(i>=0) // continue backwards to begining of buffer replacing NULLs with spaces
if(buffer[i]=='\0')
buffer[i--]=' ';
else
i--;
}
if((ptr->next_line==NULL && ln!=row) || start==NULL || load) // if a structure doesn't exist at the current row
{
if((new=(LINE *) farmalloc(sizeof(LINE)))==NULL) // allocat a new one
{
error_msg(33,11,23,"Memory allocation error",79); // indicate any error
return -1; // exit with error condition
}
}
else // a structure is already here
{
new=ptr; // point new to it
if(new->dataptr!=NULL) // clear any text that is associated with it
free(new->dataptr);
} // allocate space for text to new if any text is in buffer
if(strlen(buffer)>0 && (new->dataptr=(char *) farmalloc(strlen(buffer)+1))==NULL)
{
error_msg(33,11,23,"Memory allocation error",79); // indicate any error
return -1; // exit with error condition
}
if(strlen(buffer)>0) // if their is any text
for(i=0;i new->dataptr[i]=buffer[i];
else
new->dataptr=NULL; // else set it to NULL
if(start==NULL) //set up new as the start of the list
{
start=new;
start->prev_line=NULL;
start->next_line=NULL;
tptr=start; // tptr starts at the top of the list
}
else if((ptr->next_line==NULL && new!=ptr) || load) // otherwise add to end of list
{
if(load)
prev->next_line=new; // if in the middle of a load attach previously loaded line to new line
else
ptr->next_line=new; // else add to end of list
if(load)
new->prev_line=prev; // if load point back to previously loaded line
else
new->prev_line=ptr; // attach back to previous end of list
new->next_line=NULL;
}
if(load) // if in the middle of a load new is now prev
prev=new;
if(!load) // show memory usage
mem();
last=(row>last) ? row : last; // keep track of last row
if(!load)
for(i=strlen(buffer);i>=0;i--) // clear out that buffer
buffer[i]='\0';
if(flag==2) // if function was called with a two buffer will be loaded
{ // with text on the line after the one just added if any exist
if(new->next_line !=NULL)
if(new->next_line->dataptr != NULL)
for(i=strlen(new->next_line->dataptr);i>=0;i--)
buffer[i]=new->next_line->dataptr[i];
}
return 0; // ok exit
}


  3 Responses to “Category : Word Processors
Archive   : MEDIT.ZIP
Filename : MEDIT.C

  1. Very nice! Thank you for this wonderful archive. I wonder why I found it only now. Long live the BBS file archives!

  2. This is so awesome! 😀 I’d be cool if you could download an entire archive of this at once, though.

  3. But one thing that puzzles me is the “mtswslnkmcjklsdlsbdmMICROSOFT” string. There is an article about it here. It is definitely worth a read: http://www.os2museum.com/wp/mtswslnk/