├── .gitignore ├── BUGS ├── LICENSE ├── Makefile ├── README ├── addr-lex.c ├── addr-num.c ├── addr-op.c ├── addr-tree.c ├── addr-util.c ├── addr.h ├── address.c ├── buf-file.c ├── buf-line.c ├── buf-pos.c ├── buf.h ├── buffer.c ├── cmd-buffer.c ├── cmd-file.c ├── cmd-insert.c ├── cmd-print.c ├── cmd-register.c ├── cmd-sub.c ├── cmd.h ├── command.c ├── config.c ├── config.def.h ├── edna.1 ├── edna.h ├── examples └── config.h ├── expr.h ├── file.c ├── init.c ├── input.c ├── insert.c ├── line.c ├── line.h ├── list.c ├── list.h ├── main.c ├── parse.c ├── set.c ├── set.h ├── state.c ├── state.h ├── str.c ├── str.h ├── utf8.c ├── util.h ├── vec.c └── vec.h /.gitignore: -------------------------------------------------------------------------------- 1 | *.[ao] 2 | core 3 | edna 4 | deps.mk 5 | config.h 6 | !examples/* 7 | *~ 8 | vgcore.* 9 | .*.swp 10 | tags 11 | -------------------------------------------------------------------------------- /BUGS: -------------------------------------------------------------------------------- 1 | there's an uninitialized value error lurking somewhere near cmd_print when using the example config. 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for edna v0.2 2 | CC ?= cc 3 | CFLAGS ?= -std=c99 -fPIC -W -Wall -Wextra -Werror -pedantic -Wno-unused-parameter 4 | LDFLAGS ?= -lc 5 | SOFLAGS ?= -lc -fPIC -shared -Wl,-rpath=$(shell pwd) 6 | DEBUG ?= yes 7 | 8 | SRC != find . -maxdepth 1 -name '*.c' 9 | OBJ := ${SRC:.c=.o} 10 | TARG := edna 11 | VERS := 0.1 12 | 13 | edna: deps.mk config.h $(OBJ) 14 | 15 | # includes 16 | -include deps.mk 17 | 18 | # conditionals 19 | ifdef DEBUG 20 | CFLAGS += -g3 -O0 21 | endif 22 | 23 | # depencies 24 | str.a: string.o str_utf8.o 25 | vector.a: vector.o 26 | set.a: set.o 27 | 28 | # recipes 29 | edna: 30 | @echo LD -o $@ 31 | @$(CC) $(LDFLAGS) -o $@ $(OBJ) $(LIB) 32 | 33 | libedna.so: deps.mk $(OBJ) $(LIB) 34 | @echo LD -o $@ 35 | @$(CC) $(SOFLAGS) -o $@ $(filter-out main.o, $(OBJ)) $(LIB) 36 | 37 | deps.mk: $(SRC) 38 | @echo "Making deps.mk" 39 | @$(CC) -M $(SRC) > deps.mk 40 | 41 | config.h: 42 | @echo "Making config.h" 43 | @cp config.def.h config.h 44 | 45 | %.o: %.c 46 | @echo CC $< 47 | @$(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $< 48 | 49 | clean: 50 | rm -f edna *.o *.a core vgcore.* tests/*_test deps.mk 51 | 52 | lint: 53 | mkdir -p /tmp/report 54 | scan-build -o /tmp make 55 | 56 | .PHONY: clean lint test 57 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | edna is a work in progress text editor inspired by the UNIX standard, ed. 2 | 3 | at the moment edna has some features, but not quite at odds with ed(1) itself. 4 | edna has all of ed's line insertion commands 5 | 6 | - `a' will enter insert mode, appending lines after the current 7 | - `i' will enter insert mode, inserting lines before the current 8 | - `c' will delete the current line, and insert lines in place of the 9 | deleted 10 | (note that edna uses a single period on a line by itself to exit insert mode) 11 | 12 | e.g. 13 | 14 | % edna 15 | :i 16 | int main() { 17 | return 0; 18 | } 19 | . 20 | : 21 | 22 | --- 23 | 24 | among the commands are also: 25 | - `d' to delete the current line 26 | - `h' to explain the last error 27 | - `p' to print the current line 28 | 29 | there is also support for multiple buffers: 30 | - `o' opens a delimitted list of files in new buffers 31 | - `b' lists the all the opened buffers 32 | - `next' focuses the next buffer 33 | - `prev' focuses the previous buffer 34 | 35 | finally: 36 | - `f' to print or change the default filename 37 | - `w' to write the contents of the buffer 38 | - `e' to re-open the current file, or edit a new file 39 | - `q' to free the buffer and exit successfully 40 | 41 | `q' has two variations 42 | - `wq' to a write and quit 43 | - `Q' to quit unconditionally 44 | 45 | if edna is given a list of files as it's arguments, they are read into memory 46 | as buffers, and written back out on `w' and `wq' commands 47 | 48 | edna has a ``config.h'' header file, which allows configuring a few options: 49 | - the `PROMPT' macro is expanded and printed before a command is read 50 | from stdin. it defaults to ':', but it is fed into printf(3), 51 | allowing some flexibility. the ``"%ld:", bufgetpos(buf)'' prompt is quite 52 | handy, for instance 53 | - the `INS_PROMPT' macro is expand and printed before a line is read 54 | from stdin when in insert mode. 55 | - the `ERROR' macro is expanded and printed when a command fails in 56 | some way. following ed(1), edna defaults to the inscrutable "?\n", 57 | but you may find ``"%s\n" error'' more helpful. don't forget the 58 | newline! 59 | 60 | config.c primarily contains the arrays for commands and modes, to prevent them 61 | from being re-defined everytime config.h is included 62 | - the `commands' array is indexed when a command is read from 63 | stdin. it has a four fields: 64 | - `name', a string handle to identify the command both to the 65 | user and edna 66 | - `func' a pointer to function to be executed, 67 | - `mode', a string which allows extra context to be given to 68 | the function. 69 | - `defaddr', the default line address of the command 70 | - the `modes' array is indexed when a command tries the change the 71 | mode, it has six fields: 72 | - `name', the name of the mode 73 | - `init', currently unused 74 | - `prompt', executed first 75 | - `input', executed second 76 | - `parse, executed last 77 | if any of these commands return failure, the sixth member is called: 78 | - `error', the error handler 79 | 80 | at the moment, there are two modes, `command', and `insert'. 81 | 82 | planned features: 83 | - regex support 84 | - registers 85 | - line marks 86 | - macros 87 | - more standard ed commands (`m',`t',`x', etc.) 88 | - undo trees 89 | - command chaining 90 | - plugin support 91 | -------------------------------------------------------------------------------- /addr-lex.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "edna.h" 4 | 5 | const Operator arithops[] = { 6 | addr_plus, 7 | NULL 8 | }; 9 | 10 | const Evaluator nums[] = { 11 | addr_num, 12 | NULL 13 | }; 14 | 15 | const Evaluator numsyms[] = { 16 | addr_dot, 17 | addr_dollar, 18 | NULL 19 | }; 20 | 21 | /* 22 | * FIXME: lots of duplicated code in here 23 | */ 24 | 25 | Node * 26 | tryarith(String *s, size_t *pos) 27 | { 28 | Node *ret; 29 | size_t i, j, len; 30 | 31 | len = 0; 32 | 33 | for (i = j = 0; symbols[OP_ARITH][i]; i += len + 1, ++j) { 34 | len = strlen(symbols[OP_ARITH] + i); 35 | if (!strncmp(s->v + *pos, symbols[OP_ARITH] + i, len)) 36 | break; 37 | } 38 | 39 | if (!symbols[OP_ARITH] + i) return NULL; 40 | 41 | ret = makenode(); 42 | 43 | ret->op = arithops[j]; 44 | vec_append(ret->str, symbols[OP_ARITH]); 45 | ret->tok = OP_ARITH; 46 | 47 | *pos += len; 48 | 49 | return ret; 50 | } 51 | 52 | Node * 53 | trynum(String *s, size_t *pos) 54 | { 55 | Node *ret; 56 | 57 | ret = makenode(); 58 | 59 | for (; isdigit(s->v[*pos]); ++(*pos)) 60 | vec_append(ret->str, s->v + *pos); 61 | 62 | if (!len(ret->str)) { /* no numbers */ 63 | freenode(ret); 64 | ret = NULL; 65 | } else { 66 | ret->ev = addr_num; 67 | ret->tok = NUM_LITERAL; 68 | } 69 | return ret; 70 | 71 | } 72 | 73 | Node * 74 | trysym(String *s, size_t *pos) 75 | { 76 | Node *ret; 77 | size_t i, j, len; 78 | 79 | for (i = j = 0; symbols[NUM_SYMBOL] + i; i += len + 1, ++j) { 80 | len = strlen(symbols[NUM_SYMBOL] + i); 81 | if (!strncmp(s->v + *pos, symbols[NUM_SYMBOL] + i, len)) 82 | break; 83 | } 84 | 85 | if (!symbols[NUM_SYMBOL] + i) 86 | return NULL; 87 | 88 | ret = makenode(); 89 | 90 | ret->ev = numsyms[j]; 91 | ret->tok = NUM_SYMBOL; 92 | vec_concat(ret->str, symbols[NUM_SYMBOL] + i, len); 93 | 94 | *pos += len; 95 | 96 | return ret; 97 | } 98 | -------------------------------------------------------------------------------- /addr-num.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "edna.h" 3 | 4 | Set * 5 | addr_num (Node *nod, Buffer *buf, char *err) 6 | { 7 | return setaddmemb (makeset(), strtol (nod->str->v, NULL, 10)); 8 | } 9 | 10 | Set * 11 | addr_dot (Node *nod, Buffer *buf, char *err) 12 | { 13 | return setaddmemb (makeset(), bufgetpos(buf)); 14 | } 15 | 16 | Set * 17 | addr_dollar (Node *nod, Buffer *buf, char *err) 18 | { 19 | return setaddmemb (makeset(), bufgetlen(buf)); 20 | } 21 | -------------------------------------------------------------------------------- /addr-op.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "edna.h" 5 | 6 | Set * 7 | addr_plus(Node *left, Node *right, Buffer *buf, char *err) 8 | { 9 | Set *ret = NULL, *inc = NULL; 10 | size_t off; 11 | 12 | if (left) 13 | ret = evaltree(left, buf, err); 14 | 15 | else /* default to current line */ 16 | ret = addr_dot(left, buf, err); 17 | 18 | if (ret == NULL) 19 | goto fail; 20 | 21 | if (right) { 22 | 23 | inc = evaltree(right, buf, err); 24 | 25 | if (inc == NULL) 26 | goto fail; 27 | 28 | off = setrightmost(inc) - 1; 29 | 30 | if (setshiftright(ret, off) == NULL) 31 | goto fail; 32 | 33 | } else { 34 | 35 | inc = makeset(); 36 | 37 | off = 1; 38 | 39 | if (setshiftright(ret, off) == NULL) 40 | goto fail; 41 | 42 | } 43 | 44 | freeset(inc); 45 | return ret; 46 | 47 | fail: 48 | strcpy(err, "invalid line address"); 49 | freeset(ret); 50 | freeset(inc); 51 | return NULL; 52 | 53 | } 54 | -------------------------------------------------------------------------------- /addr-tree.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "edna.h" 3 | 4 | int 5 | addnode(Node *mother, Node *child) 6 | { 7 | int ret = -1; 8 | if (!mother->left) { 9 | mother->left = child; 10 | child->up = mother; 11 | ret = 0; 12 | } else if (!mother->right) { 13 | mother->right = child; 14 | child->up = mother; 15 | ret = 0; 16 | } 17 | return ret; 18 | } 19 | 20 | Node * 21 | getroot(Node *cur) 22 | { 23 | while (cur && cur->up) cur = cur->up; 24 | return cur; 25 | } 26 | 27 | void 28 | freenode(Node *cur) 29 | { 30 | str_free(cur->str); 31 | free(cur); 32 | } 33 | 34 | void 35 | freetree(Node *cur) 36 | { 37 | if (cur) { 38 | freetree(cur->left); 39 | freetree(cur->right); 40 | str_free(cur->str); 41 | free(cur); 42 | } 43 | return; 44 | } 45 | 46 | 47 | Node * 48 | makenode(void) 49 | { 50 | Node *ret; 51 | ret = calloc(1, sizeof *ret); 52 | if (!ret) 53 | die("calloc"); 54 | ret->str = str_alloc(); 55 | return ret; 56 | } 57 | -------------------------------------------------------------------------------- /addr-util.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "edna.h" 6 | 7 | Selection * 8 | resolveset(Set *A, Buffer *buf, char *error) 9 | { 10 | size_t i; 11 | Line *tmp; 12 | Selection *ret; 13 | Vector(size_t) *stack; 14 | 15 | if (!A) return NULL; 16 | 17 | make_vector(ret); 18 | if (!ret) die("make_vector"); 19 | 20 | stack = set2vec(A); 21 | if (!stack->c) goto invalid; 22 | 23 | for (i = 0; i < stack->c; ++i) { 24 | tmp = bufprobe(buf, stack->v[i] - 1); 25 | if (!tmp) goto invalid; 26 | vec_append(ret, &tmp); 27 | } 28 | 29 | vec_free(stack); 30 | 31 | return ret; 32 | 33 | invalid: 34 | strcpy(error, "invalid line address"); 35 | vec_free(ret); 36 | vec_free(stack); 37 | 38 | return NULL; 39 | } 40 | -------------------------------------------------------------------------------- /addr.h: -------------------------------------------------------------------------------- 1 | /* addr.h -- line addressing operations */ 2 | #ifndef _edna_addr_ 3 | #define _edna_addr_ 4 | 5 | #include "edna.h" 6 | 7 | #define VALUE bit(0) /* 0-ary operator */ 8 | #define OPERATOR bit(1) /* binary operator */ 9 | #define NUMBER bit(2) /* context-sensitive numerical value */ 10 | #define LINE bit(3) /* raw line address */ 11 | 12 | enum Token { 13 | NUM_LITERAL, 14 | NUM_SYMBOL, 15 | OP_ARITH, 16 | IDENT_LEN 17 | }; 18 | 19 | typedef unsigned int Rule; 20 | typedef enum Token Token; 21 | typedef struct Symbol Symbol; 22 | 23 | /* TODO: merge evaluator and operator, make it take a Set vector */ 24 | typedef Set * (*Evaluator) (Node *, Buffer *, char *); 25 | typedef Node* (*Lexer) (String *, size_t *); 26 | typedef Set * (*Operator) (Node *, Node *, Buffer *, char *); 27 | 28 | struct Node { 29 | Token tok; 30 | String *str; 31 | Evaluator ev; 32 | Operator op; 33 | Node *left; 34 | Node *right; 35 | Node *up; 36 | }; 37 | 38 | /* 39 | * prototypes 40 | */ 41 | 42 | /* core */ 43 | extern Set * evaltree (Node *, Buffer *, char *); 44 | extern void* getaddr (String *, size_t *, Buffer *, char *); 45 | extern Node* next (String *, size_t *); 46 | extern Node* parseaddr (String *, size_t *, char *); 47 | 48 | /* util */ 49 | extern Selection* resolveset (Set *, Buffer *, char *); 50 | 51 | /* lex */ 52 | extern Node* trynum (String *, size_t *); 53 | extern Node* trysym (String *, size_t *); 54 | extern Node* tryarith (String *, size_t *); 55 | 56 | /* tree */ 57 | extern int addnode (Node *, Node *); 58 | extern Node* getroot (Node *); 59 | extern int extendbranch_r (Node *, Node *); 60 | extern void freetree (Node *); 61 | extern void freenode (Node *); 62 | extern Node* makenode (void); 63 | 64 | /* evaluators */ 65 | extern Set * addr_num (Node *, Buffer *, char *); 66 | extern Set * addr_dot (Node *, Buffer *, char *); 67 | extern Set * addr_dollar (Node *, Buffer *, char *); 68 | 69 | /* operators */ 70 | extern Set * addr_plus (Node *, Node *, Buffer *, char *); 71 | 72 | /* tables */ 73 | extern const char* symbols[]; 74 | extern const Evaluator nums[]; 75 | extern const Evaluator numsyms[]; 76 | extern const Operator arithops[]; 77 | extern const Lexer lexers[]; 78 | extern const Rule ruleset[]; 79 | 80 | #endif 81 | -------------------------------------------------------------------------------- /address.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "edna.h" 5 | 6 | const char *symbols[] = { 7 | [NUM_LITERAL] = "1234567890\0", 8 | [NUM_SYMBOL] = ".\0" "$\0", 9 | [OP_ARITH] = "+\0", 10 | NULL 11 | }; 12 | 13 | const Lexer lexers[] = { 14 | [NUM_LITERAL] = trynum, 15 | [NUM_SYMBOL] = trysym, 16 | [OP_ARITH] = tryarith, 17 | NULL 18 | }; 19 | 20 | const Rule ruleset[] = { 21 | [NUM_LITERAL] = VALUE | NUMBER, 22 | [NUM_SYMBOL] = VALUE | LINE, 23 | [OP_ARITH] = OPERATOR, 24 | 0 25 | }; 26 | 27 | void * 28 | getaddr(String *s, size_t *pos, Buffer *buf, char *err) 29 | { 30 | Set *sel; 31 | Selection *ret; 32 | Node *tree; 33 | 34 | tree = parseaddr(s, pos, err); 35 | sel = evaltree(tree, buf, err); 36 | ret = resolveset(sel, buf, err); 37 | freetree(tree); 38 | freeset(sel); 39 | return ret; 40 | } 41 | 42 | Set * 43 | evaltree(Node *cur, Buffer *buf, char *err) 44 | { 45 | if (!cur) return NULL; 46 | 47 | if (ruleset[cur->tok] & VALUE) { 48 | return ((cur->ev) (cur, buf, err)); 49 | 50 | } else if (ruleset[cur->tok] & OPERATOR) { 51 | return ((cur->op) (cur->left, cur->right, buf, err)); 52 | 53 | } else 54 | strcpy (err, "evaltree(): unknown token"); 55 | return NULL; 56 | } 57 | 58 | Node * 59 | next(String *s, size_t *pos) 60 | { 61 | size_t i, j; 62 | Node *tmp, *ret = NULL; 63 | 64 | if (eol(s, *pos)) return NULL; 65 | 66 | i =j = 0; 67 | for (; i < IDENT_LEN; ++i) { 68 | tmp = lexers[i](s, pos); /* try and find a match */ 69 | if (!tmp) continue; 70 | ret = tmp; 71 | break; 72 | } 73 | 74 | return ret; 75 | } 76 | 77 | Node * 78 | parseaddr(String *s, size_t *pos, char *err) 79 | { 80 | Node *cur, *new; 81 | 82 | cur = NULL; 83 | while ((new = next(s, pos))) { 84 | if (!cur) { 85 | cur = new; 86 | continue; 87 | } 88 | if (ruleset[new->tok] & VALUE) { 89 | 90 | if (addnode(cur, new)) { 91 | sprintf(err, "invalid sequence of values %lu bytes in", *pos); 92 | goto fail; 93 | } 94 | 95 | continue; 96 | 97 | } else if (ruleset[new->tok] & OPERATOR) { 98 | if (ruleset[cur->tok] & VALUE) { 99 | addnode(new, cur); 100 | cur = new; 101 | continue; 102 | } 103 | for (;;) { 104 | if (cur->tok >= new->tok) { 105 | if (addnode(cur, new)) { 106 | cur = new; 107 | break; 108 | } 109 | } else { 110 | if (addnode(new, cur)) { 111 | cur = new; 112 | break; 113 | } 114 | } 115 | 116 | if (cur->up == NULL) { 117 | addnode(new, cur); 118 | cur = new; 119 | break; 120 | } 121 | cur = cur->up; 122 | } 123 | } 124 | } 125 | 126 | return getroot(cur); 127 | 128 | fail: 129 | cur = getroot(cur); 130 | freetree(cur); 131 | return NULL; 132 | } 133 | -------------------------------------------------------------------------------- /buf-file.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "edna.h" 4 | 5 | void 6 | bufclean(Buffer *buf) 7 | { 8 | assert(buf != NULL); 9 | buf->dirty = 0; 10 | } 11 | 12 | char * 13 | bufgetname(Buffer *buf) 14 | { 15 | assert(buf != NULL); 16 | 17 | if (!buf->name) return NULL; 18 | return arr(buf->name); 19 | } 20 | 21 | int 22 | bufsetname(Buffer *buf, char *name) 23 | { 24 | assert(buf != NULL); 25 | 26 | if (!buf->name) { 27 | buf->name = str_alloc(); 28 | if (!buf->name) return ENOMEM; 29 | } 30 | vec_truncate(buf->name, 0); 31 | return vec_concat(buf->name, name, strlen(name)); 32 | } 33 | 34 | int 35 | bufopen(Buffer *buf, char *mode) 36 | { 37 | char *fn; 38 | 39 | assert(buf != NULL); 40 | if (buf->file) { 41 | if (fflush(buf->file) == -1) return errno; 42 | if (fclose(buf->file) == -1) return errno; 43 | } 44 | if (buf->name == NULL) return 0; 45 | 46 | if (buf->file) fclose(buf->file); 47 | buf->file = NULL; 48 | fn = arr(buf->name); 49 | if (!fn || !*fn) goto finally; 50 | buf->file = fopen(fn, mode); 51 | if (!buf->file) perror("fopen"); 52 | 53 | finally: 54 | return 0; 55 | } 56 | -------------------------------------------------------------------------------- /buf-line.c: -------------------------------------------------------------------------------- 1 | #include "edna.h" 2 | 3 | int 4 | addline(Buffer *buf, Line *new) 5 | { 6 | Line *li = NULL; 7 | 8 | assert(buf != NULL); 9 | assert(new != NULL); 10 | 11 | li = buftell(buf); 12 | 13 | linklines(new, getnext(li)); 14 | linklines(li, new); 15 | 16 | if (buf->bot == li) buf->bot = new; 17 | 18 | ++buf->len; 19 | buf->dirty = 1; 20 | 21 | ++buf->pos; 22 | buf->cur = new; 23 | 24 | return 0; 25 | } 26 | 27 | 28 | int 29 | rmcurline(Buffer *buf) 30 | { 31 | Line *del; 32 | 33 | assert (buf != NULL); 34 | 35 | del = buf->cur; 36 | 37 | if (bufseek(buf, BUF_CUR, 1) && bufseek(buf, BUF_CUR, -1)) return -1; 38 | 39 | freelines(del, getnext(del)); 40 | buf->dirty = 1; 41 | --buf->len; 42 | 43 | return 0; 44 | } 45 | 46 | int 47 | rmline(Buffer *buf, Line *li) 48 | { 49 | assert(buf != NULL); 50 | assert(li != NULL); 51 | 52 | bufseekline(buf, li); 53 | 54 | if (bufseek(buf, BUF_CUR, 1) && bufseek(buf, BUF_CUR, -1)) return -1; 55 | 56 | freelines(li, getnext(li)); 57 | buf->dirty = 1; 58 | --buf->len; 59 | 60 | return 0; 61 | } 62 | -------------------------------------------------------------------------------- /buf-pos.c: -------------------------------------------------------------------------------- 1 | #include "edna.h" 2 | 3 | size_t 4 | bufgetpos(Buffer *buf) 5 | { 6 | assert(buf != NULL); 7 | 8 | return buf->pos; 9 | } 10 | 11 | size_t 12 | bufgetlen(Buffer *buf) 13 | { 14 | assert(buf != NULL); 15 | 16 | return buf->len; 17 | } 18 | 19 | Line * 20 | bufprobe(Buffer *buf, size_t off) 21 | { 22 | assert(buf != NULL); 23 | 24 | if (off > buf->len) return NULL; 25 | 26 | return walk(buf->top, off); 27 | } 28 | 29 | int 30 | bufseek(Buffer *buf, int whence, long off) 31 | { 32 | int dir; 33 | Line *(*get)(Line *); Line *li; 34 | 35 | assert(buf != NULL); 36 | assert(off > 0 || whence != BUF_SET); 37 | assert(off < 0 || whence != BUF_END); 38 | 39 | if (off == 0) return 0; 40 | 41 | switch (whence) { 42 | case BUF_SET: 43 | li = buf->top; 44 | break; 45 | case BUF_CUR: 46 | li = buf->cur; 47 | break; 48 | case BUF_END: 49 | li = buf->bot; 50 | break; 51 | default: 52 | return -1; 53 | } 54 | 55 | if (off > 0) { 56 | get = getnext; 57 | dir = 1; 58 | } else { 59 | get = getprev; 60 | dir = -1; 61 | } 62 | 63 | 64 | for (;;) { 65 | li = get(li); 66 | if (li == NULL) break; 67 | buf->cur = li; 68 | buf->pos += dir; 69 | if (--off == 0) break; 70 | } 71 | 72 | if (off == 0) return 0; 73 | else return -1; 74 | } 75 | 76 | int 77 | bufseekline(Buffer *buf, Line *li) 78 | { 79 | size_t off; 80 | Line *tmp; 81 | 82 | off = 0; 83 | for (tmp = buf->top; tmp && tmp != li; tmp = getnext(tmp)) 84 | ++off; 85 | if (!tmp) return -1; 86 | 87 | buf->pos = off; 88 | buf->cur = tmp; 89 | 90 | return 0; 91 | } 92 | 93 | Line * 94 | buftell(Buffer *buf) 95 | { 96 | return buf->cur; 97 | } 98 | -------------------------------------------------------------------------------- /buf.h: -------------------------------------------------------------------------------- 1 | #ifndef _edna_buf_ 2 | #define _edna_buf_ 3 | #include "edna.h" 4 | 5 | #define isdirty(b) ((b)->dirty) 6 | 7 | enum { 8 | BUF_SET, 9 | BUF_CUR, 10 | BUF_END, 11 | }; 12 | 13 | struct Buffer { 14 | /* file info */ 15 | bool dirty; 16 | FILE *file; 17 | String *name; 18 | 19 | /* line info */ 20 | size_t len; 21 | size_t pos; 22 | Line *top; 23 | Line *bot; 24 | Line *cur; 25 | 26 | /* misc. info */ 27 | Mode *mode; 28 | }; 29 | 30 | /* buffer.c */ 31 | extern Buffer * clonebuf (Buffer *); 32 | extern void copybuf (Buffer *, Buffer *); 33 | extern void freebuf (Buffer *); 34 | extern int initbuf (Buffer *, char *); 35 | extern void killbuf (Buffer *); 36 | extern Buffer * makebuf (void); 37 | 38 | /* buf-file.c */ 39 | extern void bufclean (Buffer *); 40 | extern char * bufgetname (Buffer *); 41 | extern int bufsetname (Buffer *, char *); 42 | extern int bufopen (Buffer *, char *); 43 | extern int setfilename (Buffer *, char *fn); 44 | 45 | /* buf-line.c */ 46 | extern int addline (Buffer *, Line *); 47 | extern int rmcurline (Buffer *); 48 | extern int rmline (Buffer *, Line *); 49 | 50 | /* buf-pos.c */ 51 | extern size_t bufgetlen (Buffer *); 52 | extern size_t bufgetpos (Buffer *); 53 | extern Line * bufprobe (Buffer *, size_t off); 54 | extern int bufseek (Buffer *, int, long); 55 | extern int bufseekline (Buffer *, Line *); 56 | extern Line * buftell (Buffer *); 57 | 58 | /* file.c */ 59 | extern int readbuf (Buffer *, char *); 60 | extern int writebuf (Buffer *, char *); 61 | 62 | #endif 63 | -------------------------------------------------------------------------------- /buffer.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "edna.h" 5 | 6 | Buffer * 7 | clonebuf(Buffer *src) 8 | { 9 | Buffer *ret; 10 | 11 | ret = makebuf(); 12 | memcpy(ret, src, sizeof *ret); 13 | 14 | return ret; 15 | } 16 | 17 | void 18 | freebuf(Buffer *buf) 19 | { 20 | killbuf(buf); 21 | free(buf); 22 | return; 23 | } 24 | 25 | int 26 | initbuf(Buffer *buf, char *fn) 27 | { 28 | int err; 29 | if (fn) { 30 | err = bufsetname(buf, fn); 31 | if (err) return err; 32 | } 33 | 34 | buf->cur = buf->top = buf->bot = makeline(); 35 | buf->len = 1; 36 | 37 | return 0; 38 | } 39 | 40 | 41 | void 42 | killbuf(Buffer *buf) 43 | { 44 | freelines(buf->top, NULL); 45 | if (buf->file) { 46 | if (fflush(buf->file) == -1) abort(); 47 | if (fclose(buf->file) == -1) abort(); 48 | } 49 | str_free(buf->name); 50 | memset(buf, 0, sizeof *buf); 51 | return; 52 | } 53 | 54 | Buffer * 55 | makebuf(void) 56 | { 57 | Buffer *ret; 58 | 59 | ret = calloc(1, sizeof *ret); 60 | if (!ret) die("calloc"); 61 | 62 | return ret; 63 | } 64 | -------------------------------------------------------------------------------- /cmd-buffer.c: -------------------------------------------------------------------------------- 1 | /* cmd_buffer.c -- buffer manipulation commands */ 2 | #include 3 | #include "edna.h" 4 | 5 | static void listbuf_helper(Buffer *, char); 6 | 7 | void 8 | listbuf_helper(Buffer *buf, char prefix) 9 | { 10 | bool dirty; 11 | char *fn, *mod=""; 12 | size_t len, pos; 13 | 14 | fn = bufgetname(buf); 15 | len = bufgetlen(buf); 16 | pos = bufgetpos(buf); 17 | dirty = isdirty(buf); 18 | 19 | if (!fn) fn = ""; 20 | if (dirty) mod = " [modified]"; 21 | if (printf("%c \"%s\"%s, %ld / %ld\n", prefix, fn, mod, pos, len - 1) < 0) 22 | die("printf"); 23 | } 24 | 25 | 26 | int 27 | cmd_listbuf(State *st, Buffer *buf, Arg *arg, char *error) 28 | { 29 | size_t i; 30 | 31 | for (i = 0; i < st->buffers->c; ++i) 32 | listbuf_helper(st->buffers->v[i], ' '); 33 | 34 | listbuf_helper(buf, '*'); 35 | 36 | return 0; 37 | } 38 | 39 | int 40 | cmd_openbuf(State *st, Buffer *buf, Arg *arg, char *error) 41 | { 42 | Buffer *tmp; 43 | size_t i; 44 | 45 | if (!arg->param || !arg->param->c) { 46 | strcpy(error, "no filenames provided"); 47 | return -1; 48 | } 49 | 50 | i = 0; 51 | do { 52 | tmp = makebuf(); 53 | if (!tmp) return ENOMEM; 54 | chomp(arg->param->v[i]); 55 | initbuf(tmp, arg->param->v[i]); 56 | if (readbuf(tmp, error)) { 57 | strcpy(error, "error reading file"); 58 | return -1; 59 | } 60 | bufclean(tmp); 61 | addbuf(st, tmp); 62 | } while (++i < len(arg->param)); 63 | 64 | return 0; 65 | } 66 | 67 | int 68 | cmd_switchbuf(State *st, Buffer *buf, Arg *arg, char *error) 69 | { 70 | if (!arg->mode) { 71 | strcpy(error, "no option specified"); 72 | return -1; 73 | 74 | } else if (!strcmp(arg->mode, "next")) { 75 | returnbuf(st, buf); 76 | checkoutbuf(buf, st, 0); 77 | 78 | return 0; 79 | 80 | } else if (!strcmp(arg->mode, "prev")) { 81 | returnbuf(st, buf); 82 | checkoutbuf(buf, st, (st->buffers->c != 1) ? st->buffers->c - 2 : 0 ); 83 | 84 | return 0; 85 | } 86 | 87 | strcpy (error, "unknown option"); 88 | return -1; 89 | } 90 | -------------------------------------------------------------------------------- /cmd-file.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "edna.h" 4 | 5 | int 6 | cmd_edit(State *st, Buffer *buf, Arg *arg, char *error) 7 | { 8 | char *fn; 9 | int err; 10 | 11 | if (arg->mode && !strcmp(arg->mode, "force")) 12 | goto force; 13 | 14 | if (isdirty(buf)) { 15 | strcpy(error, "buffer has unsaved changes"); 16 | return -1; 17 | } 18 | 19 | force: 20 | if (arg->param && len(arg->param)) { 21 | fn = arr(arg->param)[0]; 22 | chomp(fn); 23 | } else fn = bufgetname(buf); 24 | 25 | killbuf(buf); 26 | err = initbuf(buf, fn); 27 | if (err) return err; 28 | err = readbuf(buf, error); 29 | if (err) return err; 30 | bufclean(buf); 31 | setmode(st, "command"); 32 | 33 | return 0; 34 | } 35 | 36 | int 37 | cmd_filename(State *st, Buffer *buf, Arg *arg, char *err) 38 | { 39 | char *fn; 40 | if (len(arg->param)) { 41 | chomp(arr(arg->param)[0]); 42 | if (!bufsetname(buf, arr(arg->param)[0])) 43 | return 0; 44 | strcpy(err, "invalid filename"); 45 | return -1; 46 | } else if (!(fn = bufgetname(buf))) { 47 | strcpy(err, "no filename"); 48 | return -1; 49 | } else if (printf("%s\n", fn) < 0) die("printf"); 50 | 51 | return 0; 52 | } 53 | 54 | int 55 | cmd_quit (State *st, Buffer *buf, Arg *arg, char *error) 56 | { 57 | int err; 58 | if (arg->mode) { 59 | if (!strcmp(arg->mode, "force")) 60 | goto force; 61 | if (!strcmp(arg->mode, "write")) { 62 | err = cmd_write (st, buf, arg, error); 63 | if (err) return err; 64 | } 65 | strcpy(error, "unknown option"); 66 | return -1; 67 | } 68 | 69 | if (isdirty(buf)) { 70 | strcpy(error, "buffer has unsaved changes"); 71 | return -1; 72 | } 73 | 74 | force: 75 | setmode(st, "quit"); 76 | return 0; 77 | } 78 | 79 | int 80 | cmd_write (State *st, Buffer *buf, Arg *arg, char *error) 81 | { 82 | int ret; 83 | 84 | if (arg->param->c && bufsetname(buf, arg->param->v[0])) { 85 | strcpy (error, "invalid filename"); 86 | return -1; 87 | } 88 | 89 | ret = writebuf(buf, error); 90 | if (!ret) bufclean(buf); 91 | return ret; 92 | } 93 | -------------------------------------------------------------------------------- /cmd-insert.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "edna.h" 4 | 5 | int 6 | cmd_insert(State *st, Buffer *buf, Arg *arg, char *error) 7 | { 8 | bufseekline(buf, arg->sel->v[0]); 9 | 10 | if (arg->mode) { 11 | if (!strcmp(arg->mode, "insert")) { 12 | bufseek (buf, BUF_CUR, -1); 13 | } else if (!strcmp(arg->mode, "append")) { 14 | ; 15 | } else if (!strcmp(arg->mode, "change")) { 16 | if (cmd_delete(st, buf, arg, error)) 17 | return -1; 18 | } else { 19 | strcpy(error, "unknown option"); 20 | return 0; 21 | } 22 | } 23 | 24 | setmode(st, "insert"); 25 | return 0; 26 | } 27 | -------------------------------------------------------------------------------- /cmd-print.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "edna.h" 5 | 6 | #include "config.h" 7 | 8 | int 9 | cmd_help(State *st, Buffer *buf, Arg *arg, char *error) 10 | { 11 | if (error[0]) { 12 | if (printf("%s\n", error) < 0) die("printf"); 13 | if (fflush(stdout) == EOF) die("fflush"); 14 | } 15 | return 0; 16 | } 17 | 18 | int 19 | cmd_print(State *st, Buffer *buf, Arg *arg, char *error) 20 | { 21 | if (!arg->sel->v[0] || !arg->sel->v[0]->str) { 22 | strcpy(error, "empty selection"); 23 | return -1; 24 | } 25 | 26 | bufseekline(buf, *arg->sel->v); 27 | if (printf(PRINT_FMT) < 0) die("printf"); 28 | if (fflush(stdout) == EOF) die("fflush"); 29 | return 0; 30 | } 31 | -------------------------------------------------------------------------------- /cmd-register.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "edna.h" 3 | 4 | int 5 | cmd_delete (State *st, Buffer *buf, Arg *arg, char *error) 6 | { 7 | Line *targ; 8 | 9 | targ = *arg->sel->v; 10 | 11 | if (rmline(buf, targ)) { 12 | strcpy (error, "empty selection"); 13 | return -1; 14 | } 15 | 16 | return 0; 17 | } 18 | 19 | -------------------------------------------------------------------------------- /cmd-sub.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "edna.h" 6 | #include "cmd.h" 7 | 8 | /* TODO: assumes way too much about the internals of String */ 9 | #if 0 10 | /* 11 | * retiring this after switching from arg->vec to arg->param. 12 | * I just don't feel like maintaining this at the moment 13 | */ 14 | int 15 | subst (State *st, Buffer *buf, Arg *arg, char *error) 16 | { 17 | regex_t *re; 18 | regmatch_t *m; 19 | char *tail; 20 | size_t i, j; 21 | 22 | if (!(re = malloc (sizeof *re))) die ("malloc"); 23 | 24 | if (regcomp (re, arg->vec[0], REG_NEWLINE)) { 25 | strcpy (error, "error compiling regex"); 26 | free (re); 27 | return FAIL; 28 | } 29 | 30 | if (!(m = malloc (sizeof *m))) die ("malloc"); 31 | 32 | if (regexec (re, buf->curline->str->v, 1, m, 0) == REG_NOMATCH) { 33 | strcpy (error, "no matches"); 34 | goto finally; 35 | } 36 | 37 | i = strlen (buf->curline->str->v + m->rm_eo); 38 | if (m->rm_so + i + strlen (arg->vec[1]) 39 | > buf->curline->len) { 40 | strcpy (error, "too large a replacement (FIXME)"); 41 | goto finally; 42 | } 43 | 44 | if (!(tail = malloc (i))) die ("malloc"); 45 | memcpy (tail, buf->curline->str->v + m->rm_eo, i + 1); 46 | memcpy (buf->curline->str->v + m->rm_so, arg->vec[1], strlen (arg->vec[1]) + 1); 47 | j = strlen (buf->curline->str->v); 48 | memcpy (buf->curline->str->v + j, tail, i + 1); 49 | 50 | regfree (re); 51 | free (m); 52 | free (tail); 53 | 54 | return print (st, buf, arg, error); 55 | 56 | finally: 57 | regfree (re); 58 | free (m); 59 | return SUCC; 60 | } 61 | #endif 62 | -------------------------------------------------------------------------------- /cmd.h: -------------------------------------------------------------------------------- 1 | /* commands.h -- command-specific header file */ 2 | #ifndef _edna_cmds_ 3 | #define _edna_cmds_ 4 | 5 | #include "edna.h" 6 | 7 | struct Arg { 8 | Selection *sel; 9 | char* name; 10 | char* mode; 11 | 12 | Vector(char *) *param; 13 | }; 14 | 15 | struct Command { 16 | char* name; 17 | int (*func) (State *, Buffer *, Arg *, char *); 18 | char* mode; 19 | char* defaddr; 20 | }; 21 | 22 | /* command.c */ 23 | extern int grabline (State *, Buffer *, String *, char *); 24 | extern int cmdchck (const void *a, const void *b); 25 | extern int cmdcmp (const void *a, const void *b); 26 | extern int cmderror (State *, Buffer *, String *, char *); 27 | extern int cmdeval (State *, Buffer *, String *, char *); 28 | extern int cmdprompt (State *, Buffer *, String *, char *); 29 | 30 | /* insert.c */ 31 | extern int inserror (State *, Buffer *, String *, char *); 32 | extern int insline (State *, Buffer *, String *, char *); 33 | extern int insprompt (State *, Buffer *, String *, char *); 34 | 35 | /* parse.c */ 36 | extern int parseline (String *, Buffer *, Arg *, char *); 37 | 38 | /* cmd-buffer.c */ 39 | extern int cmd_listbuf (State *, Buffer *, Arg *, char *); 40 | extern int cmd_openbuf (State *, Buffer *, Arg *, char *); 41 | extern int cmd_switchbuf (State *, Buffer *, Arg *, char *); 42 | 43 | /* cmd-file.c */ 44 | extern int cmd_edit (State *, Buffer *, Arg *, char *); 45 | extern int cmd_filename (State *, Buffer *, Arg *, char *); 46 | extern int cmd_quit (State *, Buffer *, Arg *, char *); 47 | extern int cmd_write (State *, Buffer *, Arg *, char *); 48 | 49 | /* cmd-insert.c */ 50 | extern int cmd_insert (State *, Buffer *, Arg *, char *); 51 | 52 | /* cmd-print.c */ 53 | extern int cmd_help (State *, Buffer *, Arg *, char *); 54 | extern int cmd_print (State *, Buffer *, Arg *, char *); 55 | 56 | /* cmd-register.c */ 57 | extern int cmd_delete (State *, Buffer *, Arg *, char *); 58 | 59 | #endif 60 | -------------------------------------------------------------------------------- /command.c: -------------------------------------------------------------------------------- 1 | /* command.c -- interface for executing commands */ 2 | #define _POSIX_C_SOURCE 199309L 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #undef _POSIX_C_SOURCE 9 | 10 | #include "edna.h" 11 | #include "config.h" 12 | 13 | static int runcmd (State *, Buffer *, Command *, Arg *, char *err); 14 | static Command *findcmd (State *, char *); 15 | 16 | Command * 17 | findcmd(State *st, char *name) 18 | { 19 | size_t i; 20 | 21 | if (!name) return NULL; 22 | 23 | for (i = 0; i < st->cmds->c; ++i) { 24 | if (strlen(name) != strlen(arr(st->cmds)[i].name)) continue; 25 | if (!strcmp(name, st->cmds->v[i].name)) 26 | return st->cmds->v + i; 27 | } 28 | return NULL; 29 | } 30 | 31 | void 32 | freearg(Arg *arg) 33 | { 34 | if (arg->param) { 35 | mapv(arg->param, free(*each)); 36 | vec_free(arg->param); 37 | } 38 | if (arg->name) free(arg->name); 39 | if (arg->mode) free(arg->mode); 40 | vec_free(arg->sel); 41 | free(arg); 42 | } 43 | 44 | Arg * 45 | makearg(void) 46 | { 47 | return calloc(1, sizeof(Arg)); 48 | } 49 | 50 | int 51 | cmderror(State *st, Buffer *buf, String *s, char *err) 52 | { 53 | if (!strcmp(err, "quit")) return -1; 54 | else if (!strcmp(err, "eof")) { 55 | if (printf("\r") == -1) return errno; 56 | if (fflush(stdout) == -1) return errno; 57 | if (isdirty(buf)) { 58 | strcpy(err, "buffer has unsaved changes"); 59 | return 0; 60 | } else { 61 | return -1; 62 | } 63 | } 64 | if (printf(ERROR) < 0) return errno; 65 | if (fflush(stdout) == EOF) return errno; 66 | return 0; 67 | } 68 | 69 | int 70 | cmdeval(State *st, Buffer *buf, String *s, char *errmsg) 71 | { 72 | int err = 0; 73 | Arg *arg = 0; 74 | Command *cmd = 0; 75 | 76 | arg = makearg(); 77 | if (!arg) return ENOMEM; 78 | 79 | err = parseline(s, buf, arg, errmsg); 80 | if (err) goto finally; 81 | 82 | cmd = findcmd(st, arg->name); 83 | if (!cmd) { 84 | strcpy(errmsg, "unknown command"); 85 | err = ENOENT; 86 | goto finally; 87 | } 88 | 89 | err = runcmd(st, buf, cmd, arg, errmsg); 90 | 91 | finally: 92 | freearg(arg); 93 | return err; 94 | } 95 | 96 | int 97 | cmdprompt (State *st, Buffer *buf, String *s, char *err) 98 | { 99 | if (printf (PROMPT) < 0) return errno; 100 | if (fflush (stdout) == EOF) return errno; 101 | return 0; 102 | } 103 | 104 | int 105 | runcmd(State *st, Buffer *buf, Command *cmd, Arg *arg, char *err) 106 | { 107 | size_t pos = 0; 108 | Selection *sel = 0; 109 | String *addr = 0; 110 | 111 | if (cmd->mode) { 112 | arg->mode = malloc((strlen(cmd->mode) + 1) * sizeof *arg->mode); 113 | if (!arg->mode) return ENOMEM; 114 | strcpy(arg->mode, cmd->mode); 115 | } 116 | 117 | if (!arg->sel && cmd->defaddr) { 118 | make_vector(addr); 119 | if (!addr) goto nomem; 120 | vec_concat(addr, cmd->defaddr, strlen(cmd->defaddr)); 121 | 122 | sel = getaddr(addr, &pos, buf, err); 123 | str_free(addr); 124 | if (sel == NULL) goto nomem; 125 | 126 | vec_free(arg->sel); 127 | arg->sel = vec_clone(sel); 128 | 129 | free_vector(sel); 130 | } 131 | 132 | return cmd->func(st, buf, arg, err); 133 | 134 | nomem: 135 | free(arg->mode); 136 | return ENOMEM; 137 | } 138 | -------------------------------------------------------------------------------- /config.c: -------------------------------------------------------------------------------- 1 | #include "config.h" 2 | 3 | const Mode modes[] = { 4 | /* name, init, prompter, reader evaluator, handler, */ 5 | { "command", NULL, cmdprompt, grabline, cmdeval, cmderror, }, 6 | { "insert", NULL, insprompt, grabline, insline, inserror, }, 7 | { "quit", quit, NULL, NULL, NULL, NULL, } 8 | }; 9 | 10 | const Command commands[] = { 11 | /* name, func, mode defaddr */ 12 | { "", cmd_print, NULL, ".+1", }, 13 | { "a", cmd_insert, "append", ".", }, 14 | { "b", cmd_listbuf, NULL, NULL }, 15 | { "c", cmd_insert, "change", ".", }, 16 | { "d", cmd_delete, NULL, ".", }, 17 | { "e", cmd_edit, NULL, NULL, }, 18 | { "E", cmd_edit, "force", NULL, }, 19 | { "f", cmd_filename, NULL, NULL, }, 20 | { "h", cmd_help, NULL, NULL, }, 21 | { "i", cmd_insert, "insert", ".", }, 22 | { "next", cmd_switchbuf, "next", NULL, }, 23 | { "o", cmd_openbuf, NULL, NULL, }, 24 | { "p", cmd_print, NULL, ".", }, 25 | { "prev", cmd_switchbuf, "prev", NULL, }, 26 | { "q", cmd_quit, NULL, NULL, }, 27 | // { "s", subst, NULL, ".", }, 28 | { "w", cmd_write, NULL, NULL, }, 29 | { "wq", cmd_quit, "write", NULL, }, 30 | { "Q", cmd_quit, "force", NULL, }, 31 | }; 32 | 33 | size_t 34 | sizeof_modes(void) 35 | { 36 | return sizeof modes / sizeof *modes; 37 | } 38 | 39 | size_t 40 | sizeof_commands(void) 41 | { 42 | return sizeof commands / sizeof *commands; 43 | } 44 | -------------------------------------------------------------------------------- /config.def.h: -------------------------------------------------------------------------------- 1 | #ifndef _edna_config_ 2 | #define _edna_config_ 3 | #include "edna.h" 4 | 5 | /* settings */ 6 | #define PROMPT "\r\033[K:" 7 | #define PRINT_FMT "%s", (*arg->sel->v)->str->v 8 | #define INS_PROMPT " \b" 9 | #define ERROR "?\n" 10 | 11 | extern const Mode modes[]; 12 | extern const Command commands[]; 13 | 14 | size_t sizeof_modes(void); 15 | size_t sizeof_commands(void); 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /edna.1: -------------------------------------------------------------------------------- 1 | .Dd Mar 9, 2017 2 | .Dt EDNA 1 3 | .Os 4 | . 5 | .Sh NAME 6 | . 7 | .Nm edna 8 | .Nd text editor inspired by the UNIX standard, ed 9 | . 10 | .Sh SYNOPSYS 11 | . 12 | .Nm Ar file 13 | . 14 | .Sh DESCRIPTION 15 | . 16 | At the moment edna has some features, but not quite at odds with 17 | .Xr ed 1 18 | itself. 19 | . 20 | There are two modes: 21 | .Sq command , 22 | and 23 | .Sq insert . 24 | While starting 25 | .Nm , 26 | the 27 | .Sq command 28 | mode is active. 29 | To insert text, an insersion command have to be entered. 30 | . 31 | .Ss Insertion commands 32 | . 33 | . 34 | .Bl -tag -width indent -compact 35 | .It Ic a 36 | Enter insert mode, appending lines after the current 37 | . 38 | .It Ic i 39 | Enter insert mode, inserting lines before the current 40 | . 41 | .It Ic c 42 | Delete the current line, and insert lines in place of the deleted 43 | .El 44 | .Pp 45 | To exit insert mode, enter a 46 | .Dq "." 47 | alone at the beginning of the line. 48 | . 49 | .Ss Edition commands 50 | . 51 | .Bl -tag -width indent -compact 52 | .It Ic d 53 | Delete the current line. 54 | .El 55 | . 56 | .Ss Printing commands 57 | . 58 | .Bl -tag -width indent -compact 59 | .It Ic p 60 | Print the current line. 61 | . 62 | .It Ic h 63 | Explain the last error. 64 | .El 65 | .Pp 66 | . 67 | .Ss Buffer management commands 68 | . 69 | .Bl -tag -width indent -compact 70 | .It Ic o 71 | Opens a delimitted list of files in new buffers. 72 | . 73 | .It Ic b 74 | Lists the all the opened buffers. 75 | . 76 | .It Ic next 77 | Focus the next buffer. 78 | . 79 | .It Ic prev 80 | Focus the previous buffer. 81 | . 82 | .It Ic f 83 | Print or change the default filename. 84 | . 85 | .It Ic w 86 | Write the contents of the buffer. 87 | . 88 | .It Ic e 89 | Re-open the current file, or edit a new file. 90 | . 91 | .It Ic q 92 | Free the buffer and exit successfully. 93 | . 94 | .It Ic wq 95 | Write and quit. 96 | . 97 | .It Ic Q 98 | Quit unconditionally. 99 | .El 100 | .Pp 101 | If edna is given a list of files as it's arguments, they are read into memory 102 | as buffers, and written back out on 103 | .Ic w 104 | and 105 | .Ic wq 106 | commands. 107 | . 108 | .Sh FILES 109 | . 110 | .Nm 111 | has a 112 | .Pa config.h 113 | header file included at compilation time, which allows configuring a few options: 114 | .Bl -tag -width indent 115 | .It Dv PROMPT 116 | Macro expanded and printed before a command is read from stdin. 117 | It defaults to 118 | .Sq ":" , 119 | but it is fed into 120 | .Xr printf 3 , 121 | allowing some flexibility. 122 | the 123 | .Ql "%ld:", bufgetpos(buf) 124 | prompt is quite handy, for instance 125 | . 126 | .It Dv INS_PROMPT 127 | Macro expand and printed before a line is read from stdin when in insert mode. 128 | . 129 | .It Dv ERROR 130 | Macro expanded and printed when a command fails in some way. 131 | Following 132 | .Xr ed 1 , 133 | .Nm 134 | defaults to 135 | .Dq Li ?\(rsn , 136 | but you may find 137 | .Dq Li %s\(rsn 138 | error more helpful. 139 | .El 140 | .Pp 141 | The prompt have to contain the newline. 142 | .Pp 143 | .Pa config.c 144 | Primarily contains the arrays for commands and modes, to prevent them 145 | from being re-defined everytime. 146 | .Bl -tag -width indent 147 | .It Va commands 148 | Array indexed when a command is read from stdin 149 | It has a four fields: 150 | .Bl -tag -width indent -compact 151 | . 152 | .It Va name 153 | A string handle to identify the command both to the user and 154 | .Nm . 155 | . 156 | .It Va func 157 | A pointer to function to be executed. 158 | . 159 | .It Va mode 160 | A string which allows extra context to be given to the function. 161 | . 162 | .It Va defaddr 163 | the default line address of the command 164 | .El 165 | . 166 | .It Va modes 167 | Array indexed when a command tries the change the mode, it has six fields: 168 | .Bl -tag -width indent -compact 169 | .It Va name 170 | the name of the mode 171 | . 172 | .It Va init 173 | currently unused 174 | . 175 | .It Va prompt 176 | executed first 177 | . 178 | .It Va input 179 | executed second 180 | . 181 | .It Va parse 182 | Executed last. 183 | .El 184 | .Pp 185 | If any of these commands return failure, the sixth member is called: 186 | .Bl -tag -width indent 187 | .It Va error 188 | The error handler 189 | .El 190 | .El 191 | . 192 | .Sh EXAMPLE 193 | . 194 | Example of 195 | .Nm 196 | interactive editing session: 197 | .Bd -literal -offset indent 198 | % edna 199 | :i 200 | int main() { 201 | return 0; 202 | } 203 | . 204 | : 205 | .Ed 206 | .Pp 207 | A configuration example can be found in the source directory at 208 | .Pa examples/config.h . 209 | -------------------------------------------------------------------------------- /edna.h: -------------------------------------------------------------------------------- 1 | #ifndef _edna_ 2 | #define _edna_ 3 | #include 4 | #include 5 | #include 6 | 7 | #include "set.h" 8 | #include "str.h" 9 | #include "vec.h" 10 | #include "util.h" 11 | 12 | typedef struct Arg Arg; 13 | typedef struct Buffer Buffer; 14 | typedef struct Command Command; 15 | typedef struct Mode Mode; 16 | typedef struct Node Node; 17 | typedef struct Line Line; 18 | typedef struct State State; 19 | typedef struct Span Span; 20 | typedef Vector(Line *) Selection; 21 | 22 | #include "addr.h" 23 | #include "buf.h" 24 | #include "cmd.h" 25 | #include "line.h" 26 | #include "state.h" 27 | 28 | int readline(String *, FILE *); 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /examples/config.h: -------------------------------------------------------------------------------- 1 | #ifndef _edna_config_ 2 | #define _edna_config_ 3 | #include "edna.h" 4 | 5 | /* settings */ 6 | 7 | /* advanced examples */ 8 | #define PROMPT "%ld:", bufgetpos(buf) /* have line numbers in prompt */ 9 | #define INS_PROMPT "%ld ", bufgetpos(buf) + 1 /* have line number in ins mode */ 10 | #define PRINT_FMT "%ld|%s", bufgetpos(buf),arr(arr(arg->sel)[0]->str) /* like ed's 'n' */ 11 | #define ERROR "%s\n", err /* like ed's 'H', without the '?' */ 12 | 13 | extern const Mode modes[]; 14 | extern const Command commands[]; 15 | 16 | size_t sizeof_modes(void); 17 | size_t sizeof_commands(void); 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /expr.h: -------------------------------------------------------------------------------- 1 | #ifndef _edna_expr_ 2 | #define _edna_expr_ 3 | 4 | struct Expr { 5 | String *str; 6 | Vector(Expr) chld; 7 | }; 8 | 9 | char *expr_type(Expr *); 10 | #endif 11 | -------------------------------------------------------------------------------- /file.c: -------------------------------------------------------------------------------- 1 | /* file.c -- file manipulation functions */ 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "edna.h" 8 | 9 | int 10 | readbuf(Buffer *buf, char *errmsg) 11 | { 12 | int err; 13 | String *s; 14 | Line *new; 15 | FILE *f; 16 | 17 | err = bufopen(buf, "r"); 18 | if (err) return err; 19 | 20 | f = buf->file; 21 | if (!f) return 0; 22 | s = str_alloc(); 23 | if (!s) return ENOMEM; 24 | 25 | errno = 0; 26 | while (!feof(f)) { 27 | err = readline(s, f); 28 | if (err == -1) break; 29 | else if (err > 0) { 30 | str_free(s); 31 | return err; 32 | } 33 | 34 | new = makeline(); 35 | if (!new) return ENOMEM; 36 | changeline(new, s); 37 | addline(buf, new); 38 | } 39 | clearerr(f); 40 | 41 | str_free(s); 42 | return 0; 43 | } 44 | 45 | int 46 | writebuf (Buffer *buf, char *errmsg) 47 | { 48 | int err; 49 | FILE *f; 50 | Line *tmp; 51 | 52 | errno = 0; 53 | err = bufopen(buf, "w+"); 54 | if (err) return err; 55 | 56 | f = buf->file; 57 | for (tmp = bufprobe(buf, 1); tmp; tmp = getnext(tmp)) 58 | fputs(tmp->str->v, f); 59 | 60 | return 0; 61 | } 62 | -------------------------------------------------------------------------------- /init.c: -------------------------------------------------------------------------------- 1 | /* init.c -- initlization and cleanup routines */ 2 | #include 3 | #include 4 | 5 | #include "edna.h" 6 | #include "config.h" 7 | 8 | void 9 | freestate(State *st) 10 | { 11 | mapv(st->buffers, freebuf(*each)); 12 | vec_free(st->buffers); 13 | vec_free(st->cmds); 14 | vec_free(st->modes); 15 | free(st); 16 | } 17 | 18 | int 19 | initst(State *st) 20 | { 21 | size_t len; 22 | 23 | make_vector(st->buffers); 24 | if (!st->buffers) return ENOMEM; 25 | 26 | make_vector(st->cmds); 27 | if (!st->cmds) goto nomem; 28 | len = sizeof_commands(); 29 | vec_concat(st->cmds, commands, len); 30 | 31 | make_vector(st->modes); 32 | if (!st->modes) goto nomem; 33 | len = sizeof_modes(); 34 | vec_concat(st->modes, modes, len); 35 | 36 | setmode(st, "command"); 37 | 38 | st->running = 1; 39 | 40 | return 0; 41 | 42 | nomem: 43 | vec_free(st->buffers); 44 | vec_free(st->cmds); 45 | vec_free(st->modes); 46 | return ENOMEM; 47 | } 48 | 49 | State * 50 | makestate(void) 51 | { 52 | State *st = 0; 53 | 54 | st = calloc(1, sizeof *st); 55 | 56 | return st; 57 | } 58 | 59 | int 60 | parse_argv(State *st, char **argv, char *errmsg) 61 | { 62 | int err = 0; 63 | Buffer *tmp = 0; 64 | 65 | if (argv[1]) while (*++argv) { 66 | tmp = makebuf(); 67 | if (!tmp) return ENOMEM; 68 | 69 | err = initbuf(tmp, *argv); 70 | if (err) return err; 71 | 72 | err = readbuf(tmp, errmsg); 73 | if (err) return err; 74 | 75 | bufclean(tmp); 76 | 77 | err = addbuf(st, tmp); 78 | if (err) return err; 79 | 80 | } else { 81 | tmp = makebuf(); 82 | if (!tmp) return ENOMEM; 83 | 84 | err = initbuf(tmp, NULL); 85 | if (err) return err; 86 | 87 | err = addbuf(st, tmp); 88 | if (err) return err; 89 | } 90 | 91 | return 0; 92 | } 93 | 94 | int 95 | quit(State *st) 96 | { 97 | st->running = 0; 98 | return 0; 99 | } 100 | -------------------------------------------------------------------------------- /input.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "edna.h" 5 | 6 | int 7 | grabline(State *st, Buffer *buf, String *s, char *errmsg) 8 | { 9 | int err = 0; 10 | 11 | err = readline(s, stdin); 12 | if (err == -1) { 13 | strcpy(errmsg, "eof"); 14 | 15 | } else if (err > 0) { 16 | err = errno; 17 | strcpy(errmsg, strerror(errno)); 18 | clearerr(stdin); 19 | } 20 | 21 | return err; 22 | 23 | } 24 | 25 | int 26 | readline(String *s, FILE *f) 27 | { 28 | int ch; 29 | 30 | vec_truncate(s, 0); 31 | for (;;) { 32 | ch = fgetc(f); 33 | if (ch == EOF) { 34 | if (feof(f)) return -1; 35 | else return errno; 36 | } 37 | vec_append(s, &ch); 38 | if (ch == '\n') return 0; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /insert.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "edna.h" 7 | #include "config.h" 8 | 9 | int 10 | inserror(State *st, Buffer *buf, String *s, char *err) 11 | { 12 | if (feof(stdin)) clearerr(stdin); 13 | if (printf("\r") == -1) return errno; 14 | setmode(st, "command"); 15 | return 0; 16 | } 17 | 18 | 19 | int 20 | insline(State *st, Buffer *buf, String *str, char *errmsg) 21 | { 22 | int err; 23 | Line *new; 24 | 25 | if (!strcmp(str->v, ".\n")) return -1; 26 | 27 | new = makeline(); 28 | if (!new) return ENOMEM; 29 | 30 | err = changeline(new, str); 31 | if (err) goto fail; 32 | 33 | err = addline(buf, new); 34 | if (err) goto fail; 35 | 36 | return 0; 37 | fail: 38 | freelines(new, NULL); 39 | return err; 40 | } 41 | 42 | int 43 | insprompt (State *st, Buffer *buf, String *s, char *err) 44 | { 45 | if (printf(INS_PROMPT) < 0) return errno; 46 | if (fflush(stdout) == EOF) return errno; 47 | return 0; 48 | } 49 | -------------------------------------------------------------------------------- /line.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "edna.h" 6 | 7 | int 8 | changeline(Line *ln, String *s) 9 | { 10 | if (!ln->str) { 11 | ln->str = str_alloc(); 12 | if (!ln->str) return ENOMEM; 13 | } 14 | 15 | vec_truncate(ln->str, 0); 16 | return vec_join(ln->str, s); 17 | } 18 | 19 | size_t 20 | getlineno(Line *targ) 21 | { 22 | size_t lineno; 23 | 24 | lineno = 0; 25 | while ((targ = getprev(targ))) ++lineno; 26 | 27 | return lineno; 28 | } 29 | 30 | Line * 31 | getnext(Line *li) 32 | { 33 | if (!li) return NULL; 34 | return li->next; 35 | } 36 | 37 | Line * 38 | getprev(Line *li) 39 | { 40 | if (!li) return NULL; 41 | return li->prev; 42 | } 43 | 44 | void 45 | freelines(Line *start, Line *stop) 46 | { 47 | Line *cur, *next, *prev, *tmp; 48 | 49 | prev = getprev(start); 50 | cur = start; 51 | next = getnext(cur); 52 | while (cur && cur != stop) { 53 | tmp = getnext(next); 54 | str_free(cur->str); 55 | free(cur); 56 | cur = next; 57 | next = tmp; 58 | } 59 | 60 | linklines(prev, stop); 61 | } 62 | 63 | void 64 | linklines(Line *left, Line *right) 65 | { 66 | if (left) left->next = right; 67 | if (right) right->prev = left; 68 | } 69 | 70 | Line* 71 | makeline(void) 72 | { 73 | return calloc(1, sizeof(Line)); 74 | } 75 | 76 | Line * 77 | walk(Line *cur, int offset) 78 | { 79 | Line *li = cur; 80 | if (offset < 0) { 81 | while ((li = getprev(li))) if (!++offset) return li; 82 | return NULL; 83 | } else if (offset > 0) { 84 | while ((li = getnext(li))) if (!--offset) return li; 85 | return NULL; 86 | } else return cur; 87 | } 88 | -------------------------------------------------------------------------------- /line.h: -------------------------------------------------------------------------------- 1 | #ifndef _edna_line_ 2 | #define _edna_line_ 3 | #include "edna.h" 4 | 5 | struct Line { 6 | size_t len; 7 | String* str; 8 | Line* next; 9 | Line* prev; 10 | }; 11 | 12 | struct Span { 13 | char *ptr; 14 | size_t ext; 15 | }; 16 | 17 | /* line.c */ 18 | extern size_t getlineno (Line *); 19 | extern Line* getnext (Line *); 20 | extern Line* getprev (Line *); 21 | extern void freelines (Line *, Line *); 22 | extern void linklines (Line *, Line*); 23 | extern Line* makeline (void); 24 | extern int changeline (Line *, String *); 25 | extern Line* walk (Line *, int); 26 | #endif 27 | -------------------------------------------------------------------------------- /list.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "list.h" 7 | 8 | static void *getnext(void *); 9 | static void link(void *, void const *); 10 | static void *walk(void *, size_t); 11 | 12 | #if 0 13 | void * 14 | cadr(void *li) 15 | { 16 | List(char) *tmp; 17 | 18 | tmp = getnext(li); 19 | if (!tmp) return NULL; 20 | 21 | return car(tmp); 22 | } 23 | 24 | void * 25 | cddr(void *li) 26 | { 27 | List(char) *tmp; 28 | 29 | tmp = getnext(li); 30 | if (!tmp) return NULL; 31 | 32 | return getnext(tmp); 33 | } 34 | #endif 35 | 36 | void 37 | free_list(void *list) 38 | { 39 | void *next, *prev; 40 | 41 | next = list; 42 | while (next) { 43 | prev = next; 44 | next = getnext(prev); 45 | free(prev); 46 | } 47 | } 48 | 49 | void * 50 | list_cons(void *data, void *_list) 51 | { 52 | char *ret; 53 | size_t off; 54 | List(char) list; 55 | 56 | assert(data); 57 | assert(_list); 58 | 59 | memcpy(&list, _list, sizeof list); 60 | 61 | ret = calloc(1, sizeof list); 62 | if (!ret) return NULL; 63 | 64 | off = offsetof(List(char), z); 65 | memcpy(ret + off, &list.z, sizeof list.z); 66 | off = offsetof(List(char), next); 67 | if (list.w) memset(ret + off, 0, sizeof list.next); 68 | else memcpy(ret + off, &_list, sizeof _list); 69 | off = offsetof(List(char), val); 70 | memcpy(ret + off, data, list.z); 71 | 72 | if (list.w) { 73 | memcpy(_list, ret, sizeof list); 74 | free(ret); 75 | ret = _list; 76 | list.w = 0; 77 | } 78 | 79 | return ret; 80 | } 81 | 82 | void * 83 | getnext(void *list) 84 | { 85 | size_t off; 86 | void *ret; 87 | 88 | off = offsetof(List(char), next); 89 | memcpy(&ret, (char *)list + off, sizeof (void *)); 90 | 91 | return ret; 92 | } 93 | 94 | void 95 | link(void *prev, void const *next) 96 | { 97 | size_t off; 98 | 99 | off = offsetof(List(char), next); 100 | memcpy((char *)prev + off, &next, sizeof next); 101 | } 102 | 103 | void * 104 | walk(void *list, size_t ext) 105 | { 106 | size_t rem; 107 | void *cur, *prev; 108 | 109 | rem = ext; 110 | cur = list; 111 | while (--rem) { 112 | prev = cur; 113 | cur = getnext(cur); 114 | if (!cur) return prev; 115 | } 116 | 117 | return cur; 118 | } 119 | 120 | void 121 | list_append(void *dest, void *src) 122 | { 123 | List(char) li; 124 | 125 | memcpy(&li, dest, sizeof li); 126 | if (li.w) { 127 | memcpy(dest, src, sizeof li); 128 | free(src); 129 | } else link(walk(dest, (size_t)-1), src); 130 | } 131 | -------------------------------------------------------------------------------- /list.h: -------------------------------------------------------------------------------- 1 | #ifndef _list_ 2 | #define _list_ 3 | #include 4 | 5 | #define List(Type) \ 6 | struct { \ 7 | bool w; \ 8 | void *next; \ 9 | size_t z; \ 10 | Type val; \ 11 | } 12 | 13 | #define make_list(LI) do { \ 14 | List(char) *_li; \ 15 | _li = calloc(1, sizeof *LI); \ 16 | _li->z = sizeof (LI)->val; \ 17 | _li->w = true; \ 18 | LI = (void *)_li; \ 19 | } while (0) 20 | 21 | /* e.g. mapl(list, free(each)) */ 22 | #define mapl(LI, expr) do { \ 23 | void *each; \ 24 | List(void *) *_li; \ 25 | _li = (void *)LI; \ 26 | do { \ 27 | each = car(_li); \ 28 | expr; \ 29 | _li = cdr(_li); \ 30 | } while (_li); \ 31 | } while (0) 32 | 33 | #define car(li) ((li)->val) 34 | #define cdr(li) ((li)->next) 35 | 36 | void *list_cons(void *, void *); 37 | void free_list(void *); 38 | void list_append(void *, void *); 39 | #endif 40 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | /* edna -- ed-like text editor */ 2 | #include "edna.h" 3 | 4 | char *strerror(int); 5 | 6 | int 7 | main (int argc, char** argv) 8 | { 9 | int err; 10 | char errmsg[80]; 11 | String *str; 12 | State *st; 13 | Buffer *buf; 14 | 15 | /* init stuff */ 16 | st = makestate(); 17 | buf = makebuf(); 18 | str = str_alloc(); 19 | *errmsg = 0; 20 | 21 | if (!(st && buf && str)) { 22 | err = ENOMEM; 23 | goto exit; 24 | } 25 | 26 | err = initst(st); 27 | if (err) goto exit; 28 | 29 | err = parse_argv(st, argv, errmsg); 30 | if (err) goto exit; 31 | /* end init */ 32 | 33 | /* main execution */ 34 | err = checkoutbuf(buf, st, 0); 35 | if (err) goto exit; 36 | 37 | while (st->running) { 38 | 39 | err = callq(st->mode->prompt, st, buf, str, errmsg); 40 | if (err) goto error; 41 | 42 | err = callq(st->mode->input, st, buf, str, errmsg); 43 | if (err) goto error; 44 | 45 | err = callq(st->mode->eval, st, buf, str, errmsg); 46 | if (err) goto error; 47 | 48 | continue; 49 | 50 | error: 51 | err = callr(st->mode->error, st, buf, str, errmsg); 52 | if (err) break; 53 | } 54 | /* end main */ 55 | 56 | exit: 57 | if (err) fprintf(stderr, "edna: %s\n", strerror(err)); 58 | 59 | freebuf(buf); 60 | freestate(st); 61 | str_free(str); 62 | 63 | return 0; 64 | } 65 | -------------------------------------------------------------------------------- /parse.c: -------------------------------------------------------------------------------- 1 | /* parse.c -- functions for processing input */ 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "edna.h" 10 | 11 | static char * getname (String const *, size_t *); 12 | static char * setdelim (String const *, size_t *); 13 | static char * getarg (String const *, size_t *, char *); 14 | 15 | bool 16 | alphacheck(char const *cur, void *_) 17 | { 18 | wchar_t wc; 19 | mbtowc(&wc, cur, 4); 20 | return iswalpha(wc); 21 | } 22 | 23 | bool 24 | delimcheck(char const *cur, void *_delim) 25 | { 26 | char *delim; 27 | delim = _delim; 28 | return !!strcmp(cur, delim); 29 | } 30 | 31 | char * 32 | getseq(const String *s, size_t *pos, 33 | bool (*check)(char const *, void *), 34 | void *context) 35 | { 36 | char cur[5], tmp[len(s) - *pos], *ret; 37 | short esc, ext; 38 | size_t off; 39 | 40 | if (eol(s, *pos)) return NULL; 41 | 42 | off = esc = ext = 0; 43 | while (!eol(s, *pos)) { 44 | ext = get_uchar(cur, s->v + *pos); 45 | 46 | if (ext == -1) ext = 1; 47 | else if (ext == 0) { 48 | *pos += 1; 49 | break; 50 | } 51 | 52 | if (*cur == '\\' && !esc) { 53 | esc = 2; 54 | continue; 55 | } 56 | 57 | if (!esc && !check(cur, context)) break; 58 | 59 | memcpy(tmp + off, cur, ext); 60 | off += ext; 61 | 62 | *pos += ext; 63 | if (esc) --esc; 64 | 65 | } 66 | 67 | tmp[off] = 0; 68 | ret = malloc(off + 1); 69 | if (!ret) die("malloc"); 70 | memcpy(ret, tmp, off + 1); 71 | 72 | return ret; 73 | } 74 | 75 | char * 76 | getname(const String *s, size_t *pos) 77 | { 78 | 79 | return getseq(s, pos, alphacheck, NULL); 80 | } 81 | 82 | char * 83 | getarg(const String *s, size_t *pos, char *delim) 84 | { 85 | return getseq(s, pos, delimcheck, delim); 86 | } 87 | 88 | char * 89 | setdelim(const String *s, size_t *pos) 90 | { 91 | char *ret; 92 | 93 | if (eol(s, *pos)) return NULL; 94 | 95 | ret = malloc(5); 96 | (*pos) += get_uchar(ret, s->v + *pos); 97 | 98 | return ret; 99 | } 100 | 101 | int 102 | parseline (String *s, Buffer *buf, Arg *arg, char *error) 103 | { 104 | char *tmp = 0; 105 | char *delim = 0; 106 | size_t pos = 0; 107 | 108 | arg->sel = getaddr(s, &pos, buf, error); 109 | 110 | arg->name = getname(s, &pos); 111 | if (!arg->name) goto finally; 112 | 113 | delim = setdelim(s, &pos); 114 | if (!delim) goto finally; 115 | 116 | make_vector(arg->param); 117 | if (!arg->param) goto nomem; 118 | do { 119 | tmp = getarg(s, &pos, delim); 120 | if (!tmp) break; 121 | vec_append(arg->param, &tmp); 122 | } while (tmp); 123 | 124 | 125 | free(delim); 126 | 127 | finally: 128 | return 0; 129 | 130 | nomem: 131 | free(delim); 132 | free(arg->name); 133 | vec_free(arg->sel); 134 | return ENOMEM; 135 | } 136 | -------------------------------------------------------------------------------- /set.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "set.h" 7 | #include "util.h" 8 | #include "vec.h" 9 | 10 | Set * 11 | cloneset(Set *src) 12 | { 13 | Set *ret; 14 | 15 | ret = makeset(); 16 | memcpy(ret->v, src->v, src->c); 17 | 18 | return ret; 19 | } 20 | 21 | int 22 | expandset(Set *A) 23 | { 24 | size_t c; 25 | void *tmp; 26 | 27 | c = A->c; 28 | tmp = realloc(A->v, A->c * 2); 29 | if (tmp == NULL) die("realloc"); 30 | 31 | A->v = tmp; 32 | memset(A->v + c, 0, c); 33 | 34 | return 0; 35 | } 36 | 37 | void 38 | freeset(Set *A) 39 | { 40 | if (!A) return; 41 | free(A->v); 42 | free(A); 43 | } 44 | 45 | Set * 46 | makeset(void) 47 | { 48 | #define SETSIZ 8 49 | Set *ret; 50 | 51 | ret = malloc(sizeof *ret); 52 | if (!ret) return NULL; 53 | 54 | ret->v = calloc(SETSIZ, sizeof *ret->v); 55 | if (!ret->v) { free(ret); return NULL; } 56 | ret->c = SETSIZ; 57 | 58 | return ret; 59 | } 60 | 61 | 62 | size_t 63 | offset(uint32_t i) 64 | { 65 | size_t ret = 0; 66 | 67 | if (i == 0) return 0; 68 | 69 | do ++ret; while (i >>= 1); 70 | 71 | return ret; 72 | } 73 | 74 | void * 75 | set2vec(Set *A) 76 | { 77 | Set *B = 0; 78 | uint32_t b = 0; 79 | size_t i = 0; 80 | size_t j = 0; 81 | size_t t[32] = {0}; 82 | Vector(size_t) *ret = 0; 83 | 84 | make_vector(ret); 85 | if (!ret) die("make_vector"); 86 | B = cloneset(A); 87 | 88 | for (i = 0; i < A->c; ++i) { 89 | for (j = 0; B->v[i]; ++j) { 90 | b = B->v[i] & -B->v[i]; 91 | t[j] = offset (b) + i * 32; 92 | B->v[i] ^= b; 93 | } 94 | while (j) if (vec_append(ret, t + --j)) goto fail; 95 | } 96 | freeset(B); 97 | return ret; 98 | fail: 99 | vec_free(ret); 100 | freeset(A); 101 | return NULL; 102 | } 103 | 104 | Set * 105 | setaddmemb(Set *A, size_t memb) 106 | { 107 | if (memb / 32 > A->c) if (expandset(A)) return NULL; 108 | 109 | A->v[memb / 32] |= bit(memb % 32); 110 | 111 | return A; 112 | } 113 | 114 | Set * 115 | setaddrange(Set *A, size_t beg, size_t end) 116 | { 117 | size_t i = 0; 118 | size_t j = 0; 119 | uint32_t k = 0; 120 | uint32_t n = 0; 121 | uint32_t premask = 0; 122 | uint32_t postmask = 0; 123 | 124 | if (beg > end) return NULL; 125 | 126 | if (end > A->c && expandset(A)) return NULL; 127 | 128 | for (i = j = 0; i < end; i += 32, ++j) { 129 | premask = postmask = 0; 130 | if (i + 31U > beg) { 131 | k = bit(beg - i); 132 | if (k > 1) premask = k - 1; 133 | n = end - i > 32 ? bit(31) : bit(end - i); 134 | if (n < bit (31)) postmask = ~(n - 1); 135 | 136 | A->v[j] = ~(uint32_t)0; 137 | A->v[j] ^= premask; 138 | A->v[j] ^= postmask; 139 | } 140 | } 141 | 142 | return A; 143 | } 144 | 145 | size_t 146 | setrightmost(Set *A) 147 | { 148 | Set *C = 0; 149 | size_t i = 0, j; 150 | 151 | C = A; 152 | for (i = 0; i < A->c; ++i) if (A->v[i]) C->v = A->v + i; 153 | 154 | j = offset (*C->v); 155 | 156 | j += (C->v - A->v) * 32; 157 | 158 | return j; 159 | } 160 | 161 | Set * 162 | setshiftright(Set *A, size_t off) 163 | { 164 | uint64_t d; 165 | size_t i = 0; 166 | size_t len = 0; 167 | Set *B = 0; 168 | 169 | if (A->c * 32 < off && expandset(A)) return NULL; 170 | 171 | B = makeset(); 172 | 173 | len = A->c; 174 | for (i = 0; i < len; ++i) { 175 | d = 0; 176 | if (i + 1 < len) d = A->v[i+1]; 177 | d <<= 32; 178 | d |= A->v[i]; 179 | 180 | d <<= off % 32; 181 | B->v[i] |= d; 182 | if (d >> 32) { 183 | if (i == len - 1) { 184 | if (expandset(B)) { 185 | freeset(B); 186 | return NULL; 187 | } 188 | len += 1; 189 | } 190 | B->v[i+1] = d >> 32; 191 | } 192 | 193 | } 194 | 195 | memset(A->v, 0, off / 32); 196 | memcpy(A->v + off / 32, B->v, len - off / 32); 197 | 198 | freeset(B); 199 | 200 | return A; 201 | 202 | } 203 | -------------------------------------------------------------------------------- /set.h: -------------------------------------------------------------------------------- 1 | /* set.h -- set operations */ 2 | #ifndef _set_ 3 | #define _set_ 4 | #include 5 | #include 6 | 7 | typedef struct Set Set; 8 | 9 | struct Set { 10 | size_t c; 11 | uint32_t *v; 12 | }; 13 | 14 | extern Set * cloneset (Set *); 15 | extern void freeset (Set *); 16 | extern Set * makeset (void); 17 | extern size_t offset (uint32_t); 18 | extern void * set2vec (Set *); 19 | extern Set * setaddmemb (Set *, size_t memb); 20 | extern Set * setcomplement (Set *); 21 | extern Set * setdifference (Set *, Set *); 22 | extern Set * setintersect (Set *, Set *); 23 | extern Set * setshiftright (Set *, size_t); 24 | extern size_t setrightmost (Set *); 25 | extern Set * setunion (Set *, Set *); 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /state.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "edna.h" 5 | 6 | static Mode *findmode(State *, char *); 7 | 8 | int 9 | addbuf(State *st, Buffer *buf) 10 | { 11 | return vec_append(st->buffers, &buf); 12 | } 13 | 14 | 15 | int 16 | checkoutbuf(Buffer *dest, State *st, size_t which) 17 | { 18 | Buffer *src; 19 | 20 | if (which >= len(st->buffers)) return EINVAL; 21 | 22 | src = arr(st->buffers)[which]; 23 | memcpy(dest, src, sizeof *dest); 24 | 25 | free(arr(st->buffers)[which]); 26 | vec_delete(st->buffers, which); 27 | 28 | return 0; 29 | } 30 | 31 | Mode * 32 | findmode(State *st, char *mode) 33 | { 34 | size_t i = 0; 35 | for (; i < st->modes->c; ++i) 36 | if (!strcmp(st->modes->v[i].name, mode)) 37 | return st->modes->v + i; 38 | return NULL; 39 | } 40 | 41 | int 42 | returnbuf(State *st, Buffer *src) 43 | { 44 | Buffer *tmp; 45 | 46 | tmp = clonebuf(src); 47 | return vec_append(st->buffers, &tmp); 48 | } 49 | 50 | int 51 | setmode(State *st, char *name) 52 | { 53 | Mode *md; 54 | 55 | md = findmode(st, name); 56 | if (!md) return -1; 57 | st->mode = md; 58 | callq(st->mode->init, st); 59 | return 0; 60 | } 61 | -------------------------------------------------------------------------------- /state.h: -------------------------------------------------------------------------------- 1 | #ifndef _edna_state_ 2 | #define _edna_state_ 3 | #include "edna.h" 4 | 5 | #include "str.h" 6 | 7 | struct Mode { 8 | char* name; 9 | int (*init) (State *); 10 | int (*prompt) (State *, Buffer *, String *, char *); 11 | int (*input) (State *, Buffer *, String *, char *); 12 | int (*eval) (State *, Buffer *, String *, char *); 13 | int (*error) (State *, Buffer *, String *, char *); 14 | }; 15 | 16 | struct State { 17 | Mode *mode; 18 | Vector(Command) *cmds; 19 | Vector(Buffer *) *buffers; 20 | Vector(Mode) *modes; 21 | uint8_t running:1; 22 | }; 23 | 24 | /* state.c */ 25 | extern int addbuf (State *, Buffer *); 26 | extern int checkoutbuf (Buffer *, State *, size_t); 27 | extern int setmode (State *, char *); 28 | extern int returnbuf (State *, Buffer *); 29 | 30 | /* init.c */ 31 | extern void freestate (State *); 32 | extern int initst (State *); 33 | extern State* makestate (void); 34 | extern int parse_argv (State *, char **, char *); 35 | extern int quit (State *); 36 | #endif 37 | -------------------------------------------------------------------------------- /str.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "str.h" 5 | #include "util.h" 6 | 7 | void 8 | chomp(char *s) 9 | { 10 | char *nl; 11 | do if (*s == '\n') nl = s; while (*s++); 12 | *nl = 0; 13 | } 14 | 15 | void 16 | str_free(String *str) 17 | { 18 | vec_free(str); 19 | } 20 | 21 | String * 22 | str_alloc(void) 23 | { 24 | String *ret; 25 | make_vector(ret); 26 | return ret; 27 | } 28 | -------------------------------------------------------------------------------- /str.h: -------------------------------------------------------------------------------- 1 | #ifndef _string_ 2 | #define _string_ 3 | #include 4 | #include 5 | 6 | #include "vec.h" 7 | 8 | #ifndef isascii 9 | # define isascii(C) (!((C) & (1<<7))) 10 | #endif 11 | 12 | #define eol(str, pos) (pos >= len(str)) 13 | 14 | typedef Vector(char) String; 15 | 16 | /* string.c */ 17 | void chomp (char *); 18 | void str_free (String *); 19 | String *str_alloc (void); 20 | 21 | /* utf8.c */ 22 | int get_uchar (char *, const char *); 23 | int uchar_extent (const unsigned char); 24 | size_t ustrlen (const char *); 25 | #endif 26 | -------------------------------------------------------------------------------- /utf8.c: -------------------------------------------------------------------------------- 1 | /* str_utf8.c -- utf8 conversion and parsing routines */ 2 | #include 3 | #include 4 | #include 5 | 6 | #include "str.h" 7 | #include "util.h" 8 | 9 | int 10 | get_uchar(char *dest, char const *src) 11 | { 12 | size_t off, len; 13 | unsigned char tmp[5]; 14 | int ext; 15 | 16 | ext = uchar_extent(*src); 17 | 18 | if (ext == -1) len = 1; 19 | else len = ext; 20 | 21 | memset(tmp, 0, len); 22 | memcpy(tmp, src, len); 23 | 24 | for (off = 0; off < len; ++off) { 25 | if (isascii(tmp[off])) goto ascii; 26 | else if (tmp[off] > 0xF4) goto binary; 27 | continue; 28 | 29 | ascii: 30 | binary: 31 | if (off == 0) ++off; 32 | break; 33 | } 34 | 35 | tmp[off] = 0; 36 | 37 | memcpy(dest, tmp, off + 1); 38 | 39 | return off; 40 | } 41 | 42 | int 43 | uchar_extent(unsigned char const ch) 44 | { 45 | if (!ch) return 0; 46 | else if (isascii(ch)) return 1; 47 | else if (ch >= 0xC2 && ch <= 0xDF) return 2; 48 | else if (ch >= 0xE0 && ch <= 0xEF) return 3; 49 | else if (ch >= 0xF0 && ch <= 0xF4) return 4; 50 | else return -1; 51 | } 52 | 53 | size_t 54 | ustrlen(char const *s) 55 | { 56 | int ext; 57 | size_t ret = 0; 58 | 59 | while (*s) { 60 | ext = uchar_extent(*s); 61 | if (ext > 0) s += ext; 62 | else if (ext == -1) s += 1; 63 | } 64 | 65 | return ret; 66 | } 67 | -------------------------------------------------------------------------------- /util.h: -------------------------------------------------------------------------------- 1 | #ifndef _util_ 2 | #define _util_ 3 | #include 4 | #include 5 | #include 6 | 7 | /* unsafe macros */ 8 | #define MAX(X,Y) ((X) > (Y) ? (X) : (Y)) 9 | #define MIN(X,Y) ((X) < (Y) ? (X) : (Y)) 10 | #define callq(func, ...) ((func) ? func(__VA_ARGS__) : 0) 11 | #define callr(func, ...) ((func) ? func(__VA_ARGS__) : -1) 12 | 13 | 14 | /* safe macros */ 15 | #define bit(off) (1UL << (off)) 16 | #define die(blame) do { perror(blame); abort(); } while (0); 17 | 18 | #define eat(ret, buf, expr) \ 19 | do { \ 20 | char *_buf = buf; \ 21 | int _ext; \ 22 | size_t _len = strlen(buf); \ 23 | size_t *_ret = &(ret); \ 24 | wchar_t wc; \ 25 | \ 26 | *_ret = 0; \ 27 | while ((_ext = mbtowc(&wc, _buf, _len)) \ 28 | && (expr)) { \ 29 | if (_ext == -1) { \ 30 | *_ret = -1; \ 31 | break; \ 32 | } \ 33 | *_ret += (size_t)_ext; \ 34 | _buf += (size_t)_ext; \ 35 | _len -= (size_t)_ext; \ 36 | } \ 37 | } while (0) 38 | 39 | #endif /* _util_ */ 40 | -------------------------------------------------------------------------------- /vec.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "util.h" 7 | #include "vec.h" 8 | 9 | #define VECSIZ 16 10 | #define GROWTH 2 11 | //#define SHRINK .3 12 | 13 | static int vec_expand(void *); 14 | 15 | int 16 | vec_alloc(void *_vec) 17 | { 18 | Vector(char) vec; 19 | 20 | memcpy(&vec, _vec, sizeof vec); 21 | 22 | vec.c = 0; 23 | vec.m = VECSIZ; 24 | vec.v = calloc(VECSIZ, vec.z); 25 | if (!vec.v) return ENOMEM; 26 | 27 | memcpy(_vec, &vec, sizeof vec); 28 | 29 | return 0; 30 | } 31 | 32 | int 33 | vec_append(void *_vec, void const *data) 34 | { 35 | Vector(char) vec; 36 | 37 | memcpy(&vec, _vec, sizeof vec); 38 | 39 | return vec_insert(_vec, data, vec.c); 40 | } 41 | 42 | void * 43 | vec_clone(void const *_vec) 44 | { 45 | Vector(char) vec, *ret; 46 | 47 | memcpy(&vec, _vec, sizeof vec); 48 | 49 | ret = malloc(sizeof *ret); 50 | if (!ret) return NULL; 51 | memcpy(ret, _vec, sizeof *ret); 52 | ret->v = malloc(vec.m * vec.z); 53 | if (!ret->v) { free(ret); return NULL; } 54 | memcpy(ret->v, vec.v, vec.m); 55 | 56 | return ret; 57 | } 58 | 59 | int 60 | vec_concat(void *_vec, void const *data, size_t nmemb) 61 | { 62 | size_t len; 63 | Vector(char) vec; 64 | 65 | assert(_vec); 66 | assert(data); 67 | 68 | memcpy(&vec, _vec, sizeof vec); 69 | len = nmemb * vec.z; 70 | 71 | while ((vec.c + nmemb) * vec.z >= vec.m) if (vec_expand(&vec)) return ENOMEM; 72 | 73 | memcpy(vec.v + vec.c * vec.z, data, len); 74 | 75 | vec.c += nmemb; 76 | memcpy(_vec, &vec, sizeof vec); 77 | 78 | return 0; 79 | } 80 | 81 | void 82 | vec_delete(void *_vec, size_t which) 83 | { 84 | Vector(char) vec; 85 | 86 | memcpy(&vec, _vec, sizeof vec); 87 | if (which > vec.c) return; 88 | 89 | memmove(vec.v + which * vec.z, vec.v + (which + 1) * vec.z, (vec.c - which) * vec.z); 90 | 91 | --vec.c; 92 | 93 | memcpy(_vec, &vec, sizeof vec); 94 | } 95 | 96 | int 97 | vec_expand(void *_vec) 98 | { 99 | Vector(char) vec; 100 | void *tmp; 101 | 102 | assert(_vec); 103 | 104 | memcpy(&vec, _vec, sizeof vec); 105 | assert(vec.m); 106 | 107 | tmp = realloc(vec.v, vec.m * GROWTH); 108 | if (!tmp) return ENOMEM; 109 | 110 | vec.v = tmp; 111 | vec.m *= GROWTH; 112 | 113 | memcpy(_vec, &vec, sizeof vec); 114 | return 0; 115 | } 116 | 117 | int 118 | vec_insert(void *_vec, void const *data, size_t pos) 119 | { 120 | Vector(char) vec; 121 | 122 | assert(_vec); 123 | assert(data); 124 | 125 | memcpy(&vec, _vec, sizeof vec); 126 | 127 | assert(pos <= vec.c); 128 | 129 | if ((vec.c + 1) * vec.z >= vec.m && vec_expand(&vec)) return ENOMEM; 130 | 131 | memmove(vec.v + (pos + 1) * vec.z, vec.v + pos * vec.z, (vec.c - pos) * vec.z); 132 | memcpy(vec.v + pos * vec.z, data, vec.z); 133 | 134 | ++vec.c; 135 | 136 | memcpy(_vec, &vec, sizeof vec); 137 | 138 | return 0; 139 | } 140 | 141 | int 142 | vec_join(void *_dest, void const *_src) 143 | { 144 | Vector(char) src; 145 | 146 | memcpy(&src, _src, sizeof src); 147 | 148 | return vec_concat(_dest, src.v, src.c); 149 | } 150 | 151 | void 152 | vec_free(void *_vec) 153 | { 154 | Vector(char) vec; 155 | 156 | if (!_vec) return; 157 | 158 | memcpy(&vec, _vec, sizeof vec); 159 | free(vec.v); 160 | memset(_vec, 0, sizeof vec); 161 | free(_vec); 162 | } 163 | 164 | 165 | int 166 | vec_prepend(void *vec, void const *data) 167 | { 168 | return vec_insert(vec, data, 0); 169 | } 170 | 171 | void 172 | vec_shift(void *_vec, size_t off) 173 | { 174 | Vector(char) vec; 175 | memcpy(&vec, _vec, sizeof vec); 176 | vec_slice(_vec, off, vec.c - off); 177 | return; 178 | } 179 | 180 | void 181 | vec_slice(void *_vec, size_t beg, size_t ext) 182 | { 183 | size_t min; 184 | Vector(char) vec; 185 | 186 | memcpy(&vec, _vec, sizeof vec); 187 | if (beg >= vec.c) { 188 | vec_truncate(_vec, 0); 189 | return; 190 | } 191 | 192 | min = MIN(ext, vec.c - beg); 193 | 194 | memmove(vec.v, vec.v + beg * vec.z, min * vec.z); 195 | memset(vec.v + (beg + min) * vec.z, 0, (vec.c - min - beg) * vec.z); 196 | 197 | vec.c = min; 198 | memcpy(_vec, &vec, sizeof vec); 199 | } 200 | 201 | void 202 | vec_truncate(void *_vec, size_t off) 203 | { 204 | Vector(char) vec; 205 | 206 | assert(_vec); 207 | 208 | memcpy(&vec, _vec, sizeof vec); 209 | 210 | memset(vec.v + off * vec.z, 0, (vec.c - off) * vec.z); 211 | vec.c = off; 212 | 213 | memcpy(_vec, &vec, sizeof vec); 214 | } 215 | -------------------------------------------------------------------------------- /vec.h: -------------------------------------------------------------------------------- 1 | #ifndef _vector_ 2 | #define _vector_ 3 | #include 4 | 5 | #define VECSIZ 16 6 | 7 | #define arr(vec) ((vec)->v) 8 | #define len(vec) ((vec)->c) 9 | #define siz(vec) ((vec)->z) 10 | #define mem(vec) ((vec)->m) 11 | 12 | #define Vector(Type) \ 13 | struct { \ 14 | size_t c; \ 15 | size_t m; \ 16 | size_t z; \ 17 | Type *v; \ 18 | } 19 | 20 | /* deprecated */ 21 | #define free_vector(INST) do { \ 22 | Vector(void) *inst; \ 23 | inst = (void *) INST; \ 24 | if (inst) free(inst->v); \ 25 | memset(inst, 0, sizeof *inst); \ 26 | free(inst); \ 27 | } while (0) 28 | 29 | #define make_vector(INST) do { \ 30 | Vector(void) *inst; \ 31 | \ 32 | inst = malloc(sizeof *inst); \ 33 | if (inst) { \ 34 | inst->z = sizeof *(INST)->v; \ 35 | if (vec_alloc(inst)) { \ 36 | free(inst); \ 37 | inst = NULL; \ 38 | } \ 39 | } \ 40 | (INST) = (void *)inst; \ 41 | } while (0) 42 | 43 | /* e.g. mapv(vec, sqrt(each)) */ 44 | #define mapv(VEC, expr) do { \ 45 | size_t _i; \ 46 | void **each; \ 47 | Vector(void *) *_vec; \ 48 | \ 49 | _vec = (void *)VEC; \ 50 | for (_i = 0; _i < len(_vec); ++_i) { \ 51 | each = arr(_vec) + _i; \ 52 | expr; \ 53 | } \ 54 | } while (0) 55 | 56 | #define tovec(arr, len) { .c = len, .v = arr, .z = sizeof *arr, .m = len } 57 | 58 | int vec_alloc(void *); 59 | int vec_append(void *, void const *); 60 | void *vec_clone(void const *); 61 | int vec_concat(void *, void const *, size_t); 62 | void vec_delete(void *, size_t); 63 | int vec_insert(void *, void const *, size_t); 64 | int vec_join(void *, void const *); 65 | void vec_free(void *); 66 | int vec_prepend(void *, void const *); 67 | void vec_shift(void *, size_t); 68 | void vec_slice(void *, size_t, size_t); 69 | void vec_truncate(void *, size_t); 70 | 71 | #endif 72 | --------------------------------------------------------------------------------