source: trunk/minix/commands/elvis/cmd2.c@ 21

Last change on this file since 21 was 9, checked in by Mattia Monga, 14 years ago

Minix 3.1.2a

File size: 17.1 KB
RevLine 
[9]1/* cmd2.c */
2
3/* Author:
4 * Steve Kirkendall
5 * 14407 SW Teal Blvd. #C
6 * Beaverton, OR 97005
7 * kirkenda@cs.pdx.edu
8 */
9
10
11/* This file contains some of the commands - mostly ones that change text */
12
13#include "config.h"
14#include "ctype.h"
15#include "vi.h"
16#include "regexp.h"
17#if TOS
18# include <stat.h>
19#else
20# if OSK
21# include "osk.h"
22# else
23# if AMIGA
24# include "amistat.h"
25# else
26# include <sys/stat.h>
27# endif
28# endif
29#endif
30
31
32/*ARGSUSED*/
33void cmd_substitute(frommark, tomark, cmd, bang, extra)
34 MARK frommark;
35 MARK tomark;
36 CMD cmd;
37 int bang;
38 char *extra; /* rest of the command line */
39{
40 char *line; /* a line from the file */
41 regexp *re; /* the compiled search expression */
42 char *subst; /* the substitution string */
43 char *opt; /* substitution options */
44 long l; /* a line number */
45 char *s, *d; /* used during subtitutions */
46 char *conf; /* used during confirmation */
47 long chline; /* # of lines changed */
48 long chsub; /* # of substitutions made */
49 static optp; /* boolean option: print when done? */
50 static optg; /* boolean option: substitute globally in line? */
51 static optc; /* boolean option: confirm before subst? */
52#ifndef CRUNCH
53 long oldnlines;
54#endif
55
56
57 /* for now, assume this will fail */
58 rptlines = -1L;
59
60 if (cmd == CMD_SUBAGAIN)
61 {
62#ifndef NO_MAGIC
63 if (*o_magic)
64 subst = "~";
65 else
66#endif
67 subst = "\\~";
68 re = regcomp("");
69
70 /* if visual "&", then turn off the "p" and "c" options */
71 if (bang)
72 {
73 optp = optc = FALSE;
74 }
75 }
76 else /* CMD_SUBSTITUTE */
77 {
78 /* make sure we got a search pattern */
79 if (*extra != '/' && *extra != '?')
80 {
81 msg("Usage: s/regular expression/new text/");
82 return;
83 }
84
85 /* parse & compile the search pattern */
86 subst = parseptrn(extra);
87 re = regcomp(extra + 1);
88 }
89
90 /* abort if RE error -- error message already given by regcomp() */
91 if (!re)
92 {
93 return;
94 }
95
96 if (cmd == CMD_SUBSTITUTE)
97 {
98 /* parse the substitution string & find the option string */
99 for (opt = subst; *opt && *opt != *extra; opt++)
100 {
101 if (*opt == '\\' && opt[1])
102 {
103 opt++;
104 }
105 }
106 if (*opt)
107 {
108 *opt++ = '\0';
109 }
110
111 /* analyse the option string */
112 if (!*o_edcompatible)
113 {
114 optp = optg = optc = FALSE;
115 }
116 while (*opt)
117 {
118 switch (*opt++)
119 {
120 case 'p': optp = !optp; break;
121 case 'g': optg = !optg; break;
122 case 'c': optc = !optc; break;
123 case ' ':
124 case '\t': break;
125 default:
126 msg("Subst options are p, c, and g -- not %c", opt[-1]);
127 return;
128 }
129 }
130 }
131
132 /* if "c" or "p" flag was given, and we're in visual mode, then NEWLINE */
133 if ((optc || optp) && mode == MODE_VI)
134 {
135 addch('\n');
136 exrefresh();
137 }
138
139 ChangeText
140 {
141 /* reset the change counters */
142 chline = chsub = 0L;
143
144 /* for each selected line */
145 for (l = markline(frommark); l <= markline(tomark); l++)
146 {
147 /* fetch the line */
148 line = fetchline(l);
149
150 /* if it contains the search pattern... */
151 if (regexec(re, line, TRUE))
152 {
153 /* increment the line change counter */
154 chline++;
155
156 /* initialize the pointers */
157 s = line;
158 d = tmpblk.c;
159
160 /* do once or globally ... */
161 do
162 {
163#ifndef CRUNCH
164 /* confirm, if necessary */
165 if (optc)
166 {
167 for (conf = line; conf < re->startp[0]; conf++)
168 addch(*conf);
169 standout();
170 for ( ; conf < re->endp[0]; conf++)
171 addch(*conf);
172 standend();
173 for (; *conf; conf++)
174 addch(*conf);
175 addch('\n');
176 exrefresh();
177 if (getkey(0) != 'y')
178 {
179 /* copy accross the original chars */
180 while (s < re->endp[0])
181 *d++ = *s++;
182
183 /* skip to next match on this line, if any */
184 goto Continue;
185 }
186 }
187#endif /* not CRUNCH */
188
189 /* increment the substitution change counter */
190 chsub++;
191
192 /* copy stuff from before the match */
193 while (s < re->startp[0])
194 {
195 *d++ = *s++;
196 }
197
198 /* substitute for the matched part */
199 regsub(re, subst, d);
200 s = re->endp[0];
201 d += strlen(d);
202
203Continue:
204 /* if this regexp could conceivably match
205 * a zero-length string, then require at
206 * least 1 unmatched character between
207 * matches.
208 */
209 if (re->minlen == 0)
210 {
211 if (!*s)
212 break;
213 *d++ = *s++;
214 }
215
216 } while (optg && regexec(re, s, FALSE));
217
218 /* copy stuff from after the match */
219 while (*d++ = *s++) /* yes, ASSIGNMENT! */
220 {
221 }
222
223#ifndef CRUNCH
224 /* NOTE: since the substitution text is allowed to have ^Ms which are
225 * translated into newlines, it is possible that the number of lines
226 * in the file will increase after each line has been substituted.
227 * we need to adjust for this.
228 */
229 oldnlines = nlines;
230#endif
231
232 /* replace the old version of the line with the new */
233 d[-1] = '\n';
234 d[0] = '\0';
235 change(MARK_AT_LINE(l), MARK_AT_LINE(l + 1), tmpblk.c);
236
237#ifndef CRUNCH
238 l += nlines - oldnlines;
239 tomark += MARK_AT_LINE(nlines - oldnlines);
240#endif
241
242 /* if supposed to print it, do so */
243 if (optp)
244 {
245 addstr(tmpblk.c);
246 exrefresh();
247 }
248
249 /* move the cursor to that line */
250 cursor = MARK_AT_LINE(l);
251 }
252 }
253 }
254
255 /* free the regexp */
256 free(re);
257
258 /* if done from within a ":g" command, then finish silently */
259 if (doingglobal)
260 {
261 rptlines = chline;
262 rptlabel = "changed";
263 return;
264 }
265
266 /* Reporting */
267 if (chsub == 0)
268 {
269 msg("Substitution failed");
270 }
271 else if (chline >= *o_report)
272 {
273 msg("%ld substitutions on %ld lines", chsub, chline);
274 }
275 rptlines = 0L;
276}
277
278
279
280
281/*ARGSUSED*/
282void cmd_delete(frommark, tomark, cmd, bang, extra)
283 MARK frommark;
284 MARK tomark;
285 CMD cmd;
286 int bang;
287 char *extra;
288{
289 MARK curs2; /* an altered form of the cursor */
290
291 /* choose your cut buffer */
292 if (*extra == '"')
293 {
294 extra++;
295 }
296 if (*extra)
297 {
298 cutname(*extra);
299 }
300
301 /* make sure we're talking about whole lines here */
302 frommark = frommark & ~(BLKSIZE - 1);
303 tomark = (tomark & ~(BLKSIZE - 1)) + BLKSIZE;
304
305 /* yank the lines */
306 cut(frommark, tomark);
307
308 /* if CMD_DELETE then delete the lines */
309 if (cmd != CMD_YANK)
310 {
311 curs2 = cursor;
312 ChangeText
313 {
314 /* delete the lines */
315 delete(frommark, tomark);
316 }
317 if (curs2 > tomark)
318 {
319 cursor = curs2 - tomark + frommark;
320 }
321 else if (curs2 > frommark)
322 {
323 cursor = frommark;
324 }
325 }
326}
327
328
329/*ARGSUSED*/
330void cmd_append(frommark, tomark, cmd, bang, extra)
331 MARK frommark;
332 MARK tomark;
333 CMD cmd;
334 int bang;
335 char *extra;
336{
337 long l; /* line counter */
338
339#ifndef CRUNCH
340 /* if '!' then toggle auto-indent */
341 if (bang)
342 {
343 *o_autoindent = !*o_autoindent;
344 }
345#endif
346
347 ChangeText
348 {
349 /* if we're doing a change, delete the old version */
350 if (cmd == CMD_CHANGE)
351 {
352 /* delete 'em */
353 cmd_delete(frommark, tomark, cmd, bang, extra);
354 }
355
356 /* new lines start at the frommark line, or after it */
357 l = markline(frommark);
358 if (cmd == CMD_APPEND)
359 {
360 l++;
361 }
362
363 /* get lines until no more lines, or "." line, and insert them */
364 while (vgets('\0', tmpblk.c, BLKSIZE) >= 0)
365 {
366 addch('\n');
367 if (!strcmp(tmpblk.c, "."))
368 {
369 break;
370 }
371
372 strcat(tmpblk.c, "\n");
373 add(MARK_AT_LINE(l), tmpblk.c);
374 l++;
375 }
376 }
377
378 /* on the odd chance that we're calling this from vi mode ... */
379 redraw(MARK_UNSET, FALSE);
380}
381
382
383/*ARGSUSED*/
384void cmd_put(frommark, tomark, cmd, bang, extra)
385 MARK frommark;
386 MARK tomark;
387 CMD cmd;
388 int bang;
389 char *extra;
390{
391 /* choose your cut buffer */
392 if (*extra == '"')
393 {
394 extra++;
395 }
396 if (*extra)
397 {
398 cutname(*extra);
399 }
400
401 /* paste it */
402 ChangeText
403 {
404 cursor = paste(frommark, TRUE, FALSE);
405 }
406}
407
408
409/*ARGSUSED*/
410void cmd_join(frommark, tomark, cmd, bang, extra)
411 MARK frommark;
412 MARK tomark;
413 CMD cmd;
414 int bang;
415 char *extra;
416{
417 long l;
418 char *scan;
419 int len; /* length of the new line */
420
421 /* if only one line is specified, assume the following one joins too */
422 if (markline(frommark) == nlines)
423 {
424 msg("Nothing to join with this line");
425 return;
426 }
427 if (markline(frommark) == markline(tomark))
428 {
429 tomark += BLKSIZE;
430 }
431
432 /* get the first line */
433 l = markline(frommark);
434 strcpy(tmpblk.c, fetchline(l));
435 len = strlen(tmpblk.c);
436
437 /* build the longer line */
438 while (++l <= markline(tomark))
439 {
440 /* get the next line */
441 scan = fetchline(l);
442
443 /* remove any leading whitespace */
444 while (*scan == '\t' || *scan == ' ')
445 {
446 scan++;
447 }
448
449 /* see if the line will fit */
450 if (strlen(scan) + len + 3 > BLKSIZE)
451 {
452 msg("Can't join -- the resulting line would be too long");
453 return;
454 }
455
456 /* catenate it, with a space (or two) in between */
457 if (!bang)
458 {
459 if (len >= 1)
460 {
461 if (tmpblk.c[len - 1] == '.'
462 || tmpblk.c[len - 1] == '?'
463 || tmpblk.c[len - 1] == '!')
464 {
465 tmpblk.c[len++] = ' ';
466 }
467 tmpblk.c[len++] = ' ';
468 }
469 }
470 strcpy(tmpblk.c + len, scan);
471 len += strlen(scan);
472 }
473 tmpblk.c[len++] = '\n';
474 tmpblk.c[len] = '\0';
475
476 /* make the change */
477 ChangeText
478 {
479 frommark &= ~(BLKSIZE - 1);
480 tomark &= ~(BLKSIZE - 1);
481 tomark += BLKSIZE;
482 change(frommark, tomark, tmpblk.c);
483 }
484
485 /* Reporting... */
486 rptlines = markline(tomark) - markline(frommark) - 1L;
487 rptlabel = "joined";
488}
489
490
491
492/*ARGSUSED*/
493void cmd_shift(frommark, tomark, cmd, bang, extra)
494 MARK frommark;
495 MARK tomark;
496 CMD cmd;
497 int bang;
498 char *extra;
499{
500 long l; /* line number counter */
501 int oldidx; /* number of chars previously used for indent */
502 int newidx; /* number of chars in the new indent string */
503 int oldcol; /* previous indent amount */
504 int newcol; /* new indent amount */
505 char *text; /* pointer to the old line's text */
506
507 ChangeText
508 {
509 /* for each line to shift... */
510 for (l = markline(frommark); l <= markline(tomark); l++)
511 {
512 /* get the line - ignore empty lines unless ! mode */
513 text = fetchline(l);
514 if (!*text && !bang)
515 continue;
516
517 /* calc oldidx and oldcol */
518 for (oldidx = 0, oldcol = 0;
519 text[oldidx] == ' ' || text[oldidx] == '\t';
520 oldidx++)
521 {
522 if (text[oldidx] == ' ')
523 {
524 oldcol += 1;
525 }
526 else
527 {
528 oldcol += *o_tabstop - (oldcol % *o_tabstop);
529 }
530 }
531
532 /* calc newcol */
533 if (cmd == CMD_SHIFTR)
534 {
535 newcol = oldcol + (*o_shiftwidth & 0xff);
536 }
537 else
538 {
539 newcol = oldcol - (*o_shiftwidth & 0xff);
540 if (newcol < 0)
541 newcol = 0;
542 }
543
544 /* if no change, then skip to next line */
545 if (oldcol == newcol)
546 continue;
547
548 /* build a new indent string */
549 newidx = 0;
550 if (*o_autotab)
551 {
552 while (newcol >= *o_tabstop)
553 {
554 tmpblk.c[newidx++] = '\t';
555 newcol -= *o_tabstop;
556 }
557 }
558 while (newcol > 0)
559 {
560 tmpblk.c[newidx++] = ' ';
561 newcol--;
562 }
563 tmpblk.c[newidx] = '\0';
564
565 /* change the old indent string into the new */
566 change(MARK_AT_LINE(l), MARK_AT_LINE(l) + oldidx, tmpblk.c);
567 }
568 }
569
570 /* Reporting... */
571 rptlines = markline(tomark) - markline(frommark) + 1L;
572 if (cmd == CMD_SHIFTR)
573 {
574 rptlabel = ">ed";
575 }
576 else
577 {
578 rptlabel = "<ed";
579 }
580}
581
582
583/*ARGSUSED*/
584void cmd_read(frommark, tomark, cmd, bang, extra)
585 MARK frommark;
586 MARK tomark;
587 CMD cmd;
588 int bang;
589 char *extra;
590{
591 int fd, rc; /* used while reading from the file */
592 char *scan; /* used for finding NUL characters */
593 int hadnul; /* boolean: any NULs found? */
594 int addnl; /* boolean: forced to add newlines? */
595 int len; /* number of chars in current line */
596 long lines; /* number of lines in current block */
597 struct stat statb;
598
599 /* special case: if ":r !cmd" then let the filter() function do it */
600 if (extra[0] == '!')
601 {
602 filter(frommark, MARK_UNSET, extra + 1, TRUE);
603 return;
604 }
605
606 /* open the file */
607 fd = open(extra, O_RDONLY);
608 if (fd < 0)
609 {
610 msg("Can't open \"%s\"", extra);
611 return;
612 }
613
614#ifndef CRUNCH
615 if (stat(extra, &statb) < 0)
616 {
617 msg("Can't stat \"%s\"", extra);
618 }
619# if TOS
620 if (statb.st_mode & S_IJDIR)
621# else
622# if OSK
623 if (statb.st_mode & S_IFDIR)
624# else
625 if ((statb.st_mode & S_IFMT) != S_IFREG)
626# endif
627# endif
628 {
629 msg("\"%s\" is not a regular file", extra);
630 return;
631 }
632#endif /* not CRUNCH */
633
634 /* get blocks from the file, and add them */
635 ChangeText
636 {
637 /* insertion starts at the line following frommark */
638 tomark = frommark = (frommark | (BLKSIZE - 1L)) + 1L;
639 len = 0;
640 hadnul = addnl = FALSE;
641
642 /* add an extra newline, so partial lines at the end of
643 * the file don't trip us up
644 */
645 add(tomark, "\n");
646
647 /* for each chunk of text... */
648 while ((rc = tread(fd, tmpblk.c, BLKSIZE - 1)) > 0)
649 {
650 /* count newlines, convert NULs, etc. ... */
651 for (lines = 0, scan = tmpblk.c; rc > 0; rc--, scan++)
652 {
653 /* break up long lines */
654 if (*scan != '\n' && len + 2 > BLKSIZE)
655 {
656 *scan = '\n';
657 addnl = TRUE;
658 }
659
660 /* protect against NUL chars in file */
661 if (!*scan)
662 {
663 *scan = 0x80;
664 hadnul = TRUE;
665 }
666
667 /* starting a new line? */
668 if (*scan == '\n')
669 {
670 /* reset length at newline */
671 len = 0;
672 lines++;
673 }
674 else
675 {
676 len++;
677 }
678 }
679
680 /* add the text */
681 *scan = '\0';
682 add(tomark, tmpblk.c);
683 tomark += MARK_AT_LINE(lines) + len - markidx(tomark);
684 }
685
686 /* if partial last line, then retain that first newline */
687 if (len > 0)
688 {
689 msg("Last line had no newline");
690 tomark += BLKSIZE; /* <- for the rptlines calc */
691 }
692 else /* delete that first newline */
693 {
694 delete(tomark, (tomark | (BLKSIZE - 1L)) + 1L);
695 }
696 }
697
698 /* close the file */
699 close(fd);
700
701 /* Reporting... */
702 rptlines = markline(tomark) - markline(frommark);
703 rptlabel = "read";
704 if (mode == MODE_EX)
705 {
706 cursor = (tomark & ~BLKSIZE) - BLKSIZE;
707 }
708 else
709 {
710 cursor = frommark;
711 }
712
713 if (addnl)
714 msg("Newlines were added to break up long lines");
715 if (hadnul)
716 msg("NULs were converted to 0x80");
717}
718
719
720
721/*ARGSUSED*/
722void cmd_undo(frommark, tomark, cmd, bang, extra)
723 MARK frommark;
724 MARK tomark;
725 CMD cmd;
726 int bang;
727 char *extra;
728{
729 undo();
730}
731
732
733/* print the selected lines */
734/*ARGSUSED*/
735void cmd_print(frommark, tomark, cmd, bang, extra)
736 MARK frommark;
737 MARK tomark;
738 CMD cmd;
739 int bang;
740 char *extra;
741{
742 REG char *scan;
743 REG long l;
744 REG int col;
745
746 for (l = markline(frommark); l <= markline(tomark); l++)
747 {
748 /* display a line number, if CMD_NUMBER */
749 if (cmd == CMD_NUMBER)
750 {
751 sprintf(tmpblk.c, "%6ld ", l);
752 qaddstr(tmpblk.c);
753 col = 8;
754 }
755 else
756 {
757 col = 0;
758 }
759
760 /* get the next line & display it */
761 for (scan = fetchline(l); *scan; scan++)
762 {
763 /* expand tabs to the proper width */
764 if (*scan == '\t' && cmd != CMD_LIST)
765 {
766 do
767 {
768 qaddch(' ');
769 col++;
770 } while (col % *o_tabstop != 0);
771 }
772 else if (*scan > 0 && *scan < ' ' || *scan == '\177')
773 {
774 qaddch('^');
775 qaddch(*scan ^ 0x40);
776 col += 2;
777 }
778 else if ((*scan & 0x80) && cmd == CMD_LIST)
779 {
780 sprintf(tmpblk.c, "\\%03o", UCHAR(*scan));
781 qaddstr(tmpblk.c);
782 col += 4;
783 }
784 else
785 {
786 qaddch(*scan);
787 col++;
788 }
789
790 /* wrap at the edge of the screen */
791 if (!has_AM && col >= COLS)
792 {
793 addch('\n');
794 col -= COLS;
795 }
796 }
797 if (cmd == CMD_LIST)
798 {
799 qaddch('$');
800 }
801 addch('\n');
802 exrefresh();
803 }
804}
805
806
807/* move or copy selected lines */
808/*ARGSUSED*/
809void cmd_move(frommark, tomark, cmd, bang, extra)
810 MARK frommark;
811 MARK tomark;
812 CMD cmd;
813 int bang;
814 char *extra;
815{
816 MARK destmark;
817
818 /* parse the destination linespec. No defaults. Line 0 is okay */
819 destmark = cursor;
820 if (!strcmp(extra, "0"))
821 {
822 destmark = 0L;
823 }
824 else if (linespec(extra, &destmark) == extra || !destmark)
825 {
826 msg("invalid destination address");
827 return;
828 }
829
830 /* flesh the marks out to encompass whole lines */
831 frommark &= ~(BLKSIZE - 1);
832 tomark = (tomark & ~(BLKSIZE - 1)) + BLKSIZE;
833 destmark = (destmark & ~(BLKSIZE - 1)) + BLKSIZE;
834
835 /* make sure the destination is valid */
836 if (cmd == CMD_MOVE && destmark >= frommark && destmark < tomark)
837 {
838 msg("invalid destination address");
839 }
840
841 /* Do it */
842 ChangeText
843 {
844 /* save the text to a cut buffer */
845 cutname('\0');
846 cut(frommark, tomark);
847
848 /* if we're not copying, delete the old text & adjust destmark */
849 if (cmd != CMD_COPY)
850 {
851 delete(frommark, tomark);
852 if (destmark >= frommark)
853 {
854 destmark -= (tomark - frommark);
855 }
856 }
857
858 /* add the new text */
859 paste(destmark, FALSE, FALSE);
860 }
861
862 /* move the cursor to the last line of the moved text */
863 cursor = destmark + (tomark - frommark) - BLKSIZE;
864 if (cursor < MARK_FIRST || cursor >= MARK_LAST + BLKSIZE)
865 {
866 cursor = MARK_LAST;
867 }
868
869 /* Reporting... */
870 rptlabel = ( (cmd == CMD_COPY) ? "copied" : "moved" );
871}
872
873
874
875/* execute EX commands from a file */
876/*ARGSUSED*/
877void cmd_source(frommark, tomark, cmd, bang, extra)
878 MARK frommark;
879 MARK tomark;
880 CMD cmd;
881 int bang;
882 char *extra;
883{
884 /* must have a filename */
885 if (!*extra)
886 {
887 msg("\"source\" requires a filename");
888 return;
889 }
890
891 doexrc(extra);
892}
893
894
895#ifndef NO_AT
896/*ARGSUSED*/
897void cmd_at(frommark, tomark, cmd, bang, extra)
898 MARK frommark;
899 MARK tomark;
900 CMD cmd;
901 int bang;
902 char *extra;
903{
904 static nest = FALSE;
905 int result;
906 char buf[MAXRCLEN];
907
908 /* don't allow nested macros */
909 if (nest)
910 {
911 msg("@ macros can't be nested");
912 return;
913 }
914 nest = TRUE;
915
916 /* require a buffer name */
917 if (*extra == '"')
918 extra++;
919 if (!*extra || !isascii(*extra) ||!islower(*extra))
920 {
921 msg("@ requires a cut buffer name (a-z)");
922 }
923
924 /* get the contents of the buffer */
925 result = cb2str(*extra, buf, (unsigned)(sizeof buf));
926 if (result <= 0)
927 {
928 msg("buffer \"%c is empty", *extra);
929 }
930 else if (result >= sizeof buf)
931 {
932 msg("buffer \"%c is too large to execute", *extra);
933 }
934 else
935 {
936 /* execute the contents of the buffer as ex commands */
937 exstring(buf, result, '\\');
938 }
939
940 nest = FALSE;
941}
942#endif
Note: See TracBrowser for help on using the repository browser.