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
write_line(c)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
edit_mode()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
insert_mode()124 insert_mode()
125 {
126 mode_ind = 'i';
127 mode = INSERT_MODE;
128 }
129
search_mode()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
replace_mode()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
savedot(c)148 savedot(c)
149 int c;
150 {
151 if (do_dot)
152 return;
153
154 dotb[doti++] = c;
155 dotb[doti] = 0;
156 }
157
dotcmd()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
vigetch()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
u_save(c)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
restore_it()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
stop_edit()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
for_line(stop_null)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
for_word(stop_null)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
back_line()288 back_line()
289 {
290 if (linelim)
291 return(linelim-1);
292 else
293 return(0);
294 }
295
back_word()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
del_in_line()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
ins_in_line(c)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
ins_string(s)362 ins_string(s)
363 char *s;
364 {
365 while (*s)
366 ins_in_line(*s++);
367 }
368
append_line()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
rep_char()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
replace_in_line(c)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
back_space()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
get_motion()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
delete_cmd()433 delete_cmd()
434 {
435 int cpos;
436
437 cpos = get_motion();
438 del_chars(cpos, linelim);
439 }
440
change_cmd()441 change_cmd()
442 {
443 delete_cmd();
444 insert_mode();
445 }
446
del_chars(first,last)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
del_to_end()466 del_to_end()
467 {
468 if (linelim < 0)
469 return;
470 line[linelim] = 0;
471 linelim = back_line();
472 }
473
cr_line()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
save_hist()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
back_hist()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
search_hist()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
search_again()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
for_hist()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
col_0()578 col_0()
579 {
580 linelim = 0;
581 }
582
last_col()583 last_col()
584 {
585 linelim = strlen(line);
586 if (linelim > 0)
587 --linelim;
588 }
589
find_char()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
to_char()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