/* ****** LINE EDITOR ***** When the editor is invoked, the user is first prompted for the name of the file to edit. If the file does not exist, a message to that effect will be written, and the user will begin with an empty buffer. At the conclusion of the editing session, the specified file is created and the buffer is written to it. If the file exists, it will be read into a buffer and the editing session will begin. At the end of the editing session, the revised file is written to disk. The edit prompt is #. There is always a dummy first line, denoted [BOF], in the buffer (but not in the file). In the following command list, current refers to the current line. Initially, current is the top line. The commands available are: f 'xxxxx' -- Finds the next line containing xxxxx. The search starts with the current line and proceeds toward the end of the buffer. If such a line is found, the line is displayed and it becmoes the current line. If no such line is found, the current line is unchanged. The first character of the second string becomes the delimiter. This delimiter can be anything. (You are not restricted to '.) i -- Insert an arbitrary number of lines after the current line. Insert is terminated by generating EOF. Current is unchanged. d x -- Delete x lines after the current line. t b -- Display the first line [BOF] and make it the current line. t e -- Display the last line and make it the current line. t . -- Display the current line. t x -- Display x lines starting with the current line. The current line is not changed. s 'xx' 'yy' Substitute yy for xx in the current line. (The strings xx and yy need not be the same length.) As in find, any character can be used as the delimiter. (You are not restricted to '.) q -- Quit the editor and write the revised file to disk. m x -- Move current so that it references the line x lines down and display the new current line. Example: Given current -> Line 1 Line 2 Line 3 after m 2, we would have Line 1 Line 2 current -> Line 3 and Line 3 would be displayed. The maximum length of a line is 80 characters (not counting line terminators) and the maximum number of lines in the buffer is restricted to 101 (100 actual lines plus the dummy line [BOF]). */ #include #include #include #define MAX_LENGTH 82 /* maximum length of line is 80 plus newline terminator plus '\0' string terminator */ #define MAX_LINES 101 /* 1 + maximum number of lines */ #define TRUE 1 typedef struct node { char line[ MAX_LENGTH ]; /* text of line */ struct node *link; /* pointer to next line */ } NODE; NODE top[ MAX_LINES ], /* storage for file */ *bottom, /* pointer to bottom line */ *current = top, /* pointer to current line */ *avail; /* pointer to next available unused node */ char inbuff[ MAX_LENGTH ]; /* buffer for input commands */ char file_name[ MAX_LENGTH ]; /* name of file to edit */ void insert( void ), /* functions to execute commands */ delet( const char *s ), type( const char *s ), move( const char *s ), quit( void ), find( const char *s ), substitute( const char *s, const char *t ); void create( void ); /* creates a linked list of NODEs */ NODE *read_file( void ); /* reads file into NODEs */ /* reads buff and stores strings in s2 and s3 */ int spec_scanf( const char *buff, char *s2, char *s3 ); /* skip white space and return position of pointer in buffer */ char *skip_whitesp( const char *ptr ); /* skip non-white space and return position of pointer in buffer */ char *skip_nonwhitesp( const char *ptr ); /* get and store delimited string and return the position of the last character scanned */ char *get_str_delim( const char *ptr, char *s ); main() { char err_msg[] = "*** Error. Command is: "; char s1[ MAX_LENGTH ]; /* used to store */ char s2[ MAX_LENGTH ]; /* parts of */ char s3[ MAX_LENGTH ]; /* commands */ int count; /* counts parts of a command stored */ create(); /* link the NODEs together */ bottom = read_file(); /* read file and return pointer to last node used */ avail = bottom -> link; /* available list of nodes starts at node following bottom */ bottom -> link = NULL; /* NULL link field of last node used */ while ( TRUE ) { printf( "# " ); /* print prompt */ fgets( inbuff, MAX_LENGTH, stdin ); /* store command */ /* parse command */ count = sscanf( inbuff, "%s%s%s", s1, s2, s3 ); printf( "\n" ); switch ( *s1 ) { case 'i': /* insert */ if ( count != 1 ) printf( "%s i\n", err_msg ); else insert(); break; case 'd': /* delete */ if ( count != 2 ) printf( "%s d \n", err_msg ); else delet( s2 ); break; case 't': /* type */ if ( count != 2 ) { printf( "%s", err_msg ); printf( "t (b e . or )\n" ); } else type( s2 ); break; case 'm': /* move */ if ( count != 2 ) printf( "%s m \n", err_msg ); else move( s2 ); break; case 'q': /* quit */ if ( count != 1 ) printf( "%s q\n", err_msg ); else quit(); break; case 'f': /* find */ /* requires reparsing with special function spec_scanf because of delimiters of string to find */ count = spec_scanf( inbuff, s2, s3 ); if ( count != 2 ) printf( "%s f 'xxx'\n", err_msg ); else find( s2 ); break; case 's': /* substitute */ /* requires reparsing with special function spec_scanf because of delimiters of strings to substitute */ count = spec_scanf( inbuff, s2, s3 ); if ( count != 3 ) printf( "%s s 'xx' 'yy'\n", err_msg ); else substitute( s2, s3 ); break; default: printf( "*** Unknown command\n" ); break; } } } /* create Creates a linked list of MAX_LINES NODEs. */ void create( void ) { NODE *ptr = top; int i; for ( i = 1; i < MAX_LINES; i++, ptr++ ) ptr -> link = ptr + 1; ptr -> link = NULL; } /* read_file Copies [BOF] into top and then reads a file into the linked list starting at the node after top. If the file does not exist, the list will consist of simply the [BOF] node. Returns a pointer to the last node. Does not NULL link field of last node. */ NODE *read_file( void ) { NODE *temp, *ptr = top; FILE *fp; strcpy( ptr -> line, "[BOF]\n" ); /* dummy first node */ printf( "\nEdit which file? " ); fgets( inbuff, MAX_LENGTH, stdin ); sscanf( inbuff, "%s", file_name ); printf( "\n" ); /* read file, if possible */ if ( ( fp = fopen( file_name, "r" ) ) != NULL ) { for ( temp = ptr -> link; temp != NULL && fgets( temp -> line, MAX_LENGTH, fp ) != NULL; temp = temp -> link, ptr = ptr -> link ) ; if ( fgets( inbuff, MAX_LENGTH, fp ) != NULL ) printf( "*** Not enough room to read in entire file\n" ); fclose( fp ); } else /* couldn't open file */ printf( "*** File does not exist\n" ); return ptr; } /* quit writes file and terminates editor. */ void quit( void ) { FILE *fp; NODE *ptr = top; fp = fopen( file_name, "w" ); for ( ptr = ptr -> link; ptr != NULL; ptr = ptr -> link ) fprintf( fp, "%s", ptr -> line ); exit( EXIT_SUCCESS ); } /* insert Inserts lines following current. Gets nodes from avail list. */ void insert( void ) { NODE *ptr = avail, /* ptr is node for next line to read */ *before_ptr = NULL; /* trails ptr in avail list */ while ( TRUE ) { if ( ptr == NULL ) { printf( "*** buffer full--can't add more lines\n" ); break; } clearerr( stdin ); /* turn off EOF flag */ if ( fgets( ptr -> line, MAX_LENGTH, stdin ) == NULL ) break; before_ptr = ptr; ptr = ptr -> link; } /* if no nodes added, just return */ if ( before_ptr == NULL ) return; /* If nodes were added, insert the added nodes after current. before_ptr points to the last node added. avail points to the first node added. */ before_ptr -> link = current -> link; current -> link = avail; /* update avail */ avail = ptr; /* if nodes were added at end of buffer, update bottom */ if ( before_ptr -> link == NULL ) bottom = before_ptr; } /* delet Delete s lines after current. */ void delet( const char *s ) { printf( "Entered delet\n" ); } /* type Display lines. */ void type( const char *s ) /* Options for s: s can be a nonnegative integer s can be . (= display current line) s can be b (= display top line) s can be e (= display last line) Only if s is b or e is current reset. */ { printf( "Entered type\n" ); } /* move Move current forward s lines, reset current, and display current. */ void move( const char *s ) { printf( "Entered move\n" ); } /* find Finds first line after or in current line that contains s, prints it, and moves current. */ void find( const char *s ) { printf( "Entered find\n" ); } /* substitute Substitute t for s in current line. */ void substitute( const char *s, const char *t ) { printf( "Entered substitute\n" ); } /* spec_scanf Reads from buff and stores strings in s2 and s3, if possible. Begin by skipping white space. Then skip non- white space (which corresponds to skipping the command f or s). Then skip white space. Then store the following string between delimiters in s2. Then skip white space. Then store the following string between delimiters in s3. Return the number of strings skipped and stored. (Skipping f or s counts as 1.) */ int spec_scanf( const char *buff, char *s2, char *s3 ) { buff = skip_whitesp( buff ); if ( *buff == '\0' ) return 0; buff = skip_nonwhitesp( buff ); buff = skip_whitesp( buff ); if ( *buff == '\0' ) return 1; buff = get_str_delim( buff, s2 ); if ( *buff++ == '\0' ) return 1; buff = skip_whitesp( buff ); buff = get_str_delim( buff, s3 ); if ( *buff == '\0' ) return 2; else return 3; } /* skip_whitesp Move ptr in string as long as it points to a blank, tab, or newline. Notice that \0 terminates pointer movement. Return final position of pointer. */ char *skip_whitesp( const char *ptr ) { char c; while ( ( c = *ptr ) == ' ' || c == '\t' || c == '\n' ) ptr++; return ptr; } /* skip_nonwhitesp Move ptr in string as long as it does not point to a blank, tab, newline, or \0. Return final position of pointer. */ char *skip_nonwhitesp( const char *ptr ) { char c; while ( ( c = *ptr ) != ' ' && c != '\t' && c != '\n' && c != '\0' ) ptr++; return ptr; } /* get_str_delim Scan and store delimited string. The string to read is pointed to by ptr. The string is stored at the location pointed to by s. The delimiter is the first character in the string. Return position of second delimiter. If the string is improperly delimited, return pointer to the null terminator of string. */ char *get_str_delim( const char *ptr, char *s ) { char delimit = *ptr; char c; if ( delimit == '\0' ) return ptr; ptr++; /* ptr now points to the first character to store */ /* scan and store until reaching delimiter or end of string */ while ( ( c = *ptr ) != delimit && c != '\0' ) { ptr++; *s++ = c; } *s = '\0'; return ptr; }