/* extract_pattern: copy a pattern string from the command buffer; return pointer to the copy */ char * extract_pattern(int delimiter) { static char *lhbuf = NULL; /* buffer */ static int lhbufsz = 0; /* buffer size */ char *nd; int len; for (nd = ibufp; *nd != delimiter && *nd != '\n'; nd++) switch (*nd) { default: break; case '[': if ((nd = parse_char_class(++nd)) == NULL) { seterrmsg("unbalanced brackets ([])"); return NULL; } break; case '\\': if (*++nd == '\n') { seterrmsg("trailing backslash (\\)"); return NULL; } break; } len = nd - ibufp; REALLOC(lhbuf, lhbufsz, len + 1, NULL); memcpy(lhbuf, ibufp, len); lhbuf[len] = '\0'; ibufp = nd; return (isbinary) ? NUL_TO_NEWLINE(lhbuf, len) : lhbuf; }
/* push_undo_stack: return pointer to initialized undo node */ undo_t * push_undo_stack(int type, long from, long to) { undo_t *t; #if defined(sun) || defined(NO_REALLOC_NULL) if (ustack == NULL && (ustack = (undo_t *) malloc((usize = USIZE) * sizeof(undo_t))) == NULL) { fprintf(stderr, "%s\n", strerror(errno)); seterrmsg("out of memory"); return NULL; } #endif t = ustack; if (u_p < usize || (t = (undo_t *) realloc(ustack, (usize += USIZE) * sizeof(undo_t))) != NULL) { ustack = t; ustack[u_p].type = type; ustack[u_p].t = get_addressed_line_node(to); ustack[u_p].h = get_addressed_line_node(from); return ustack + u_p++; } /* out of memory - release undo stack */ fprintf(stderr, "%s\n", strerror(errno)); seterrmsg("out of memory"); clear_undo_stack(); free(ustack); ustack = NULL; usize = 0; return NULL; }
/* set_active_node: add a line node to the global-active list */ int set_active_node(line_t *lp) { if (active_last + 1 > active_size) { int ti = active_size; line_t **ts; SPL1(); #if defined(sun) || defined(NO_REALLOC_NULL) if (active_list != NULL) { #endif if ((ts = (line_t **) realloc(active_list, (ti += MINBUFSZ) * sizeof(line_t **))) == NULL) { fprintf(stderr, "%s\n", strerror(errno)); seterrmsg("out of memory"); SPL0(); return ERR; } #if defined(sun) || defined(NO_REALLOC_NULL) } else { if ((ts = (line_t **) malloc((ti += MINBUFSZ) * sizeof(line_t **))) == NULL) { fprintf(stderr, "%s\n", strerror(errno)); seterrmsg("out of memory"); SPL0(); return ERR; } } #endif active_size = ti; active_list = ts; SPL0(); } active_list[active_last++] = lp; return 0; }
/* get_compiled_pattern: return pointer to compiled pattern from command buffer */ pattern_t * get_compiled_pattern(void) { static pattern_t *exp = NULL; char *exps; char delimiter; int n; if ((delimiter = *ibufp) == ' ') { seterrmsg("invalid pattern delimiter"); return NULL; } else if (delimiter == '\n' || *++ibufp == '\n' || *ibufp == delimiter) { if (!exp) seterrmsg("no previous pattern"); return exp; } else if ((exps = extract_pattern(delimiter)) == NULL) return NULL; /* buffer alloc'd && not reserved */ if (exp && !patlock) regfree(exp); else if ((exp = (pattern_t *) malloc(sizeof(pattern_t))) == NULL) { perror(NULL); seterrmsg("out of memory"); return NULL; } patlock = 0; if ((n = regcomp(exp, exps, 0)) != 0) { regerror(n, exp, errmsg, sizeof errmsg); free(exp); return exp = NULL; } return exp; }
/* exec_global: apply command list in the command buffer to the active lines in a range; return command status */ int exec_global(int interact, int gflag) { static char *ocmd = NULL; static int ocmdsz = 0; line_t *lp = NULL; int status; int n; char *cmd = NULL; if (!interact) { if (!strcmp(ibufp, "\n")) cmd = "p\n"; /* null cmd-list == `p' */ else if ((cmd = get_extended_line(&n, 0)) == NULL) return ERR; } clear_undo_stack(); while ((lp = next_active_node()) != NULL) { if ((current_addr = get_line_node_addr(lp)) < 0) return ERR; if (interact) { /* print current_addr; get a command in global syntax */ if (display_lines(current_addr, current_addr, gflag) < 0) return ERR; while ((n = get_tty_line()) > 0 && ibuf[n - 1] != '\n') clearerr(stdin); if (n < 0) return ERR; else if (n == 0) { seterrmsg("unexpected end-of-file"); return ERR; } else if (n == 1 && !strcmp(ibuf, "\n")) continue; else if (n == 2 && !strcmp(ibuf, "&\n")) { if (cmd == NULL) { seterrmsg("no previous command"); return ERR; } else cmd = ocmd; } else if ((cmd = get_extended_line(&n, 0)) == NULL) return ERR; else { REALLOC(ocmd, ocmdsz, n + 1, ERR); memcpy(ocmd, cmd, n + 1); cmd = ocmd; } } ibufp = cmd; for (; *ibufp;) if ((status = extract_addr_range()) < 0 || (status = exec_command()) < 0 || (status > 0 && (status = display_lines( current_addr, current_addr, status)) < 0)) return status; } return 0; }
tpl_doc *tpl_ctx_get_tpl_doc(tpl_ctx *t, const char *inpath) { char tpath[MAXPATHLEN] = { 0 }; int err; tpl_doc *tdoc = NULL; tpl_doc *ndoc; tpl_doc *mdoc; const char *ext = NULL; char pext[MAXPATHLEN] = { 0 }; const char *next = NULL; int off = 0; ext = getext(inpath); if(ext == NULL) { seterrmsg(t, "File path has no extensions"); return NULL; } do { if(next != NULL) { if(strcmp(next, pext) != 0) off = 0; ext = next; } err = get_tpl_path(t, ext, tpath, &off); if(err) { if(tdoc != NULL) return tdoc; seterrmsg(t, "Did not find template for file extension '%s'", ext); return NULL; } ndoc = tpl_doc_parse(t, tpath); if(ndoc == NULL) { seterrmsg(t, "Failed to parse template file '%s': %s", tpath, tpl_ctx_error(t)); return NULL; } if(tdoc == NULL) tdoc = ndoc; else { mdoc = tpl_doc_merge(ndoc, tdoc); strncpy(pext, ext, sizeof(pext)); tpl_doc_destroy(tdoc); tpl_doc_destroy(ndoc); tdoc = mdoc; } } while((next = tpl_doc_get_definition(tdoc, "-template")) != NULL); return tdoc; }
/* build_active_list: add line matching a pattern to the global-active list */ int build_active_list(int isgcmd) { regex_t *pat; line_t *lp; int n; char *s; char delimiter; if ((delimiter = *ibufp) == ' ' || delimiter == '\n') { seterrmsg("invalid pattern delimiter"); return ERR; } else if ((pat = get_compiled_pattern()) == NULL) return ERR; else if (*ibufp == delimiter) ibufp++; clear_active_list(); lp = get_addressed_line_node(first_addr); for (n = first_addr; n <= second_addr; n++, lp = lp->q_forw) { if ((s = get_sbuf_line(lp)) == NULL) return ERR; if (isbinary) NUL_TO_NEWLINE(s, lp->len); if (!regexec(pat, s, 0, NULL, 0) == isgcmd && set_active_node(lp) < 0) return ERR; } return 0; }
/* get_stream_line: read a line of text from a stream; return line length */ int get_stream_line(FILE *fp) { int c; int i = 0; while (((c = DESGETCHAR(fp)) != EOF || (!feof(fp) && !ferror(fp))) && c != '\n') { REALLOC(sbuf, sbufsz, i + 1, ERR); if (!(sbuf[i++] = c)) isbinary = 1; } REALLOC(sbuf, sbufsz, i + 2, ERR); if (c == '\n') sbuf[i++] = c; else if (ferror(fp)) { perror(NULL); seterrmsg("cannot read input file"); return ERR; } else if (i) { sbuf[i++] = '\n'; newline_added = 1; } sbuf[i] = '\0'; return (isbinary && newline_added && i) ? --i : i; }
/* get_stream_line: read a line of text from a stream; return line length */ int get_stream_line(FILE *fp) { int c; int i = 0; while (((c = des ? get_des_char(fp) : getc(fp)) != EOF || (!feof(fp) && !ferror(fp))) && c != '\n') { REALLOC(sbuf, sbufsz, i + 1, ERR); if (!(sbuf[i++] = c)) isbinary = 1; } REALLOC(sbuf, sbufsz, i + 2, ERR); if (c == '\n') sbuf[i++] = c; else if (ferror(fp)) { fprintf(stderr, "%s\n", strerror(errno)); seterrmsg("cannot read input file"); return ERR; } else if (i) { sbuf[i++] = '\n'; newline_added = 1; } sbuf[i] = '\0'; return (isbinary && newline_added && i) ? --i : i; }
/* extract_subst_template: return pointer to copy of substitution template in the command buffer */ char * extract_subst_template(void) { int n = 0; int i = 0; char c; char delimiter = *ibufp++; if (*ibufp == '%' && *(ibufp + 1) == delimiter) { ibufp++; if (!rhbuf) seterrmsg("no previous substitution"); return rhbuf; } while (*ibufp != delimiter) { REALLOC(rhbuf, rhbufsz, i + 2, NULL); if ((c = rhbuf[i++] = *ibufp++) == '\n' && *ibufp == '\0') { i--, ibufp--; break; } else if (c != '\\') ; else if ((rhbuf[i++] = *ibufp++) != '\n') ; else if (!isglobal) { while ((n = get_tty_line()) == 0 || (n > 0 && ibuf[n - 1] != '\n')) clearerr(stdin); if (n < 0) return NULL; } } REALLOC(rhbuf, rhbufsz, i + 1, NULL); rhbuf[rhbufi = i] = '\0'; return rhbuf; }
/* put_stream_line: write a line of text to a stream; return status */ int put_stream_line(FILE *fp, char *s, int len) { while (len--) if ((des ? put_des_char(*s++, fp) : fputc(*s++, fp)) < 0) { fprintf(stderr, "%s\n", strerror(errno)); seterrmsg("cannot write file"); return ERR; } return 0; }
/* write_file: write a range of lines to a named file/pipe; return line count */ int write_file(char *fn, char *mode, int n, int m) { FILE *fp; int size; fp = (*fn == '!') ? popen(fn+1, "w") : fopen(strip_escapes(fn), mode); if (fp == NULL) { perror(fn); seterrmsg("cannot open output file"); return ERR; } else if ((size = write_stream(fp, n, m)) < 0) return ERR; else if (((*fn == '!') ? pclose(fp) : fclose(fp)) < 0) { perror(fn); seterrmsg("cannot close output file"); return ERR; } fprintf(stderr, !scripted ? "%d\n" : "", size); return n ? m - n + 1 : 0; }
/* substitute_matching_text: replace text matched by a pattern according to a substitution template; return pointer to the modified text */ int substitute_matching_text(pattern_t *pat, line_t *lp, int gflag, int kth) { int off = 0; int changed = 0; int matchno = 0; int i = 0; regmatch_t rm[SE_MAX]; char *txt; char *eot; if ((txt = get_sbuf_line(lp)) == NULL) return ERR; if (isbinary) NUL_TO_NEWLINE(txt, lp->len); eot = txt + lp->len; if (!regexec(pat, txt, SE_MAX, rm, 0)) { do { if (!kth || kth == ++matchno) { changed++; i = rm[0].rm_so; REALLOC(rbuf, rbufsz, off + i, ERR); if (isbinary) NEWLINE_TO_NUL(txt, rm[0].rm_eo); memcpy(rbuf + off, txt, i); off += i; if ((off = apply_subst_template(txt, rm, off, pat->re_nsub)) < 0) return ERR; } else { i = rm[0].rm_eo; REALLOC(rbuf, rbufsz, off + i, ERR); if (isbinary) NEWLINE_TO_NUL(txt, i); memcpy(rbuf + off, txt, i); off += i; } txt += rm[0].rm_eo; } while (*txt && (!changed || ((gflag & GSG) && rm[0].rm_eo)) && !regexec(pat, txt, SE_MAX, rm, REG_NOTBOL)); i = eot - txt; REALLOC(rbuf, rbufsz, off + i + 2, ERR); if (i > 0 && !rm[0].rm_eo && (gflag & GSG)) { seterrmsg("infinite substitution loop"); return ERR; } if (isbinary) NEWLINE_TO_NUL(txt, i); memcpy(rbuf + off, txt, i); memcpy(rbuf + off + i, "\n", 2); } return changed ? off + i + 1 : 0; }
/* write_file: write a range of lines to a named file/pipe; return line count */ long write_file(const char *fn, const char *mode, long n, long m) { FILE *fp; long size; fp = (*fn == '!') ? popen(fn+1, "w") : fopen(strip_escapes(fn), mode); if (fp == NULL) { fprintf(stderr, "%s: %s\n", fn, strerror(errno)); seterrmsg("cannot open output file"); return ERR; } else if ((size = write_stream(fp, n, m)) < 0) return ERR; else if (((*fn == '!') ? pclose(fp) : fclose(fp)) < 0) { fprintf(stderr, "%s: %s\n", fn, strerror(errno)); seterrmsg("cannot close output file"); return ERR; } if (!scripted) fprintf(stderr, "%lu\n", size); return n ? m - n + 1 : 0; }
/* put_stream_line: write a line of text to a stream; return status */ int put_stream_line(FILE *fp, char *s, int len) { while (len--) { if (DESPUTCHAR(*s, fp) < 0) { perror(NULL); seterrmsg("cannot write file"); return ERR; } s++; } return 0; }
/* read_file: read a named file/pipe into the buffer; return line count */ int read_file(char *fn, int n) { FILE *fp; int size; fp = (*fn == '!') ? popen(fn + 1, "r") : fopen(strip_escapes(fn), "r"); if (fp == NULL) { perror(fn); seterrmsg("cannot open input file"); return ERR; } else if ((size = read_stream(fp, n)) < 0) return ERR; else if (((*fn == '!') ? pclose(fp) : fclose(fp)) < 0) { perror(fn); seterrmsg("cannot close input file"); return ERR; } fprintf(stderr, !scripted ? "%d\n" : "", size); return current_addr - n; }
int tpl_ctx_process(tpl_ctx *ctx, const char *inpath, void *in, void *out) { tpl_doc *tdoc; tdoc = tpl_ctx_get_tpl_doc(ctx, inpath); if(tdoc == NULL) { seterrmsg(ctx, "Failed to gather template for file '%s': %s", inpath, tpl_ctx_error(ctx)); return -1; } tpl_doc_process(tdoc, in, out); return 0; }
/* read_file: read a named file/pipe into the buffer; return line count */ long read_file(char *fn, long n) { FILE *fp; long size; fp = (*fn == '!') ? popen(fn + 1, "r") : fopen(strip_escapes(fn), "r"); if (fp == NULL) { fprintf(stderr, "%s: %s\n", fn, strerror(errno)); seterrmsg("cannot open input file"); return ERR; } else if ((size = read_stream(fp, n)) < 0) return ERR; else if (((*fn == '!') ? pclose(fp) : fclose(fp)) < 0) { fprintf(stderr, "%s: %s\n", fn, strerror(errno)); seterrmsg("cannot close input file"); return ERR; } if (!scripted) fprintf(stderr, "%lu\n", size); return current_addr - n; }
/* search_and_replace: for each line in a range, change text matching a pattern according to a substitution template; return status */ int search_and_replace(pattern_t *pat, int gflag, int kth) { undo_t *up; char *txt; char *eot; int lc; int xa = current_addr; int nsubs = 0; line_t *lp; int len; current_addr = first_addr - 1; for (lc = 0; lc <= second_addr - first_addr; lc++) { lp = get_addressed_line_node(++current_addr); if ((len = substitute_matching_text(pat, lp, gflag, kth)) < 0) return ERR; else if (len) { up = NULL; if (delete_lines(current_addr, current_addr) < 0) return ERR; txt = rbuf; eot = rbuf + len; SPL1(); do { if ((txt = put_sbuf_line(txt)) == NULL) { SPL0(); return ERR; } else if (up) up->t = get_addressed_line_node(current_addr); else if ((up = push_undo_stack(UADD, current_addr, current_addr)) == NULL) { SPL0(); return ERR; } } while (txt != eot); SPL0(); nsubs++; xa = current_addr; } } current_addr = xa; if (nsubs == 0 && !(gflag & GLB)) { seterrmsg("no match"); return ERR; } else if ((gflag & (GPR | GLS | GNP)) && display_lines(current_addr, current_addr, gflag) < 0) return ERR; return 0; }
int tpl_ctx_get_outpath(tpl_ctx *ctx, const char *inpath, char outpath[MAXPATHLEN]) { tpl_doc *t; char *outext; char *inext; t = tpl_ctx_get_tpl_doc(ctx, inpath); if(t == NULL) { seterrmsg(ctx, "Did not find templates for file '%s': %s", inpath, tpl_ctx_error(ctx)); return -1; } outext = tpl_doc_get_definition(t, "-output"); if(outext == NULL) { seterrmsg(ctx, "Template output file extension not defined for '%s'", inpath); return -1; } strncpy(outpath, inpath, MAXPATHLEN); inext = (char *)getext(outpath); strncpy(inext, outext, MAXPATHLEN - strlen(inpath)); return 0; }
/* pop_undo_stack: undo last change to the editor buffer */ int pop_undo_stack(void) { long n; long o_current_addr = current_addr; long o_addr_last = addr_last; if (u_current_addr == -1 || u_addr_last == -1) { seterrmsg("nothing to undo"); return ERR; } else if (u_p) modified = 1; get_addressed_line_node(0); /* this get_addressed_line_node last! */ SPL1(); for (n = u_p; n-- > 0;) { switch(ustack[n].type) { case UADD: REQUE(ustack[n].h->q_back, ustack[n].t->q_forw); break; case UDEL: REQUE(ustack[n].h->q_back, ustack[n].h); REQUE(ustack[n].t, ustack[n].t->q_forw); break; case UMOV: case VMOV: REQUE(ustack[n - 1].h, ustack[n].h->q_forw); REQUE(ustack[n].t->q_back, ustack[n - 1].t); REQUE(ustack[n].h, ustack[n].t); n--; break; default: /*NOTREACHED*/ ; } ustack[n].type ^= 1; } /* reverse undo stack order */ for (n = u_p; n-- > (u_p + 1)/ 2;) USWAP(ustack[n], ustack[u_p - 1 - n]); if (isglobal) clear_active_list(); current_addr = u_current_addr, u_current_addr = o_current_addr; addr_last = u_addr_last, u_addr_last = o_addr_last; SPL0(); return 0; }
/* get_extended_line: get a an extended line from stdin */ char * get_extended_line(int *sizep, int nonl) { static char *cvbuf = NULL; /* buffer */ static int cvbufsz = 0; /* buffer size */ int l, n; char *t = ibufp; while (*t++ != '\n') ; if ((l = t - ibufp) < 2 || !has_trailing_escape(ibufp, ibufp + l - 1)) { *sizep = l; return ibufp; } *sizep = -1; REALLOC(cvbuf, cvbufsz, l, NULL); memcpy(cvbuf, ibufp, l); *(cvbuf + --l - 1) = '\n'; /* strip trailing esc */ if (nonl) l--; /* strip newline */ for (;;) { if ((n = get_tty_line()) < 0) return NULL; else if (n == 0 || ibuf[n - 1] != '\n') { seterrmsg("unexpected end-of-file"); return NULL; } REALLOC(cvbuf, cvbufsz, l + n, NULL); memcpy(cvbuf + l, ibuf, n); l += n; if (n < 2 || !has_trailing_escape(cvbuf, cvbuf + l - 1)) break; *(cvbuf + --l - 1) = '\n'; /* strip trailing esc */ if (nonl) l--; /* strip newline */ } REALLOC(cvbuf, cvbufsz, l + 1, NULL); cvbuf[l] = '\0'; *sizep = l; return cvbuf; }
/* set_active_node: add a line node to the global-active list */ static int set_active_node(line_t *lp) { if (active_last + 1 > active_size) { int ti = active_size; line_t **ts; SPL1(); if ((ts = reallocarray(active_list, (ti += MINBUFSZ), sizeof(line_t **))) == NULL) { perror(NULL); seterrmsg("out of memory"); SPL0(); return ERR; } active_size = ti; active_list = ts; SPL0(); } active_list[active_last++] = lp; return 0; }
/* get_tty_line: read a line of text from stdin; return line length */ int get_tty_line(void) { int oi = 0; int i = 0; int c; for (;;) switch (c = getchar()) { default: oi = 0; REALLOC(ibuf, ibufsz, i + 2, ERR); if (!(ibuf[i++] = c)) isbinary = 1; if (c != '\n') continue; lineno++; ibuf[i] = '\0'; ibufp = ibuf; return i; case EOF: if (ferror(stdin)) { fprintf(stderr, "stdin: %s\n", strerror(errno)); seterrmsg("cannot read stdin"); clearerr(stdin); ibufp = NULL; return ERR; } else { clearerr(stdin); if (i != oi) { oi = i; continue; } else if (i) ibuf[i] = '\0'; ibufp = ibuf; return i; } } }
/* push_undo_stack: return pointer to initialized undo node */ undo_t * push_undo_stack(int type, int from, int to) { undo_t *t; t = ustack; if (u_p < usize || (t = reallocarray(ustack, (usize += USIZE), sizeof(undo_t))) != NULL) { ustack = t; ustack[u_p].type = type; ustack[u_p].t = get_addressed_line_node(to); ustack[u_p].h = get_addressed_line_node(from); return ustack + u_p++; } /* out of memory - release undo stack */ perror(NULL); seterrmsg("out of memory"); clear_undo_stack(); free(ustack); ustack = NULL; usize = 0; return NULL; }