| tex.c - neatvi - [fork] simple vi-type editor with UTF-8 support | |
| git clone git://src.adamsgaard.dk/neatvi | |
| Log | |
| Files | |
| Refs | |
| README | |
| --- | |
| tex.c (20738B) | |
| --- | |
| 1 #include <ctype.h> | |
| 2 #include <fcntl.h> | |
| 3 #include <stdio.h> | |
| 4 #include <stdlib.h> | |
| 5 #include <string.h> | |
| 6 #include <sys/stat.h> | |
| 7 #include <unistd.h> | |
| 8 #include "vi.h" | |
| 9 | |
| 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 int xlim = 256; /* do not process lines longer th… | |
| 26 static char xkwd[EXLEN]; /* the last searched keyword */ | |
| 27 static char xrep[EXLEN]; /* the last replacement */ | |
| 28 static int xkwddir; /* the last search direction */ | |
| 29 static int xgdep; /* global command recursion depth */ | |
| 30 | |
| 31 static struct buf { | |
| 32 char ft[32]; /* file type */ | |
| 33 char *path; /* file path */ | |
| 34 struct lbuf *lb; | |
| 35 int row, off, top; | |
| 36 short id, td; /* buffer id and text direction */ | |
| 37 long mtime; /* modification time */ | |
| 38 } bufs[8]; | |
| 39 | |
| 40 static int bufs_cnt = 0; /* number of allocated buffers */ | |
| 41 | |
| 42 static int bufs_find(char *path) | |
| 43 { | |
| 44 int i; | |
| 45 for (i = 0; i < LEN(bufs); i++) | |
| 46 if (bufs[i].lb && !strcmp(bufs[i].path, path)) | |
| 47 return i; | |
| 48 return -1; | |
| 49 } | |
| 50 | |
| 51 static void bufs_free(int idx) | |
| 52 { | |
| 53 if (bufs[idx].lb) { | |
| 54 free(bufs[idx].path); | |
| 55 lbuf_free(bufs[idx].lb); | |
| 56 memset(&bufs[idx], 0, sizeof(bufs[idx])); | |
| 57 } | |
| 58 } | |
| 59 | |
| 60 static long mtime(char *path) | |
| 61 { | |
| 62 struct stat st; | |
| 63 if (!stat(path, &st)) | |
| 64 return st.st_mtime; | |
| 65 return -1; | |
| 66 } | |
| 67 | |
| 68 static int bufs_open(char *path) | |
| 69 { | |
| 70 int i; | |
| 71 for (i = 0; i < LEN(bufs) - 1; i++) | |
| 72 if (!bufs[i].lb) | |
| 73 break; | |
| 74 bufs_free(i); | |
| 75 bufs[i].id = ++bufs_cnt; | |
| 76 bufs[i].path = uc_dup(path); | |
| 77 bufs[i].lb = lbuf_make(); | |
| 78 bufs[i].row = 0; | |
| 79 bufs[i].off = 0; | |
| 80 bufs[i].top = 0; | |
| 81 bufs[i].td = +1; | |
| 82 bufs[i].mtime = -1; | |
| 83 strcpy(bufs[i].ft, syn_filetype(path)); | |
| 84 return i; | |
| 85 } | |
| 86 | |
| 87 static void bufs_switch(int idx) | |
| 88 { | |
| 89 struct buf tmp; | |
| 90 bufs[0].row = xrow; | |
| 91 bufs[0].off = xoff; | |
| 92 bufs[0].top = xtop; | |
| 93 bufs[0].td = xtd; | |
| 94 memcpy(&tmp, &bufs[idx], sizeof(tmp)); | |
| 95 memmove(&bufs[1], &bufs[0], sizeof(tmp) * idx); | |
| 96 memcpy(&bufs[0], &tmp, sizeof(tmp)); | |
| 97 xrow = bufs[0].row; | |
| 98 xoff = bufs[0].off; | |
| 99 xtop = bufs[0].top; | |
| 100 xtd = bufs[0].td; | |
| 101 } | |
| 102 | |
| 103 char *ex_path(void) | |
| 104 { | |
| 105 return bufs[0].path; | |
| 106 } | |
| 107 | |
| 108 struct lbuf *ex_lbuf(void) | |
| 109 { | |
| 110 return bufs[0].lb; | |
| 111 } | |
| 112 | |
| 113 char *ex_filetype(void) | |
| 114 { | |
| 115 return bufs[0].ft; | |
| 116 } | |
| 117 | |
| 118 /* replace % and # with current and alternate path names; returns a stat… | |
| 119 static char *ex_pathexpand(char *src, int spaceallowed) | |
| 120 { | |
| 121 static char buf[1024]; | |
| 122 char *dst = buf; | |
| 123 char *end = dst + sizeof(buf); | |
| 124 while (dst + 1 < end && *src && *src != '\n' && | |
| 125 (spaceallowed || (*src != ' ' && *src != '\t')))… | |
| 126 if (*src == '%' || *src == '#') { | |
| 127 int idx = *src == '#'; | |
| 128 if (!bufs[idx].path || !bufs[idx].path[0]) { | |
| 129 ex_show("pathname \"%\" or \"#\" is not … | |
| 130 return NULL; | |
| 131 } | |
| 132 dst += snprintf(dst, end - dst, "%s", bufs[idx].… | |
| 133 src++; | |
| 134 } else { | |
| 135 if (*src == '\\' && src[1]) | |
| 136 src++; | |
| 137 *dst++ = *src++; | |
| 138 } | |
| 139 } | |
| 140 if (dst + 1 >= end) | |
| 141 dst = end - 1; | |
| 142 *dst = '\0'; | |
| 143 return buf; | |
| 144 } | |
| 145 | |
| 146 /* the previous search keyword */ | |
| 147 int ex_kwd(char **kwd, int *dir) | |
| 148 { | |
| 149 if (kwd) | |
| 150 *kwd = xkwd; | |
| 151 if (dir) | |
| 152 *dir = xkwddir; | |
| 153 return xkwddir == 0; | |
| 154 } | |
| 155 | |
| 156 /* set the previous search keyword */ | |
| 157 void ex_kwdset(char *kwd, int dir) | |
| 158 { | |
| 159 if (kwd) { | |
| 160 snprintf(xkwd, sizeof(xkwd), "%s", kwd); | |
| 161 reg_put('/', kwd, 0); | |
| 162 } | |
| 163 xkwddir = dir; | |
| 164 } | |
| 165 | |
| 166 static int ex_search(char **pat) | |
| 167 { | |
| 168 struct sbuf *kw; | |
| 169 char *b = *pat; | |
| 170 char *e = b; | |
| 171 char *pat_re; | |
| 172 struct rstr *re; | |
| 173 int dir, row; | |
| 174 kw = sbuf_make(); | |
| 175 while (*++e) { | |
| 176 if (*e == **pat) | |
| 177 break; | |
| 178 sbuf_chr(kw, (unsigned char) *e); | |
| 179 if (*e == '\\' && e[1]) | |
| 180 e++; | |
| 181 } | |
| 182 if (sbuf_len(kw)) | |
| 183 ex_kwdset(sbuf_buf(kw), **pat == '/' ? 1 : -1); | |
| 184 sbuf_free(kw); | |
| 185 *pat = *e ? e + 1 : e; | |
| 186 if (ex_kwd(&pat_re, &dir)) | |
| 187 return -1; | |
| 188 re = rstr_make(pat_re, xic ? RE_ICASE : 0); | |
| 189 if (!re) | |
| 190 return -1; | |
| 191 row = xrow + dir; | |
| 192 while (row >= 0 && row < lbuf_len(xb)) { | |
| 193 if (rstr_find(re, lbuf_get(xb, row), 0, NULL, 0) >= 0) | |
| 194 break; | |
| 195 row += dir; | |
| 196 } | |
| 197 rstr_free(re); | |
| 198 return row >= 0 && row < lbuf_len(xb) ? row : -1; | |
| 199 } | |
| 200 | |
| 201 static int ex_lineno(char **num) | |
| 202 { | |
| 203 int n = xrow; | |
| 204 switch ((unsigned char) **num) { | |
| 205 case '.': | |
| 206 *num += 1; | |
| 207 break; | |
| 208 case '$': | |
| 209 n = lbuf_len(xb) - 1; | |
| 210 *num += 1; | |
| 211 break; | |
| 212 case '\'': | |
| 213 if (lbuf_jump(xb, (unsigned char) *++(*num), &n, NULL)) | |
| 214 return -1; | |
| 215 *num += 1; | |
| 216 break; | |
| 217 case '/': | |
| 218 case '?': | |
| 219 n = ex_search(num); | |
| 220 break; | |
| 221 default: | |
| 222 if (isdigit((unsigned char) **num)) { | |
| 223 n = atoi(*num) - 1; | |
| 224 while (isdigit((unsigned char) **num)) | |
| 225 *num += 1; | |
| 226 } | |
| 227 } | |
| 228 while (**num == '-' || **num == '+') { | |
| 229 n += atoi((*num)++); | |
| 230 while (isdigit((unsigned char) **num)) | |
| 231 (*num)++; | |
| 232 } | |
| 233 return n; | |
| 234 } | |
| 235 | |
| 236 /* parse ex command addresses */ | |
| 237 static int ex_region(char *loc, int *beg, int *end) | |
| 238 { | |
| 239 int naddr = 0; | |
| 240 if (!strcmp("%", loc)) { | |
| 241 *beg = 0; | |
| 242 *end = MAX(0, lbuf_len(xb)); | |
| 243 return 0; | |
| 244 } | |
| 245 if (!*loc) { | |
| 246 *beg = xrow; | |
| 247 *end = xrow == lbuf_len(xb) ? xrow : xrow + 1; | |
| 248 return 0; | |
| 249 } | |
| 250 while (*loc) { | |
| 251 int end0 = *end; | |
| 252 *end = ex_lineno(&loc) + 1; | |
| 253 *beg = naddr++ ? end0 - 1 : *end - 1; | |
| 254 if (!naddr++) | |
| 255 *beg = *end - 1; | |
| 256 while (*loc && *loc != ';' && *loc != ',') | |
| 257 loc++; | |
| 258 if (!*loc) | |
| 259 break; | |
| 260 if (*loc == ';') | |
| 261 xrow = *end - 1; | |
| 262 loc++; | |
| 263 } | |
| 264 if (*beg < 0 && *end == 0) | |
| 265 *beg = 0; | |
| 266 if (*beg < 0 || *beg >= lbuf_len(xb)) | |
| 267 return 1; | |
| 268 if (*end < *beg || *end > lbuf_len(xb)) | |
| 269 return 1; | |
| 270 return 0; | |
| 271 } | |
| 272 | |
| 273 static int ec_write(char *loc, char *cmd, char *arg); | |
| 274 | |
| 275 static int ex_modifiedbuffer(char *msg) | |
| 276 { | |
| 277 if (!lbuf_modified(xb)) | |
| 278 return 0; | |
| 279 if (xaw && ex_path()[0]) | |
| 280 return ec_write("", "w", ""); | |
| 281 if (msg) | |
| 282 ex_show(msg); | |
| 283 return 1; | |
| 284 } | |
| 285 | |
| 286 static int ec_buffer(char *loc, char *cmd, char *arg) | |
| 287 { | |
| 288 char ln[128]; | |
| 289 int id; | |
| 290 int i; | |
| 291 id = arg[0] ? atoi(arg) : 0; | |
| 292 for (i = 0; i < LEN(bufs) && bufs[i].lb; i++) { | |
| 293 if (id) { | |
| 294 if (id == bufs[i].id) | |
| 295 break; | |
| 296 } else { | |
| 297 char c = i < 2 ? "%#"[i] : ' '; | |
| 298 snprintf(ln, LEN(ln), "%i %c %s", | |
| 299 (int) bufs[i].id, c, bufs[i].pat… | |
| 300 ex_print(ln); | |
| 301 } | |
| 302 } | |
| 303 if (id) { | |
| 304 if (i < LEN(bufs) && bufs[i].lb) | |
| 305 bufs_switch(i); | |
| 306 else | |
| 307 ex_show("no such buffer\n"); | |
| 308 } | |
| 309 return 0; | |
| 310 } | |
| 311 | |
| 312 static int ec_quit(char *loc, char *cmd, char *arg) | |
| 313 { | |
| 314 if (!strchr(cmd, '!')) | |
| 315 if (ex_modifiedbuffer("buffer modified\n")) | |
| 316 return 1; | |
| 317 xquit = 1; | |
| 318 return 0; | |
| 319 } | |
| 320 | |
| 321 static int ec_edit(char *loc, char *cmd, char *arg) | |
| 322 { | |
| 323 char msg[128]; | |
| 324 char *path; | |
| 325 int fd; | |
| 326 if (!strchr(cmd, '!')) | |
| 327 if (xb && ex_modifiedbuffer("buffer modified\n")) | |
| 328 return 1; | |
| 329 if (!(path = ex_pathexpand(arg, 0))) | |
| 330 return 1; | |
| 331 if (path[0] && bufs_find(path) >= 0) { | |
| 332 bufs_switch(bufs_find(path)); | |
| 333 return 0; | |
| 334 } | |
| 335 if (path[0] || !bufs[0].path) | |
| 336 bufs_switch(bufs_open(path)); | |
| 337 fd = open(ex_path(), O_RDONLY); | |
| 338 if (fd >= 0) { | |
| 339 int rd = lbuf_rd(xb, fd, 0, lbuf_len(xb)); | |
| 340 close(fd); | |
| 341 snprintf(msg, sizeof(msg), "\"%s\" %d lines [r]\n", | |
| 342 ex_path(), lbuf_len(xb)); | |
| 343 if (rd) | |
| 344 ex_show("read failed\n"); | |
| 345 else | |
| 346 ex_show(msg); | |
| 347 } | |
| 348 lbuf_saved(xb, path[0] != '\0'); | |
| 349 bufs[0].mtime = mtime(ex_path()); | |
| 350 xrow = MAX(0, MIN(xrow, lbuf_len(xb) - 1)); | |
| 351 xoff = 0; | |
| 352 xtop = MAX(0, MIN(xtop, lbuf_len(xb) - 1)); | |
| 353 return 0; | |
| 354 } | |
| 355 | |
| 356 static int ec_read(char *loc, char *cmd, char *arg) | |
| 357 { | |
| 358 char msg[128]; | |
| 359 int beg, end, pos; | |
| 360 char *path; | |
| 361 char *obuf; | |
| 362 int n = lbuf_len(xb); | |
| 363 path = arg[0] ? arg : ex_path(); | |
| 364 if (ex_region(loc, &beg, &end)) | |
| 365 return 1; | |
| 366 pos = lbuf_len(xb) ? end : 0; | |
| 367 if (arg[0] == '!') { | |
| 368 char *ecmd = ex_pathexpand(arg, 1); | |
| 369 if (!ecmd) | |
| 370 return 1; | |
| 371 obuf = cmd_pipe(ecmd + 1, NULL, 0, 1); | |
| 372 if (obuf) | |
| 373 lbuf_edit(xb, obuf, pos, pos); | |
| 374 free(obuf); | |
| 375 } else { | |
| 376 int fd = open(path, O_RDONLY); | |
| 377 if (fd < 0) { | |
| 378 ex_show("read failed\n"); | |
| 379 return 1; | |
| 380 } | |
| 381 if (lbuf_rd(xb, fd, pos, pos)) { | |
| 382 ex_show("read failed\n"); | |
| 383 close(fd); | |
| 384 return 1; | |
| 385 } | |
| 386 close(fd); | |
| 387 } | |
| 388 xrow = end + lbuf_len(xb) - n - 1; | |
| 389 snprintf(msg, sizeof(msg), "\"%s\" %d lines [r]\n", | |
| 390 path, lbuf_len(xb) - n); | |
| 391 ex_show(msg); | |
| 392 return 0; | |
| 393 } | |
| 394 | |
| 395 static int ec_write(char *loc, char *cmd, char *arg) | |
| 396 { | |
| 397 char msg[128]; | |
| 398 char *path; | |
| 399 char *ibuf; | |
| 400 int beg, end; | |
| 401 path = arg[0] ? arg : ex_path(); | |
| 402 if (cmd[0] == 'x' && !lbuf_modified(xb)) | |
| 403 return ec_quit("", cmd, ""); | |
| 404 if (ex_region(loc, &beg, &end)) | |
| 405 return 1; | |
| 406 if (!loc[0]) { | |
| 407 beg = 0; | |
| 408 end = lbuf_len(xb); | |
| 409 } | |
| 410 if (arg[0] == '!') { | |
| 411 char *ecmd = ex_pathexpand(arg, 1); | |
| 412 if (!ecmd) | |
| 413 return 1; | |
| 414 ibuf = lbuf_cp(xb, beg, end); | |
| 415 ex_print(NULL); | |
| 416 cmd_pipe(ecmd + 1, ibuf, 1, 0); | |
| 417 free(ibuf); | |
| 418 } else { | |
| 419 int fd; | |
| 420 if (!strchr(cmd, '!') && bufs[0].path && | |
| 421 !strcmp(bufs[0].path, path) && | |
| 422 mtime(bufs[0].path) > bufs[0].mtime) { | |
| 423 ex_show("write failed: file changed\n"); | |
| 424 return 1; | |
| 425 } | |
| 426 if (!strchr(cmd, '!') && arg[0] && mtime(arg) >= 0) { | |
| 427 ex_show("write failed: file exists\n"); | |
| 428 return 1; | |
| 429 } | |
| 430 fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, conf_mode(… | |
| 431 if (fd < 0) { | |
| 432 ex_show("write failed: cannot create file\n"); | |
| 433 return 1; | |
| 434 } | |
| 435 if (lbuf_wr(xb, fd, beg, end)) { | |
| 436 ex_show("write failed\n"); | |
| 437 close(fd); | |
| 438 return 1; | |
| 439 } | |
| 440 close(fd); | |
| 441 } | |
| 442 snprintf(msg, sizeof(msg), "\"%s\" %d lines [w]\n", | |
| 443 path, end - beg); | |
| 444 ex_show(msg); | |
| 445 if (!ex_path()[0]) { | |
| 446 free(bufs[0].path); | |
| 447 bufs[0].path = uc_dup(path); | |
| 448 } | |
| 449 if (!strcmp(ex_path(), path)) | |
| 450 lbuf_saved(xb, 0); | |
| 451 if (!strcmp(ex_path(), path)) | |
| 452 bufs[0].mtime = mtime(path); | |
| 453 if (cmd[0] == 'x' || (cmd[0] == 'w' && cmd[1] == 'q')) | |
| 454 ec_quit("", cmd, ""); | |
| 455 return 0; | |
| 456 } | |
| 457 | |
| 458 static int ec_insert(char *loc, char *cmd, char *arg) | |
| 459 { | |
| 460 struct sbuf *sb; | |
| 461 char *s; | |
| 462 int beg, end; | |
| 463 int n; | |
| 464 if (ex_region(loc, &beg, &end) && (beg != 0 || end != 0)) | |
| 465 return 1; | |
| 466 sb = sbuf_make(); | |
| 467 while ((s = ex_read(""))) { | |
| 468 if (!strcmp(".", s)) { | |
| 469 free(s); | |
| 470 break; | |
| 471 } | |
| 472 sbuf_str(sb, s); | |
| 473 sbuf_chr(sb, '\n'); | |
| 474 free(s); | |
| 475 } | |
| 476 if (cmd[0] == 'a') | |
| 477 if (beg + 1 <= lbuf_len(xb)) | |
| 478 beg++; | |
| 479 if (cmd[0] != 'c') | |
| 480 end = beg; | |
| 481 n = lbuf_len(xb); | |
| 482 lbuf_edit(xb, sbuf_buf(sb), beg, end); | |
| 483 xrow = MIN(lbuf_len(xb) - 1, end + lbuf_len(xb) - n - 1); | |
| 484 sbuf_free(sb); | |
| 485 return 0; | |
| 486 } | |
| 487 | |
| 488 static int ec_print(char *loc, char *cmd, char *arg) | |
| 489 { | |
| 490 int beg, end; | |
| 491 int i; | |
| 492 if (!cmd[0] && !loc[0]) | |
| 493 if (xrow >= lbuf_len(xb)) | |
| 494 return 1; | |
| 495 if (ex_region(loc, &beg, &end)) | |
| 496 return 1; | |
| 497 for (i = beg; i < end; i++) | |
| 498 ex_print(lbuf_get(xb, i)); | |
| 499 xrow = end; | |
| 500 xoff = 0; | |
| 501 return 0; | |
| 502 } | |
| 503 | |
| 504 static int ec_null(char *loc, char *cmd, char *arg) | |
| 505 { | |
| 506 int beg, end; | |
| 507 if (!xvis) | |
| 508 return ec_print(loc, cmd, arg); | |
| 509 if (ex_region(loc, &beg, &end)) | |
| 510 return 1; | |
| 511 xrow = MAX(beg, end - 1); | |
| 512 xoff = 0; | |
| 513 return 0; | |
| 514 } | |
| 515 | |
| 516 static void ex_yank(int reg, int beg, int end) | |
| 517 { | |
| 518 char *buf = lbuf_cp(xb, beg, end); | |
| 519 reg_put(reg, buf, 1); | |
| 520 free(buf); | |
| 521 } | |
| 522 | |
| 523 static int ec_delete(char *loc, char *cmd, char *arg) | |
| 524 { | |
| 525 int beg, end; | |
| 526 if (ex_region(loc, &beg, &end) || !lbuf_len(xb)) | |
| 527 return 1; | |
| 528 ex_yank(arg[0], beg, end); | |
| 529 lbuf_edit(xb, NULL, beg, end); | |
| 530 xrow = beg; | |
| 531 return 0; | |
| 532 } | |
| 533 | |
| 534 static int ec_yank(char *loc, char *cmd, char *arg) | |
| 535 { | |
| 536 int beg, end; | |
| 537 if (ex_region(loc, &beg, &end) || !lbuf_len(xb)) | |
| 538 return 1; | |
| 539 ex_yank(arg[0], beg, end); | |
| 540 return 0; | |
| 541 } | |
| 542 | |
| 543 static int ec_put(char *loc, char *cmd, char *arg) | |
| 544 { | |
| 545 int beg, end; | |
| 546 int lnmode; | |
| 547 char *buf; | |
| 548 int n = lbuf_len(xb); | |
| 549 buf = reg_get(arg[0], &lnmode); | |
| 550 if (!buf || ex_region(loc, &beg, &end)) | |
| 551 return 1; | |
| 552 lbuf_edit(xb, buf, end, end); | |
| 553 xrow = MIN(lbuf_len(xb) - 1, end + lbuf_len(xb) - n - 1); | |
| 554 return 0; | |
| 555 } | |
| 556 | |
| 557 static int ec_lnum(char *loc, char *cmd, char *arg) | |
| 558 { | |
| 559 char msg[128]; | |
| 560 int beg, end; | |
| 561 if (ex_region(loc, &beg, &end)) | |
| 562 return 1; | |
| 563 sprintf(msg, "%d\n", end); | |
| 564 ex_print(msg); | |
| 565 return 0; | |
| 566 } | |
| 567 | |
| 568 static int ec_undo(char *loc, char *cmd, char *arg) | |
| 569 { | |
| 570 return lbuf_undo(xb); | |
| 571 } | |
| 572 | |
| 573 static int ec_redo(char *loc, char *cmd, char *arg) | |
| 574 { | |
| 575 return lbuf_redo(xb); | |
| 576 } | |
| 577 | |
| 578 static int ec_mark(char *loc, char *cmd, char *arg) | |
| 579 { | |
| 580 int beg, end; | |
| 581 if (ex_region(loc, &beg, &end)) | |
| 582 return 1; | |
| 583 lbuf_mark(xb, arg[0], end - 1, 0); | |
| 584 return 0; | |
| 585 } | |
| 586 | |
| 587 static void replace(struct sbuf *dst, char *rep, char *ln, int *offs) | |
| 588 { | |
| 589 while (rep[0]) { | |
| 590 if (rep[0] == '\\' && rep[1]) { | |
| 591 if (rep[1] >= '0' && rep[1] <= '9') { | |
| 592 int grp = (rep[1] - '0') * 2; | |
| 593 int len = offs[grp + 1] - offs[grp]; | |
| 594 sbuf_mem(dst, ln + offs[grp], len); | |
| 595 } else { | |
| 596 sbuf_chr(dst, (unsigned char) rep[1]); | |
| 597 } | |
| 598 rep++; | |
| 599 } else { | |
| 600 sbuf_chr(dst, (unsigned char) rep[0]); | |
| 601 } | |
| 602 rep++; | |
| 603 } | |
| 604 } | |
| 605 | |
| 606 static int ec_substitute(char *loc, char *cmd, char *arg) | |
| 607 { | |
| 608 struct rstr *re; | |
| 609 int offs[32]; | |
| 610 int beg, end; | |
| 611 char *pat = NULL, *rep = NULL; | |
| 612 char *s = arg; | |
| 613 int i; | |
| 614 if (ex_region(loc, &beg, &end)) | |
| 615 return 1; | |
| 616 pat = re_read(&s); | |
| 617 if (pat && pat[0]) | |
| 618 ex_kwdset(pat, +1); | |
| 619 if (pat && *s) { | |
| 620 s--; | |
| 621 rep = re_read(&s); | |
| 622 } | |
| 623 if (pat || rep) | |
| 624 snprintf(xrep, sizeof(xrep), "%s", rep ? rep : ""); | |
| 625 free(pat); | |
| 626 free(rep); | |
| 627 if (ex_kwd(&pat, NULL)) | |
| 628 return 1; | |
| 629 re = rstr_make(pat, xic ? RE_ICASE : 0); | |
| 630 if (!re) | |
| 631 return 1; | |
| 632 for (i = beg; i < end; i++) { | |
| 633 char *ln = lbuf_get(xb, i); | |
| 634 struct sbuf *r = NULL; | |
| 635 while (rstr_find(re, ln, LEN(offs) / 2, offs, 0) >= 0) { | |
| 636 if (!r) | |
| 637 r = sbuf_make(); | |
| 638 sbuf_mem(r, ln, offs[0]); | |
| 639 replace(r, xrep, ln, offs); | |
| 640 ln += offs[1]; | |
| 641 if (offs[1] <= 0) /* zero-length match */ | |
| 642 sbuf_chr(r, (unsigned char) *ln++); | |
| 643 if (!*ln || *ln == '\n' || !strchr(s, 'g')) | |
| 644 break; | |
| 645 } | |
| 646 if (r) { | |
| 647 sbuf_str(r, ln); | |
| 648 lbuf_edit(xb, sbuf_buf(r), i, i + 1); | |
| 649 sbuf_free(r); | |
| 650 } | |
| 651 } | |
| 652 rstr_free(re); | |
| 653 return 0; | |
| 654 } | |
| 655 | |
| 656 static int ec_exec(char *loc, char *cmd, char *arg) | |
| 657 { | |
| 658 int beg, end; | |
| 659 char *text; | |
| 660 char *rep; | |
| 661 char *ecmd; | |
| 662 ex_modifiedbuffer(NULL); | |
| 663 if (!(ecmd = ex_pathexpand(arg, 1))) | |
| 664 return 1; | |
| 665 if (!loc[0]) { | |
| 666 ex_print(NULL); | |
| 667 return cmd_exec(ecmd); | |
| 668 } | |
| 669 if (ex_region(loc, &beg, &end)) | |
| 670 return 1; | |
| 671 text = lbuf_cp(xb, beg, end); | |
| 672 rep = cmd_pipe(ecmd, text, 1, 1); | |
| 673 if (rep) | |
| 674 lbuf_edit(xb, rep, beg, end); | |
| 675 free(text); | |
| 676 free(rep); | |
| 677 return 0; | |
| 678 } | |
| 679 | |
| 680 static int ec_make(char *loc, char *cmd, char *arg) | |
| 681 { | |
| 682 char make[EXLEN]; | |
| 683 char *target; | |
| 684 ex_modifiedbuffer(NULL); | |
| 685 if (!(target = ex_pathexpand(arg, 0))) | |
| 686 return 1; | |
| 687 sprintf(make, "make %s", target); | |
| 688 ex_print(NULL); | |
| 689 if (cmd_exec(make)) | |
| 690 return 1; | |
| 691 return 0; | |
| 692 } | |
| 693 | |
| 694 static int ec_ft(char *loc, char *cmd, char *arg) | |
| 695 { | |
| 696 if (arg[0]) | |
| 697 snprintf(bufs[0].ft, sizeof(bufs[0].ft), "%s", arg); | |
| 698 else | |
| 699 ex_print(ex_filetype()); | |
| 700 return 0; | |
| 701 } | |
| 702 | |
| 703 static int ec_cmap(char *loc, char *cmd, char *arg) | |
| 704 { | |
| 705 if (arg[0]) | |
| 706 xkmap_alt = conf_kmapfind(arg); | |
| 707 else | |
| 708 ex_print(conf_kmap(xkmap)[0]); | |
| 709 if (arg[0] && !strchr(cmd, '!')) | |
| 710 xkmap = xkmap_alt; | |
| 711 return 0; | |
| 712 } | |
| 713 | |
| 714 static int ex_exec(char *ln); | |
| 715 | |
| 716 static int ec_glob(char *loc, char *cmd, char *arg) | |
| 717 { | |
| 718 struct rstr *re; | |
| 719 int offs[32]; | |
| 720 int beg, end, not; | |
| 721 char *pat; | |
| 722 char *s = arg; | |
| 723 int i; | |
| 724 if (!loc[0] && !xgdep) | |
| 725 strcpy(loc, "%"); | |
| 726 if (ex_region(loc, &beg, &end)) | |
| 727 return 1; | |
| 728 not = strchr(cmd, '!') || cmd[0] == 'v'; | |
| 729 pat = re_read(&s); | |
| 730 if (pat && pat[0]) | |
| 731 ex_kwdset(pat, +1); | |
| 732 free(pat); | |
| 733 if (ex_kwd(&pat, NULL)) | |
| 734 return 1; | |
| 735 if (!(re = rstr_make(pat, xic ? RE_ICASE : 0))) | |
| 736 return 1; | |
| 737 xgdep++; | |
| 738 for (i = beg + 1; i < end; i++) | |
| 739 lbuf_globset(xb, i, xgdep); | |
| 740 i = beg; | |
| 741 while (i < lbuf_len(xb)) { | |
| 742 char *ln = lbuf_get(xb, i); | |
| 743 if ((rstr_find(re, ln, LEN(offs) / 2, offs, 0) < 0) == n… | |
| 744 xrow = i; | |
| 745 if (ex_exec(s)) | |
| 746 break; | |
| 747 i = MIN(i, xrow); | |
| 748 } | |
| 749 while (i < lbuf_len(xb) && !lbuf_globget(xb, i, xgdep)) | |
| 750 i++; | |
| 751 } | |
| 752 for (i = 0; i < lbuf_len(xb); i++) | |
| 753 lbuf_globget(xb, i, xgdep); | |
| 754 xgdep--; | |
| 755 rstr_free(re); | |
| 756 return 0; | |
| 757 } | |
| 758 | |
| 759 static struct option { | |
| 760 char *abbr; | |
| 761 char *name; | |
| 762 int *var; | |
| 763 } options[] = { | |
| 764 {"ai", "autoindent", &xai}, | |
| 765 {"aw", "autowrite", &xaw}, | |
| 766 {"ic", "ignorecase", &xic}, | |
| 767 {"td", "textdirection", &xtd}, | |
| 768 {"shape", "shape", &xshape}, | |
| 769 {"order", "xorder", &xorder}, | |
| 770 {"hl", "highlight", &xhl}, | |
| 771 {"hll", "highlightline", &xhll}, | |
| 772 {"lim", "linelimit", &xlim}, | |
| 773 }; | |
| 774 | |
| 775 static char *cutword(char *s, char *d) | |
| 776 { | |
| 777 while (isspace(*s)) | |
| 778 s++; | |
| 779 while (*s && !isspace(*s)) | |
| 780 *d++ = *s++; | |
| 781 while (isspace(*s)) | |
| 782 s++; | |
| 783 *d = '\0'; | |
| 784 return s; | |
| 785 } | |
| 786 | |
| 787 static int ec_set(char *loc, char *cmd, char *arg) | |
| 788 { | |
| 789 char tok[EXLEN]; | |
| 790 char opt[EXLEN]; | |
| 791 char *s = arg; | |
| 792 int val = 0; | |
| 793 int i; | |
| 794 if (*s) { | |
| 795 s = cutword(s, tok); | |
| 796 if (tok[0] == 'n' && tok[1] == 'o') { | |
| 797 strcpy(opt, tok + 2); | |
| 798 val = 0; | |
| 799 } else { | |
| 800 char *r = strchr(tok, '='); | |
| 801 if (r) { | |
| 802 *r = '\0'; | |
| 803 strcpy(opt, tok); | |
| 804 val = atoi(r + 1); | |
| 805 } else { | |
| 806 strcpy(opt, tok); | |
| 807 val = 1; | |
| 808 } | |
| 809 } | |
| 810 for (i = 0; i < LEN(options); i++) { | |
| 811 struct option *o = &options[i]; | |
| 812 if (!strcmp(o->abbr, opt) || !strcmp(o->name, op… | |
| 813 *o->var = val; | |
| 814 return 0; | |
| 815 } | |
| 816 } | |
| 817 ex_show("unknown option"); | |
| 818 return 1; | |
| 819 } | |
| 820 return 0; | |
| 821 } | |
| 822 | |
| 823 static struct excmd { | |
| 824 char *abbr; | |
| 825 char *name; | |
| 826 int (*ec)(char *loc, char *cmd, char *arg); | |
| 827 } excmds[] = { | |
| 828 {"b", "buffer", ec_buffer}, | |
| 829 {"p", "print", ec_print}, | |
| 830 {"a", "append", ec_insert}, | |
| 831 {"i", "insert", ec_insert}, | |
| 832 {"d", "delete", ec_delete}, | |
| 833 {"c", "change", ec_insert}, | |
| 834 {"e", "edit", ec_edit}, | |
| 835 {"e!", "edit!", ec_edit}, | |
| 836 {"g", "global", ec_glob}, | |
| 837 {"g!", "global!", ec_glob}, | |
| 838 {"=", "=", ec_lnum}, | |
| 839 {"k", "mark", ec_mark}, | |
| 840 {"pu", "put", ec_put}, | |
| 841 {"q", "quit", ec_quit}, | |
| 842 {"q!", "quit!", ec_quit}, | |
| 843 {"r", "read", ec_read}, | |
| 844 {"v", "vglobal", ec_glob}, | |
| 845 {"w", "write", ec_write}, | |
| 846 {"w!", "write!", ec_write}, | |
| 847 {"wq", "wq", ec_write}, | |
| 848 {"wq!", "wq!", ec_write}, | |
| 849 {"u", "undo", ec_undo}, | |
| 850 {"redo", "redo", ec_redo}, | |
| 851 {"se", "set", ec_set}, | |
| 852 {"s", "substitute", ec_substitute}, | |
| 853 {"x", "xit", ec_write}, | |
| 854 {"x!", "xit!", ec_write}, | |
| 855 {"ya", "yank", ec_yank}, | |
| 856 {"!", "!", ec_exec}, | |
| 857 {"make", "make", ec_make}, | |
| 858 {"ft", "filetype", ec_ft}, | |
| 859 {"cm", "cmap", ec_cmap}, | |
| 860 {"cm!", "cmap!", ec_cmap}, | |
| 861 {"", "", ec_null}, | |
| 862 }; | |
| 863 | |
| 864 static int ex_idx(char *cmd) | |
| 865 { | |
| 866 int i; | |
| 867 for (i = 0; i < LEN(excmds); i++) | |
| 868 if (!strcmp(excmds[i].abbr, cmd) || !strcmp(excmds[i].na… | |
| 869 return i; | |
| 870 return -1; | |
| 871 } | |
| 872 | |
| 873 /* read ex command addresses */ | |
| 874 static char *ex_loc(char *src, char *loc) | |
| 875 { | |
| 876 while (*src == ':' || *src == ' ' || *src == '\t') | |
| 877 src++; | |
| 878 while (*src && !isalpha((unsigned char) *src) && *src != '=' && … | |
| 879 if (*src == '\'') | |
| 880 *loc++ = *src++; | |
| 881 if (*src == '/' || *src == '?') { | |
| 882 int d = *src; | |
| 883 *loc++ = *src++; | |
| 884 while (*src && *src != d) { | |
| 885 if (*src == '\\' && src[1]) | |
| 886 *loc++ = *src++; | |
| 887 *loc++ = *src++; | |
| 888 } | |
| 889 } | |
| 890 if (*src) | |
| 891 *loc++ = *src++; | |
| 892 } | |
| 893 *loc = '\0'; | |
| 894 return src; | |
| 895 } | |
| 896 | |
| 897 /* read ex command name */ | |
| 898 static char *ex_cmd(char *src, char *cmd) | |
| 899 { | |
| 900 char *cmd0 = cmd; | |
| 901 while (*src == ' ' || *src == '\t') | |
| 902 src++; | |
| 903 while (isalpha((unsigned char) *src) && cmd < cmd0 + 16) | |
| 904 if ((*cmd++ = *src++) == 'k' && cmd == cmd0 + 1) | |
| 905 break; | |
| 906 if (*src == '!' || *src == '=') | |
| 907 *cmd++ = *src++; | |
| 908 *cmd = '\0'; | |
| 909 return src; | |
| 910 } | |
| 911 | |
| 912 /* read ex command argument for excmd command */ | |
| 913 static char *ex_arg(char *src, char *dst, char *excmd) | |
| 914 { | |
| 915 int c0 = excmd[0]; | |
| 916 int c1 = excmd[1]; | |
| 917 while (*src == ' ' || *src == '\t') | |
| 918 src++; | |
| 919 if (c0 == '!' || c0 == 'g' || c0 == 'v' || | |
| 920 ((c0 == 'r' || c0 == 'w') && src[0] == '!')) { | |
| 921 while (*src && *src != '\n') { | |
| 922 if (*src == '\\' && src[1]) | |
| 923 *dst++ = *src++; | |
| 924 *dst++ = *src++; | |
| 925 } | |
| 926 } else if ((c0 == 's' && c1 != 'e') || c0 == '&' || c0 == '~') { | |
| 927 int delim = *src; | |
| 928 int cnt = 2; | |
| 929 *dst++ = *src++; | |
| 930 while (*src && *src != '\n' && cnt > 0) { | |
| 931 if (*src == delim) | |
| 932 cnt--; | |
| 933 if (*src == '\\' && src[1]) | |
| 934 *dst++ = *src++; | |
| 935 *dst++ = *src++; | |
| 936 } | |
| 937 } | |
| 938 while (*src && *src != '\n' && *src != '|' && *src != '"') { | |
| 939 if (*src == '\\' && src[1]) | |
| 940 *dst++ = *src++; | |
| 941 *dst++ = *src++; | |
| 942 } | |
| 943 if (*src == '"') { | |
| 944 while (*src != '\n') | |
| 945 src++; | |
| 946 } | |
| 947 if (*src == '\n' || *src == '|') | |
| 948 src++; | |
| 949 *dst = '\0'; | |
| 950 return src; | |
| 951 } | |
| 952 | |
| 953 /* execute a single ex command */ | |
| 954 static int ex_exec(char *ln) | |
| 955 { | |
| 956 char loc[EXLEN], cmd[EXLEN], arg[EXLEN]; | |
| 957 int ret = 0; | |
| 958 if (strlen(ln) >= EXLEN) { | |
| 959 ex_show("command too long"); | |
| 960 return 1; | |
| 961 } | |
| 962 while (*ln) { | |
| 963 int idx; | |
| 964 ln = ex_loc(ln, loc); | |
| 965 ln = ex_cmd(ln, cmd); | |
| 966 idx = ex_idx(cmd); | |
| 967 ln = ex_arg(ln, arg, idx >= 0 ? excmds[idx].abbr : "unkn… | |
| 968 if (idx >= 0) | |
| 969 ret = excmds[idx].ec(loc, cmd, arg); | |
| 970 } | |
| 971 return ret; | |
| 972 } | |
| 973 | |
| 974 /* execute a single ex command */ | |
| 975 void ex_command(char *ln) | |
| 976 { | |
| 977 ex_exec(ln); | |
| 978 lbuf_modified(xb); | |
| 979 reg_put(':', ln, 0); | |
| 980 } | |
| 981 | |
| 982 /* ex main loop */ | |
| 983 void ex(void) | |
| 984 { | |
| 985 while (!xquit) { | |
| 986 char *ln = ex_read(":"); | |
| 987 if (ln) | |
| 988 ex_command(ln); | |
| 989 free(ln); | |
| 990 } | |
| 991 } | |
| 992 | |
| 993 int ex_init(char **files) | |
| 994 { | |
| 995 char arg[EXLEN]; | |
| 996 char *s = arg; | |
| 997 char *r = files[0] ? files[0] : ""; | |
| 998 while (*r && s + 2 < arg + sizeof(arg)) { | |
| 999 if (*r == ' ' || *r == '%' || *r == '#') | |
| 1000 *s++ = '\\'; | |
| 1001 *s++ = *r++; | |
| 1002 } | |
| 1003 *s = '\0'; | |
| 1004 if (ec_edit("", "e", arg)) | |
| 1005 return 1; | |
| 1006 if (getenv("EXINIT")) | |
| 1007 ex_command(getenv("EXINIT")); | |
| 1008 return 0; | |
| 1009 } | |
| 1010 | |
| 1011 void ex_done(void) | |
| 1012 { | |
| 1013 int i; | |
| 1014 for (i = 0; i < LEN(bufs); i++) | |
| 1015 bufs_free(i); | |
| 1016 } |