Update the man page to remove two of the BUGS.
[sgt/tweak] / actions.c
1 #include "tweak.h"
2
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <string.h>
6 #include <ctype.h>
7
8 static void act_exit (void);
9 static void act_save (void);
10 static void act_exitsave (void);
11 static void act_top (void);
12 static void act_pgup (void);
13 static void act_up (void);
14 static void act_home (void);
15 static void act_left (void);
16 static void act_right (void);
17 static void act_end (void);
18 static void act_down (void);
19 static void act_pgdn (void);
20 static void act_bottom (void);
21 static void act_togins (void);
22 static void act_chmode (void);
23 extern void act_self_ins (void); /* this one must be external */
24 static void act_delete (void);
25 static void act_delch (void);
26 static void act_mark (void);
27 static void act_cut (void);
28 static void act_copy (void);
29 static void act_paste (void);
30 static void act_susp (void);
31 static void act_goto (void);
32 static void act_togstat (void);
33 static void act_search (void);
34 static void act_search_backwards (void);
35 static void act_recentre (void);
36 static void act_width (void);
37 static void act_offset (void);
38 #ifdef TEST_BUFFER
39 static void act_diagnostics (void);
40 #endif
41
42 static Search *last_search = NULL;
43
44 keyact parse_action (char *name) {
45 char *names[] = {
46 "exit", "top-of-file", "page-up", "move-up",
47 "begin-line", "move-left", "move-right", "end-line",
48 "move-down", "page-down", "bottom-of-file", "toggle-insert",
49 "change-mode", "delete-left", "delete-right", "mark-place",
50 "cut", "copy", "paste", "suspend", "goto-position",
51 "toggle-status", "search", "search-back", "save-file",
52 "exit-and-save", "screen-recentre", "new-width", "new-offset"
53 #ifdef TEST_BUFFER
54 , "diagnostics"
55 #endif
56 };
57 keyact actions[] = {
58 act_exit, act_top, act_pgup, act_up, act_home, act_left,
59 act_right, act_end, act_down, act_pgdn, act_bottom,
60 act_togins, act_chmode, act_delete, act_delch, act_mark,
61 act_cut, act_copy, act_paste, act_susp, act_goto,
62 act_togstat, act_search, act_search_backwards, act_save,
63 act_exitsave, act_recentre, act_width, act_offset
64 #ifdef TEST_BUFFER
65 , act_diagnostics
66 #endif
67 };
68 int i;
69
70 for (i=0; i<sizeof(names)/sizeof(*names); i++)
71 if (!strcmp(name, names[i]))
72 return actions[i];
73 return NULL;
74 }
75
76 static fileoffset_t begline(fileoffset_t x) {
77 fileoffset_t y = x + width-offset;
78 y -= (y % width);
79 y -= width-offset;
80 if (y < 0)
81 y = 0;
82 return y;
83 }
84
85 static fileoffset_t endline(fileoffset_t x) {
86 fileoffset_t y = x + width-offset;
87 y -= (y % width);
88 y += width-1;
89 y -= width-offset;
90 if (y < 0)
91 y = 0;
92 return y;
93 }
94
95 static void act_exit(void) {
96 static char question[] = "File is modified. Save before quitting? [yn] ";
97 if (modified) {
98 int c;
99
100 display_moveto (display_rows-1, 0);
101 display_clear_to_eol ();
102 display_set_colour (COL_MINIBUF);
103 display_write_str (question);
104 display_refresh();
105 do {
106 #if defined(unix) && !defined(GO32)
107 if (update_required) {
108 update();
109 display_moveto (display_rows-1, 0);
110 display_clear_to_eol ();
111 display_set_colour (COL_MINIBUF);
112 display_write_str (question);
113 display_refresh();
114 }
115 safe_update = TRUE;
116 #endif
117 c = display_getkey();
118 #if defined(unix) && !defined(GO32)
119 safe_update = FALSE;
120 #endif
121 if (c >= 'a' && c <= 'z')
122 c += 'A'-'a';
123 } while (c != 'Y' && c != 'N' && c != '\007');
124 if (c == 'Y') {
125 act_save();
126 if (modified)
127 return; /* couldn't save, so don't quit */
128 draw_scr(); /* update the ** on status line! */
129 } else if (c == '\007') {
130 return; /* don't even quit */
131 }
132 }
133 finished = TRUE;
134 }
135
136 static void act_save(void) {
137 static int backed_up = FALSE;
138
139 if (!backed_up) {
140 if (!backup_file()) {
141 display_beep();
142 strcpy (message, "Unable to back up file!");
143 return;
144 }
145 backed_up = TRUE;
146 }
147 if (!save_file()) {
148 display_beep();
149 strcpy (message, "Unable to save file!");
150 return;
151 }
152 modified = FALSE;
153 }
154
155 static void act_exitsave(void) {
156 act_save();
157 draw_scr(); /* update ** on status line */
158 act_exit();
159 }
160
161 static void act_top (void) {
162 cur_pos = top_pos = 0;
163 edit_type = !!edit_type;
164 }
165
166 static void act_pgup(void) {
167 cur_pos -= (scrlines-1)*width;
168 if (cur_pos < 0) {
169 cur_pos = 0;
170 edit_type = !!edit_type;
171 }
172 if (top_pos > cur_pos)
173 top_pos = begline(cur_pos);
174 }
175
176 static void act_up(void) {
177 cur_pos -= width;
178 if (cur_pos < 0) {
179 cur_pos = 0;
180 edit_type = !!edit_type;
181 }
182 if (top_pos > cur_pos)
183 top_pos = begline(cur_pos);
184 }
185
186 static void act_home(void) {
187 cur_pos = begline(cur_pos);
188 if (cur_pos < 0)
189 cur_pos = 0;
190 if (top_pos > cur_pos)
191 top_pos = begline(cur_pos);
192 edit_type = !!edit_type;
193 }
194
195 static void act_left(void) {
196 if (edit_type == 2) {
197 edit_type = 1;
198 return;
199 } else {
200 cur_pos--;
201 edit_type = 2*!!edit_type;
202 if (cur_pos < 0) {
203 cur_pos = 0;
204 edit_type = !!edit_type;
205 }
206 if (top_pos > cur_pos)
207 top_pos = begline(cur_pos);
208 }
209 }
210
211 static void act_right(void) {
212 fileoffset_t new_top;
213
214 if (edit_type == 1) {
215 if (cur_pos < file_size)
216 edit_type = 2;
217 return;
218 } else {
219 cur_pos++;
220 if (cur_pos > file_size)
221 cur_pos = file_size;
222 new_top = cur_pos - (scrlines-1) * width;
223 if (new_top < 0)
224 new_top = 0;
225 new_top = begline(new_top);
226 if (top_pos < new_top)
227 top_pos = new_top;
228 edit_type = !!edit_type;
229 }
230 }
231
232 static void act_end(void) {
233 fileoffset_t new_top;
234
235 cur_pos = endline(cur_pos);
236 edit_type = !!edit_type;
237 if (cur_pos >= file_size)
238 cur_pos = file_size;
239 new_top = cur_pos - (scrlines-1) * width;
240 if (new_top < 0)
241 new_top = 0;
242 new_top = begline(new_top);
243 if (top_pos < new_top)
244 top_pos = new_top;
245 }
246
247 static void act_down(void) {
248 fileoffset_t new_top;
249
250 cur_pos += width;
251 if (cur_pos >= file_size) {
252 cur_pos = file_size;
253 edit_type = !!edit_type;
254 }
255 new_top = cur_pos - (scrlines-1) * width;
256 if (new_top < 0)
257 new_top = 0;
258 new_top = begline(new_top);
259 if (top_pos < new_top)
260 top_pos = new_top;
261 }
262
263 static void act_pgdn(void) {
264 fileoffset_t new_top;
265
266 cur_pos += (scrlines-1) * width;
267 if (cur_pos >= file_size) {
268 cur_pos = file_size;
269 edit_type = !!edit_type;
270 }
271 new_top = cur_pos - (scrlines-1) * width;
272 if (new_top < 0)
273 new_top = 0;
274 new_top = begline(new_top);
275 if (top_pos < new_top)
276 top_pos = new_top;
277 }
278
279 static void act_bottom (void) {
280 fileoffset_t new_top;
281
282 cur_pos = file_size;
283 edit_type = !!edit_type;
284 new_top = cur_pos - (scrlines-1) * width;
285 if (new_top < 0)
286 new_top = 0;
287 new_top = begline(new_top);
288 if (top_pos < new_top)
289 top_pos = new_top;
290 }
291
292 static void act_togins(void) {
293 if (look_mode || fix_mode) {
294 display_beep();
295 sprintf(message, "Can't engage Insert mode when in %s mode",
296 (look_mode ? "LOOK" : "FIX"));
297 insert_mode = FALSE; /* safety! */
298 } else
299 insert_mode = !insert_mode;
300 }
301
302 static void act_chmode(void) {
303 if (ascii_enabled)
304 edit_type = !edit_type; /* 0 -> 1, [12] -> 0 */
305 else if (edit_type == 0) /* just in case */
306 edit_type = 1;
307 }
308
309 void act_self_ins(void) {
310 int insert = insert_mode;
311 unsigned char c;
312
313 if (look_mode) {
314 display_beep();
315 strcpy (message, "Can't modify file in LOOK mode");
316 return;
317 }
318
319 if (edit_type) {
320 if (last_char >= '0' && last_char <= '9')
321 last_char -= '0';
322 else if (last_char >= 'A' && last_char <= 'F')
323 last_char -= 'A'-10;
324 else if (last_char >= 'a' && last_char <= 'f')
325 last_char -= 'a'-10;
326 else {
327 display_beep();
328 strcpy(message, "Not a valid character when in hex editing mode");
329 return;
330 }
331 }
332
333 if ( (!insert || edit_type == 2) && cur_pos == file_size) {
334 display_beep();
335 strcpy(message, "End of file reached");
336 return;
337 }
338
339 switch (edit_type) {
340 case 0: /* ascii mode */
341 c = last_char;
342 break;
343 case 1: /* hex, first digit */
344 if (insert)
345 c = 0;
346 else
347 buf_fetch_data(filedata, &c, 1, cur_pos);
348 c &= 0xF;
349 c |= 16 * last_char;
350 break;
351 case 2: /* hex, second digit */
352 buf_fetch_data(filedata, &c, 1, cur_pos);
353 c &= 0xF0;
354 c |= last_char;
355 insert = FALSE;
356 break;
357 }
358
359 if (insert) {
360 buf_insert_data(filedata, &c, 1, cur_pos);
361 file_size++;
362 modified = TRUE;
363 } else if (cur_pos < file_size) {
364 buf_overwrite_data(filedata, &c, 1, cur_pos);
365 modified = TRUE;
366 } else {
367 display_beep();
368 strcpy(message, "End of file reached");
369 }
370 act_right();
371 }
372
373 static void act_delete(void) {
374 if (!insert_mode || (edit_type!=2 && cur_pos==0)) {
375 display_beep();
376 strcpy (message, "Can't delete while not in Insert mode");
377 } else if (cur_pos > 0 || edit_type == 2) {
378 act_left();
379 buf_delete (filedata, 1, cur_pos);
380 file_size--;
381 edit_type = !!edit_type;
382 modified = TRUE;
383 }
384 }
385
386 static void act_delch(void) {
387 if (!insert_mode) {
388 display_beep();
389 strcpy (message, "Can't delete while not in Insert mode");
390 } else if (cur_pos < file_size) {
391 buf_delete (filedata, 1, cur_pos);
392 file_size--;
393 edit_type = !!edit_type;
394 modified = TRUE;
395 }
396 }
397
398 static void act_mark (void) {
399 if (look_mode) {
400 display_beep();
401 strcpy (message, "Can't cut or paste in LOOK mode");
402 marking = FALSE; /* safety */
403 return;
404 }
405 marking = !marking;
406 mark_point = cur_pos;
407 }
408
409 static void act_cut (void) {
410 fileoffset_t marktop, marksize;
411
412 if (!marking || mark_point==cur_pos) {
413 display_beep();
414 strcpy (message, "Set mark first");
415 return;
416 }
417 if (!insert_mode) {
418 display_beep();
419 strcpy (message, "Can't cut while not in Insert mode");
420 return;
421 }
422 marktop = cur_pos;
423 marksize = mark_point - cur_pos;
424 if (marksize < 0) {
425 marktop += marksize;
426 marksize = -marksize;
427 }
428 if (cutbuffer)
429 buf_free (cutbuffer);
430 cutbuffer = buf_cut (filedata, marksize, marktop);
431 file_size -= marksize;
432 cur_pos = marktop;
433 if (cur_pos < 0)
434 cur_pos = 0;
435 if (top_pos > cur_pos)
436 top_pos = begline(cur_pos);
437 edit_type = !!edit_type;
438 modified = TRUE;
439 marking = FALSE;
440 }
441
442 static void act_copy (void) {
443 fileoffset_t marktop, marksize;
444
445 if (!marking) {
446 display_beep();
447 strcpy (message, "Set mark first");
448 return;
449 }
450 marktop = cur_pos;
451 marksize = mark_point - cur_pos;
452 if (marksize < 0) {
453 marktop += marksize;
454 marksize = -marksize;
455 }
456 if (cutbuffer)
457 buf_free (cutbuffer);
458 cutbuffer = buf_copy (filedata, marksize, marktop);
459 marking = FALSE;
460 }
461
462 static void act_paste (void) {
463 fileoffset_t cutsize, new_top;
464
465 cutsize = buf_length (cutbuffer);
466 if (!insert_mode) {
467 if (cur_pos + cutsize > file_size) {
468 display_beep();
469 strcpy (message, "Too close to end of file to paste");
470 return;
471 }
472 buf_delete (filedata, cutsize, cur_pos);
473 file_size -= cutsize;
474 }
475 buf_paste (filedata, cutbuffer, cur_pos);
476 modified = TRUE;
477 cur_pos += cutsize;
478 file_size += cutsize;
479 edit_type = !!edit_type;
480 new_top = cur_pos - (scrlines-1) * width;
481 if (new_top < 0)
482 new_top = 0;
483 new_top = begline(new_top);
484 if (top_pos < new_top)
485 top_pos = new_top;
486 }
487
488 static void act_susp (void) {
489 suspend();
490 }
491
492 static void act_goto (void) {
493 char buffer[80];
494 fileoffset_t position, new_top;
495 int error;
496
497 if (!get_str("Enter position to go to: ", buffer, FALSE))
498 return; /* user break */
499
500 position = parse_num (buffer, &error);
501 if (error) {
502 display_beep();
503 strcpy (message, "Unable to parse position value");
504 return;
505 }
506
507 if (position < 0 || position > file_size) {
508 display_beep();
509 strcpy (message, "Position is outside bounds of file");
510 return;
511 }
512
513 cur_pos = position;
514 edit_type = !!edit_type;
515 new_top = cur_pos - (scrlines-1) * width;
516 if (new_top < 0)
517 new_top = 0;
518 new_top = begline(new_top);
519 if (top_pos > cur_pos)
520 top_pos = begline(cur_pos);
521 if (top_pos < new_top)
522 top_pos = new_top;
523 }
524
525 static void act_togstat (void) {
526 if (statfmt == decstatus)
527 statfmt = hexstatus;
528 else
529 statfmt = decstatus;
530 }
531
532 static int search_prompt(char *withdef, char *withoutdef)
533 {
534 char buffer[80];
535 int len;
536
537 if (!get_str(last_search ? withdef : withoutdef, buffer, TRUE))
538 return 0; /* user break */
539 if (!last_search && !*buffer) {
540 strcpy (message, "Search aborted.");
541 return 0;
542 }
543
544 if (!*buffer) {
545 len = last_search->len;
546 } else {
547 len = parse_quoted (buffer);
548 if (len == -1) {
549 display_beep();
550 strcpy (message, "Invalid escape sequence in search string");
551 return 0;
552 }
553 if (last_search)
554 free_search(last_search);
555 last_search = build_search (buffer, len);
556 }
557
558 return 1;
559 }
560
561 static void act_search (void) {
562 int len;
563 fileoffset_t posn, dfapos;
564 DFA dfa;
565 static unsigned char sblk[SEARCH_BLK];
566 static char withdef[] = "Search forward (default=last): ";
567 static char withoutdef[] = "Search forward: ";
568
569 if (!search_prompt(withdef, withoutdef))
570 return;
571
572 dfa = last_search->forward;
573 len = last_search->len;
574 dfapos = 0;
575
576 for (posn = cur_pos+1; posn < file_size; posn++) {
577 unsigned char *q;
578 int size = SEARCH_BLK;
579
580 if (size > file_size-posn)
581 size = file_size-posn;
582 buf_fetch_data (filedata, sblk, size, posn);
583 q = sblk;
584 while (size--) {
585 posn++;
586 dfapos = dfa[dfapos][*q++];
587 if (dfapos == len) {
588 fileoffset_t new_top;
589
590 cur_pos = posn - len;
591 edit_type = !!edit_type;
592 new_top = cur_pos - (scrlines-1) * width;
593 new_top = begline(new_top);
594 if (top_pos < new_top)
595 top_pos = new_top;
596 return;
597 }
598 }
599 }
600 strcpy (message, "Not found.");
601 }
602
603 static void act_search_backwards (void) {
604 int len;
605 fileoffset_t posn, dfapos;
606 DFA dfa;
607 static unsigned char sblk[SEARCH_BLK];
608 static char withdef[] = "Search backward (default=last): ";
609 static char withoutdef[] = "Search backward: ";
610
611 if (!search_prompt(withdef, withoutdef))
612 return;
613
614 dfa = last_search->reverse;
615 len = last_search->len;
616 dfapos = 0;
617
618 posn = cur_pos + len - 1;
619 if (posn >= file_size)
620 posn = file_size;
621
622 for (; posn >= 0; posn--) {
623 unsigned char *q;
624 int size = SEARCH_BLK;
625
626 if (size > posn)
627 size = posn;
628 buf_fetch_data (filedata, sblk, size, posn-size);
629 q = sblk + size;
630 while (size--) {
631 posn--;
632 dfapos = dfa[dfapos][*--q];
633 if (dfapos == len) {
634 fileoffset_t new_top;
635
636 cur_pos = posn;
637 edit_type = !!edit_type;
638 new_top = cur_pos - (scrlines-1) * width;
639 new_top = begline(new_top);
640 if (top_pos > new_top)
641 top_pos = new_top;
642 return;
643 }
644 }
645 }
646 strcpy (message, "Not found.");
647 }
648
649 static void act_recentre (void) {
650 top_pos = cur_pos - (display_rows-2)/2 * width;
651 if (top_pos < 0)
652 top_pos = 0;
653 top_pos = begline(top_pos);
654 }
655
656 static void act_width (void) {
657 char buffer[80];
658 char prompt[80];
659 fileoffset_t w;
660 fileoffset_t new_top;
661 int error;
662
663 sprintf (prompt, "Enter screen width in bytes (now %"OFF"d): ", width);
664 if (!get_str (prompt, buffer, FALSE))
665 return;
666 w = parse_num (buffer, &error);
667 if (error) {
668 display_beep();
669 strcpy (message, "Unable to parse width value");
670 return;
671 }
672 if (w > 0) {
673 width = w;
674 fix_offset();
675 new_top = cur_pos - (scrlines-1) * width;
676 new_top = begline(new_top);
677 if (top_pos < new_top)
678 top_pos = new_top;
679 }
680 }
681
682 static void act_offset (void) {
683 char buffer[80];
684 char prompt[80];
685 fileoffset_t o;
686 fileoffset_t new_top;
687 int error;
688
689 sprintf (prompt, "Enter start-of-file offset in bytes (now %"OFF"d): ",
690 realoffset);
691 if (!get_str (prompt, buffer, FALSE))
692 return;
693 o = parse_num (buffer, &error);
694 if (error) {
695 display_beep();
696 strcpy (message, "Unable to parse offset value");
697 return;
698 }
699 if (o >= 0) {
700 realoffset = o;
701 fix_offset();
702 new_top = cur_pos - (scrlines-1) * width;
703 new_top = begline(new_top);
704 if (top_pos < new_top)
705 top_pos = new_top;
706 }
707 }
708
709 #ifdef TEST_BUFFER
710 static void act_diagnostics(void)
711 {
712 extern void buffer_diagnostic(buffer *buf, char *title);
713
714 buffer_diagnostic(filedata, "filedata");
715 buffer_diagnostic(cutbuffer, "cutbuffer");
716 }
717 #endif