├── Readme.md ├── document.cc ├── document.h ├── makefile ├── test.txt └── text_editor.cc /Readme.md: -------------------------------------------------------------------------------- 1 | ##Text Editor Instructions 2 | This program is is a basic line editor that is run from the command line.file (test.txt) to edit, but any text file will do. 3 | 4 | ###Available Commands: 5 | * g Get an existing file and load it into the editor. (The angle brackets are not typed as part of the command.) 6 | * n Turn line numbering on if off and off if on (line numbering on means that the number of each line is displayed at the left when the t command is given). 7 | * q Leave the editor without saving the text. 8 | * x [name] leave the editor and save the text in a file. The brackets indicate that the name is optional; if it is left out, the current file name is used. 9 | * s [name] Save the file under the current name if a new name is not provided, otherwise change the current name to name and save. 10 | * h Print a screen summarizing the available editor commands. 11 | * a [N] Start adding text after line N and before line N+1. Pressing return starts a new line, and typing control-D signals the end of add mode. If N is zero or missing, add lines at the beginning of the file. 12 | * t [M [N]] Display lines M through N on the screen. If M is given but not N, display only line M; if neither is given, display the whole document. 13 | * d M [N] Delete lines M through N (just line M if N is not given) and move the remaining lines up to fill the gap. 14 | * m M [N] L Move lines M through N so that they come immediately after line L (and they no longer appear at their current location). When N is missing, move just line M 15 | * c M [N] L Copy lines M through N so that they come immediately after line L (but they still appear at their original location). When N is missing, copy just line M 16 | * f [M [N]] /word/ Display each line between M and N on which word appears. The slash characters are part of the command, not part of the string to be found. 17 | * R [M [N]] /old/new/ Replace each occurrence of string old with string new on each of lines M through N. The slash characters are part of the command, not part of the strings. 18 | -------------------------------------------------------------------------------- /document.cc: -------------------------------------------------------------------------------- 1 | //Craig Barstow 2 | //Document.cc 3 | //File containing the functions for the text_ed class 4 | 5 | #include 6 | #include 7 | #include 8 | using namespace std; 9 | #include "document.h" 10 | const int DEFCAP = 5; 11 | 12 | //Note that the document holds capacity lines, numbered 1... capacity. 13 | //Spot 0 in the array is ignored, so array length is always "capacity+1" 14 | 15 | //PRIVATE 16 | 17 | bool text_ed::copy_to_buffer(int start, int end) { // Copy lines start through end to the buffer. 18 | int buff_capacity=end-start+1; 19 | int i,j,k; 20 | buffer= new string [buff_capacity+1]; 21 | for (i=start,j=0; i<=end; i++,j++) { //copy document contents into buffer 22 | buffer[j]=document[i]; 23 | } 24 | return true; 25 | } 26 | 27 | bool text_ed::copy_to_document(int start, int end, int location) { // Copy the entire buffer to the document starting at line start. 28 | int buff_capacity=end-start+1; 29 | int i,j; 30 | for (i=location+1,j=0; jstart; i--) { 47 | document[i+num_lines] = document[i]; 48 | } 49 | last_line += num_lines; 50 | return true; 51 | } 52 | 53 | void text_ed::expand_document() { // Add more lines to the document array, e.g., double capacity. 54 | int i; 55 | capacity *= 2; //double document capacity 56 | string* temp = new string[capacity+1]; 57 | for (i=0; i last_line-1) { 141 | n=last_line+1; 142 | } 143 | return true; 144 | } 145 | 146 | bool text_ed::delete_lines(int start, int end) { // Delete lines from start position to end position. 147 | int i; 148 | int num_lines = end-start; 149 | for (i=start; i<=last_line; i++) { //delete lines from start to end positions 150 | document[i] = document[i+num_lines+1]; 151 | } 152 | last_line = last_line-num_lines-1; 153 | return true; 154 | } 155 | 156 | bool text_ed::get_line(string& dest, int n) { // Copy line n into dest. 157 | if (n>last_line) { 158 | return false; 159 | } 160 | dest = document[n]; 161 | return true; 162 | } 163 | 164 | bool text_ed::copy_lines(int start, int end, int location) { //copy lines start through end to follow line location. 165 | int buff_capacity=end-start+1; 166 | copy_to_buffer(start,end); 167 | open_up(location, buff_capacity); 168 | copy_to_document(start,end,location); 169 | return true; 170 | } 171 | 172 | bool text_ed::move_lines(int start, int end, int location) { //move lines between two given lines (start to end) to after the given line location 173 | if (locationend) { //make sure new line location isnt between the start and end selections 174 | copy_lines(start,end,location); 175 | delete_lines(start,end); //delete the original lines so there aren't duplicates 176 | return true; 177 | } else { 178 | cout << "Error. Make sure location given is not between the two lines selected." << endl; 179 | return false; 180 | } 181 | } 182 | 183 | bool text_ed::replace(string oldstr, string newstr, int start, int end) { //replace old word with new word between given lines (start to end) 184 | int i, j; 185 | for (i=start; i<=end; i++) { //for each line in the given area 186 | for(j=0;j 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | using namespace std; 12 | #include "document.h" 13 | #define NOT_FOUND -1 14 | 15 | void Show_Commands() { //display all available commands 16 | cout << "h Show this list" << endl; 17 | cout << "g Get a file" << endl; 18 | cout << "n Toggle line numbering" << endl; 19 | cout << "q Quit without saving file" << endl; 20 | cout << "x [name] Save file and exit" << endl; 21 | cout << "s [name] Save file" << endl; 22 | cout << "a [N] Add lines after line N" << endl; 23 | cout << "t [M [N]] Type lines M through N" << endl; 24 | cout << "d M [N] Delete lines M through N" << endl; 25 | cout << "m M [N] L Move lines M through N to follow line L" << endl; 26 | cout << "c M [N] L Copy lines M through N to follow line L" << endl; 27 | cout << "f [M [N]] /word/ Type lines M through N containing word" << endl; 28 | cout << "r [M [N]] /word1/word2/ Replace word1 by word2 on lines M through N" << endl; 29 | } 30 | 31 | bool get_file(text_ed & T, string cl) { //load file to be edited 32 | string filename; 33 | string::size_type start,end; 34 | start = cl.find_first_not_of(' ',1); //find start of filename substring starting after the 'g' command 35 | if (start == string::npos) { //if string not found return false 36 | return false; 37 | } 38 | end = cl.find(' ',start); 39 | if ( end == string::npos ) { //if end of filename substring cant be found, set it equal to the end of the string 40 | end = cl.size(); 41 | } 42 | filename = cl.substr(start,end-start); 43 | return T.load_file(filename); //load the file 44 | } 45 | 46 | bool save_file(text_ed & T, string cl) { //take out couts 47 | string fn, new_name,CL=cl; 48 | int start,end; 49 | bool ok; 50 | string current_file = T.filename(); 51 | if (cl.find_first_not_of(" ", 1)==string::npos) { //if no filename given, save as current filename 52 | T.save_file(current_file); 53 | cout << "Saved as \"" << current_file <<"\""<< endl; 54 | return true; 55 | } else { //if filename give after s in command line, save as given filename 56 | new_name = cl.substr(2,cl.length()); 57 | ok = T.save_file(new_name); 58 | if (!ok) { //if it cant save to the given filename return an error message 59 | cout << "Can't save to file \"" << T.filename() << "\"." << endl; 60 | } 61 | return ok; //return the boolean value indicating whether the save was successful 62 | } 63 | } 64 | 65 | bool type_lines(text_ed & T, string cl, bool toggle) { //type lines between two given integers. If only one integer is given, type just that line. If no integers are given, type the whole document. 66 | bool error_state=false; 67 | string aline,m_string,n_string; 68 | int i=0,m=0,n=0,p=1,m_begin, m_end, n_begin, n_end,document_length,count=0; 69 | if (cl.find_first_not_of(" ",1)!=string::npos){ //this section selects the first integer from the substring 70 | m_begin= cl.find_first_not_of(" ",1); 71 | m_end = cl.find_first_of(" ", m_begin)-2; 72 | m_string=cl.substr(m_begin,m_end); 73 | if (!(istringstream(m_string) >> m)) { //if the substring cant be converted to an integer, set error state to false 74 | error_state=true; 75 | } else {count++;} //if it can be converted, increment the count 76 | if (cl.find_first_not_of(" ", m_end+2)!=string::npos){ //this section selects the second integer from the substring 77 | n_begin = cl.find_first_not_of(" ", m_end+2); 78 | n_end = cl.find_first_of("",n_begin); 79 | n_string=cl.substr(n_begin,n_end); 80 | if (!(istringstream(n_string) >> n)){ 81 | error_state=true; 82 | } else {count++;} //if it can be converted, increment the count 83 | } 84 | //SECTION TO MAKE SURE INTEGERS MAKE SENSE 85 | if (m<=0){ //if the line number is below 1, this automatically sets it to one 86 | error_state=true; 87 | } 88 | document_length = T.get_last_line(); 89 | if (m>document_length) { //this makes sure that the starting position is less than the last line of the document 90 | error_state = true; 91 | cout << "The first line number given exceeds file length." << endl; 92 | } 93 | if (n>document_length) { //if the ending position is greater than the document length, toggle error state to true 94 | error_state=true; 95 | } 96 | if (count!=1 && n> m) { //if the substring can be converted to an integer, increment the count 150 | count++; 151 | } 152 | //SECTION TO MAKE SURE INTEGER MAKES SENSE 153 | if ((m < 0 || m > T.get_last_line()) && count==1) { 154 | error_state=true; 155 | } 156 | if (count==1 && m==0) { //if m is equal to zero, set count to zero so lines will be added at the beginning of the document 157 | count=0; 158 | } 159 | } 160 | if (!error_state) { 161 | switch (count) { 162 | case 0: //if no integer is present, add lines starting at document start 163 | cout << " Press Control + D to stop"<> m)) { //if substring representing first integer can't be converted to integer form, toggle error state to true 197 | error_state=true; 198 | } else {count++;} //else, increment count to show that first integer is present 199 | if (cl.find_first_not_of(" ", m_end+2)!=string::npos){ //if second integer is part of substring isolate it 200 | n_start = cl.find_first_not_of(" ", m_end+2); 201 | n_end = cl.find_first_of("",n_start); 202 | N_string=cl.substr(n_start,n_end); 203 | if (!(istringstream(N_string) >> n)){ //if substring representing second integer can't be converted to int form, toggle error state to true 204 | error_state=true; 205 | } else {count++;} //increment count to show that second integer is present 206 | } 207 | //section to make sure the integer values are reasonable 208 | if (m<=0){ //if integer value of starting position is less 1, toggle error 209 | error_state=true; 210 | } 211 | if (count==2) { //if two integers are present, if second is less than first, or if first is greater than document length, toggle error state 212 | if (nT.get_last_line()) { 213 | error_state=true; 214 | } 215 | } 216 | if (!error_state) { 217 | if (count==2){ //if two integers are present, delete the lines between them 218 | T.delete_lines(m,n); 219 | } 220 | else if (count==1) { //if one integer is present, delete just that line 221 | T.delete_lines(m,m); 222 | } 223 | } else { 224 | return false; //return false if something other than 1 or two integers are present 225 | } return true; //if !error_state 226 | } 227 | return false; //return false if nothing is found after the 'd' statement in the command line, or if error state is true 228 | } 229 | 230 | bool copy_line (text_ed & T, string cl) { //Copy area between two given line numbers (inclusive) to after a third given line number if three numbers are given. If only two integers are given, move the line at the first position to after the position given by the second integer 231 | bool error_state=false; 232 | string aline,M_string,N_string,O_string; 233 | int i = 0,m=0,n=0,o=0,p=1,m_start, m_end, n_start, n_end, o_start, o_end, number, count=0; 234 | if (cl.find_first_not_of(" ",1)!=string::npos){ //section to isolate the first integer from the string entered into the command line 235 | m_start= cl.find_first_not_of(" ",1); 236 | m_end = cl.find_first_of(" ", m_start)-2; 237 | M_string=cl.substr(m_start,m_end); 238 | if (!(istringstream(M_string) >> m)) { //if the substring representing the first integer cannot be converted to an integer, toggle the error state to true 239 | error_state=true; 240 | m=1; 241 | } else {count++;} //increment count to show one integer is present 242 | if (cl.find_first_not_of(" ", m_end+2)!=string::npos){ //section to isolate the second integer from the string entered into the command line 243 | n_start = cl.find_first_not_of(" ", m_end+2); 244 | n_end = cl.find_first_of(" ",n_start); 245 | N_string=cl.substr(n_start,n_end); 246 | if (!(istringstream(N_string) >> n)){ //if the substring representing the second integer cannot be converted to an integer, toggle the error state to true 247 | error_state=true; 248 | } else {count++;} //increment count to show that two integers are present 249 | if (cl.find_first_not_of(" ", n_end+1)!=string::npos) { //section to isolate the third integer from the string entered into the command line 250 | o_start = cl.find_first_not_of(" ", n_end+1); 251 | o_end = cl.find_first_of("",o_start); 252 | O_string=cl.substr(o_start,o_end); 253 | if (istringstream(O_string) >> o){ //if substring can be converted to an integer, increment count to show a third integer is present 254 | count++; 255 | } 256 | } 257 | } 258 | //SECTION TO MAKE SURE INTEGERS MAKE SENSE 259 | if (m<1){ //if first integer is less than 1, toggle error state to true 260 | error_state=true; 261 | } 262 | if (n>T.get_last_line()) { //if second int is greater than document length, toggle error state 263 | error_state=true; 264 | } 265 | if (n<1) { //if second it is less than one, toggle error state to true 266 | error_state=true; 267 | } 268 | if (count==3) { 269 | if (nT.get_last_line()) { //if third integer is greater than document length, reset it 276 | o=T.get_last_line(); 277 | } 278 | } 279 | if (!error_state) { 280 | if (count==3){ //if three integers are present, copy lines from the start position to the end position to after the given location 281 | T.copy_lines(m,n,o); 282 | } 283 | else if (count==2) { //if two integers are present, copy the line given by the first position to after the position denoted by the second integer 284 | T.copy_lines(m,m,n); 285 | } 286 | return true; //return true if error state is false 287 | } else { 288 | return false; //return false if error state is true 289 | } 290 | } 291 | return false; //return false if nothing is found after the initial 'c' substring in the input string 292 | } 293 | 294 | bool move_line (text_ed & T, string cl) { 295 | bool error_state=false; 296 | string aline,M_string,N_string,O_string; 297 | int i = 0,m=0,n=0,o=0,p=1,m_start, m_end, n_start, n_end, o_start, o_end, number, count=0; 298 | if (cl.find_first_not_of(" ",1)!=string::npos){ //section to isolate the substring representing the first integer from the given string 299 | m_start= cl.find_first_not_of(" ",1); 300 | m_end = cl.find_first_of(" ", m_start)-2; 301 | M_string=cl.substr(m_start,m_end); 302 | if (!(istringstream(M_string) >> m)) { //if substring cant be converted to an integer, toggle error state to true 303 | error_state=true; 304 | } else {count++;} //if it can, increment count to show first integer is present 305 | if (cl.find_first_not_of(" ", m_end+2)!=string::npos){ //section to isolate the substring representing the second integer from the given string 306 | n_start = cl.find_first_not_of(" ", m_end+2); 307 | n_end = cl.find_first_of(" ",n_start); 308 | N_string=cl.substr(n_start,n_end); 309 | if (!(istringstream(N_string) >> n)){ //if substring cant be converted to an integer, toggle error state to true 310 | error_state=true; 311 | } else {count++;} //if it can be, increment count to show second integer is present 312 | if (cl.find_first_not_of(" ", n_end+1)!=string::npos) { //section to isolate the substring representing the third integer from the given string 313 | o_start = cl.find_first_not_of(" ", n_end+1); 314 | o_end = cl.find_first_of("",o_start); 315 | O_string=cl.substr(o_start,o_end); 316 | if (istringstream(O_string) >> o){ //if substring can be converted to an integer, increment count to show third integer is present 317 | count++; 318 | } 319 | } 320 | } 321 | //SECTION TO MAKE SURE THE INTEGERS MAKE SENSE 322 | if (m<=0){ //if first integer is less than 1, toggle error state to true 323 | error_state=true; 324 | } 325 | if (count<=2){ 326 | if (n>T.get_last_line()) { //if second int is greater than document length, reset it 327 | n=T.get_last_line(); 328 | } 329 | if (n<=0) { //if second it is less than zero, toggle error state to true 330 | error_state=true; 331 | } 332 | } 333 | if (count==3) { 334 | if (nT.get_last_line()) { //if the third integer is less than three, toggle error state to true 338 | error_state = true; 339 | } 340 | } 341 | if (!error_state) { 342 | if (count==3){ //if three integers are present, move the lines from the first to the second integer to after the position given by the third integer 343 | T.move_lines(m,n,o); 344 | } else if (count==2) { //if two integers are present, move the first line to after the position given by the second 345 | T.move_lines(m,m,n); //n+1 346 | } else if (count==1) { 347 | return false; //return false if only one integer is found 348 | } 349 | return true; //if error state is false, return true 350 | } 351 | } 352 | return false; //if error state is true or the first integer isnt found, return false 353 | } 354 | 355 | bool find_and_type(text_ed & T, string cl, bool toggle) { //search area from first given line to second for a given word. Type all lines where word is found. 356 | bool error_state=false; 357 | string aline,m_string,n_string, word; 358 | int i = 0,m=0,n=0,p=1,m_begin, m_end, n_begin, n_end,document_length, word_start, word_end,count=0; 359 | if (cl.find_first_not_of(" ",1)!=string::npos){ //section to isolate substring representing the first integer from the string 360 | m_begin= cl.find_first_not_of(" ",1); 361 | m_end = cl.find_first_of(" ", m_begin)-2; 362 | m_string=cl.substr(m_begin,m_end); 363 | if (istringstream(m_string) >> m) { //if substring can be converted to an integer, increment count to show the first integer is present 364 | count++;} 365 | if (cl.find_first_not_of(" ", m_end+2)!=string::npos){ //section to isolate substring representing the second integer from the string 366 | n_begin = cl.find_first_not_of(" ", m_end+2); 367 | n_end = cl.find_first_of("",n_begin); 368 | n_string=cl.substr(n_begin,n_end); 369 | if (istringstream(n_string) >> n){ //if substring can be converted to an integer, increment count to show the second integer is present 370 | count++; 371 | } 372 | } 373 | //SECTION TO MAKE SURE THE INTEGER VALUES MAKE SENSE 374 | document_length = T.get_last_line(); 375 | if ((m<=0 || m>document_length) && count!=0){ //if first integer is less than the starting line number toggle error state on 376 | error_state=true; 377 | } 378 | if (n>document_length) { //if ending integer value is greater than the document length, toggle error state on 379 | error_state=true; 380 | } 381 | if (n> m) { //if first substring can be converted to an integer, increment the count to show it is present 462 | count++; 463 | } 464 | if (cl.find_first_not_of(" ", m_end+1)!=string::npos){ //section to isolate second integer from the string 465 | n_begin = cl.find_first_not_of(" ", m_end+1); 466 | n_end = cl.find_first_of(" ",n_begin); 467 | n_length = n_end-n_begin; 468 | n_string=cl.substr(n_begin,n_length); 469 | if (istringstream(n_string) >> n){ //if the second substring can be converted to an integer, increment the count to show it is present 470 | count++; 471 | } 472 | } 473 | } 474 | //SECTION TO MAKE SURE THE INTEGER VALUES MAKE SENSE 475 | document_length = T.get_last_line(); 476 | if ((m<=0 || m>document_length)&& count!=0){ //if first integer is less than the starting line number, set it to the starting line number 477 | error_state=true; 478 | } 479 | if (n>document_length) { //if ending integer value is greater than the document length, toggle error state to true 480 | error_state=true; 481 | } 482 | if (n "; 522 | getline(cin,command_line); //get input from command line 523 | command = command_line[0]; 524 | switch (command) { 525 | case 'h': 526 | case 'H': Show_Commands(); break; 527 | case 'a': 528 | case 'A': success = add_lines(T,command_line); break; 529 | case 'q': 530 | case 'Q': return false; 531 | case 's': 532 | case 'S': 533 | if (command_line.find_first_not_of(" ",1)!=string::npos) { //if filename is present, toggle bool to true 534 | filename_presence = true; 535 | } 536 | if (filename_presence) { //if filename is present, save file to filename and retrieve new filename 537 | success = save_file(T,command_line) && get_file(T,command_line); 538 | } else { //otherwise save the file under the current name 539 | success = save_file(T,command_line); 540 | } 541 | break; 542 | break; 543 | case 't': 544 | case 'T': success = type_lines(T,command_line, toggle); break; 545 | case 'x': 546 | case 'X': success = save_file(T,command_line); 547 | return false; 548 | break; 549 | case 'g': 550 | case 'G': success = get_file(T,command_line); break; 551 | case 'n': 552 | case 'N': if (!toggle) { 553 | toggle=true; 554 | } else { 555 | toggle=false; 556 | } 557 | success = true; 558 | break; 559 | case 'd': 560 | case 'D': success = delete_line(T,command_line); break; 561 | case 'c': 562 | case 'C': success = copy_line(T,command_line); break; 563 | case 'm': 564 | case 'M': success = move_line(T,command_line); break; 565 | case 'f': 566 | case 'F': success = find_and_type(T,command_line, toggle); break; 567 | case 'r': 568 | case 'R': success = find_and_replace(T,command_line); break; 569 | default: if (command) { 570 | cout << "\"" << command << "\" is not a valid command." << endl; 571 | cout << "Type \"h\" for help." << endl; 572 | success = true; 573 | } else { 574 | success = true; 575 | } 576 | } 577 | if ( !success ) cout << "Error" << endl; 578 | return true; 579 | } 580 | 581 | int main (int argc, char** argv) { 582 | bool toggle=true; //initiate with line numbering on 583 | text_ed T; 584 | if (argc > 1) { 585 | if (!T.load_file(argv[1])) 586 | cerr << "Could not open file \"" << argv[1] << "\"." << endl; 587 | } 588 | bool keep_going; 589 | do { 590 | keep_going = Do_Command(T,toggle); 591 | } while (keep_going); 592 | } 593 | --------------------------------------------------------------------------------