ArchWizard

DGD/

source navigation ]
diff markup ]
identifier search ]
file search ]
Version: [ 1.0.a0 ] [ 1.1 ] [ 1.2 ] [ 1.2p1 ] [ 1.2p2 ] [ 1.2p3 ] [ 1.2p4 ] [ 1.2.151 ]

  1 # define INCLUDE_CTYPE
  2 # include "ed.h"
  3 # include "edcmd.h"
  4 # include "fileio.h"
  5 
  6 /*
  7  * This file defines the command subroutines for edcmd.c
  8  */
  9 
 10 extern char *skipst             P((char*));
 11 extern char *pattern            P((char*, int, char*));
 12 extern void  cb_count           P((cmdbuf*));
 13 extern void  not_in_global      P((cmdbuf*));
 14 extern void  cb_do              P((cmdbuf*, Int));
 15 extern void  cb_buf             P((cmdbuf*, block));
 16 extern void  add                P((cmdbuf*, Int, block, Int));
 17 extern block delete             P((cmdbuf*, Int, Int));
 18 extern void  change             P((cmdbuf*, Int, Int, block));
 19 extern void  startblock         P((cmdbuf*));
 20 extern void  addblock           P((cmdbuf*, char*));
 21 extern void  endblock           P((cmdbuf*));
 22 
 23 
 24 /*
 25  * NAME:        find()
 26  * DESCRIPTION: scan a line for a pattern. If the pattern is found, longjump
 27  *              out.
 28  */
 29 static void find(ptr, text)
 30 char *ptr, *text;
 31 {
 32     register cmdbuf *cb;
 33 
 34     cb = (cmdbuf *) ptr;
 35     if (rx_exec(cb->regexp, text, 0, cb->ignorecase) > 0) {
 36         longjmp(cb->env, TRUE);
 37     }
 38     cb->lineno++;
 39 }
 40 
 41 /*
 42  * NAME:        cmdbuf->search()
 43  * DESCRIPTION: search a range of lines for the occurance of a pattern. When
 44  *              found, jump out immediately.
 45  */
 46 Int cb_search(cb, first, last, reverse)
 47 cmdbuf *cb;
 48 Int first, last;
 49 int reverse;
 50 {
 51     if (setjmp(cb->env)) {
 52         /* found */
 53         return (reverse) ? last - cb->lineno : first + cb->lineno;
 54     }
 55 
 56     cb->lineno = 0;
 57     cb->ignorecase = IGNORECASE(cb->vars);
 58     eb_range(cb->edbuf, first, last, find, (char *) cb, reverse);
 59     /* not found */
 60     return 0;
 61 }
 62 
 63 
 64 /*
 65  * NAME:        println()
 66  * DESCRIPTION: output a line of text. The format is decided by flags.
 67  *              Non-ascii characters (eight bit set) have no special processing.
 68  */
 69 static void println(ptr, text)
 70 char *ptr;
 71 register char *text;
 72 {
 73     char buffer[2 * MAX_LINE_SIZE + 14];        /* all ^x + number + list */
 74     register cmdbuf *cb;
 75     register char *p;
 76 
 77     cb = (cmdbuf *) ptr;
 78 
 79     if (cb->flags & CB_NUMBER) {
 80         sprintf(buffer, "%6ld  ", (long) cb->lineno++);
 81         p = buffer + 8;
 82     } else {
 83         p = buffer;
 84     }
 85 
 86     while (*text != '\0') {
 87         if (UCHAR(*text) < ' ') {
 88             /* control character */
 89             if (*text == HT && !(cb->flags & CB_LIST)) {
 90                 *p++ = HT;
 91             } else {
 92                 *p++ = '^'; *p++ = (*text & 0x1f) + '@';
 93             }
 94         } else if (*text == 0x7f) {
 95             /* DEL */
 96             *p++ = '^'; *p++ = '?';
 97         } else {
 98             /* normal character */
 99             *p++ = *text;
100         }
101         text++;
102     }
103     if (cb->flags & CB_LIST) {
104         *p++ = '$';
105     }
106     *p = '\0';
107     output("%s\012", buffer);   /* LF */
108 }
109 
110 /*
111  * NAME:        cmdbuf->print()
112  * DESCRIPTION: print a range of lines, according to the format specified in
113  *              the flags. Afterwards, the current line is set to the last line
114  *              printed.
115  */
116 int cb_print(cb)
117 register cmdbuf *cb;
118 {
119     register char *p;
120 
121     /* handle flags right now */
122     p = cb->cmd;
123     for (;;) {
124         switch (*p++) {
125         case '-':
126         case '+':
127         case 'p':
128             /* ignore */
129             continue;
130 
131         case 'l':
132             cb->flags |= CB_LIST;
133             continue;
134 
135         case '#':
136             cb->flags |= CB_NUMBER;
137             continue;
138         }
139         cb->cmd = --p;
140         break;
141     }
142 
143     cb->lineno = cb->first;
144     eb_range(cb->edbuf, cb->first, cb->last, println, (char *) cb, FALSE);
145     cb->this = cb->last;
146     return 0;
147 }
148 
149 /*
150  * NAME:        cmdbuf->list()
151  * DESCRIPTION: output a range of lines in a hopefully unambiguous format
152  */
153 int cb_list(cb)
154 cmdbuf *cb;
155 {
156     cb->flags |= CB_LIST;
157     return cb_print(cb);
158 }
159 
160 /*
161  * NAME:        cmdbuf->number()
162  * DESCRIPTION: output a range of lines preceded by line numbers
163  */
164 int cb_number(cb)
165 cmdbuf *cb;
166 {
167     cb->flags |= CB_NUMBER;
168     return cb_print(cb);
169 }
170 
171 /*
172  * NAME:        cmdbuf->page()
173  * DESCRIPTION: show a page of lines
174  */
175 int cb_page(cb)
176 register cmdbuf *cb;
177 {
178     register Int offset, window;
179 
180     if (cb->edbuf->lines == 0) {
181         error("No lines in buffer");
182     }
183 
184     window = WINDOW(cb->vars);
185     switch (*(cb->cmd)++) {
186     default:    /* next line */
187         cb->cmd--;
188         cb->this++;
189     case '+':   /* top */
190         offset = 0;
191         break;
192 
193     case '-':   /* bottom */
194         offset = 1 - window;
195         break;
196 
197     case '.':   /* middle */
198         offset = 1 - (window + 1) / 2;
199         break;
200     }
201 
202     /* set first */
203     if (cb->first < 0) {
204         cb->first = cb->this;
205     }
206     cb->first += offset;
207     if (cb->first <= 0) {
208         cb->first = 1;
209     } else if (cb->first > cb->edbuf->lines) {
210         cb->first = cb->edbuf->lines;
211     }
212 
213     /* set last */
214     cb->last = cb->first + window - 1;
215     if (cb->last < cb->first) {
216         cb->last = cb->first;
217     } else if (cb->last > cb->edbuf->lines) {
218         cb->last = cb->edbuf->lines;
219     }
220 
221     return cb_print(cb);
222 }
223 
224 /*
225  * NAME:        cmdbuf->assign()
226  * DESCRIPTION: show the specified line number
227  */
228 int cb_assign(cb)
229 register cmdbuf *cb;
230 {
231     output("%ld\012",
232            (long) (cb->first < 0) ? cb->edbuf->lines : cb->first);      /* LF */
233     return 0;
234 }
235 
236 
237 /*
238  * NAME:        cmdbuf->mark()
239  * DESCRIPTION: set a mark in the range [a-z] to line number
240  */
241 int cb_mark(cb)
242 register cmdbuf *cb;
243 {
244     if (!islower(cb->cmd[0])) {
245         error("Mark must specify a letter");
246     }
247     cb->mark[*(cb->cmd)++ - 'a'] = cb->first;
248     return 0;
249 }
250 
251 
252 /*
253  * NAME:        cmdbuf->append()
254  * DESCRIPTION: append a block of lines, read from user, to edit buffer
255  */
256 int cb_append(cb)
257 register cmdbuf *cb;
258 {
259     not_in_global(cb);
260     cb_do(cb, cb->first);
261 
262     startblock(cb);
263     cb->flags |= CB_INSERT;
264     return 0;
265 }
266 
267 /*
268  * NAME:        cmdbuf->insert()
269  * DESCRIPTION: insert a block of lines in the edit buffer
270  */
271 int cb_insert(cb)
272 cmdbuf *cb;
273 {
274     not_in_global(cb);
275     if (cb->first > 0) {
276         cb->first--;
277     }
278     return cb_append(cb);
279 }
280 
281 /*
282  * NAME:        cmdbuf->change()
283  * DESCRIPTION: change a subrange of lines in the edit buffer
284  */
285 int cb_change(cb)
286 cmdbuf *cb;
287 {
288     register Int *m;
289 
290     not_in_global(cb);
291     cb_do(cb, cb->first);
292 
293     /* erase marks of changed lines */
294     for (m = cb->mark; m < &cb->mark[26]; m++) {
295         if (*m >= cb->first && *m <= cb->last) {
296             *m = 0;
297         }
298     }
299 
300     startblock(cb);
301     cb->flags |= CB_INSERT | CB_CHANGE;
302     return 0;
303 }
304 
305 
306 /*
307  * NAME:        cmdbuf->delete()
308  * DESCRIPTION: delete a subrange of lines in the edit buffer
309  */
310 int cb_delete(cb)
311 register cmdbuf *cb;
312 {
313     cb_do(cb, cb->first);
314 
315     cb_buf(cb, delete(cb, cb->first, cb->last));
316 
317     cb->edit++;
318 
319     return RET_FLAGS;
320 }
321 
322 /*
323  * NAME:        cmdbuf->copy()
324  * DESCRIPTION: copy a subrange of lines in the edit buffer
325  */
326 int cb_copy(cb)
327 register cmdbuf *cb;
328 {
329     cb_do(cb, cb->a_addr);
330     add(cb, cb->a_addr, eb_yank(cb->edbuf, cb->first, cb->last),
331       cb->last - cb->first + 1);
332 
333     cb->edit++;
334 
335     return RET_FLAGS;
336 }
337 
338 /*
339  * NAME:        cmdbuf->move()
340  * DESCRIPTION: move a subrange of lines in the edit buffer
341  */
342 int cb_move(cb)
343 register cmdbuf *cb;
344 {
345     Int mark[26];
346     register Int offset, *m1, *m2;
347 
348     if (cb->a_addr >= cb->first - 1 && cb->a_addr <= cb->last) {
349         error("Move to moved line");
350     }
351 
352     cb_do(cb, cb->first);
353     memset(mark, '\0', sizeof(mark));
354     if (cb->a_addr < cb->last) {
355         offset = cb->a_addr + 1 - cb->first;
356     } else {
357         offset = cb->a_addr - cb->last;
358         cb->a_addr -= cb->last - cb->first + 1;
359     }
360     /* make a copy of the marks of the lines to move */
361     for (m1 = mark, m2 = cb->mark; m1 < &mark[26]; m1++, m2++) {
362         if (*m2 >= cb->first && *m2 <= cb->last) {
363             *m1 = *m2;
364         } else {
365             *m1 = 0;
366         }
367     }
368     add(cb, cb->a_addr, delete(cb, cb->first, cb->last),
369       cb->last - cb->first + 1);
370     /* copy back adjusted marks of moved lines */
371     for (m1 = mark, m2 = cb->mark; m1 < &mark[26]; m1++, m2++) {
372         if (*m1 != 0) {
373             *m2 = *m1 + offset;
374         }
375     }
376 
377     cb->edit++;
378 
379     return RET_FLAGS;
380 }
381 
382 /*
383  * NAME:        cmdbuf->put()
384  * DESCRIPTION: put a block in the edit buffer
385  */
386 int cb_put(cb)
387 register cmdbuf *cb;
388 {
389     register block b;
390 
391     if (isalpha(cb->a_buffer)) {
392         /* 'a' and 'A' both refer to buffer 'a' */
393         b = cb->zbuf[tolower(cb->a_buffer) - 'a'];
394     } else {
395         b = cb->buf;
396     }
397     if (b == (block) 0) {
398         error("Nothing in buffer");
399     }
400 
401     cb_do(cb, cb->first);
402     add(cb, cb->first, b, bk_size(cb->edbuf->lb, b));
403 
404     cb->edit++;
405 
406     return RET_FLAGS;
407 }
408 
409 /*
410  * NAME:        cmdbuf->yank()
411  * DESCRIPTION: yank a block of lines from the edit buffer
412  */
413 int cb_yank(cb)
414 register cmdbuf *cb;
415 {
416     cb_buf(cb, eb_yank(cb->edbuf, cb->first, cb->last));
417     return 0;
418 }
419 
420 
421 /*
422  * NAME:        shift()
423  * DESCRIPTION: shift a line left or right
424  */
425 static void shift(ptr, text)
426 char *ptr;
427 register char *text;
428 {
429     register cmdbuf *cb;
430     register int idx;
431 
432     cb = (cmdbuf *) ptr;
433 
434     /* first determine the number of leading spaces */
435     idx = 0;
436     while (*text == ' ' || *text == HT) {
437         if (*text++ == ' ') {
438             idx++;
439         } else {
440             idx = (idx + 8) & ~7;
441         }
442     }
443 
444     if (*text == '\0') {
445         /* don't shift lines with ws only */
446         addblock(cb, text);
447         cb->lineno++;
448     } else {
449         idx += cb->shift;
450         if (idx < MAX_LINE_SIZE) {
451             char buffer[MAX_LINE_SIZE];
452             register char *p;
453 
454             p = buffer;
455             /* fill with leading ws */
456             while (idx >= 8) {
457                 *p++ = HT;
458                 idx -= 8;
459             }
460             while (idx > 0) {
461                 *p++ = ' ';
462                 --idx;
463             }
464             if (p - buffer + strlen(text) < MAX_LINE_SIZE) {
465                 strcpy(p, text);
466                 addblock(cb, buffer);
467                 cb->lineno++;
468                 return;
469             }
470         }
471 
472         /* Error: line too long. Finish block of lines already shifted. */
473         cb->last = cb->lineno;
474         endblock(cb);
475         error("Result of shift would be too long");
476     }
477 }
478 
479 /*
480  * NAME:        cmdbuf->shift()
481  * DESCRIPTION: shift a range of lines left or right
482  */
483 static int cb_shift(cb)
484 register cmdbuf *cb;
485 {
486     cb_do(cb, cb->first);
487     startblock(cb);
488     cb->lineno = cb->first - 1;
489     cb->flags |= CB_CHANGE;
490     eb_range(cb->edbuf, cb->first, cb->last, shift, (char *) cb, FALSE);
491     endblock(cb);
492 
493     return RET_FLAGS;
494 }
495 
496 /*
497  * NAME:        cmdbuf->lshift()
498  * DESCRIPTION: shift a range of lines to the left
499  */
500 int cb_lshift(cb)
501 register cmdbuf *cb;
502 {
503     cb->shift = -SHIFTWIDTH(cb->vars);
504     return cb_shift(cb);
505 }
506 
507 /*
508  * NAME:        cmdbuf->rshift()
509  * DESCRIPTION: shift a range of lines to the right
510  */
511 int cb_rshift(cb)
512 register cmdbuf *cb;
513 {
514     cb->shift = SHIFTWIDTH(cb->vars);
515     return cb_shift(cb);
516 }
517 
518 
519 # define STACKSZ        1024    /* size of indent stack */
520 
521 /* token definitions in indent */
522 # define SEMICOLON      0
523 # define LBRACKET       1
524 # define RBRACKET       2
525 # define LOPERATOR      3
526 # define ROPERATOR      4
527 # define LHOOK          5
528 # define RHOOK          6
529 # define TOKEN          7
530 # define ELSE           8
531 # define IF             9
532 # define FOR            10      /* WHILE, RLIMIT */
533 # define DO             11
534 # define EOT            12
535 
536 /*
537  * NAME:        noshift()
538  * DESCRIPTION: add this line to the current block without shifting it
539  */
540 static void noshift(cb, text)
541 cmdbuf *cb;
542 char *text;
543 {
544     addblock(cb, text);
545     cb->lineno++;
546 }
547 
548 /*
549  * NAME:        indent()
550  * DESCRIPTION: Parse and indent a line of text. This isn't perfect, as
551  *              keywords could be defined as macros, comments are very hard to
552  *              handle properly, (, [ and ({ will match any of ), ] and }),
553  *              and last but not least everyone has his own taste of
554  *              indentation.
555  */
556 static void indent(ptr, text)
557 char *ptr, *text;
558 {
559     static char f[] = { 7, 1, 7, 1, 2, 1, 6, 4, 2, 6, 7, 2, 0, };
560     static char g[] = { 2, 2, 1, 7, 1, 5, 1, 3, 6, 2, 2, 2, 0, };
561     char ident[MAX_LINE_SIZE];
562     char line[MAX_LINE_SIZE];
563     register cmdbuf *cb;
564     register char *p, *sp;
565     register int *ip, idx;
566     register int top, token;
567     char *start;
568     bool do_indent;
569 
570     cb = (cmdbuf *) ptr;
571 
572     do_indent = FALSE;
573     idx = 0;
574     p = text = strcpy(line, text);
575 
576     /* process status vars */
577     if (cb->quote != '\0') {
578         cb->shift = 0;  /* in case a comment starts on this line */
579         noshift(cb, p);
580     } else if ((cb->flags & CB_PPCONTROL) || *p == '#') {
581         noshift(cb, p);
582         while (*p != '\0') {
583             if (*p == '\\' && *++p == '\0') {
584                 cb->flags |= CB_PPCONTROL;
585                 return;
586             }
587             p++;
588         }
589         cb->flags &= ~CB_PPCONTROL;
590         return;
591     } else {
592         /* count leading ws */
593         while (*p == ' ' || *p == HT) {
594             if (*p++ == ' ') {
595                 idx++;
596             } else {
597                 idx = (idx + 8) & ~7;
598             }
599         }
600         if (*p == '\0') {
601             noshift(cb, p);
602             return;
603         } else if (cb->flags & CB_COMMENT) {
604             shift(cb, text);    /* use previous shift */
605         } else {
606             do_indent = TRUE;
607         }
608     }
609 
610     /* process this line */
611     start = p;
612     while (*p != '\0') {
613 
614         /* lexical scanning: find the next token */
615         ident[0] = '\0';
616         if (cb->flags & CB_COMMENT) {
617             /* comment */
618             while (*p != '*') {
619                 if (*p == '\0') {
620                     return;
621                 }
622                 p++;
623             }
624             while (*p == '*') {
625                 p++;
626             }
627             if (*p == '/') {
628                 cb->flags &= ~CB_COMMENT;
629                 p++;
630             }
631             continue;
632 
633         } else if (cb->quote != '\0') {
634             /* string or character constant */
635             for (;;) {
636                 if (*p == cb->quote) {
637                     cb->quote = '\0';
638                     p++;
639                     break;
640                 } else if (*p == '\0') {
641                     cb->last = cb->lineno;
642                     endblock(cb);
643                     error("Unterminated string");
644                 } else if (*p == '\\' && *++p == '\0') {
645                     break;
646                 }
647                 p++;
648             }
649             token = TOKEN;
650 
651         } else {
652             switch (*p++) {
653             case ' ':   /* white space */
654             case HT:
655                 continue;
656 
657             case '\'':  /* start of string */
658             case '"':
659                 cb->quote = p[-1];
660                 continue;
661 
662             case '/':
663                 if (*p == '*') {        /* start of comment */
664                     cb->flags |= CB_COMMENT;
665                     if (do_indent) {
666                         /* this line hasn't been indented yet */
667                         cb->shift = cb->ind[0] - idx;
668                         shift(cb, text);
669                         do_indent = FALSE;
670                     } else {
671                         register char *q;
672                         register int idx2;
673 
674                         /*
675                          * find how much the comment has shifted, so the same
676                          * shift can be used if the comment continues on the
677                          * next line
678                          */
679                         idx2 = cb->ind[0];
680                         for (q = start; q < p - 1;) {
681                             if (*q++ == HT) {
682                                 idx = (idx + 8) & ~7;
683                                 idx2 = (idx2 + 8) & ~7;
684                             } else {
685                                 idx++;
686                                 idx2++;
687                             }
688                         }
689                         cb->shift = idx2 - idx;
690                     }
691                     p++;
692                     continue;
693                 }
694                 token = TOKEN;
695                 break;
696 
697             case '{':
698                 token = LBRACKET;
699                 break;
700 
701             case '(':
702                 if (cb->flags & CB_JSKEYWORD) {
703                     /*
704                      * LOPERATOR & ROPERATOR are a kludge. The operator
705                      * precedence parser that is used could not work if
706                      * parenthesis after keywords was not treated specially.
707                      */
708                     token = LOPERATOR;
709                     break;
710                 }
711                 if (*p == '{') {
712                     p++;        /* ({ is one token */
713                 }
714             case '[':
715                 token = LHOOK;
716                 break;
717 
718             case '}':
719                 if (*p != ')') {
720                     token = RBRACKET;
721                     break;
722                 }
723                 p++;
724                 /* }) is one token; fall through */
725             case ')':
726             case ']':
727                 token = RHOOK;
728                 break;
729 
730             case ';':
731                 token = SEMICOLON;
732                 break;
733 
734             default:
735                 if (isalpha(*--p) || *p == '_') {
736                     register char *q;
737 
738                     /* Identifier. See if it's a keyword. */
739                     q = ident;
740                     do {
741                         *q++ = *p++;
742                     } while (isalnum(*p) || *p == '_');
743                     *q = '\0';
744 
745                     if      (strcmp(ident, "if") == 0)          token = IF;
746                     else if (strcmp(ident, "else") == 0)        token = ELSE;
747                     else if (strcmp(ident, "for") == 0 ||
748                              strcmp(ident, "while") == 0 ||
749                              strcmp(ident, "rlimits") == 0)     token = FOR;
750                     else if (strcmp(ident, "do") == 0)          token = DO;
751                     else    /* not a keyword */                 token = TOKEN;
752                 } else {
753                     /* anything else is a "token" */
754                     p++;
755                     token = TOKEN;
756                 }
757                 break;
758             }
759         }
760 
761         /* parse */
762         sp = cb->stack;
763         ip = cb->ind;
764         for (;;) {
765             top = *sp;
766             if (top == LOPERATOR && token == RHOOK) {
767                 /* ) after LOPERATOR is ROPERATOR */
768                 token = ROPERATOR;
769             }
770 
771             if (f[top] <= g[token]) {   /* shift the token on the stack */
772                 register int i;
773 
774                 if (sp == cb->stackbot) {
775                     /* out of stack. Finish already indented block. */
776                     cb->last = cb->lineno;
777                     endblock(cb);
778                     error("Nesting too deep");
779                 }
780 
781                 /* handle indentation */
782                 i = *ip;
783                 /* if needed, reduce indentation prior to shift */
784                 if ((token == LBRACKET && 
785                   (*sp == ROPERATOR || *sp == ELSE || *sp == DO)) ||
786                   token == RBRACKET ||
787                   (token == IF && *sp == ELSE)) {
788                     /* back up */
789                     i -= SHIFTWIDTH(cb->vars);
790                 }
791                 /* shift the current line, if appropriate */
792                 if (do_indent) {
793                     cb->shift = i - idx;
794                     if (i > 0 && token != RHOOK &&
795                       (*sp == LOPERATOR || *sp == LHOOK)) {
796                         /* half indent after ( [ ({ (HACK!) */
797                         cb->shift += SHIFTWIDTH(cb->vars) / 2;
798                     } else if (token == TOKEN && *sp == LBRACKET &&
799                       (strcmp(ident, "case") == 0 ||
800                       strcmp(ident, "default") == 0)) {
801                         /* back up if this is a switch label */
802                         cb->shift -= SHIFTWIDTH(cb->vars);
803                     }
804                     shift(cb, text);
805                     do_indent = FALSE;
806                 }
807                 /* change indentation after current token */
808                 if (token == LBRACKET || token == ROPERATOR || token == ELSE ||
809                   token == DO) {
810                     /* add indentation */
811                     i += SHIFTWIDTH(cb->vars);
812                 } else if (token == SEMICOLON &&
813                   (*sp == ROPERATOR || *sp == ELSE)) {
814                     /* in case it is followed by a comment */
815                     i -= SHIFTWIDTH(cb->vars);
816                 }
817 
818                 *--sp = token;
819                 *--ip = i;
820                 break;
821             }
822 
823             /* reduce handle */
824             do {
825                 top = *sp++;
826                 ip++;
827             } while (f[*sp] >= g[top]);
828         }
829         cb->stack = sp;
830         cb->ind = ip;
831         if (token >= IF) {      /* but not ELSE */
832             cb->flags |= CB_JSKEYWORD;
833         } else {
834             cb->flags &= ~CB_JSKEYWORD;
835         }
836     }
837 }
838 
839 /*
840  * NAME:        cmdbuf->indent()
841  * DESCRIPTION: indent a range of lines
842  */
843 int cb_indent(cb)
844 register cmdbuf *cb;
845 {
846     char s[STACKSZ];
847     int i[STACKSZ];
848 
849     /* setup stacks */
850     cb->stackbot = s;
851     cb->stack = s + STACKSZ - 1;
852     cb->stack[0] = EOT;
853     cb->ind = i + STACKSZ - 1;
854     cb->ind[0] = 0;
855     cb->quote = '\0';
856 
857     cb_do(cb, cb->first);
858     startblock(cb);
859     cb->lineno = cb->first - 1;
860     cb->flags |= CB_CHANGE;
861     cb->flags &= ~(CB_PPCONTROL | CB_COMMENT | CB_JSKEYWORD);
862     eb_range(cb->edbuf, cb->first, cb->last, indent, (char *) cb, FALSE);
863     endblock(cb);
864 
865     return 0;
866 }
867 
868 
869 /*
870  * NAME:        join()
871  * DESCRIPTION: join a string to the one already in the join buffer
872  */
873 static void join(ptr, text)
874 char *ptr;
875 register char *text;
876 {
877     register cmdbuf *cb;
878     register char *p;
879 
880     cb = (cmdbuf *) ptr;
881 
882     p = cb->buffer + cb->buflen;
883     if (cb->buflen != 0 && !(cb->flags & CB_EXCL)) {
884         /* do special processing */
885         text = skipst(text);
886         if (*text != '\0' && *text != ')' && p[-1] != ' ' && p[-1] != HT) {
887             if (p[-1] == '.') {
888                 *p++ = ' ';
889             }
890             *p++ = ' ';
891         }
892         cb->buflen = p - cb->buffer;
893     }
894     cb->buflen += strlen(text);
895     if (cb->buflen >= MAX_LINE_SIZE) {
896         error("Result of join would be too long");
897     }
898     strcpy(p, text);
899 }
900 
901 /*
902  * NAME:        cmdbuf->join()
903  * DESCRIPTION: join a range of lines in the edit buffer
904  */
905 int cb_join(cb)
906 register cmdbuf *cb;
907 {
908     char buf[MAX_LINE_SIZE + 1];
909     register Int *m;
910 
911     if (cb->edbuf->lines == 0) {
912         error("No lines in buffer");
913     }
914     if (cb->first < 0) {
915         cb->first = cb->this;
916     }
917     if (cb->last < 0) {
918         cb->last = (cb->first == cb->edbuf->lines) ? cb->first : cb->first + 1;
919     }
920 
921     cb_do(cb, cb->first);
922 
923     cb->this = cb->othis = cb->first;
924     buf[0] = '\0';
925     cb->buffer = buf;
926     cb->buflen = 0;
927     eb_range(cb->edbuf, cb->first, cb->last, join, (char *) cb, FALSE);
928 
929     /* erase marks for joined lines */
930     for (m = cb->mark; m < &cb->mark[26]; m++) {
931         if (*m > cb->first && *m <= cb->last) {
932             *m = 0;
933         }
934     }
935 
936     cb->flags |= CB_CHANGE;
937     startblock(cb);
938     addblock(cb, buf);
939     endblock(cb);
940 
941     return RET_FLAGS;
942 }
943 
944 
945 /*
946  * NAME:        sub()
947  * DESCRIPTION: add a string to the current substitute buffer
948  */
949 static void sub(cb, text, size)
950 register cmdbuf *cb;
951 char *text;
952 unsigned int size;
953 {
954     register char *p, *q;
955     register unsigned int i;
956 
957     i = size;
958     if (cb->buflen + i >= MAX_LINE_SIZE) {
959         if (cb->flags & CB_CURRENTBLK) {
960             /* finish already processed block */
961             endblock(cb);
962         }
963         cb->this = cb->othis = cb->lineno;
964         error("Line overflow in substitute");
965     }
966 
967     p = cb->buffer + cb->buflen;
968     q = text;
969     if (cb->flags & CB_TLOWER) {        /* lowercase one letter */
970         *p++ = tolower(*q);
971         q++;
972         cb->flags &= ~CB_TLOWER;
973         --i;
974     } else if (cb->flags & CB_TUPPER) { /* uppercase one letter */
975         *p++ = toupper(*q);
976         q++;
977         cb->flags &= ~CB_TUPPER;
978         --i;
979     }
980 
981     if (cb->flags & CB_LOWER) {         /* lowercase string */
982         while (i > 0) {
983             *p++ = tolower(*q);
984             q++;
985             --i;
986         }
987     } else if (cb->flags & CB_UPPER) {          /* uppercase string */
988         while (i > 0) {
989             *p++ = toupper(*q);
990             q++;
991             --i;
992         }
993     } else if (i > 0) {         /* don't change case */
994         memcpy(p, q, i);
995     }
996     cb->buflen += size;
997 }
998 
999 /*
1000  * NAME:        subst()
1001  * DESCRIPTION: do substitutions in a line. If something is substituted on line
1002  *              N, and the next substitution happens on line N + 2, line N + 1
1003  *              is joined in the new block also.
1004  */
1005 static void subst(ptr, text)
1006 char *ptr;
1007 register char *text;
1008 {
1009     char line[MAX_LINE_SIZE];
1010     register cmdbuf *cb;
1011     register int idx, size;
1012     register char *p;
1013     register Int *k, *l;
1014     Int newlines;
1015     bool found;
1016 
1017     cb = (cmdbuf *) ptr;
1018 
1019     found = FALSE;
1020     newlines = 0;
1021     idx = 0;
1022 
1023     /*
1024      * Because the write buffer might be flushed, and the text would
1025      * not remain in memory, use a local copy.
1026      */
1027     text = strcpy(line, text);
1028     while (rx_exec(cb->regexp, text, idx, IGNORECASE(cb->vars)) > 0) {
1029         if (cb->flags & CB_SKIPPED) {
1030             /*
1031              * add the previous line, in which nothing was substituted, to
1032              * the block. Has to be done here, before the contents of the buffer
1033              * are changed.
1034              */
1035             addblock(cb, cb->buffer);
1036             cb->flags &= ~CB_SKIPPED;
1037             /*
1038              * check if there were newlines in the last substitution. If there
1039              * are, marks on the previous line (without substitutions) will
1040              * also have to be changed.
1041              */
1042             if (cb->offset > 0) {
1043                 for (k = cb->mark, l = cb->moffset; k < &cb->mark[26]; k++, l++)
1044                 {
1045                     if (*k == cb->lineno - 1 && *l == 0) {
1046                         *l = *k + cb->offset;
1047                     }
1048                 }
1049             }
1050         }
1051         found = TRUE;
1052         cb->flags &= ~(CB_UPPER | CB_LOWER | CB_TUPPER | CB_TLOWER);
1053         size = cb->regexp->start - text - idx;
1054         if (size > 0) {
1055             /* copy first unchanged part of line to buffer */
1056             sub(cb, text + idx, size);
1057         }
1058         p = cb->replace;
1059         while (*p != '\0') {
1060             switch (*p) {
1061             case '&':
1062                 /* insert matching string */
1063                 sub(cb, cb->regexp->start, cb->regexp->size);
1064                 break;
1065 
1066             case '\\':          /* special substitute characters */
1067                 switch (*++p) {
1068                 case '1':
1069                 case '2':
1070                 case '3':
1071                 case '4':
1072                 case '5':
1073                 case '6':
1074                 case '7':
1075                 case '8':
1076                 case '9':
1077                     /* insert subexpression between \( \) */
1078                     if (cb->regexp->se[*p - '1'].start != (char*) NULL) {
1079                         sub(cb, cb->regexp->se[*p - '1'].start,
1080                             cb->regexp->se[*p - '1'].size);
1081                         break;
1082                     }
1083                     /* if no subexpression, fall though */
1084                 default:
1085                     sub(cb, p, 1);      /* ignore preceding backslash */
1086                     break;
1087 
1088                 case 'n':
1089                     cb->buffer[cb->buflen++] = '\0';
1090                     newlines++;         /* insert newline */
1091                     break;
1092 
1093                 case 'U':
1094                     /* convert string to uppercase */
1095                     cb->flags |= CB_UPPER;
1096                     cb->flags &= ~(CB_LOWER | CB_TUPPER | CB_TLOWER);
1097                     break;
1098 
1099                 case 'L':
1100                     /* convert string to lowercase */
1101                     cb->flags |= CB_LOWER;
1102                     cb->flags &= ~(CB_UPPER | CB_TUPPER | CB_TLOWER);
1103                     break;
1104 
1105                 case 'e':
1106                 case 'E':
1107                     /* end case conversion */
1108                     cb->flags &= ~(CB_UPPER | CB_LOWER | CB_TUPPER | CB_TLOWER);
1109                     break;
1110 
1111                 case 'u':
1112                     /* convert char to uppercase */
1113                     cb->flags |= CB_TUPPER;
1114                     cb->flags &= ~CB_TLOWER;
1115                     break;
1116 
1117                 case 'l':
1118                     /* convert char to lowercase */
1119                     cb->flags &= ~CB_TUPPER;
1120                     cb->flags |= CB_TLOWER;
1121                     break;
1122 
1123                 case '\0':      /* sigh */
1124                     continue;
1125                 }
1126                 break;
1127 
1128             default:            /* normal char */
1129                 sub(cb, p, 1);
1130                 break;
1131             }
1132             p++;
1133         }
1134 
1135         idx = cb->regexp->start + cb->regexp->size - text;
1136         if (!(cb->flags & CB_GLOBSUBST) || text[idx] == '\0' ||
1137             (cb->regexp->size == 0 && text[++idx] == '\0')) {
1138             break;
1139         }
1140     }
1141 
1142     if (found) {
1143         if (text[idx] != '\0') {
1144             /* concatenate unchanged part of line after found pattern */
1145             cb->flags &= ~(CB_UPPER | CB_LOWER | CB_TUPPER | CB_TLOWER);
1146             sub(cb, text + idx, strlen(text + idx));
1147         }
1148         if (!(cb->flags & CB_CURRENTBLK)) {
1149             /* start a new block of lines with substitutions in them */
1150             cb->flags |= CB_CHANGE;
1151             cb->first = cb->lineno;
1152             startblock(cb);
1153             cb->flags |= CB_CURRENTBLK;
1154         }
1155         /* add this changed line to block */
1156         cb->buffer[cb->buflen] = '\0';
1157         if (newlines == 0) {
1158             addblock(cb, cb->buffer);
1159         } else {
1160             /*
1161              * There were newlines in the substituted string. Add all
1162              * lines to the current block, and save the marks in range.
1163              */
1164             p = cb->buffer;
1165             do {
1166                 addblock(cb, p);
1167                 p += strlen(p) + 1;
1168             } while (p <= cb->buffer + cb->buflen);
1169 
1170             for (k = cb->mark, l = cb->moffset; k < &cb->mark[26]; k++, l++) {
1171                 if (*k == cb->lineno && *l == 0) {
1172                     *l = *k + cb->offset;
1173                 }
1174             }
1175             cb->offset += newlines;
1176         }
1177         cb->buflen = 0;
1178         cb->last = cb->lineno;
1179     } else {
1180         if (cb->flags & CB_SKIPPED) {
1181             /* two lines without substitutions now. Finish previous block. */
1182             endblock(cb);
1183             cb->lineno += cb->offset;
1184             cb->offset = 0;
1185             cb->flags &= ~(CB_CURRENTBLK | CB_SKIPPED);
1186         } else if (cb->flags & CB_CURRENTBLK) {
1187             /*
1188              * no substitution on this line, but there was one on the previous
1189              * line. mark this line as skipped, so it can still be added to
1190              * the block of changed lines if the next line has substitutions.
1191              */
1192             strcpy(cb->buffer, text);
1193             cb->flags |= CB_SKIPPED;
1194         }
1195     }
1196     cb->lineno++;
1197 }
1198 
1199 /*
1200  * NAME:        cmdbuf->substitute()
1201  * DESCRIPTION: do substitutions on a range of lines
1202  */
1203 int cb_subst(cb)
1204 register cmdbuf *cb;
1205 {
1206     char buf[MAX_LINE_SIZE], delim;
1207     Int m[26];
1208     Int edit;
1209     register char *p;
1210     register Int *k, *l;
1211 
1212     delim = cb->cmd[0];
1213     if (delim == '\0' || strchr("0123456789gpl#-+", delim) != (char*) NULL) {
1214         /* no search pattern & replace string specified */
1215         if (cb->search[0] == '\0') {
1216             error("No previous substitute to repeat");
1217         }
1218     } else if (!isalpha(delim)) {
1219         register char *q;
1220 
1221         /* get search pattern */
1222         p = pattern(cb->cmd + 1, delim, cb->search);
1223         /* get replace string */
1224         q = cb->replace;
1225         while (*p != '\0') {
1226             if (*p == delim) {
1227                 p++;
1228                 break;
1229             }
1230             if (q == cb->replace + STRINGSZ - 1) {
1231                 cb->search[0] = '\0';
1232                 error("Replace string too large");
1233             }
1234             if ((*q++ = *p++) == '\\' && *p != '\0') {
1235                 *q++ = *p++;
1236             }
1237         }
1238         *q = '\0';
1239         cb->cmd = p;
1240     } else {
1241         /* cause error */
1242         cb->search[0] = '\0';
1243     }
1244 
1245     if (cb->search[0] == '\0') {
1246         error("Missing regular expression for substitute");
1247     }
1248 
1249     /* compile regexp */
1250     p = rx_comp(cb->regexp, cb->search);
1251     if (p != (char *) NULL) {
1252         error(p);
1253     }
1254 
1255     cb_count(cb);       /* get count */
1256     /* handle global flag */
1257     if (cb->cmd[0] == 'g') {
1258         cb->flags |= CB_GLOBSUBST;
1259         cb->cmd++;
1260     } else {
1261         cb->flags &= ~CB_GLOBSUBST;
1262     }
1263 
1264     /* make a blank mark table */
1265     cb->moffset = m;
1266     for (l = m; l < &m[26]; ) {
1267         *l++ = 0;
1268     }
1269     cb->offset = 0;
1270 
1271     /* do substitutions */
1272     cb_do(cb, cb->first);
1273     cb->lineno = cb->first;
1274     edit = cb->edit;
1275     cb->buffer = buf;
1276     cb->buflen = 0;
1277     cb->flags &= ~(CB_CURRENTBLK | CB_SKIPPED);
1278     eb_range(cb->edbuf, cb->first, cb->last, subst, (char *) cb, FALSE);
1279     if (cb->flags & CB_CURRENTBLK) {
1280         /* finish current block, if needed */
1281         endblock(cb);
1282     }
1283 
1284     cb->othis = cb->uthis;
1285     if (edit == cb->edit) {
1286         error("Substitute pattern match failed");
1287     }
1288 
1289     /* some marks may have been messed up. fix them */
1290     for (l = m, k = cb->mark; l < &m[26]; l++, k++) {
1291         if (*l != 0) {
1292             *k = *l;
1293         }
1294     }
1295 
1296     return RET_FLAGS;
1297 }
1298 
1299 
1300 /*
1301  * NAME:        getfname()
1302  * DESCRIPTION: copy a string to another buffer, unless it has length 0 or
1303  *              is too long
1304  */
1305 static bool getfname(cb, buffer)
1306 register cmdbuf *cb;
1307 char *buffer;
1308 {
1309     register char *p, *q;
1310 
1311     /* find the end of the filename */
1312     p = strchr(cb->cmd, ' ');
1313     q = strchr(cb->cmd, HT);
1314     if (q != (char *) NULL && (p == (char *) NULL || p > q)) {
1315         p = q;
1316     }
1317     q = strchr(cb->cmd, '|');
1318     if (q != (char *) NULL && (p == (char *) NULL || p > q)) {
1319         p = q;
1320     }
1321     if (p == (char *) NULL) {
1322         p = strchr(cb->cmd, '\0');
1323     }
1324 
1325     /* checks */
1326     if (p == cb->cmd) {
1327         return FALSE;
1328     }
1329     if (p - cb->cmd >= STRINGSZ) {
1330         error("Filename too long");
1331     }
1332 
1333     /* copy */
1334     memcpy(buffer, cb->cmd, p - cb->cmd);
1335     buffer[p - cb->cmd] = '\0';
1336     cb->cmd = p;
1337     return TRUE;
1338 }
1339 
1340 /*
1341  * NAME:        cmdbuf->file()
1342  * DESCRIPTION: get/set the file name & current line, etc.
1343  */
1344 int cb_file(cb)
1345 register cmdbuf *cb;
1346 {
1347     not_in_global(cb);
1348 
1349     if (getfname(cb, cb->fname)) {
1350         /* file name is changed: mark the file as "not edited" */
1351         cb->flags |= CB_NOIMAGE;
1352     }
1353 
1354     /* give statistics */
1355     if (cb->fname[0] == '\0') {
1356         output("No file");
1357     } else {
1358         output("\"%s\"", cb->fname);
1359     }
1360     if (cb->flags & CB_NOIMAGE) {
1361         output(" [Not edited]");
1362     }
1363     if (cb->edit > 0) {
1364         output(" [Modified]");
1365     }
1366     output(" line %ld of %ld --%d%%--\012", /* LF */
1367            (long) cb->this, (long) cb->edbuf->lines,
1368            (cb->edbuf->lines == 0) ? 0 :
1369                                 (int) ((100 * cb->this) / cb->edbuf->lines));
1370 
1371     return 0;
1372 }
1373 
1374 /*
1375  * NAME:        io->show()
1376  * DESCRIPTION: show statistics on the file just read/written
1377  */
1378 static void io_show(iob)
1379 register io *iob;
1380 {
1381     output("%ld lines, %ld characters", (long) iob->lines,
1382            (long) (iob->chars + iob->zero - iob->split - iob->ill));
1383     if (iob->zero > 0) {
1384         output(" [%ld zero]", (long) iob->zero);
1385     }
1386     if (iob->split > 0) {
1387         output(" [%ld split]", (long) iob->split);
1388     }
1389     if (iob->ill) {
1390         output(" [incomplete last line]");
1391     }
1392     output("\012");     /* LF */
1393 }
1394 
1395 /*
1396  * NAME:        cmdbuf->read()
1397  * DESCRIPTION: insert a file in the current edit buffer
1398  */
1399 int cb_read(cb)
1400 register cmdbuf *cb;
1401 {
1402     char buffer[STRINGSZ];
1403     io iob;
1404 
1405     not_in_global(cb);
1406 
1407     if (!getfname(cb, buffer)) {
1408         if (cb->fname[0] == '\0') {
1409             error("No current filename");
1410         }
1411         /* read current file, by default. I don't know why, but ex has it
1412            that way. */
1413         strcpy(buffer, cb->fname);
1414     }
1415 
1416     cb_do(cb, cb->first);
1417     output("\"%s\" ", buffer);
1418     if (!io_load(cb->edbuf, buffer, cb->first, &iob)) {
1419         error("is unreadable");
1420     }
1421     io_show(&iob);
1422 
1423     cb->edit++;
1424     cb->this = cb->first + iob.lines;
1425 
1426     return 0;
1427 }
1428 
1429 /*
1430  * NAME:        cmdbuf->edit()
1431  * DESCRIPTION: edit a new file
1432  */
1433 int cb_edit(cb)
1434 register cmdbuf *cb;
1435 {
1436     io iob;
1437 
1438     not_in_global(cb);
1439 
1440     if (cb->edit > 0 && !(cb->flags & CB_EXCL)) {
1441         error("No write since last change (edit! overrides)");
1442     }
1443 
1444     getfname(cb, cb->fname);
1445     if (cb->fname[0] == '\0') {
1446         error("No current filename");
1447     }
1448 
1449     m_static();
1450     eb_clear(cb->edbuf);
1451     m_dynamic();
1452     cb->flags &= ~CB_NOIMAGE;
1453     cb->edit = 0;
1454     cb->first = cb->this = 0;
1455     memset(cb->mark, '\0', sizeof(cb->mark));
1456     cb->buf = 0;
1457     memset(cb->zbuf, '\0', sizeof(cb->zbuf));
1458     cb->undo = (block) -1;      /* not 0! */
1459 
1460     output("\"%s\" ", cb->fname);
1461     if (!io_load(cb->edbuf, cb->fname, cb->first, &iob)) {
1462         error("is unreadable");
1463     }
1464     io_show(&iob);
1465     if (iob.zero > 0 || iob.split > 0 || iob.ill) {
1466         /* the editbuffer in memory is not a perfect image of the file read */
1467         cb->flags |= CB_NOIMAGE;
1468     }
1469 
1470     cb->this = iob.lines;
1471 
1472     return 0;
1473 }
1474 
1475 /*
1476  * NAME:        cmdbuf->quit()
1477  * DESCRIPTION: quit editing
1478  */
1479 int cb_quit(cb)
1480 cmdbuf *cb;
1481 {
1482     not_in_global(cb);
1483 
1484     if (cb->edit > 0 && !(cb->flags & CB_EXCL)) {
1485         error("No write since last change (quit! overrides)");
1486     }
1487 
1488     return RET_QUIT;
1489 }
1490 
1491 /*
1492  * NAME:        cmdbuf->write()
1493  * DESCRIPTION: write a range of lines to a file
1494  */
1495 int cb_write(cb)
1496 register cmdbuf *cb;
1497 {
1498     char buffer[STRINGSZ];
1499     bool append;
1500     io iob;
1501 
1502     not_in_global(cb);
1503 
1504     if (strncmp(cb->cmd, ">>", 2) == 0) {
1505         append = TRUE;
1506         cb->cmd = skipst(cb->cmd + 2);
1507     } else {
1508         append = FALSE;
1509     }
1510 
1511     /* check if write can be done */
1512     if (!getfname(cb, buffer)) {
1513         if (cb->fname[0] == '\0') {
1514             error("No current filename");
1515         }
1516         strcpy(buffer, cb->fname);
1517     }
1518     if (strcmp(buffer, cb->fname) == 0) {
1519         if (cb->first == 1 && cb->last == cb->edbuf->lines) {
1520             if ((cb->flags & (CB_NOIMAGE|CB_EXCL)) == CB_NOIMAGE) {
1521                 error("File is changed (use w! to override)");
1522             }
1523         } else if (!(cb->flags & CB_EXCL)) {
1524             error("Use w! to write partial buffer");
1525         }
1526     }
1527 
1528     output("\"%s\" ", buffer);
1529     if (!io_save(cb->edbuf, buffer, cb->first, cb->last, append, &iob)) {
1530         error("write failed");
1531     }
1532     io_show(&iob);
1533 
1534     if (cb->first == 1 && cb->last == cb->edbuf->lines) {
1535         /* file is now perfect image of editbuffer in memory */
1536         cb->flags &= ~CB_NOIMAGE;
1537         cb->edit = 0;
1538     }
1539 
1540     return 0;
1541 }
1542 
1543 /*
1544  * NAME:        cmdbuf->wq()
1545  * DESCRIPTION: write a range of lines to a file and quit
1546  */
1547 int cb_wq(cb)
1548 cmdbuf *cb;
1549 {
1550     cb->first = 1;
1551     cb->last = cb->edbuf->lines;
1552     cb_write(cb);
1553     return cb_quit(cb);
1554 }
1555 
1556 /*
1557  * NAME:        cmdbuf->xit()
1558  * DESCRIPTION: write to the current file if modified, and quit
1559  */
1560 int cb_xit(cb)
1561 register cmdbuf *cb;
1562 {
1563     if (cb->edit > 0) {
1564         cb->flags |= CB_EXCL;
1565         return cb_wq(cb);
1566     } else {
1567         not_in_global(cb);
1568 
1569         return RET_QUIT;
1570     }
1571 }
1572 
1573 
1574 /*
1575  * NAME:        cmdbuf->set()
1576  * DESCRIPTION: get/set variable(s)
1577  */
1578 int cb_set(cb)
1579 register cmdbuf *cb;
1580 {
1581     char buffer[STRINGSZ];
1582     register char *p, *q;
1583 
1584     not_in_global(cb);
1585 
1586     p = cb->cmd;
1587     if (strlen(p) >= STRINGSZ) {
1588         p[STRINGSZ - 1] = '\0'; /* must fit in the buffer */
1589     }
1590     if (*p == '\0') {
1591         /* no arguments */
1592         va_show(cb->vars);
1593     } else {
1594         do {
1595             /* copy argument */
1596             q = buffer;
1597             while (*p != '\0' && *p != ' ' && *p != HT) {
1598                 *q++ = *p++;
1599             }
1600             *q = '\0';
1601             /* let va_set() process it */
1602             va_set(cb->vars, buffer);
1603             p = skipst(p);
1604         } while (*p != '\0');
1605         cb->cmd = p;
1606     }
1607     return 0;
1608 }
1609 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ file search ] ~

This page was automatically generated by the LXR engine.
Visit the LXR main site for more information.