1 /* cmd2.c */
2
3 /* Author:
4 * Steve Kirkendall
5 * 16820 SW Tallac Way
6 * Beaverton, OR 97006
7 * kirkenda@jove.cs.pdx.edu, or ...uunet!tektronix!psueea!jove!kirkenda
8 */
9
10
11 /* This file contains some of the commands - mostly ones that change text */
12
13 #include "config.h"
14 #include "vi.h"
15 #include "regexp.h"
16
17
18 /*ARGSUSED*/
cmd_substitute(frommark,tomark,cmd,bang,extra)19 void cmd_substitute(frommark, tomark, cmd, bang, extra)
20 MARK frommark;
21 MARK tomark;
22 CMD cmd;
23 int bang;
24 char *extra; /* rest of the command line */
25 {
26 char *line; /* a line from the file */
27 regexp *re; /* the compiled search expression */
28 char *subst; /* the substitution string */
29 char *opt; /* substitution options */
30 int optp; /* boolean option: print when done? */
31 int optg; /* boolean option: substitute globally in line? */
32 long l; /* a line number */
33 char *s, *d; /* used during subtitutions */
34 long chline; /* # of lines changed */
35 long chsub; /* # of substitutions made */
36
37
38 /* make sure we got a search pattern */
39 if (*extra != '/' && *extra != '?')
40 {
41 msg("Usage: s/regular expression/new text/");
42 return;
43 }
44
45 /* parse & compile the search pattern */
46 subst = parseptrn(extra);
47 re = regcomp(extra + 1);
48 if (!re)
49 {
50 return;
51 }
52
53 /* parse the substitution string & find the option string */
54 for (opt = subst; *opt && (*opt != *extra || opt[-1] == '\\'); opt++)
55 {
56 }
57 if (*opt)
58 {
59 *opt++ = '\0';
60 }
61
62 /* analyse the option string */
63 optp = optg = 0;
64 while (*opt)
65 {
66 switch (*opt++)
67 {
68 case 'p': optp = 1; break;
69 case 'g': optg = 1; break;
70 case ' ':
71 case '\t': break;
72 default:
73 msg("Subst options are p and g -- not %c", opt[-1]);
74 return;
75 }
76 }
77
78 ChangeText
79 {
80 /* reset the change counters */
81 chline = chsub = 0L;
82
83 /* for each selected line */
84 for (l = markline(frommark); l <= markline(tomark); l++)
85 {
86 /* fetch the line */
87 line = fetchline(l);
88
89 /* if it contains the search pattern... */
90 if (regexec(re, line, TRUE))
91 {
92 /* increment the line change counter */
93 chline++;
94
95 /* initialize the pointers */
96 s = line;
97 d = tmpblk.c;
98
99 /* do once or globally ... */
100 do
101 {
102 /* increment the substitution change counter */
103 chsub++;
104
105 /* this may be the first line to redraw */
106 redrawrange(l, l + 1L, l + 1L);
107
108 /* copy stuff from before the match */
109 while (s < re->startp[0])
110 {
111 *d++ = *s++;
112 }
113
114 /* subtitute for the matched part */
115 regsub(re, subst, d);
116 s = re->endp[0];
117 d += strlen(d);
118
119 } while (optg && regexec(re, s, FALSE));
120
121 /* copy stuff from after the match */
122 while (*d++ = *s++) /* yes, ASSIGNMENT! */
123 {
124 }
125
126 /* replace the old version of the line with the new */
127 changeline(l, tmpblk.c);
128
129 /* if supposed to print it, do so */
130 if (optp)
131 {
132 addstr(tmpblk.c);
133 addch('\n');
134 exrefresh();
135 }
136 }
137 }
138 }
139
140 /* report what happened */
141 if (chsub == 0)
142 {
143 msg("Substitution failed");
144 }
145
146 /* tweak for redrawing */
147 if (chline > 1 || redrawafter && redrawafter != markline(cursor))
148 {
149 mustredraw = TRUE;
150 }
151
152 /* free the regexp */
153 free(re);
154
155 /* Reporting */
156 if (chline >= *o_report)
157 {
158 msg("%ld substitutions on %ld lines", chsub, chline);
159 }
160 rptlines = 0;
161 }
162
163
164
165
166 /*ARGSUSED*/
cmd_delete(frommark,tomark,cmd,bang,extra)167 void cmd_delete(frommark, tomark, cmd, bang, extra)
168 MARK frommark;
169 MARK tomark;
170 CMD cmd;
171 int bang;
172 char *extra;
173 {
174 MARK curs2; /* al altered form of the cursor */
175
176 /* choose your cut buffer */
177 if (*extra == '"')
178 {
179 extra++;
180 }
181 if (*extra)
182 {
183 cutname(*extra);
184 }
185
186 /* make sure we're talking about whole lines here */
187 frommark = frommark & ~(BLKSIZE - 1);
188 tomark = (tomark & ~(BLKSIZE - 1)) + BLKSIZE;
189
190 /* yank the lines */
191 cut(frommark, tomark);
192
193 /* if CMD_DELETE then delete the lines */
194 if (cmd != CMD_YANK)
195 {
196 curs2 = cursor;
197 ChangeText
198 {
199 /* delete the lines */
200 delete(frommark, tomark);
201 }
202 if (curs2 > tomark)
203 {
204 cursor = curs2 - tomark + frommark;
205 }
206 else if (curs2 > frommark)
207 {
208 cursor = frommark;
209 }
210 }
211 }
212
213
214 /*ARGSUSED*/
cmd_append(frommark,tomark,cmd,bang,extra)215 void cmd_append(frommark, tomark, cmd, bang, extra)
216 MARK frommark;
217 MARK tomark;
218 CMD cmd;
219 int bang;
220 char *extra;
221 {
222 long l; /* line counter */
223
224 ChangeText
225 {
226 /* if we're doing a change, delete the old version */
227 if (cmd == CMD_CHANGE)
228 {
229 /* delete 'em */
230 cmd_delete(frommark, tomark, cmd, bang, extra);
231 }
232
233 /* new lines start at the frommark line, or after it */
234 l = markline(frommark);
235 if (cmd == CMD_APPEND)
236 {
237 l++;
238 }
239
240 /* get lines until no more lines, or "." line, and insert them */
241 while (vgets('\0', tmpblk.c, BLKSIZE) >= 0)
242 {
243 addch('\n');
244 if (!strcmp(tmpblk.c, "."))
245 {
246 break;
247 }
248
249 addline(l, tmpblk.c);
250 l++;
251 }
252 }
253
254 /* on the odd chance that we're calling this from vi mode ... */
255 redraw(MARK_UNSET, FALSE);
256 }
257
258
259 /*ARGSUSED*/
cmd_put(frommark,tomark,cmd,bang,extra)260 void cmd_put(frommark, tomark, cmd, bang, extra)
261 MARK frommark;
262 MARK tomark;
263 CMD cmd;
264 int bang;
265 char *extra;
266 {
267 /* choose your cut buffer */
268 if (*extra == '"')
269 {
270 extra++;
271 }
272 if (*extra)
273 {
274 cutname(*extra);
275 }
276
277 /* paste it */
278 ChangeText
279 {
280 cursor = paste(frommark, !bang, FALSE);
281 }
282 }
283
284
285 /*ARGSUSED*/
cmd_join(frommark,tomark,cmd,bang,extra)286 void cmd_join(frommark, tomark, cmd, bang, extra)
287 MARK frommark;
288 MARK tomark;
289 CMD cmd;
290 int bang;
291 char *extra;
292 {
293 long l;
294 char *scan;
295 int len; /* length of the new line */
296
297 /* if only one line is specified, assume the following one joins too */
298 if (markline(frommark) == nlines)
299 {
300 msg("Nothing to join with this line");
301 return;
302 }
303 if (markline(frommark) == markline(tomark))
304 {
305 tomark = m_down(tomark, 1L);
306 }
307
308 /* get the first line */
309 l = markline(frommark);
310 strcpy(tmpblk.c, fetchline(l++));
311 len = strlen(tmpblk.c);
312
313 /* build the longer line */
314 while (l <= markline(tomark))
315 {
316 /* get the next line */
317 scan = fetchline(l++);
318
319 /* remove any leading whitespace */
320 while (*scan == '\t' || *scan == ' ')
321 {
322 scan++;
323 }
324
325 /* see if the line will fit */
326 if (strlen(scan) + len + 1 > BLKSIZE)
327 {
328 msg("Can't join -- the resulting line would be too long");
329 return;
330 }
331
332 /* catenate it, with a space in between */
333 tmpblk.c[len++] = ' ';
334 strcpy(tmpblk.c + len, scan);
335 len += strlen(scan);
336 }
337
338 /* make the change */
339 ChangeText
340 {
341 frommark &= ~(BLKSIZE - 1);
342 tomark &= ~(BLKSIZE - 1);
343 tomark += BLKSIZE;
344 delete(frommark, tomark);
345 addline(markline(frommark), tmpblk.c);
346 }
347 }
348
349
350
351 /*ARGSUSED*/
cmd_shift(frommark,tomark,cmd,bang,extra)352 void cmd_shift(frommark, tomark, cmd, bang, extra)
353 MARK frommark;
354 MARK tomark;
355 CMD cmd;
356 int bang;
357 char *extra;
358 {
359 long l; /* line number counter */
360 int oldidx; /* number of chars previously used for indent */
361 int newidx; /* number of chars in the new indent string */
362 int oldcol; /* previous indent amount */
363 int newcol; /* new indent amount */
364 char *text; /* pointer to the old line's text */
365
366 /* figure out how much of the screen we must redraw (for vi mode) */
367 if (markline(frommark) != markline(tomark))
368 {
369 mustredraw = TRUE;
370 redrawrange(markline(frommark), markline(tomark) + 1L, markline(tomark) + 1L);
371 }
372
373 ChangeText
374 {
375 /* for each line to shift... */
376 for (l = markline(frommark); l <= markline(tomark); l++)
377 {
378 /* get the line - ignore empty lines unless ! mode */
379 text = fetchline(l);
380 if (!*text && !bang)
381 continue;
382
383 /* calc oldidx and oldcol */
384 for (oldidx = 0, oldcol = 0;
385 text[oldidx] == ' ' || text[oldidx] == '\t';
386 oldidx++)
387 {
388 if (text[oldidx] == ' ')
389 {
390 oldcol += 1;
391 }
392 else
393 {
394 oldcol += *o_tabstop - (oldcol % *o_tabstop);
395 }
396 }
397
398 /* calc newcol */
399 if (cmd == CMD_SHIFTR)
400 {
401 newcol = oldcol + (*o_shiftwidth & 0xff);
402 }
403 else
404 {
405 newcol = oldcol - (*o_shiftwidth & 0xff);
406 if (newcol < 0)
407 newcol = 0;
408 }
409
410 /* if no change, then skip to next line */
411 if (oldcol == newcol)
412 continue;
413
414 /* build a new indent string */
415 newidx = 0;
416 while (newcol >= *o_tabstop)
417 {
418 tmpblk.c[newidx++] = '\t';
419 newcol -= *o_tabstop;
420 }
421 while (newcol > 0)
422 {
423 tmpblk.c[newidx++] = ' ';
424 newcol--;
425 }
426 tmpblk.c[newidx] = '\0';
427
428 /* change the old indent string into the new */
429 change(MARK_AT_LINE(l), MARK_AT_LINE(l) + oldidx, tmpblk.c);
430 }
431 }
432
433 /* Reporting... */
434 rptlines = markline(tomark) - markline(frommark) + 1L;
435 if (cmd == CMD_SHIFTR)
436 {
437 rptlabel = ">ed";
438 }
439 else
440 {
441 rptlabel = "<ed";
442 }
443 }
444
445
446 /*ARGSUSED*/
cmd_read(frommark,tomark,cmd,bang,extra)447 void cmd_read(frommark, tomark, cmd, bang, extra)
448 MARK frommark;
449 MARK tomark;
450 CMD cmd;
451 int bang;
452 char *extra;
453 {
454 long l; /* line number counter - where new lines go */
455 int fd, rc; /* used while reading from the file */
456 char *scan; /* used for finding newlines */
457 char *line; /* points to the start of a line */
458 int prevrc; /* used to detect abnormal EOF */
459
460 /* special case: if ":r !cmd" then let the filter() function do it */
461 if (bang || extra[0] == '!')
462 {
463 if (extra[0] == '!')
464 {
465 extra++;
466 }
467 frommark = (frommark & ~(BLKSIZE - 1)) + BLKSIZE;
468 filter(frommark, MARK_UNSET, extra);
469 return;
470 }
471
472 /* first line goes after the selected line */
473 l = markline(frommark) + 1;
474
475 /* open the file */
476 fd = open(extra, O_RDONLY);
477 if (fd < 0)
478 {
479 msg("Can't open \"%s\"", extra);
480 return;
481 }
482
483 /* get blocks from the file, and add each line in the block */
484 ChangeText
485 {
486 /* NOTE! lint worries needlessly about the order of evaluation
487 * of the 'rc' expressions in the test clause of this for(;;){}
488 */
489 for (prevrc = rc = 0;
490 (rc = rc + tread(fd, tmpblk.c + rc, BLKSIZE - rc)) > 0;
491 prevrc = rc)
492 {
493 /* if we couldn't read anything, we damn well better have \n */
494 if (prevrc == rc)
495 {
496 if (rc == BLKSIZE)
497 {
498 rc--;
499 }
500 if (tmpblk.c[rc - 1] != '\n' || rc <= 0)
501 {
502 tmpblk.c[rc++] = '\n';
503 }
504 }
505
506 /* for each complete line in this block, add it */
507 for (line = scan = tmpblk.c; rc > 0; rc--, scan++)
508 {
509 if (*scan == '\n')
510 {
511 *scan = '\0';
512 addline(l, line);
513 l++;
514 line = scan + 1;
515 }
516 else if (!*scan)
517 {
518 /* protect against NUL chars in file */
519 *scan = 0x7f;
520 }
521 }
522
523 /* any extra chars are shifted to the start of the buffer */
524 rc = scan - line;
525 for (scan = tmpblk.c; scan < tmpblk.c + rc; )
526 {
527 *scan++ = *line++;
528 }
529 }
530 }
531
532 /* close the file */
533 close(fd);
534
535 /* Reporting... */
536 rptlines = l - markline(frommark) - 1L;
537 rptlabel = "read";
538 }
539
540
541 /*ARGSUSED*/
cmd_list(frommark,tomark,cmd,bang,extra)542 void cmd_list(frommark, tomark, cmd, bang, extra)
543 MARK frommark;
544 MARK tomark;
545 CMD cmd;
546 int bang;
547 char *extra;
548 {
549 long l; /* line number counter */
550 register char *scan; /* used for moving through the line */
551
552 for (l = markline(frommark); l <= markline(tomark); l++)
553 {
554 /* list the line */
555 scan = fetchline(l);
556
557 while (*scan)
558 {
559 /* if the char is non-printable, write it as \000 */
560 if (*scan < ' ' || *scan > '~')
561 {
562 /* build the \000 form & write it */
563 addch('\\');
564 addch('0' + ((*scan >> 6) & 3));
565 addch('0' + ((*scan >> 3) & 7));
566 addch('0' + (*scan & 7));
567 }
568 else
569 {
570 addch(*scan);
571 }
572 scan++;
573 }
574
575 /* write a $ and a \n */
576 addstr("$\n");
577 exrefresh();
578 }
579 }
580
581
582 /*ARGSUSED*/
cmd_undo(frommark,tomark,cmd,bang,extra)583 void cmd_undo(frommark, tomark, cmd, bang, extra)
584 MARK frommark;
585 MARK tomark;
586 CMD cmd;
587 int bang;
588 char *extra;
589 {
590 undo();
591 }
592
593
594 /* print the selected lines */
595 /*ARGSUSED*/
cmd_print(frommark,tomark,cmd,bang,extra)596 void cmd_print(frommark, tomark, cmd, bang, extra)
597 MARK frommark;
598 MARK tomark;
599 CMD cmd;
600 int bang;
601 char *extra;
602 {
603 register char *scan;
604 register long l;
605 register int col;
606
607 for (l = markline(frommark); l <= markline(tomark); l++)
608 {
609 /* get the next line & display it */
610 for (col = 0, scan = fetchline(l); *scan; scan++)
611 {
612 /* expand tabs to the proper width */
613 if (*scan == '\t')
614 {
615 do
616 {
617 qaddch(' ');
618 col++;
619 } while (col % *o_tabstop != 0);
620 }
621 else
622 {
623 qaddch(*scan);
624 }
625
626 /* wrap at the edge of the screen */
627 if (!has_AM && col >= COLS)
628 {
629 addch('\n');
630 col = 0;
631 }
632 }
633 addch('\n');
634 exrefresh();
635 }
636 }
637
638
639 /* move or copy selected lines */
640 /*ARGSUSED*/
cmd_move(frommark,tomark,cmd,bang,extra)641 void cmd_move(frommark, tomark, cmd, bang, extra)
642 MARK frommark;
643 MARK tomark;
644 CMD cmd;
645 int bang;
646 char *extra;
647 {
648 MARK destmark;
649
650 /* parse the destination linespec. No defaults. Line 0 is okay */
651 destmark = cursor;
652 if (!strcmp(extra, "0"))
653 {
654 destmark = 0L;
655 }
656 else if (linespec(extra, &destmark) == extra || !destmark)
657 {
658 msg("invalid destination address");
659 return;
660 }
661
662 /* flesh the marks out to encompass whole lines */
663 frommark &= ~(BLKSIZE - 1);
664 tomark = (tomark & ~(BLKSIZE - 1)) + BLKSIZE;
665 destmark = (destmark & ~(BLKSIZE - 1)) + BLKSIZE;
666
667 /* make sure the destination is valid */
668 if (cmd == CMD_MOVE && destmark >= frommark && destmark < tomark)
669 {
670 msg("invalid destination address");
671 }
672
673 /* Do it */
674 ChangeText
675 {
676 /* save the text to a cut buffer */
677 cutname('\0');
678 cut(frommark, tomark);
679
680 /* if we're not copying, delete the old text & adjust destmark */
681 if (cmd != CMD_COPY)
682 {
683 delete(frommark, tomark);
684 if (destmark >= frommark)
685 {
686 destmark -= (tomark - frommark);
687 }
688 }
689
690 /* add the new text */
691 paste(destmark, FALSE, FALSE);
692 }
693
694 /* move the cursor to the last line of the moved text */
695 cursor = destmark + (tomark - frommark) - BLKSIZE;
696 if (cursor < MARK_FIRST || cursor >= MARK_LAST + BLKSIZE)
697 {
698 cursor = MARK_LAST;
699 }
700
701 /* Reporting... */
702 rptlabel = ( (cmd == CMD_COPY) ? "copied" : "moved" );
703 }
704
705
706
707 /* execute EX commands from a file */
708 /*ARGSUSED*/
cmd_source(frommark,tomark,cmd,bang,extra)709 void cmd_source(frommark, tomark, cmd, bang, extra)
710 MARK frommark;
711 MARK tomark;
712 CMD cmd;
713 int bang;
714 char *extra;
715 {
716 /* must have a filename */
717 if (!*extra)
718 {
719 msg("\"source\" requires a filename");
720 return;
721 }
722
723 doexrc(extra);
724 }
725