lbuf: useq should always be nonzero
[neatvi.git] / lbuf.c
blob51893b6ccc969b33561a1aaa1d9c7920eed7e9eb
1 #include <stdlib.h>
2 #include <stdio.h>
3 #include <string.h>
4 #include <unistd.h>
5 #include "vi.h"
7 #define NMARKS 128
9 /* line operations */
10 struct lopt {
11 char *buf; /* text inserted or deleted */
12 int ins; /* insertion operation if non-zero */
13 int beg, end;
14 int seq; /* operation number */
15 int *mark, *mark_off; /* saved marks */
18 /* line buffers */
19 struct lbuf {
20 int mark[NMARKS]; /* mark lines */
21 int mark_off[NMARKS]; /* mark line offsets */
22 struct lopt hist[128]; /* buffer history */
23 int undo; /* current index into hist[] */
24 int useq; /* current operation sequence */
25 char **ln; /* lines */
26 int ln_n; /* number of lbuf in l[] */
27 int ln_sz; /* size of l[] */
28 int mod_new; /* clear modification marks */
29 int useq_zero; /* useq for lbuf_saved() */
30 int useq_last; /* useq before hist[] */
33 struct lbuf *lbuf_make(void)
35 struct lbuf *lb = malloc(sizeof(*lb));
36 int i;
37 memset(lb, 0, sizeof(*lb));
38 for (i = 0; i < LEN(lb->mark); i++)
39 lb->mark[i] = -1;
40 lb->useq = 1;
41 return lb;
44 static void lopt_done(struct lopt *lo)
46 free(lo->buf);
47 free(lo->mark);
48 free(lo->mark_off);
51 void lbuf_free(struct lbuf *lb)
53 int i;
54 for (i = 0; i < lb->ln_n; i++)
55 free(lb->ln[i]);
56 for (i = 0; i < LEN(lb->hist); i++)
57 lopt_done(&lb->hist[i]);
58 free(lb->ln);
59 free(lb);
62 /* insert a line at pos */
63 static void lbuf_insertline(struct lbuf *lb, int pos, char *s)
65 if (lb->ln_n == lb->ln_sz) {
66 int nsz = lb->ln_sz + 512;
67 char **nln = malloc(nsz * sizeof(nln[0]));
68 memcpy(nln, lb->ln, lb->ln_n * sizeof(lb->ln[0]));
69 free(lb->ln);
70 lb->ln = nln;
71 lb->ln_sz = nsz;
73 memmove(lb->ln + pos + 1, lb->ln + pos,
74 (lb->ln_n - pos) * sizeof(lb->ln[0]));
75 lb->ln_n++;
76 lb->ln[pos] = s;
79 /* low-level insertion */
80 static void lbuf_insert(struct lbuf *lb, int pos, char *s)
82 int len = strlen(s);
83 struct sbuf *sb;
84 int lb_len = lbuf_len(lb);
85 int beg = pos, end;
86 int i;
87 sb = sbuf_make();
88 for (i = 0; i < len; i++) {
89 sbuf_chr(sb, (unsigned char) s[i]);
90 if (s[i] == '\n') {
91 lbuf_insertline(lb, pos++, sbuf_done(sb));
92 sb = sbuf_make();
95 sbuf_free(sb);
96 for (i = 0; i < LEN(lb->mark); i++) /* updating marks */
97 if (lb->mark[i] >= pos)
98 lb->mark[i] += lbuf_len(lb) - lb_len;
99 end = beg + lbuf_len(lb) - lb_len;
100 if (lb->mod_new || lb->mark['['] < 0 || lb->mark['['] > beg)
101 lbuf_mark(lb, '[', beg, 0);
102 if (lb->mod_new || lb->mark[']'] < 0 || lb->mark[']'] < end - 1)
103 lbuf_mark(lb, ']', end - 1, 0);
104 lb->mod_new = 0;
107 /* low-level deletion */
108 static void lbuf_delete(struct lbuf *lb, int beg, int end)
110 int i;
111 for (i = beg; i < end; i++)
112 free(lb->ln[i]);
113 memmove(lb->ln + beg, lb->ln + end, (lb->ln_n - end) * sizeof(lb->ln[0]));
114 lb->ln_n -= end - beg;
115 for (i = 0; i < LEN(lb->mark); i++) /* updating marks */
116 if (lb->mark[i] > beg)
117 lb->mark[i] = MAX(beg, lb->mark[i] + beg - end);
118 if (lb->mod_new || lb->mark['['] < 0 || lb->mark['['] > beg)
119 lbuf_mark(lb, '[', beg, 0);
120 if (lb->mod_new || lb->mark[']'] < 0 || lb->mark[']'] < beg)
121 lbuf_mark(lb, ']', beg, 0);
122 lb->mod_new = 0;
125 /* append undo/redo history */
126 static void lbuf_opt(struct lbuf *lb, int ins, int beg, int end)
128 struct lopt *lo = &lb->hist[0];
129 int n = LEN(lb->hist);
130 int i;
131 if (lb->undo) {
132 for (i = 0; i < lb->undo; i++)
133 lopt_done(&lb->hist[i]);
134 memmove(lb->hist + 1, lb->hist + lb->undo,
135 (n - lb->undo) * sizeof(lb->hist[0]));
136 memset(lb->hist + n - lb->undo + 1, 0,
137 (lb->undo - 1) * sizeof(lb->hist[0]));
138 } else {
139 if (lb->hist[n - 1].buf)
140 lb->useq_last = lb->hist[n - 1].seq;
141 lopt_done(&lb->hist[n - 1]);
142 memmove(lb->hist + 1, lb->hist, (n - 1) * sizeof(lb->hist[0]));
144 memset(lo, 0, sizeof(*lo));
145 lo->ins = ins;
146 lo->beg = beg;
147 lo->end = end;
148 lo->buf = lbuf_cp(lb, beg, end);
149 lo->seq = lb->useq;
150 lb->undo = 0;
153 void lbuf_rd(struct lbuf *lbuf, int fd, int pos)
155 char buf[1 << 8];
156 struct sbuf *sb;
157 int nr;
158 sb = sbuf_make();
159 while ((nr = read(fd, buf, sizeof(buf))) > 0)
160 sbuf_mem(sb, buf, nr);
161 lbuf_put(lbuf, pos, sbuf_buf(sb));
162 sbuf_free(sb);
165 void lbuf_wr(struct lbuf *lbuf, int fd, int beg, int end)
167 int i;
168 for (i = beg; i < end; i++)
169 write(fd, lbuf->ln[i], strlen(lbuf->ln[i]));
172 void lbuf_rm(struct lbuf *lb, int beg, int end)
174 if (end > lb->ln_n)
175 end = lb->ln_n;
176 if (beg == end)
177 return;
178 lbuf_opt(lb, 0, beg, end);
179 lbuf_delete(lb, beg, end);
182 void lbuf_put(struct lbuf *lb, int pos, char *s)
184 int lb_len = lbuf_len(lb);
185 if (!*s)
186 return;
187 lbuf_insert(lb, pos, s);
188 lbuf_opt(lb, 1, pos, pos + lbuf_len(lb) - lb_len);
191 char *lbuf_cp(struct lbuf *lb, int beg, int end)
193 struct sbuf *sb;
194 int i;
195 sb = sbuf_make();
196 for (i = beg; i < end; i++)
197 if (i < lb->ln_n)
198 sbuf_str(sb, lb->ln[i]);
199 return sbuf_done(sb);
202 char *lbuf_get(struct lbuf *lb, int pos)
204 return pos >= 0 && pos < lb->ln_n ? lb->ln[pos] : NULL;
207 int lbuf_len(struct lbuf *lb)
209 return lb->ln_n;
212 void lbuf_mark(struct lbuf *lbuf, int mark, int pos, int off)
214 if (mark >= NMARKS)
215 return;
216 lbuf->mark[mark] = pos;
217 lbuf->mark_off[mark] = off;
220 int lbuf_jump(struct lbuf *lbuf, int mark, int *pos, int *off)
222 if (mark >= NMARKS || lbuf->mark[mark] < 0)
223 return 1;
224 *pos = lbuf->mark[mark];
225 if (off)
226 *off = lbuf->mark_off[mark];
227 return 0;
230 static struct lopt *lbuf_lopt(struct lbuf *lb, int i)
232 struct lopt *lo = &lb->hist[i];
233 return i >= 0 && i < LEN(lb->hist) && lo->buf ? lo : NULL;
236 static void lbuf_savemarks(struct lbuf *lb, struct lopt *lo)
238 int i;
239 lo->mark = malloc(sizeof(lb->mark));
240 lo->mark_off = malloc(sizeof(lb->mark_off));
241 for (i = 0; i < LEN(lb->mark); i++)
242 lo->mark[i] = -1;
243 lo->mark['*'] = lb->mark['*'];
244 lo->mark_off['*'] = lb->mark_off['*'];
247 static void lbuf_loadmarks(struct lbuf *lb, struct lopt *lo)
249 int i;
250 for (i = 0; lo->mark && i < LEN(lb->mark); i++) {
251 if (lo->mark[i] >= 0) {
252 lb->mark[i] = lo->mark[i];
253 lb->mark_off[i] = lo->mark_off[i];
258 int lbuf_undo(struct lbuf *lb)
260 struct lopt *lo = lbuf_lopt(lb, lb->undo);
261 int useq = lo ? lo->seq : 0;
262 if (!lo)
263 return 1;
264 lb->mod_new = 1;
265 while (lo && lo->seq == useq) {
266 lb->undo++;
267 if (lo->ins)
268 lbuf_delete(lb, lo->beg, lo->end);
269 else
270 lbuf_insert(lb, lo->beg, lo->buf);
271 lbuf_loadmarks(lb, lo);
272 lo = lbuf_lopt(lb, lb->undo);
274 return 0;
277 int lbuf_redo(struct lbuf *lb)
279 struct lopt *lo = lbuf_lopt(lb, lb->undo - 1);
280 int useq = lo ? lo->seq : 0;
281 if (!lo)
282 return 1;
283 lb->mod_new = 1;
284 while (lo && lo->seq == useq) {
285 lb->undo--;
286 if (lo->ins)
287 lbuf_insert(lb, lo->beg, lo->buf);
288 else
289 lbuf_delete(lb, lo->beg, lo->end);
290 lbuf_loadmarks(lb, lo);
291 lo = lbuf_lopt(lb, lb->undo - 1);
293 return 0;
296 static int lbuf_seq(struct lbuf *lb)
298 struct lopt *lo = lbuf_lopt(lb, lb->undo);
299 return lo ? lo->seq : lb->useq_last;
302 /* mark buffer as saved and, if clear, clear the undo history */
303 void lbuf_saved(struct lbuf *lb, int clear)
305 int i;
306 if (clear) {
307 for (i = 0; i < LEN(lb->hist); i++)
308 lopt_done(&lb->hist[i]);
309 memset(lb->hist, 0, sizeof(lb->hist));
310 lb->undo = 0;
311 lb->useq_last = lb->useq;
313 lb->useq_zero = lbuf_seq(lb);
316 /* was the file modified since the last lbuf_modreset() */
317 int lbuf_modified(struct lbuf *lb)
319 struct lopt *lo = lbuf_lopt(lb, 0);
320 if (!lb->undo && lo && !lo->mark)
321 lbuf_savemarks(lb, lo);
322 lb->mod_new = 1;
323 lb->useq++;
324 return lbuf_seq(lb) != lb->useq_zero;