12 char *ins
; /* inserted text */
13 char *del
; /* deleted text */
14 int pos
, n_ins
, n_del
; /* modification location */
15 int seq
; /* operation number */
16 int *mark
, *mark_off
; /* saved marks */
21 int mark
[NMARKS
]; /* mark lines */
22 int mark_off
[NMARKS
]; /* mark line offsets */
23 char **ln
; /* buffer lines */
24 int ln_n
; /* number of lines in ln[] */
25 int ln_sz
; /* size of ln[] */
26 int useq
; /* current operation sequence */
27 struct lopt
*hist
; /* buffer history */
28 int hist_sz
; /* size of hist[] */
29 int hist_n
; /* current history head in hist[] */
30 int hist_u
; /* current undo head in hist[] */
31 int useq_zero
; /* useq for lbuf_saved() */
32 int useq_last
; /* useq before hist[] */
35 struct lbuf
*lbuf_make(void)
37 struct lbuf
*lb
= malloc(sizeof(*lb
));
39 memset(lb
, 0, sizeof(*lb
));
40 for (i
= 0; i
< LEN(lb
->mark
); i
++)
46 static void lopt_done(struct lopt
*lo
)
54 static void lbuf_savemark(struct lbuf
*lb
, struct lopt
*lo
, int m
)
56 if (lb
->mark
[m
] >= 0) {
58 lo
->mark
= malloc(sizeof(lb
->mark
));
59 lo
->mark_off
= malloc(sizeof(lb
->mark_off
));
60 memset(lo
->mark
, 0xff, sizeof(lb
->mark
));
62 lo
->mark
[m
] = lb
->mark
[m
];
63 lo
->mark_off
[m
] = lb
->mark_off
[m
];
67 static void lbuf_loadmark(struct lbuf
*lb
, struct lopt
*lo
, int m
)
69 if (lo
->mark
&& lo
->mark
[m
] >= 0) {
70 lb
->mark
[m
] = lo
->mark
[m
];
71 lb
->mark_off
[m
] = lo
->mark_off
[m
];
75 void lbuf_free(struct lbuf
*lb
)
78 for (i
= 0; i
< lb
->ln_n
; i
++)
80 for (i
= 0; i
< lb
->hist_n
; i
++)
81 lopt_done(&lb
->hist
[i
]);
87 /* insert a line at pos */
88 static void lbuf_insertline(struct lbuf
*lb
, int pos
, char *s
)
90 if (lb
->ln_n
== lb
->ln_sz
) {
91 int nsz
= lb
->ln_sz
+ 512;
92 char **nln
= malloc(nsz
* sizeof(nln
[0]));
93 memcpy(nln
, lb
->ln
, lb
->ln_n
* sizeof(lb
->ln
[0]));
98 memmove(lb
->ln
+ pos
+ 1, lb
->ln
+ pos
,
99 (lb
->ln_n
- pos
) * sizeof(lb
->ln
[0]));
104 /* low-level replacement */
105 static void lbuf_replace(struct lbuf
*lb
, char *s
, int pos
, int n_del
)
110 for (i
= 0; i
< n_del
; i
++)
111 free(lb
->ln
[pos
+ i
]);
112 memmove(lb
->ln
+ pos
, lb
->ln
+ pos
+ n_del
,
113 (lb
->ln_n
- pos
- n_del
) * sizeof(lb
->ln
[0]));
115 while (s
&& (r
= strchr(s
, '\n'))) {
116 char *n
= malloc(r
- s
+ 2);
117 memcpy(n
, s
, r
- s
+ 1);
119 lbuf_insertline(lb
, pos
+ n_ins
++, n
);
122 for (i
= 0; i
< LEN(lb
->mark
); i
++) { /* updating marks */
123 if (!s
&& lb
->mark
[i
] >= pos
&& lb
->mark
[i
] < pos
+ n_del
)
125 else if (lb
->mark
[i
] >= pos
+ n_del
)
126 lb
->mark
[i
] += n_ins
- n_del
;
127 else if (lb
->mark
[i
] >= pos
+ n_ins
)
128 lb
->mark
[i
] = pos
+ n_ins
- 1;
130 lbuf_mark(lb
, '[', pos
, 0);
131 lbuf_mark(lb
, ']', pos
+ n_ins
- n_del
, 0);
134 static int uc_newlines(char *s
)
137 for (n
= 0; (s
= strchr(s
, '\n')); n
++)
142 /* append undo/redo history */
143 static void lbuf_opt(struct lbuf
*lb
, char *buf
, int pos
, int n_del
)
147 for (i
= lb
->hist_u
; i
< lb
->hist_n
; i
++)
148 lopt_done(&lb
->hist
[i
]);
149 lb
->hist_n
= lb
->hist_u
;
150 if (lb
->hist_n
== lb
->hist_sz
) {
151 int sz
= lb
->hist_sz
+ 128;
152 struct lopt
*hist
= malloc(sz
* sizeof(hist
[0]));
153 memcpy(hist
, lb
->hist
, lb
->hist_n
* sizeof(hist
[0]));
158 lo
= &lb
->hist
[lb
->hist_n
];
160 lb
->hist_u
= lb
->hist_n
;
161 memset(lo
, 0, sizeof(*lo
));
164 lo
->del
= n_del
? lbuf_cp(lb
, pos
, pos
+ n_del
) : NULL
;
165 lo
->n_ins
= buf
? uc_newlines(buf
) : 0;
166 lo
->ins
= buf
? uc_dup(buf
) : NULL
;
168 for (i
= 0; i
< LEN(lb
->mark
); i
++)
169 if (lb
->mark
[i
] >= pos
&& lb
->mark
[i
] < pos
+ n_del
)
171 lbuf_savemark(lb
, lo
, i
);
174 void lbuf_rd(struct lbuf
*lbuf
, int fd
, int beg
, int end
)
180 while ((nr
= read(fd
, buf
, sizeof(buf
))) > 0)
181 sbuf_mem(sb
, buf
, nr
);
182 lbuf_edit(lbuf
, sbuf_buf(sb
), beg
, end
);
186 void lbuf_wr(struct lbuf
*lbuf
, int fd
, int beg
, int end
)
189 for (i
= beg
; i
< end
; i
++)
190 write(fd
, lbuf
->ln
[i
], strlen(lbuf
->ln
[i
]));
193 /* replace lines beg through end with buf */
194 void lbuf_edit(struct lbuf
*lb
, char *buf
, int beg
, int end
)
200 if (beg
== end
&& !buf
)
202 lbuf_opt(lb
, buf
, beg
, end
- beg
);
203 lbuf_replace(lb
, buf
, beg
, end
- beg
);
206 char *lbuf_cp(struct lbuf
*lb
, int beg
, int end
)
211 for (i
= beg
; i
< end
; i
++)
213 sbuf_str(sb
, lb
->ln
[i
]);
214 return sbuf_done(sb
);
217 char *lbuf_get(struct lbuf
*lb
, int pos
)
219 return pos
>= 0 && pos
< lb
->ln_n
? lb
->ln
[pos
] : NULL
;
222 int lbuf_len(struct lbuf
*lb
)
227 void lbuf_mark(struct lbuf
*lbuf
, int mark
, int pos
, int off
)
231 lbuf
->mark
[mark
] = pos
;
232 lbuf
->mark_off
[mark
] = off
;
235 int lbuf_jump(struct lbuf
*lbuf
, int mark
, int *pos
, int *off
)
237 if (mark
>= NMARKS
|| lbuf
->mark
[mark
] < 0)
239 *pos
= lbuf
->mark
[mark
];
241 *off
= lbuf
->mark_off
[mark
];
245 int lbuf_undo(struct lbuf
*lb
)
250 useq
= lb
->hist
[lb
->hist_u
- 1].seq
;
251 while (lb
->hist_u
&& lb
->hist
[lb
->hist_u
- 1].seq
== useq
) {
252 struct lopt
*lo
= &lb
->hist
[--(lb
->hist_u
)];
253 lbuf_replace(lb
, lo
->del
, lo
->pos
, lo
->n_ins
);
254 lbuf_loadmark(lb
, lo
, '*');
255 for (i
= 0; i
< LEN(lb
->mark
); i
++)
256 lbuf_loadmark(lb
, lo
, i
);
261 int lbuf_redo(struct lbuf
*lb
)
264 if (lb
->hist_u
== lb
->hist_n
)
266 useq
= lb
->hist
[lb
->hist_u
].seq
;
267 while (lb
->hist_u
< lb
->hist_n
&& lb
->hist
[lb
->hist_u
].seq
== useq
) {
268 struct lopt
*lo
= &lb
->hist
[lb
->hist_u
++];
269 lbuf_replace(lb
, lo
->ins
, lo
->pos
, lo
->n_del
);
270 lbuf_loadmark(lb
, lo
, '*');
275 static int lbuf_seq(struct lbuf
*lb
)
277 return lb
->hist_u
? lb
->hist
[lb
->hist_u
- 1].seq
: lb
->useq_last
;
280 /* mark buffer as saved and, if clear, clear the undo history */
281 void lbuf_saved(struct lbuf
*lb
, int clear
)
285 for (i
= 0; i
< lb
->hist_n
; i
++)
286 lopt_done(&lb
->hist
[i
]);
289 lb
->useq_last
= lb
->useq
;
291 lb
->useq_zero
= lbuf_seq(lb
);
295 /* was the file modified since the last lbuf_modreset() */
296 int lbuf_modified(struct lbuf
*lb
)
298 struct lopt
*lo
= lb
->hist_n
? &lb
->hist
[lb
->hist_n
- 1] : NULL
;
299 if (lb
->hist_u
== lb
->hist_n
&& lo
&& !lo
->mark
)
300 lbuf_savemark(lb
, lo
, '*');
302 return lbuf_seq(lb
) != lb
->useq_zero
;