10 int xrow
, xoff
, xtop
; /* current row, column, and top row */
11 int xleft
; /* the first visible column */
12 int xquit
; /* exit if set */
13 int xvis
; /* visual mode */
14 int xai
= 1; /* autoindent option */
15 int xic
= 1; /* ignorecase option */
16 int xaw
; /* autowrite option */
17 int xhl
= 1; /* syntax highlight option */
18 int xhll
; /* highlight current line */
19 int xled
= 1; /* use the line editor */
20 int xtd
= +1; /* current text direction */
21 int xshape
= 1; /* perform letter shaping */
22 int xorder
= 1; /* change the order of characters */
23 int xkmap
= 0; /* the current keymap */
24 int xkmap_alt
= 1; /* the alternate keymap */
25 static char xkwd
[EXLEN
]; /* the last searched keyword */
26 static char xrep
[EXLEN
]; /* the last replacement */
27 static int xkwddir
; /* the last search direction */
28 static int xgdep
; /* global command recursion depth */
35 long mtime
; /* modification time */
38 static int bufs_find(char *path
)
41 for (i
= 0; i
< LEN(bufs
); i
++)
42 if (bufs
[i
].lb
&& !strcmp(bufs
[i
].path
, path
))
47 static void bufs_free(int idx
)
51 lbuf_free(bufs
[idx
].lb
);
52 memset(&bufs
[idx
], 0, sizeof(bufs
[idx
]));
56 static long mtime(char *path
)
64 static int bufs_open(char *path
)
67 for (i
= 0; i
< LEN(bufs
) - 1; i
++)
71 bufs
[i
].path
= uc_dup(path
);
72 bufs
[i
].lb
= lbuf_make();
77 strcpy(bufs
[i
].ft
, syn_filetype(path
));
81 static void bufs_swap(int i
, int j
)
86 memcpy(&tmp
, &bufs
[i
], sizeof(tmp
));
87 memcpy(&bufs
[i
], &bufs
[j
], sizeof(tmp
));
88 memcpy(&bufs
[j
], &tmp
, sizeof(tmp
));
91 static void bufs_switch(int idx
)
106 struct lbuf
*ex_lbuf(void)
111 char *ex_filetype(void)
116 /* read ex command location */
117 static char *ex_loc(char *s
, char *loc
)
119 while (*s
== ':' || isspace((unsigned char) *s
))
121 while (*s
&& !isalpha((unsigned char) *s
) && *s
!= '=' && *s
!= '!') {
124 if (*s
== '/' || *s
== '?') {
127 while (*s
&& *s
!= d
) {
128 if (*s
== '\\' && s
[1])
140 /* read ex command name */
141 static char *ex_cmd(char *s
, char *cmd
)
145 while (isspace((unsigned char) *s
))
147 while (isalpha((unsigned char) *s
))
148 if ((*cmd
++ = *s
++) == 'k' && cmd
== cmd0
+ 1)
150 if (*s
== '!' || *s
== '=')
156 /* read ex command argument */
157 static char *ex_arg(char *s
, char *arg
)
160 while (isspace((unsigned char) *s
))
162 while (*s
&& !isspace((unsigned char) *s
)) {
163 if (*s
== '\\' && s
[1])
171 static char *ex_argeol(char *ec
)
174 char *s
= ex_cmd(ec
, arg
);
175 while (isspace((unsigned char) *s
))
180 /* the previous search keyword */
181 int ex_kwd(char **kwd
, int *dir
)
190 /* set the previous search keyword */
191 void ex_kwdset(char *kwd
, int dir
)
194 snprintf(xkwd
, sizeof(xkwd
), "%s", kwd
);
195 reg_put('/', kwd
, 0);
200 static int ex_search(char **pat
)
212 sbuf_chr(kw
, (unsigned char) *e
);
213 if (*e
== '\\' && e
[1])
217 ex_kwdset(sbuf_buf(kw
), **pat
== '/' ? 1 : -1);
219 *pat
= *e
? e
+ 1 : e
;
220 if (ex_kwd(&pats
[0], &dir
))
222 re
= rset_make(1, pats
, xic
? RE_ICASE
: 0);
226 while (row
>= 0 && row
< lbuf_len(xb
)) {
227 if (rset_find(re
, lbuf_get(xb
, row
), 0, NULL
, 0) >= 0)
232 return row
>= 0 && row
< lbuf_len(xb
) ? row
: -1;
235 static int ex_lineno(char **num
)
238 switch ((unsigned char) **num
) {
243 n
= lbuf_len(xb
) - 1;
247 if (lbuf_jump(xb
, (unsigned char) *++(*num
), &n
, NULL
))
256 if (isdigit((unsigned char) **num
)) {
258 while (isdigit((unsigned char) **num
))
262 while (**num
== '-' || **num
== '+') {
264 while (isdigit((unsigned char) **num
))
270 /* parse ex command location */
271 static int ex_region(char *loc
, int *beg
, int *end
)
274 if (!strcmp("%", loc
)) {
276 *end
= MAX(0, lbuf_len(xb
));
281 *end
= xrow
== lbuf_len(xb
) ? xrow
: xrow
+ 1;
286 *end
= ex_lineno(&loc
) + 1;
287 *beg
= naddr
++ ? end0
- 1 : *end
- 1;
290 while (*loc
&& *loc
!= ';' && *loc
!= ',')
298 if (*beg
< 0 || *beg
>= lbuf_len(xb
))
300 if (*end
< *beg
|| *end
> lbuf_len(xb
))
305 static int ec_write(char *ec
);
307 static int ex_modifiedbuffer(char *msg
)
309 if (!lbuf_modified(xb
))
311 if (xaw
&& ex_path()[0])
312 return ec_write("w");
318 static int ec_quit(char *ec
)
322 if (!strchr(cmd
, '!'))
323 if (ex_modifiedbuffer("buffer modified\n"))
329 static int ex_expand(char *d
, char *s
)
332 int c
= (unsigned char) *s
++;
334 if (!bufs
[0].path
|| !bufs
[0].path
[0]) {
335 ex_show("\"%\" is unset\n");
338 strcpy(d
, bufs
[0].path
);
343 if (!bufs
[1].path
|| !bufs
[1].path
[0]) {
344 ex_show("\"#\" is unset\n");
347 strcpy(d
, bufs
[1].path
);
351 if (c
== '\\' && (*s
== '%' || *s
== '#'))
359 static int ec_edit(char *ec
)
362 char arg
[EXLEN
], cmd
[EXLEN
];
367 if (!strchr(cmd
, '!'))
368 if (xb
&& ex_modifiedbuffer("buffer modified\n"))
370 if (ex_expand(path
, arg
))
375 if (arg
[0] && bufs_find(path
) >= 0) {
376 bufs_switch(bufs_find(path
));
379 if (path
[0] || !bufs
[0].path
)
380 bufs_switch(bufs_open(path
));
381 fd
= open(ex_path(), O_RDONLY
);
383 int rd
= lbuf_rd(xb
, fd
, 0, lbuf_len(xb
));
385 snprintf(msg
, sizeof(msg
), "\"%s\" %d lines [r]\n",
386 ex_path(), lbuf_len(xb
));
388 ex_show("read failed\n");
392 lbuf_saved(xb
, path
[0] != '\0');
393 bufs
[0].mtime
= mtime(ex_path());
394 xrow
= MAX(0, MIN(xrow
, lbuf_len(xb
) - 1));
396 xtop
= MAX(0, MIN(xtop
, lbuf_len(xb
) - 1));
400 static int ec_read(char *ec
)
402 char arg
[EXLEN
], loc
[EXLEN
];
407 int n
= lbuf_len(xb
);
410 path
= arg
[0] ? arg
: ex_path();
411 if (ex_region(loc
, &beg
, &end
))
414 int pos
= MIN(xrow
+ 1, lbuf_len(xb
));
415 if (ex_expand(arg
, ex_argeol(ec
)))
417 obuf
= cmd_pipe(arg
+ 1, NULL
, 0, 1);
419 lbuf_edit(xb
, obuf
, pos
, pos
);
422 int fd
= open(path
, O_RDONLY
);
423 int pos
= lbuf_len(xb
) ? end
: 0;
425 ex_show("read failed\n");
428 if (lbuf_rd(xb
, fd
, pos
, pos
)) {
429 ex_show("read failed\n");
435 xrow
= end
+ lbuf_len(xb
) - n
- 1;
436 snprintf(msg
, sizeof(msg
), "\"%s\" %d lines [r]\n",
437 path
, lbuf_len(xb
) - n
);
442 static int ec_write(char *ec
)
444 char cmd
[EXLEN
], arg
[EXLEN
], loc
[EXLEN
];
452 path
= arg
[0] ? arg
: ex_path();
453 if (cmd
[0] == 'x' && !lbuf_modified(xb
))
455 if (ex_region(loc
, &beg
, &end
))
462 if (ex_expand(arg
, ex_argeol(ec
)))
464 ibuf
= lbuf_cp(xb
, beg
, end
);
466 cmd_pipe(arg
+ 1, ibuf
, 1, 0);
470 if (!strchr(cmd
, '!') && bufs
[0].path
&&
471 !strcmp(bufs
[0].path
, path
) &&
472 mtime(bufs
[0].path
) > bufs
[0].mtime
) {
473 ex_show("write failed: file changed\n");
476 if (!strchr(cmd
, '!') && arg
[0] && mtime(arg
) >= 0) {
477 ex_show("write failed: file exists\n");
480 fd
= open(path
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600);
482 ex_show("write failed: cannot create file\n");
485 if (lbuf_wr(xb
, fd
, beg
, end
)) {
486 ex_show("write failed\n");
492 snprintf(msg
, sizeof(msg
), "\"%s\" %d lines [w]\n",
497 bufs
[0].path
= uc_dup(path
);
499 if (!strcmp(ex_path(), path
))
501 if (!strcmp(ex_path(), path
))
502 bufs
[0].mtime
= mtime(path
);
503 if (cmd
[0] == 'x' || (cmd
[0] == 'w' && cmd
[1] == 'q'))
508 static int ec_insert(char *ec
)
510 char arg
[EXLEN
], cmd
[EXLEN
], loc
[EXLEN
];
518 if (ex_region(loc
, &beg
, &end
) && (beg
!= 0 || end
!= 0))
521 while ((s
= ex_read(""))) {
522 if (!strcmp(".", s
)) {
531 if (beg
+ 1 <= lbuf_len(xb
))
536 lbuf_edit(xb
, sbuf_buf(sb
), beg
, end
);
537 xrow
= MIN(lbuf_len(xb
) - 1, end
+ lbuf_len(xb
) - n
- 1);
542 static int ec_print(char *ec
)
544 char cmd
[EXLEN
], loc
[EXLEN
];
549 if (!cmd
[0] && !loc
[0]) {
550 if (xrow
>= lbuf_len(xb
) - 1)
554 if (ex_region(loc
, &beg
, &end
))
556 for (i
= beg
; i
< end
; i
++)
557 ex_print(lbuf_get(xb
, i
));
563 static int ec_null(char *ec
)
570 if (ex_region(loc
, &beg
, &end
))
572 xrow
= MAX(beg
, end
- 1);
577 static void ex_yank(int reg
, int beg
, int end
)
579 char *buf
= lbuf_cp(xb
, beg
, end
);
580 reg_put(reg
, buf
, 1);
584 static int ec_delete(char *ec
)
591 if (ex_region(loc
, &beg
, &end
) || !lbuf_len(xb
))
593 ex_yank(arg
[0], beg
, end
);
594 lbuf_edit(xb
, NULL
, beg
, end
);
599 static int ec_yank(char *ec
)
606 if (ex_region(loc
, &beg
, &end
) || !lbuf_len(xb
))
608 ex_yank(arg
[0], beg
, end
);
612 static int ec_put(char *ec
)
619 int n
= lbuf_len(xb
);
622 buf
= reg_get(arg
[0], &lnmode
);
623 if (!buf
|| ex_region(loc
, &beg
, &end
))
625 lbuf_edit(xb
, buf
, end
, end
);
626 xrow
= MIN(lbuf_len(xb
) - 1, end
+ lbuf_len(xb
) - n
- 1);
630 static int ec_lnum(char *ec
)
636 if (ex_region(loc
, &beg
, &end
))
638 sprintf(msg
, "%d\n", end
);
643 static int ec_undo(char *ec
)
645 return lbuf_undo(xb
);
648 static int ec_redo(char *ec
)
650 return lbuf_redo(xb
);
653 static int ec_mark(char *ec
)
655 char loc
[EXLEN
], arg
[EXLEN
];
659 if (ex_region(loc
, &beg
, &end
))
661 lbuf_mark(xb
, arg
[0], end
- 1, 0);
665 static void replace(struct sbuf
*dst
, char *rep
, char *ln
, int *offs
)
668 if (rep
[0] == '\\' && rep
[1]) {
669 if (rep
[1] >= '0' && rep
[1] <= '9') {
670 int grp
= (rep
[1] - '0') * 2;
671 int len
= offs
[grp
+ 1] - offs
[grp
];
672 sbuf_mem(dst
, ln
+ offs
[grp
], len
);
674 sbuf_chr(dst
, (unsigned char) rep
[1]);
678 sbuf_chr(dst
, (unsigned char) rep
[0]);
684 static int ec_substitute(char *ec
)
691 char *pat
= NULL
, *rep
= NULL
;
695 if (ex_region(loc
, &beg
, &end
))
706 rep
= uc_dup(pat
? "" : xrep
);
707 snprintf(xrep
, sizeof(xrep
), "%s", rep
);
709 if (ex_kwd(&pats
[0], NULL
))
711 re
= rset_make(1, pats
, xic
? RE_ICASE
: 0);
716 for (i
= beg
; i
< end
; i
++) {
717 char *ln
= lbuf_get(xb
, i
);
718 struct sbuf
*r
= sbuf_make();
719 while (rset_find(re
, ln
, LEN(offs
) / 2, offs
, 0) >= 0) {
720 sbuf_mem(r
, ln
, offs
[0]);
721 replace(r
, rep
, ln
, offs
);
723 if (!*ln
|| !strchr(s
, 'g'))
725 if (offs
[1] <= 0) /* zero-length match */
726 sbuf_chr(r
, (unsigned char) *ln
++);
729 lbuf_edit(xb
, sbuf_buf(r
), i
, i
+ 1);
737 static int ec_exec(char *ec
)
744 ex_modifiedbuffer(NULL
);
746 if (ex_expand(arg
, ex_argeol(ec
)))
750 return cmd_exec(arg
);
752 if (ex_region(loc
, &beg
, &end
))
754 text
= lbuf_cp(xb
, beg
, end
);
755 rep
= cmd_pipe(arg
, text
, 1, 1);
757 lbuf_edit(xb
, rep
, beg
, end
);
763 static int ec_make(char *ec
)
767 ex_modifiedbuffer(NULL
);
768 if (ex_expand(arg
, ex_argeol(ec
)))
770 sprintf(make
, "make %s", arg
);
777 static int ec_ft(char *ec
)
782 snprintf(bufs
[0].ft
, sizeof(bufs
[0].ft
), "%s", arg
);
784 ex_print(ex_filetype());
788 static int ec_cmap(char *ec
)
795 xkmap_alt
= conf_kmapfind(arg
);
797 ex_print(conf_kmap(xkmap
)[0]);
798 if (arg
[0] && !strchr(cmd
, '!'))
803 static int ex_exec(char *ln
);
805 static int ec_glob(char *ec
)
807 char loc
[EXLEN
], cmd
[EXLEN
];
816 if (!loc
[0] && !xgdep
)
818 if (ex_region(loc
, &beg
, &end
))
820 not = strchr(cmd
, '!') || cmd
[0] == 'v';
826 if (ex_kwd(&pats
[0], NULL
))
828 if (!(re
= rset_make(1, pats
, xic
? RE_ICASE
: 0)))
831 for (i
= beg
+ 1; i
< end
; i
++)
832 lbuf_globset(xb
, i
, xgdep
);
834 while (i
< lbuf_len(xb
)) {
835 char *ln
= lbuf_get(xb
, i
);
836 if ((rset_find(re
, ln
, LEN(offs
) / 2, offs
, 0) < 0) == not) {
842 while (i
< lbuf_len(xb
) && !lbuf_globget(xb
, i
, xgdep
))
845 for (i
= 0; i
< lbuf_len(xb
); i
++)
846 lbuf_globget(xb
, i
, xgdep
);
852 static struct option
{
857 {"ai", "autoindent", &xai
},
858 {"aw", "autowrite", &xaw
},
859 {"ic", "ignorecase", &xic
},
860 {"td", "textdirection", &xtd
},
861 {"shape", "shape", &xshape
},
862 {"order", "xorder", &xorder
},
863 {"hl", "highlight", &xhl
},
864 {"hll", "highlightline", &xhll
},
867 static char *cutword(char *s
, char *d
)
871 while (*s
&& !isspace(*s
))
879 static int ec_set(char *ec
)
890 if (tok
[0] == 'n' && tok
[1] == 'o') {
891 strcpy(opt
, tok
+ 2);
894 char *r
= strchr(tok
, '=');
904 for (i
= 0; i
< LEN(options
); i
++) {
905 struct option
*o
= &options
[i
];
906 if (!strcmp(o
->abbr
, opt
) || !strcmp(o
->name
, opt
)) {
911 ex_show("unknown option");
917 static struct excmd
{
922 {"p", "print", ec_print
},
923 {"a", "append", ec_insert
},
924 {"i", "insert", ec_insert
},
925 {"d", "delete", ec_delete
},
926 {"c", "change", ec_insert
},
927 {"e", "edit", ec_edit
},
928 {"e!", "edit!", ec_edit
},
929 {"g", "global", ec_glob
},
930 {"g!", "global!", ec_glob
},
932 {"k", "mark", ec_mark
},
933 {"pu", "put", ec_put
},
934 {"q", "quit", ec_quit
},
935 {"q!", "quit!", ec_quit
},
936 {"r", "read", ec_read
},
937 {"v", "vglobal", ec_glob
},
938 {"w", "write", ec_write
},
939 {"w!", "write!", ec_write
},
940 {"wq", "wq", ec_write
},
941 {"wq!", "wq!", ec_write
},
942 {"u", "undo", ec_undo
},
943 {"r", "redo", ec_redo
},
944 {"se", "set", ec_set
},
945 {"s", "substitute", ec_substitute
},
946 {"x", "xit", ec_write
},
947 {"x!", "xit!", ec_write
},
948 {"ya", "yank", ec_yank
},
950 {"make", "make", ec_make
},
951 {"ft", "filetype", ec_ft
},
952 {"cm", "cmap", ec_cmap
},
953 {"cm!", "cmap!", ec_cmap
},
957 /* read an ex command and its arguments from src into dst */
958 static void ex_line(int (*ec
)(char *s
), char *dst
, char **src
)
960 if (!ec
|| (ec
!= ec_glob
&& ec
!= ec_exec
)) {
961 while (**src
&& **src
!= '|' && **src
!= '\n')
966 } else { /* the rest of the line for :g and :! */
968 *src
= strchr(*src
, '\0');
972 /* execute a single ex command */
973 static int ex_exec(char *ln
)
981 for (i
= 0; i
< LEN(excmds
); i
++) {
982 if (!strcmp(excmds
[i
].abbr
, cmd
) ||
983 !strcmp(excmds
[i
].name
, cmd
)) {
984 ex_line(excmds
[i
].ec
, ec
, &ln
);
985 ret
= excmds
[i
].ec(ec
);
989 if (i
== LEN(excmds
))
990 ex_line(NULL
, ec
, &ln
);
995 /* execute a single ex command */
996 void ex_command(char *ln
)
1000 reg_put(':', ln
, 0);
1007 char *ln
= ex_read(":");
1014 int ex_init(char **files
)
1018 char *r
= files
[0] ? files
[0] : "";
1021 while (*r
&& s
+ 2 < cmd
+ sizeof(cmd
)) {
1029 if (getenv("EXINIT"))
1030 ex_command(getenv("EXINIT"));
1037 for (i
= 0; i
< LEN(bufs
); i
++)