xref: /original-bsd/contrib/sc/vi.c (revision 6884d44a)
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