1 /* SC A Spreadsheet Calculator 2 * 3 * One line vi emulation 4 * $Revision: 6.8 $ 5 */ 6 7 8 #include <signal.h> 9 #include <curses.h> 10 11 #ifdef BSD42 12 #include <strings.h> 13 #else 14 #ifndef SYSIII 15 #include <string.h> 16 #endif 17 #endif 18 19 #if !defined(strchr) && !defined(UPORT) 20 #define strchr index 21 #endif 22 extern char *strchr(); 23 24 #include <stdio.h> 25 #include <ctype.h> 26 #include "sc.h" 27 28 #define istext(a) (isalnum(a) || ((a) == '_')) 29 30 extern int showrange; 31 extern char mode_ind; /* Mode indicator */ 32 33 /* values for mode below */ 34 35 #define INSERT_MODE 0 /* Insert mode */ 36 #define EDIT_MODE 1 /* Edit mode */ 37 #define REP_MODE 2 /* Replace mode */ 38 #define SEARCH_MODE 3 /* Get arguments for '/' command */ 39 40 static int mode = INSERT_MODE; 41 static char *history[HISTLEN]; 42 static int histp = -1; 43 static char *last_search; 44 static char *undo_line; 45 static int undo_lim; 46 static char dotb[100]; 47 static int doti = 0; 48 static int do_dot = 0; 49 50 void 51 write_line(c) 52 int c; 53 { 54 if (mode == EDIT_MODE) { 55 switch(c) { 56 case (ctl('h')): linelim = back_line(); break; 57 case (ctl('m')): cr_line(); break; 58 case ESC: stop_edit(); break; 59 case '+': for_hist(); break; 60 case '-': back_hist(); break; 61 case '$': last_col(); break; 62 case '.': dotcmd(); break; 63 case '/': search_mode(); break; 64 case '0': col_0(); break; 65 case 'D': u_save(c);del_to_end(); break; 66 case 'I': u_save(c);col_0();insert_mode();break; 67 case 'R': replace_mode(); break; 68 case 'X': u_save(c); back_space(); break; 69 case 'a': u_save(c); append_line(); break; 70 case 'b': linelim = back_word(); break; 71 case 'c': u_save(c); change_cmd(); break; 72 case 'd': u_save(c); delete_cmd(); break; 73 case 'f': linelim = find_char(); break; 74 case 'h': linelim = back_line(); break; 75 case 'i': u_save(c); insert_mode(); break; 76 case 'j': for_hist(); break; 77 case 'k': back_hist(); break; 78 case 'l': linelim = for_line(0); break; 79 case 'n': search_again(); break; 80 case 'q': stop_edit(); break; 81 case 'r': u_save(c); rep_char(); break; 82 case 't': linelim = to_char(); break; 83 case 'u': restore_it(); break; 84 case 'w': linelim = for_word(0); break; 85 case 'x': u_save(c); del_in_line(); break; 86 default: break; 87 } 88 } else if (mode == INSERT_MODE) { 89 savedot(c); 90 switch(c) { 91 case (ctl('h')): back_space(); break; 92 case (ctl('m')): cr_line(); break; 93 case ESC: edit_mode(); break; 94 default: ins_in_line(c); break; 95 } 96 } else if (mode == SEARCH_MODE) { 97 switch(c) { 98 case (ctl('h')): back_space(); break; 99 case (ctl('m')): search_hist(); break; 100 case ESC: edit_mode(); break; 101 default: ins_in_line(c); break; 102 } 103 } else if (mode == REP_MODE) { 104 savedot(c); 105 switch(c) { 106 case (ctl('h')): back_space(); break; 107 case (ctl('m')): cr_line(); break; 108 case ESC: edit_mode(); break; 109 default: replace_in_line(c); break; 110 } 111 } 112 } 113 114 edit_mode() 115 { 116 mode = EDIT_MODE; 117 mode_ind = 'e'; 118 histp = -1; 119 if (line[linelim] == '\0') 120 linelim = back_line(); 121 } 122 123 void 124 insert_mode() 125 { 126 mode_ind = 'i'; 127 mode = INSERT_MODE; 128 } 129 130 search_mode() 131 { 132 line[0] = '/'; 133 line[1] = 0; 134 linelim = 1; 135 histp = -1; 136 mode_ind = '/'; 137 mode = SEARCH_MODE; 138 } 139 140 replace_mode() 141 { 142 mode_ind = 'R'; 143 mode = REP_MODE; 144 } 145 146 /* dot command functions. Saves info so we can redo on a '.' command */ 147 148 savedot(c) 149 int c; 150 { 151 if (do_dot) 152 return; 153 154 dotb[doti++] = c; 155 dotb[doti] = 0; 156 } 157 158 dotcmd() 159 { 160 int c; 161 162 do_dot = 1; 163 doti = 0; 164 while(dotb[doti] != 0) { 165 c = dotb[doti++]; 166 write_line(c); 167 } 168 do_dot = 0; 169 doti = 0; 170 } 171 172 vigetch() 173 { 174 int c; 175 176 if(do_dot) { 177 if (dotb[doti] != 0) { 178 return(dotb[doti++]); 179 } else { 180 do_dot = 0; 181 doti = 0; 182 return(nmgetch()); 183 } 184 } 185 c = nmgetch(); 186 savedot(c); 187 return(c); 188 } 189 190 /* saves the current line for possible use by an undo cmd */ 191 192 u_save(c) 193 int c; 194 { 195 if (undo_line) { 196 xfree(undo_line); 197 undo_line = 0; 198 } 199 undo_line = strcpy(xmalloc((unsigned)(strlen(line)+1)), line); 200 undo_lim = linelim; 201 202 /* reset dot command if not processing it. */ 203 204 if (!do_dot) { 205 doti = 0; 206 savedot(c); 207 } 208 } 209 210 /* Restores the current line saved by u_save() */ 211 212 restore_it() 213 { 214 register char *tempc; 215 register int tempi; 216 217 if (!undo_line) 218 return; 219 tempc = strcpy(xmalloc((unsigned)(strlen(line)+1)), line); 220 tempi = linelim; 221 strcpy(line, undo_line); 222 linelim = undo_lim; 223 xfree(undo_line); 224 undo_line = tempc; 225 undo_lim = tempi; 226 } 227 228 /* This command stops the editing process. */ 229 230 stop_edit() 231 { 232 showrange = 0; 233 linelim = -1; 234 (void) move(1, 0); 235 (void) clrtoeol(); 236 } 237 238 /* 239 * Motion commands. Forward motion commands take an argument 240 * which, when set, cause the forward motion to continue onto 241 * the null at the end of the line instead of stopping at the 242 * the last character of the line. 243 */ 244 245 for_line(stop_null) 246 int stop_null; 247 { 248 if (linelim >= 0 && line[linelim] != 0 && 249 (line[linelim+1] != 0 || stop_null)) 250 return(linelim+1); 251 else 252 return(linelim); 253 } 254 255 for_word(stop_null) 256 int stop_null; 257 { 258 register int c; 259 register int cpos; 260 261 cpos = linelim; 262 263 if (line[cpos] == ' ') { 264 while (line[cpos] == ' ') 265 cpos++; 266 if (cpos > 0 && line[cpos] == 0) 267 --cpos; 268 return(cpos); 269 } 270 271 if (istext(line[cpos])) { 272 while ((c = line[cpos]) && istext(c)) 273 cpos++; 274 } else { 275 while ((c = line[cpos]) && !istext(c) && c != ' ') 276 cpos++; 277 } 278 279 while (line[cpos] == ' ') 280 cpos++; 281 282 if (cpos > 0 && line[cpos] == 0 && !stop_null) 283 --cpos; 284 285 return(cpos); 286 } 287 288 back_line() 289 { 290 if (linelim) 291 return(linelim-1); 292 else 293 return(0); 294 } 295 296 back_word() 297 { 298 register int c; 299 register int cpos; 300 301 cpos = linelim; 302 303 if (line[cpos] == ' ') { 304 /* Skip white space */ 305 while (cpos > 0 && line[cpos] == ' ') 306 --cpos; 307 } else if (cpos > 0 && (line[cpos-1] == ' ' 308 || istext(line[cpos]) && !istext(line[cpos-1]) 309 || !istext(line[cpos]) && istext(line[cpos-1]))) { 310 /* Started on the first char of a word - back up to prev. word */ 311 --cpos; 312 while (cpos > 0 && line[cpos] == ' ') 313 --cpos; 314 } 315 316 /* Skip across the word - goes 1 too far */ 317 if (istext(line[cpos])) { 318 while (cpos > 0 && (c = line[cpos]) && istext(c)) 319 --cpos; 320 } else { 321 while (cpos > 0 && (c = line[cpos]) && !istext(c) && c != ' ') 322 --cpos; 323 } 324 325 /* We are done - fix up the one too far */ 326 if (cpos > 0 && line[cpos] && line[cpos+1]) 327 cpos++; 328 329 return(cpos); 330 } 331 332 /* Text manipulation commands */ 333 334 del_in_line() 335 { 336 register int len, i; 337 338 if (linelim >= 0) { 339 len = strlen(line); 340 if (linelim == len && linelim > 0) 341 linelim--; 342 for (i = linelim; i < len; i++) 343 line[i] = line[i+1]; 344 } 345 if (linelim > 0 && line[linelim] == 0) 346 --linelim; 347 } 348 349 ins_in_line(c) 350 int c; 351 { 352 register int i, len; 353 354 len = strlen(line); 355 for (i = len; i >= linelim; --i) 356 line[i+1] = line[i]; 357 line[linelim++] = c; 358 line[len+1] = 0; 359 } 360 361 void 362 ins_string(s) 363 char *s; 364 { 365 while (*s) 366 ins_in_line(*s++); 367 } 368 369 append_line() 370 { 371 register int i; 372 373 i = linelim; 374 if (i >= 0 && line[i]) 375 linelim++; 376 insert_mode(); 377 } 378 379 rep_char() 380 { 381 int c; 382 383 c = vigetch(); 384 if (line[linelim] != 0) { 385 line[linelim] = c; 386 } else { 387 line[linelim] = c; 388 line[linelim+1] = 0; 389 } 390 } 391 392 replace_in_line(c) 393 { 394 register int len; 395 396 len = strlen(line); 397 line[linelim++] = c; 398 if (linelim > len) 399 line[linelim] = 0; 400 } 401 402 back_space() 403 { 404 if (linelim == 0) 405 return; 406 407 if (line[linelim] == 0) { 408 linelim = back_line(); 409 del_in_line(); 410 linelim = strlen(line); 411 } else { 412 linelim = back_line(); 413 del_in_line(); 414 } 415 } 416 417 get_motion() 418 { 419 int c; 420 421 c = vigetch(); 422 switch (c) { 423 case 'b': return(back_word()); 424 case 'f': return(find_char()+1); 425 case 'h': return(back_line()); 426 case 'l': return(for_line(1)); 427 case 't': return(to_char()+1); 428 case 'w': return(for_word(1)); 429 default: return(linelim); 430 } 431 } 432 433 delete_cmd() 434 { 435 int cpos; 436 437 cpos = get_motion(); 438 del_chars(cpos, linelim); 439 } 440 441 change_cmd() 442 { 443 delete_cmd(); 444 insert_mode(); 445 } 446 447 del_chars(first, last) 448 register int first, last; 449 { 450 int temp; 451 452 if (first == last) 453 return; 454 455 if (last < first) { 456 temp = last; last = first; first = temp; 457 } 458 459 linelim = first; 460 while(first < last) { 461 del_in_line(); 462 --last; 463 } 464 } 465 466 del_to_end() 467 { 468 if (linelim < 0) 469 return; 470 line[linelim] = 0; 471 linelim = back_line(); 472 } 473 474 cr_line() 475 { 476 showrange = 0; 477 insert_mode(); 478 save_hist(); 479 linelim = 0; 480 (void) yyparse (); 481 linelim = -1; 482 } 483 484 /* History functions */ 485 486 save_hist() 487 { 488 register int i; 489 490 /* free the oldest one */ 491 if (history[HISTLEN-1]) { 492 xfree(history[HISTLEN-1]); 493 history[HISTLEN-1] = 0; 494 } 495 496 /* Move the others back */ 497 for (i = HISTLEN-1; i > 0; --i) 498 history[i] = history[i-1]; 499 500 history[0] = xmalloc((unsigned) strlen(line)+1); 501 strcpy(history[0], line); 502 } 503 504 back_hist() 505 { 506 if (histp == -1 || histp < HISTLEN-1 && history[histp + 1]) 507 histp++; 508 509 if (history[histp]) { 510 strcpy(line, history[histp]); 511 linelim = 0; 512 } else 513 line[linelim = 0] = 0; 514 515 } 516 517 search_hist() 518 { 519 if (last_search) { 520 xfree(last_search); 521 last_search = 0; 522 } 523 524 if(linelim < 1) { 525 linelim = 0; 526 edit_mode(); 527 return; 528 } 529 530 last_search = strcpy(xmalloc((unsigned)(strlen(line+1)+1)), line+1); 531 search_again(); 532 mode = EDIT_MODE; 533 } 534 535 search_again() 536 { 537 int found_it; 538 int do_next; 539 int prev_histp; 540 char *look_here; 541 542 prev_histp = histp; 543 if (!last_search) 544 return; 545 546 do { 547 back_hist(); 548 if (prev_histp == histp) 549 break; 550 prev_histp = histp; 551 look_here = line; 552 found_it = do_next = 0; 553 while ((look_here = strchr(look_here, last_search[0])) && 554 !found_it && !do_next) { 555 556 if (strncmp(look_here, last_search, strlen(last_search)) == 0) 557 found_it++; 558 else if (look_here < line + strlen(line) - 1) 559 look_here++; 560 else 561 do_next++; 562 } 563 } while (!found_it); 564 } 565 566 for_hist() 567 { 568 if (histp > 0) 569 histp--; 570 571 if (histp >= 0 && history[histp]) { 572 strcpy(line, history[histp]); 573 linelim = 0; 574 } else 575 line[linelim = 0] = 0; 576 } 577 578 col_0() 579 { 580 linelim = 0; 581 } 582 583 last_col() 584 { 585 linelim = strlen(line); 586 if (linelim > 0) 587 --linelim; 588 } 589 590 find_char() 591 { 592 register int c; 593 register int i; 594 595 596 c = vigetch(); 597 i = linelim; 598 while(line[i] && line[i] != c) 599 i++; 600 if (!line[i]) 601 i = linelim; 602 return(i); 603 } 604 605 to_char() 606 { 607 register int i; 608 609 i = find_char(); 610 if (i > 0 && i != linelim) 611 --i; 612 613 return(i); 614 } 615