]> git.neil.brown.name Git - edlib.git/blob - doc-text.c
7f4ed19441b04fbf2f019fef8f97a9d6c06a3f2a
[edlib.git] / doc-text.c
1 /*
2  * Copyright Neil Brown ©2015-2023 <neil@brown.name>
3  * May be distributed under terms of GPLv2 - see file:COPYING
4  *
5  * Generic text document.
6  *
7  * This allows for a file to be read in, and edited by creating
8  * a linked list of chunks of text - the original isn't changed.
9  * This means that pointers outside of the edit region are mostly
10  * left untouched.
11  *
12  * Indefinite undo is kept as a record of changes to the list of chunks.
13  * New text is added to the end of a list of allocations.
14  *
15  * Text.
16  *
17  * The text of a document is stored in a collection of allocations
18  * which are immutable once created.  They are attached to the 'document'
19  * and freed only when the document is discard.
20  * The current state of the document is represented by a linked list of
21  * 'chunks' which each point to part of some allocation.
22  *
23  * Each chunk can have 'attributes' which add arbitrary annotations to
24  * ranges of text.  Unlike the text itself, these are not immutable.  Only
25  * the 'current' attributes are stored.  It is assumed that following
26  * 'undo', the appropriate attributes can be re-computed.  i.e. they are
27  * a cache.  The owner can get notified of changes which imply that
28  * attributes may have been lost.
29  *
30  * When text is removed from a document, the 'chunk' is modified to
31  * reference less text.  If the chunk becomes empty, it is removed
32  * from the list, but not freed - as it will be in the undo list.
33  * When text is added to a document a new chunk is created which
34  * points to the next free space in the latest allocation, and text is
35  * added there.  If the text is being added to the end of a chunk and
36  * it already points to the end of the latest allocation, then no new
37  * chunk is allocated.
38  *
39  * Text is assumed to be UTF-8 encoded.  This becomes relevant when adding
40  * a string to the document and it won't all fit in the current allocation.
41  * In that case we ensure the first byte that goes in the next allocation
42  * matches 0xxxxxxx or 11xxxxxx., not 10xxxxxx.
43  *
44  * Undo/redo information is stored as a list of edits.  Each edit
45  * changes either the start or the end of a 'chunk'. When a chunk becomes
46  * empty it is removed from the chunk list.  The 'prev' pointer is preserved
47  * so when an undo makes it non-empty, it knows where to be added back.
48  *
49  * A text always has a least one allocation which is created with the text.
50  * If the text is empty, there will not be any chunks though, so all text refs
51  * will point to NULL.  The NULL chunk is at the end of the text.
52  * The ->txt pointer of a chunk never changes.  It is set when the chunk
53  * is created and then only start and end are changed.
54  */
55
56 #define _GNU_SOURCE /* for asprintf */
57 #include <unistd.h>
58 #include <stdlib.h>
59 #include <time.h>
60 #include <string.h>
61 #include <memory.h>
62 #include <locale.h>
63 #include <stdio.h>
64 #include <fcntl.h>
65 #include <errno.h>
66 #include <dirent.h>
67
68 /* A doc_ref is treated as a pointer to a chunk, and an offset
69  * from the start of 'txt'.  So 'o' must be between c->start and
70  * c->end inclusive.
71  * A 'c' of NULL means end of file.
72  * The normalized form requires that 'o' does
73  * not point to the end of the chunk.
74  */
75 #define PRIVATE_DOC_REF
76
77 struct doc_ref {
78         struct text_chunk *c;
79         unsigned int o;
80 };
81 struct text;
82 #define DOC_DATA_TYPE struct text
83 #define DOC_NEXT(d,m,r,b) text_next(d,r,b)
84 #define DOC_PREV(d,m,r,b) text_prev(d,r,b)
85 #include "core.h"
86 #include "misc.h"
87
88 /* text is allocated is large blocks - possibly a whole
89  * file or some other large unit being added to a document.
90  * For small additions (normal typing) the default allocation
91  * size is 4K.
92  * When more is allocated than needed, extra can be added on to
93  * the end - 'free' is the next index with free space.
94  */
95 struct text_alloc {
96         struct text_alloc *prev;
97         int size;
98         int free;
99         char text[];
100 };
101
102 #define DEFAULT_SIZE ((int)(4096 - sizeof(struct text_alloc)))
103 #define MAX_SIZE ((int)((1<<20) - sizeof(struct text_alloc)))
104
105 /* The text document is a list of text_chunk.
106  * The 'txt' pointer is within the text[] of a text_alloc.
107  * 'start' and 'end' narrow that.
108  * Each alloc potentially is divided into multiple
109  * separate chunks which are never merged.  The only
110  * chunk that can change size is the last one allocated,
111  * which may grow into the free space.
112  */
113 struct text_chunk {
114         char                    *txt safe;
115         unsigned int            start;
116         unsigned int            end;
117         struct list_head        lst;
118         struct attrset          *attrs;
119 };
120
121 /* An 'edit' consists of one or more text_edit structs linked together.
122  * The 'first' text_edit in a group has 'first' set.  So when popping
123  * off the 'undo' list, we pop until we find the 'first' one.  When
124  * popping off the 'redo' list, we pop a first, then any following
125  * non-first entries.
126  * Each entry identifies a chunk. If 'at_start' is set, the 'len' is
127  * added to the 'start' pointer (subtracted for undo).  Otherwise
128  * the len added to the end.  If the resulting length is zero, the
129  * chunk is removed from the list.
130  *
131  * Each edit can have an altnext.
132  * For the undo list, this is an alternate redo to reflect a branching
133  * change history.
134  * For the redo list, this is a second change that happened from the
135  * same starting point.  If there is a third change, we insert a no-op
136  * edit so as to get an extra altnext.
137  * In the undo list, altnext is an alternate forward path.
138  * if alt_is_second, then we are currently on the second path, and after
139  * undoing it, will go up the first.
140  * if !alt_is_second, we are currently on the first path, and
141  * don't want to go back up the second (until we undo all the way to the
142  * start and try again).
143  */
144 struct text_edit {
145         struct text_chunk       *target safe;
146         struct text_edit        *next, *altnext;
147         bool                    first:1;
148         bool                    at_start:1;
149         bool                    alt_is_second:1;
150         signed int              len:29; // bytes add, -ve for removed.
151 };
152
153 /* A text document is a document with allocations, a list
154  * of chunks, and some undo info.
155  */
156 struct text {
157         struct doc              doc;
158
159         struct text_alloc       *alloc safe;
160         struct list_head        text;
161         struct text_edit        *undo, *redo;
162         /* If prev_edit is Redo then next edit is ->redo or ->undo->altnext
163          * or ->undo
164          * If prev_edit is Undo, then next edit is ->undo->altnext or ->undo
165          * If prev_edit is AltUndo, then next edit is ->undo
166          */
167         enum { Redo, Undo, AltUndo } prev_edit;
168
169         bool                    revising_marks;
170         char                    file_changed; /* '2' means it has changed, but
171                                                * we are editing anyway
172                                                */
173         char                    newfile; /* file doesn't exist yet */
174         bool                    autosave_exists;
175         struct stat             stat;
176         const char              *fname;
177         const char              *autosave_name;
178         struct text_edit        *saved;
179         struct auto_save {
180                 int             changes;
181                 int             timer_started;
182                 time_t          last_change;
183         } as;
184 };
185
186 #include "core-pane.h"
187
188 static int text_advance_towards(struct text *t safe, struct doc_ref *ref safe,
189                                 struct doc_ref *target safe);
190 static int text_retreat_towards(struct text *t safe, struct doc_ref *ref safe,
191                                 struct doc_ref *target safe);
192 static bool text_ref_same(struct text *t safe, struct doc_ref *r1 safe,
193                           struct doc_ref *r2 safe);
194 static bool _text_ref_same(struct text *t safe, struct doc_ref *r1 safe,
195                            struct doc_ref *r2 safe);
196 static int text_locate(struct text *t safe, struct doc_ref *r safe,
197                        struct doc_ref *dest safe);
198 static void text_check_consistent(struct text *t safe);
199 static void text_normalize(struct text *t safe, struct doc_ref *r safe);
200 static void text_cleanout(struct text *t safe);
201 static void text_add_str(struct text *t safe, struct mark *pm safe,
202                          const char *str safe, off_t size,
203                          bool *first_edit safe);
204 static void text_check_autosave(struct pane *p safe);
205 static bool check_readonly(const struct cmd_info *ci safe);
206
207 static MEMPOOL(text);
208 static MEMPOOL(undo);
209 static struct map *text_map;
210
211 static struct text_alloc *safe
212 text_new_alloc(struct text *t safe, int size)
213 {
214         struct text_alloc *new;
215         if (size == 0)
216                 size = DEFAULT_SIZE;
217         size += sizeof(struct text_alloc);
218         size = ((size-1) | 255) + 1;
219         new = alloc_buf(size, text);
220         new->prev = t->alloc;
221         t->alloc = new;
222         new->size = size - sizeof(struct text_alloc);
223         new->free = 0;
224         return new;
225 }
226
227 static bool check_file_changed(struct pane *p safe)
228 {
229         struct text *t = p->doc_data;
230         struct stat st;
231
232         if (t->file_changed)
233                 /* '1' means it has changed, '2' means "but we don't care" */
234                 return t->file_changed == 1;
235         if (!t->fname)
236                 return False;
237         if (stat(t->fname, &st) != 0) {
238                 memset(&st, 0, sizeof(st));
239                 if (t->newfile)
240                         return False;
241         }
242         if (st.st_ino != t->stat.st_ino ||
243             st.st_dev != t->stat.st_dev ||
244             st.st_mtime != t->stat.st_mtime ||
245             st.st_mtim.tv_nsec != t->stat.st_mtim.tv_nsec) {
246                 t->file_changed = 1;
247                 call("doc:notify:doc:status-changed", p);
248                 return True;
249         }
250         return False;
251 }
252
253 DEF_CMD(text_readonly)
254 {
255         struct doc *d = ci->home->data;
256         struct text *t = ci->home->doc_data;
257
258         if (t->file_changed && !d->readonly && ci->num)
259                 t->file_changed = 2;
260         /* Use default handling */
261         return Efallthrough;
262 }
263
264 static const char *safe autosave_name(const char *name safe)
265 {
266         char *tempname = malloc(strlen(name) + 3 + 10);
267         const char *base;
268         char *tbase;
269
270         strcpy(tempname, name);
271         base = strrchr(name, '/');
272         if (base)
273                 base += 1;
274         else
275                 base = name;
276         tbase = tempname + (base - name);
277         sprintf(tbase, "#%s#", base);
278         return tempname;
279 }
280
281 DEF_CMD(text_load_file)
282 {
283         int fd = ci->num2;
284         const char *name = ci->str;
285         off_t size;
286         struct text_alloc *a;
287         struct text_chunk *c = NULL;
288         int len;
289         struct text *t = ci->home->doc_data;
290
291         if (t->saved != t->undo)
292                 return Einval;
293         if (fd < 0 && (ci->num & 6) && t->fname) {
294                 /* re-open existing file name */
295                 if (ci->num & 4)
296                         fd = open(t->autosave_name, O_RDONLY);
297                 else
298                         fd = open(t->fname, O_RDONLY);
299                 name = t->fname;
300         }
301         if (fd < 0) {
302                 size = 0;
303                 t->newfile = 1;
304         } else {
305                 size = lseek(fd, 0, SEEK_END);
306                 lseek(fd, 0, SEEK_SET);
307         }
308         if (size < 0)
309                 goto err;
310         if ((ci->num & 1) && t->fname && fd >= 0) {
311                 struct stat stb;
312
313                 fstat(fd, &stb);
314                 if (stb.st_ino == t->stat.st_ino &&
315                     stb.st_dev == t->stat.st_dev &&
316                     stb.st_size == t->stat.st_size &&
317                     stb.st_mtime == t->stat.st_mtime) {
318                         if (fd != ci->num2)
319                                 close(fd);
320                         return Efalse;
321                 }
322         }
323
324         if (size > 0) {
325                 struct mark *m;
326                 text_cleanout(t);
327                 alloc(c, text);
328                 a = text_new_alloc(t, size);
329                 if (!a)
330                         goto err;
331                 while (a->free < size &&
332                        (len = read(fd, a->text + a->free, size - a->free)) > 0)
333                         a->free += len;
334
335                 c->txt = a->text;
336                 c->attrs = NULL;
337                 c->start = 0;
338                 c->end = a->free;
339                 list_add(&c->lst, &t->text);
340                 hlist_for_each_entry(m, &t->doc.marks, all) {
341                         m->ref.c = c;
342                         m->ref.o = 0;
343                 }
344         }
345         if (name) {
346                 struct stat stb;
347
348                 if (fstat(fd, &t->stat) < 0) {
349                         t->newfile = 1;
350                         memset(&t->stat, 0, sizeof(t->stat));
351                 }
352                 if (name != t->fname) {
353                         const char *dname;
354                         free((void*)t->fname);
355                         t->fname = strdup(name);
356                         dname = strrchr(name, '/');
357                         if (dname)
358                                 dname += 1;
359                         else
360                                 dname = name;
361                         call("doc:set-name", ci->home, 0, NULL, dname);
362                 }
363                 if (!t->autosave_name)
364                         t->autosave_name = autosave_name(name);
365                 if (stat(t->autosave_name, &stb) == 0 &&
366                     stb.st_mtime > t->stat.st_mtime)
367                         t->autosave_exists = True;
368         }
369         if (ci->num & 4) {
370                 /* restored from autoload, so nothing matches saved version */
371                 t->saved = (void*)1;
372                 t->file_changed = 2;
373         } else {
374                 /* Current state is 'saved' */
375                 t->saved = t->undo;
376                 t->file_changed = 0;
377         }
378         call("doc:notify:doc:status-changed", ci->home);
379         pane_notify("doc:replaced", ci->home);
380         if (fd != ci->num2)
381                 close(fd);
382         return 1;
383 err:
384         unalloc(c, text);
385         if (fd != ci->num2)
386                 close(fd);
387         return Efallthrough;
388 }
389
390 DEF_CMD(text_insert_file)
391 {
392         struct text *t = ci->home->doc_data;
393         struct mark *pm = ci->mark, *early;
394         struct text_alloc *a;
395         int len;
396         int fd = ci->num;
397         off_t size, start;
398         bool first = True;
399         bool status_changes = (t->undo == t->saved);
400
401         if (check_readonly(ci))
402                 return Efail;
403         if (!pm || fd < 0 || fd == NO_NUMERIC)
404                 return Enoarg;
405         size = lseek(fd, 0, SEEK_END);
406         lseek(fd, 0, SEEK_SET);
407         if (size < 0)
408                 return Efail;
409         a = t->alloc;
410         if (a->size - a->free < size)
411                 a = text_new_alloc(t, size);
412         if (!a)
413                 return Efail;
414
415         early = mark_dup(pm);
416         mark_step(early, 0);
417
418         start = a->free;
419         while (a->free < start + size &&
420                (len = read(fd, a->text + a->free, start + size - a->free)) > 0)
421                 a->free += len;
422         text_add_str(t, pm, a->text + start, size, &first);
423
424         text_check_consistent(t);
425         text_check_autosave(ci->home);
426         if (status_changes)
427                 call("doc:notify:doc:status-changed", ci->home);
428         pane_notify("doc:replaced", ci->home, 0, early, NULL,
429                     0, pm);
430         mark_free(early);
431
432         return 1;
433 }
434
435 static bool do_text_output_file(struct pane *p safe, struct doc_ref *start,
436                                 struct doc_ref *end, int fd)
437 {
438         struct text *t = p->doc_data;
439         struct text_chunk *c;
440         int offset = 0;
441
442         if (start) {
443                 c = start->c;
444                 offset = start->o;
445         } else
446                 c = list_first_entry_or_null(&t->text, struct text_chunk, lst);
447
448         list_for_each_entry_from(c, &t->text, lst) {
449                 char *s = c->txt + c->start;
450                 int ln = c->end - c->start;
451                 if (end && end->c == c)
452                         ln = end->o;
453                 if (write(fd, s + offset, ln - offset) != ln - offset)
454                         return False;
455                 offset = 0;
456                 if (end && end->c == c)
457                         break;
458         }
459         if (fsync(fd) != 0)
460                 return False;
461         return True;
462 }
463
464 static bool do_text_write_file(struct pane *p safe, struct doc_ref *start,
465                                struct doc_ref *end,
466                                const char *fname safe)
467 {
468         /* Don't worry about links for now
469          * Create a temp file with #basename#~, write to that,
470          * copy mode across, fsync and then rename
471          */
472         struct text *t = p->doc_data;
473         char *tempname = malloc(strlen(fname) + 3 + 10);
474         const char *base;
475         char *tbase;
476         int cnt = 0;
477         int fd = -1;
478         struct stat stb;
479
480         strcpy(tempname, fname);
481         base = strrchr(fname, '/');
482         if (base)
483                 base += 1;
484         else
485                 base = fname;
486         tbase = tempname + (base - fname);
487         while (cnt < 20 && fd == -1) {
488                 if (cnt)
489                         sprintf(tbase, "#%s#~%d", base, cnt);
490                 else
491                         sprintf(tbase, "#%s#~", base);
492                 fd = open(tempname, O_WRONLY|O_CREAT|O_EXCL, 0666);
493                 if (fd < 0 && errno != EEXIST)
494                         break;
495                 cnt += 1;
496         }
497         if (fd < 0)
498                 return False;
499
500         if (!do_text_output_file(p, start, end, fd))
501                 goto error;
502         if (stat(fname, &stb) == 0 &&
503             S_ISREG(stb.st_mode))
504                 /* Preserve modes, but not setuid */
505                 fchmod(fd, stb.st_mode & 0777);
506         if (fname == t->fname && check_file_changed(p)) {
507                 /* We are saving to a file which changed since we read it,
508                  * so let's move that changed file to a backup
509                  */
510                 int i;
511
512                 for (i = 1 ; i < 1000; i++) {
513                         char *new = NULL;
514                         if (asprintf(&new, "%s~%d~", fname, i) < 0)
515                                 break;
516                         if (link(fname, new) == 0) {
517                                 free(new);
518                                 break;
519                         }
520                         free(new);
521                         if (errno != EEXIST)
522                                 break;
523                 }
524         }
525         if (rename(tempname, fname) < 0)
526                 goto error;
527         fstat(fd, &t->stat);
528         close(fd);
529         free(tempname);
530         return True;
531 error:
532         close(fd);
533         unlink(tempname);
534         free(tempname);
535         return False;
536
537 }
538
539 static void autosaves_record(struct pane *p safe, const char *path safe,
540                              bool create)
541 {
542         DIR *d;
543         char *home = getenv("HOME");
544         char *dirname = getenv("EDLIB_AUTOSAVE");
545         int num;
546         bool changed = False;
547
548         if (!home)
549                 home = "/tmp";
550         if (!dirname)
551                 dirname = strconcat(p, home, "/.edlib_autosave");
552         d = opendir(dirname);
553         if (!d) {
554                 if (!create)
555                         return;
556                 if (mkdir(dirname, 0770) < 0)
557                         return;
558                 d = opendir(dirname);
559                 if (!d)
560                         return;
561                 num = 1;
562         } else {
563                 struct dirent *de;
564
565                 num = 1;
566                 while ((de = readdir(d)) != NULL) {
567                         char *ep = NULL;
568                         long n;
569                         int len;
570                         char current[PATH_MAX];
571
572                         if (de->d_name[0] == '.')
573                                 continue;
574                         n = strtoul(de->d_name, &ep, 10);
575                         if (!ep || ep == de->d_name || *ep != '\0')
576                                 continue;
577                         if (n >= num)
578                                 num = n + 1;
579                         len = readlinkat(dirfd(d), de->d_name,
580                                          current, sizeof(current));
581                         if (len <= 0 || len >= (int)sizeof(current))
582                                 continue;
583                         current[len] = 0;
584                         if (strcmp(current, path) == 0) {
585                                 if (!create) {
586                                         unlinkat(dirfd(d), de->d_name, 0);
587                                         changed = True;
588                                 }
589                                 create = False;
590                                 break;
591                         }
592                 }
593         }
594         if (create) {
595                 char nbuf[20];
596                 snprintf(nbuf, sizeof(nbuf), "%d", num);
597                 symlinkat(path, dirfd(d), nbuf);
598         }
599         if (changed) {
600                 struct pane *doc;
601                 doc = call_ret(pane, "doc:open", p, -1, NULL, dirname);
602                 if (doc)
603                         pane_call(doc, "doc:notify:doc:revisit", p);
604         }
605         closedir(d);
606 }
607
608 static void do_text_autosave(struct pane *p safe)
609 {
610         struct text *t = p->doc_data;
611         int fd = -1;
612
613         if (!t->fname)
614                 return;
615         check_file_changed(p);
616
617         if (!t->autosave_name)
618                 t->autosave_name = autosave_name(t->fname);
619         if (t->as.changes == 0) {
620                 unlink(t->autosave_name);
621                 t->autosave_exists = False;
622                 autosaves_record(p, t->fname, False);
623                 return;
624         }
625         fd = open(t->autosave_name, O_WRONLY|O_CREAT|O_TRUNC, 0666);
626         if (fd < 0)
627                 return;
628
629         if (!do_text_output_file(p, NULL, NULL, fd)) {
630                 close(fd);
631                 unlink(t->autosave_name);
632                 return;
633         }
634         t->as.changes = 0;
635         close(fd);
636         autosaves_record(p, t->fname, True);
637 }
638
639 DEF_CMD(text_autosave_delete)
640 {
641         struct pane *home = ci->home;
642         struct text *t = home->data;
643         const char *name = ci->str;
644         int ret = 1;
645
646         if (!t->fname || !name)
647                 return Enoarg;
648
649         if (!t->autosave_name)
650                 t->autosave_name = autosave_name(t->fname);
651
652         if (strcmp(name, t->autosave_name) != 0 ||
653             unlink(t->autosave_name) < 0)
654                 ret = Efail;
655         t->autosave_exists = False;
656         autosaves_record(home, t->fname, False);
657
658         return ret;
659 }
660
661 DEF_CMD(text_autosave_tick)
662 {
663         struct pane *home = ci->home;
664         struct text *t = home->data;
665
666         t->as.timer_started = 0;
667         if (!t->fname)
668                 return Efalse;
669         if (t->as.changes == 0)
670                 /* This will delete the file */
671                 do_text_autosave(home);
672         if (time(NULL) - t->as.last_change >= 30)
673                 do_text_autosave(home);
674         else {
675                 t->as.timer_started = 1;
676                 call_comm("event:timer", home, &text_autosave_tick,
677                           (t->as.last_change + 30 - time(NULL)) * 1000);
678         }
679         return Efalse;
680 }
681
682 static void text_check_autosave(struct pane *p safe)
683 {
684         struct text *t = p->doc_data;
685
686         if (t->undo == t->saved)
687                 t->as.changes = 0;
688         else
689                 t->as.changes += 1;
690         t->as.last_change = time(NULL);
691         if (!t->fname)
692                 return;
693         if (t->as.changes > 300 || t->as.changes == 0)
694                 do_text_autosave(p);
695         else if (!t->as.timer_started) {
696                 t->as.timer_started = 1;
697                 call_comm("event:timer", p, &text_autosave_tick,
698                           30 * 1000);
699         }
700 }
701
702 DEF_CMD(text_save_file)
703 {
704         struct doc *d = ci->home->data;
705         struct text *t = ci->home->doc_data;
706         int ret;
707         char *msg;
708         int change_status = 0;
709
710         if (!t->fname) {
711                 asprintf(&msg, "** No file name known for %s ***", d->name);
712                 ret = Efail;
713         } else {
714                 ret = do_text_write_file(ci->home, NULL, NULL, t->fname);
715                 if (ret) {
716                         asprintf(&msg, "Successfully wrote %s", t->fname);
717                         t->saved = t->undo;
718                         change_status = 1;
719                         t->file_changed = 0;
720                         t->newfile = 0;
721                 } else
722                         asprintf(&msg, "*** Failed to write %s ***", t->fname);
723         }
724         call("Message", ci->focus, 0, NULL, msg);
725         free(msg);
726         if (change_status)
727                 call("doc:notify:doc:status-changed", ci->home);
728         text_check_autosave(ci->home);
729         if (ret == 0)
730                 return 1;
731         return Efail;
732 }
733
734 DEF_CMD(text_write_file)
735 {
736         int ret;
737         bool use_marks = ci->mark && ci->mark2;
738
739         if (ci->str) {
740                 ret = do_text_write_file(ci->home,
741                                          use_marks ? &ci->mark->ref: NULL,
742                                          use_marks ? &ci->mark2->ref: NULL,
743                                          ci->str);
744                 return ret ? 1 : Efail;
745         }
746         if (ci->num >= 0 && ci->num != NO_NUMERIC) {
747                 ret = do_text_output_file(ci->home,
748                                           use_marks ? &ci->mark->ref: NULL,
749                                           use_marks ? &ci->mark2->ref: NULL,
750                                           ci->num);
751                 return ret ? 1 : Efail;
752         }
753         return Enoarg;
754 }
755
756 DEF_CMD(text_same_file)
757 {
758         struct text *t = ci->home->doc_data;
759         struct stat stb, stb2;
760         int fd = ci->num2;
761
762         if (t->fname == NULL)
763                 return Efallthrough;
764         if (ci->str && strcmp(ci->str, t->fname) == 0)
765                 return 1;
766         if (fd >= 0) {
767                 if (fstat(fd, &stb) != 0)
768                         return Efallthrough;
769         } else if (ci->str) {
770                 if (stat(ci->str, &stb) != 0)
771                         return Efallthrough;
772         } else
773                 return Efallthrough;
774         if (t->stat.st_ino != stb.st_ino ||
775             t->stat.st_dev != stb.st_dev)
776                 return Efallthrough;
777         /* Must check file hasn't changed beneath us */
778         if (stat(t->fname, &stb2) != 0)
779                 stb2.st_ino = 0;
780         if (stb2.st_ino == stb.st_ino &&
781             stb2.st_dev == stb.st_dev)
782                 return 1;
783         return Efallthrough;
784 }
785
786 static void text_add_edit(struct text *t safe, struct text_chunk *target safe,
787                           bool *first safe, int at_start, int len)
788 {
789         struct text_edit *e;
790
791         if (len == 0)
792                 return;
793
794         if (t->saved == t->undo)
795                 /* Must never merge undo entries across a save point */
796                 *first = 1;
797
798         if (t->redo) {
799                 /* Cannot add an edit before some redo edits, as they
800                  * will get confused.  We need to record the redo history
801                  * here in the undo history, possibly allocating
802                  * a nop edit (len == 0)
803                  */
804                 if (t->undo == NULL || t->undo->altnext != NULL) {
805                         alloc(e, undo);
806                         e->target = target; /* ignored */
807                         e->first = 0;
808                         e->at_start = 0;
809                         e->len = 0; /* This is a no-op */
810                         e->next = t->undo;
811                         t->undo = e;
812                 }
813                 t->undo->altnext = t->redo;
814                 t->undo->alt_is_second = 0;
815                 t->redo = NULL;
816         }
817         /* factoring out t->undo here avoids a bug in smatch. */
818         e = t->undo;
819         if (e && e->len != 0 && e->len + len != 0 && !*first &&
820             e->target == target && e->at_start == at_start) {
821                 /* This new edit can be merged with the previous one */
822                 e->len += len;
823         } else {
824                 alloc(e, undo);
825                 e->target = target;
826                 e->first = *first;
827                 e->at_start = at_start;
828                 e->len = len;
829                 *first = 0;
830                 e->next = t->undo;
831                 e->altnext = NULL;
832                 e->alt_is_second = 0;
833                 t->undo = e;
834         }
835 }
836
837 static void _text_add_str(struct text *t safe, struct doc_ref *pos safe,
838                           const char *str safe, off_t len,
839                           struct doc_ref *start, bool *first_edit safe)
840 {
841         /* Text is added to the end of the referenced chunk, or
842          * in new chunks which are added afterwards.  This allows
843          * the caller to reliably updated any pointers to accommodate
844          * changes.
845          * The added text has no attributes.
846          *
847          * 'pos' is moved to point to the end of the inserted text.
848          * 'start' is set to point to the start which may be the
849          * original 'pos', or may not if a chunk was inserted.
850          */
851         /* easy/common case first: pos is at the end of a chunk,
852          * which is the last chunk in the current allocation.
853          */
854         struct text_alloc *a = t->alloc;
855         off_t len2;
856         off_t orig_len;
857
858         if (len < 0)
859                 len = strlen(str);
860         orig_len = len;
861         if (start)
862                 *start = *pos;
863
864         len2 = len;
865         if (pos->c && pos->o == pos->c->end &&
866             pos->c->txt + pos->o == a->text + a->free &&
867             str != a->text + a->free &&
868             (a->size - a->free >= len ||
869              (len2 = utf8_round_len(str, a->size - a->free)) > 0)) {
870                 /* Some of this ('len2') can be added to the current chunk */
871                 memcpy(a->text+a->free, str, len2);
872                 a->free += len2;
873                 pos->c->end += len2;
874                 pos->o += len2;
875                 str += len2;
876                 text_add_edit(t, pos->c, first_edit, 0, len2);
877                 len -= len2;
878         }
879         if (!len)
880                 return;
881         /* Need a new chunk.  Might need to split the current chunk first.
882          * Old chunk must be first to simplify updating of pointers */
883         if (pos->c == NULL || pos->o < pos->c->end) {
884                 struct text_chunk *c;
885                 alloc(c, text);
886                 if (pos->c == NULL || pos->o == pos->c->start) {
887                         /* At the start of a chunk, so create a new one here */
888                         c->txt = safe_cast NULL;
889                         c->start = c->end = 0;
890                         c->attrs = NULL;
891                         if (pos->c)
892                                 list_add_tail(&c->lst, &pos->c->lst);
893                         else
894                                 list_add_tail(&c->lst, &t->text);
895
896                         if (start && start->c == pos->c && start->o == pos->o) {
897                                 start->c = c;
898                                 start->o = c->start;
899                         }
900                         pos->c = c;
901                         pos->o = c->start;
902                 } else {
903                         /* Not at the start, so we need to split at pos->o */
904                         c->txt = pos->c->txt;
905                         c->start = pos->o;
906                         c->end = pos->c->end;
907                         c->attrs = attr_copy_tail(pos->c->attrs, c->start);
908                         pos->c->end = pos->o;
909                         attr_trim(&pos->c->attrs, pos->c->end);
910                         list_add(&c->lst, &pos->c->lst);
911                         text_add_edit(t, c, first_edit, 0, c->end - c->start);
912                         /* this implicitly truncates pos->c, so don't need
913                          * to record that. */
914                 }
915         }
916         while (len > 0) {
917                 /* Make sure we have an empty chunk */
918                 if (pos->c->end > pos->c->start) {
919                         struct text_chunk *c;
920                         alloc(c, text);
921                         c->start = c->end = 0;
922                         c->attrs = NULL;
923                         list_add(&c->lst, &pos->c->lst);
924                         if (start && start->c == pos->c && start->o == pos->o) {
925                                 start->c = c;
926                                 start->o = 0;
927                         }
928                         pos->c = c;
929                         pos->o = c->start;
930                 }
931                 /* Make sure we have some space in 'a' */
932                 len2 = len;
933                 if (a->size - a->free < len &&
934                     (len2 = utf8_round_len(str, a->size - a->free)) == 0) {
935                         if (orig_len < 128 ||
936                             t->alloc->size < DEFAULT_SIZE)
937                                 a = text_new_alloc(t, DEFAULT_SIZE);
938                         else if (len > DEFAULT_SIZE && len > t->alloc->size)
939                                 a = text_new_alloc(t, ((len +256) | 4095) + 1 - 256);
940                         else if (t->alloc->size * 2 < MAX_SIZE)
941                                 a = text_new_alloc(t, t->alloc->size * 2);
942                         else
943                                 a = text_new_alloc(t, MAX_SIZE);
944                         len2 = len;
945                         if (len2 > a->size)
946                                 len2 = utf8_round_len(str, a->size);
947                 }
948                 pos->c->txt = a->text + a->free;
949                 pos->c->end = len2;
950                 pos->o = len2;
951                 if (str != pos->c->txt)
952                         memcpy(pos->c->txt, str, len2);
953                 text_add_edit(t, pos->c, first_edit, 0, len2);
954                 a->free += len2;
955                 str += len2;
956                 len -= len2;
957         }
958 }
959
960 /* Text insertion, deletion, and undo can modify chunks which various
961  * marks point to - so those marks will need to be updated.
962  * Modification include splitting a chunk, inserting chunks,
963  * or deleting chunks and recombining chunks (for undo).
964  * Also reducing or increasing the range of a chunk.
965  * When a chunk is split, the original becomes the first part.
966  * So any mark pointing past the end of that original must be moved
967  * to the new chunk.
968  * When a chunk is deleted, any mark pointing to a deleted chunk
969  * must be redirected to the (new) point of deletion.
970  * When a chunk is inserted, marks before the insertion mark must remain
971  * before the inserted chunk, marks after must remain after the insertion
972  * point.
973  * When two chunks are recombined it will appear that the second chunk
974  * was deleted.  In this case though, references to the second chunk need
975  * to be repositioned in the first.
976  * When range is reduced, offset must be moved back into range.
977  * When range is increased, and this mark is after change, offset in this mark
978  * need to line up with changed point.
979  *
980  * So text_update_prior_after_change() is called on marks before the
981  * mark-of-change in reverse order until the function returns zero.
982  * If it finds a mark pointing to a deleted chunk, that mark changes to
983  * point the same place as the mark-of-change.
984  * If it finds a mark at, or immediately after, the mark-of-change,
985  * that mark is moved to point to the start of insert.
986  *
987  * Then text_update_following_after_change() is called on marks after
988  * the mark-of-change in order until that function returns zero.
989  * If a mark points outside the range of a chunk, the other half of the
990  * chunk is found (by walking forward) and the pointer is updated.
991  * If a deleted chunk is found, that mark is redirected to the mark-of-change.
992  * If a location at the start is found, it is move to the end.
993  */
994
995 static int text_update_prior_after_change(struct text *t safe,
996                                           struct doc_ref *pos safe,
997                                           struct doc_ref *spos safe,
998                                           struct doc_ref *epos safe)
999 {
1000         int ret = 1;
1001
1002         if (pos->c == NULL)
1003                 /* Was at the end, now must be at the start of the change */
1004                 *pos = *spos;
1005         else if (pos->c->start >= pos->c->end)
1006                 /* This chunk was deleted */
1007                 *pos = *spos;
1008         else if (_text_ref_same(t, pos, epos))
1009                 *pos = *spos;
1010         else if (pos->o < pos->c->start)
1011                 /* Text deleted from under me */
1012                 pos->o = pos->c->start;
1013         else if (pos->o > pos->c->end)
1014                 /* Text deleted under me */
1015                 pos->o = pos->c->end;
1016         else if (pos->o == pos->c->end)
1017                 /* This mark is OK, but previous mark might be
1018                  * at start of next chunk, so keep looking
1019                  */
1020                 ;
1021         else
1022                 /* no insert or delete here, so all done */
1023                 ret = 0;
1024         text_normalize(t, pos);
1025         return ret;
1026 }
1027
1028 static int text_update_following_after_change(struct text *t safe,
1029                                               struct doc_ref *pos safe,
1030                                               struct doc_ref *spos safe,
1031                                               struct doc_ref *epos safe)
1032 {
1033         /* A change has happened between spos and epos. pos should be
1034          * at or after epos.
1035          */
1036         struct text_chunk *c;
1037         int ret = 1;
1038
1039         if (pos->c == NULL)
1040                 return 1;
1041
1042         if (pos->c->start >= pos->c->end) {
1043                 /* This chunk was deleted */
1044                 if (epos->c &&
1045                     pos->c->txt == epos->c->txt &&
1046                     pos->o >= epos->c->start &&
1047                     pos->o <= epos->c->end)
1048                         /* chunks were rejoined */
1049                         pos->c = epos->c;
1050                 else
1051                         *pos = *epos;
1052         } else if (pos->c == epos->c &&
1053                    pos->o < epos->o)
1054                 /* Text inserted, need to push forward. */
1055                 pos->o = epos->o;
1056         else if (pos->o < pos->c->start)
1057                 /* must have been deleted... */
1058                 pos->o = pos->c->start;
1059         else if (pos->o > pos->c->end) {
1060                 /* This was split, or text was deleted off the end */
1061
1062                 c = epos->c;
1063                 if (c)
1064                         list_for_each_entry_from(c, &t->text, lst) {
1065                                 if (c->txt == pos->c->txt &&
1066                                     c->start <= pos->o &&
1067                                     c->end >= pos->o) {
1068                                         pos->c = c;
1069                                         break;
1070                                 }
1071                         }
1072                 if (pos->o > pos->c->end)
1073                         /* no split found, so just a delete */
1074                         pos->o = pos->c->end;
1075         } else if (_text_ref_same(t, pos, spos))
1076                 *pos = *epos;
1077         else if (pos->o == pos->c->start)
1078                 /* This mark is OK, but next mark might be
1079                  * at end of previous chunk, so keep looking
1080                  */
1081                 ;
1082         else
1083                 /* This is beyond the change point and no deletion or split
1084                  * happened here, so all done.
1085                  */
1086                 ret = 0;
1087         text_normalize(t, pos);
1088         return ret;
1089 }
1090
1091 static void text_del(struct text *t safe, struct doc_ref *pos safe,
1092                      unsigned int len, bool *first_edit safe)
1093 {
1094         while (len) {
1095                 struct text_chunk *c = pos->c;
1096                 if (c == NULL)
1097                         /* nothing more to delete */
1098                         break;
1099                 if (pos->o == c->start &&
1100                     len >= c->end - c->start) {
1101                         /* The whole chunk is deleted, simply disconnect it */
1102                         if (c != list_last_entry(&t->text,
1103                                                  struct text_chunk, lst)) {
1104                                 pos->c = list_next_entry(c, lst);
1105                                 pos->o = pos->c->start;
1106                         } else if (c != list_first_entry(&t->text,
1107                                                          struct text_chunk,
1108                                                          lst)) {
1109                                 pos->c = list_prev_entry(c, lst);
1110                                 pos->o = pos->c->end;
1111                         } else {
1112                                 /* Deleted final chunk */
1113                                 pos->c = NULL;
1114                                 pos->o = 0;
1115                         }
1116                         __list_del(c->lst.prev, c->lst.next); /* no poison,
1117                                                                * retain place
1118                                                                * in list */
1119                         attr_free(&c->attrs);
1120                         text_add_edit(t, c, first_edit, 0, c->start - c->end);
1121                         len -= c->end - c->start;
1122                         /* make sure undo knows this is empty at not attached */
1123                         c->end = c->start;
1124                 } else if (pos->o == c->start) {
1125                         /* If the start of the chunk is deleted, just update.
1126                          * Note that len must be less that full size, else
1127                          * previous branch would have been taken.
1128                          */
1129                         struct attrset *s;
1130                         c->start += len;
1131                         pos->o = c->start;
1132                         s = attr_copy_tail(c->attrs, c->start);
1133                         attr_free(&c->attrs);
1134                         c->attrs = s;
1135                         text_add_edit(t, c, first_edit, 1, len);
1136                         len = 0;
1137                 } else if (c->end - pos->o <= len) {
1138                         /* If the end of the chunk is deleted, just update
1139                          * and move forward */
1140                         int diff = c->end - pos->o;
1141                         len -= diff;
1142                         c->end = pos->o;
1143                         attr_trim(&c->attrs, c->end);
1144                         text_add_edit(t, c, first_edit, 0, -diff);
1145                         if (len && c != list_last_entry(&t->text,
1146                                                         struct text_chunk,
1147                                                         lst)) {
1148                                 pos->c = list_next_entry(c, lst);
1149                                 pos->o = pos->c->start;
1150                         } else
1151                                 len = 0;
1152                 } else {
1153                         /* must be deleting out of the middle of the chunk.
1154                          * need to create new chunk for the 'after' bit.
1155                          */
1156                         struct text_chunk *c2;
1157                         alloc(c2, text);
1158                         c2->txt = c->txt;
1159                         c2->start = pos->o + len;
1160                         c2->end = c->end;
1161                         c->end = pos->o;
1162                         c2->attrs = attr_copy_tail(c->attrs, c2->start);
1163                         attr_trim(&c->attrs, c->end);
1164                         list_add(&c2->lst, &c->lst);
1165                         /* This implicitly trims c, so we only have len
1166                          * left to trim */
1167                         text_add_edit(t, c2, first_edit, 0,
1168                                       c2->end - c2->start);
1169                         text_add_edit(t, c, first_edit, 0, -len);
1170                         len = 0;
1171                 }
1172         }
1173 }
1174
1175 /* text_undo and text_redo:
1176  *
1177  * The 'start' and 'end' reported identify the range changed.  For a reversed
1178  * insertion they will be the same.  If the undo results in the buffer being
1179  * empty, both start and end will point to a NULL chunk.
1180  * When undoing a split, both will be at the point of the split.
1181  */
1182 static void text_undo(struct text *t safe, struct text_edit *e safe,
1183                       struct doc_ref *start safe, struct doc_ref *end safe)
1184 {
1185         if (e->len == 0)
1186                 /* no-op */
1187                 return;
1188         if (e->target->end == e->target->start) {
1189                 /* need to re-link */
1190                 struct list_head *l = e->target->lst.prev;
1191                 if (e->target->lst.next != l->next) abort();
1192                 list_add(&e->target->lst, l);
1193         }
1194         start->c = end->c = e->target;
1195         start->o = e->target->end; // incase was deletion at end
1196         end->o = e->target->start; // incase was deletion at start
1197         if (e->at_start) {
1198                 e->target->start -= e->len;
1199                 if (e->len > 0)
1200                         /* was deletion, this is insertion */
1201                         start->o = e->target->start;
1202                 else
1203                         /* was insertion - not really possible */
1204                         start->o = end->o = e->target->start;
1205         } else {
1206                 e->target->end -= e->len;
1207                 if (e->len > 0)
1208                         /* Was insertion, now deleting */
1209                         start->o = end->o = e->target->end;
1210                 else
1211                         /* Was deletion, now inserting */
1212                         end->o = e->target->end;
1213         }
1214         if (e->target->start == e->target->end) {
1215                 /* The undo deletes this chunk, so it must have been inserted,
1216                  * either as new text or for a chunk-split.
1217                  * If new text, leave start/end pointing just past the chunk.
1218                  * if split, leave them at the point of splitting.
1219                  */
1220                 if (e->target == list_last_entry(&t->text,
1221                                                  struct text_chunk, lst)) {
1222                         end->c = NULL;
1223                         end->o = 0;
1224                 } else {
1225                         end->c = list_next_entry(e->target, lst);
1226                         end->o = end->c->start;
1227                 }
1228                 *start = *end;
1229
1230                 __list_del(e->target->lst.prev, e->target->lst.next);
1231                 /* If this was created for a split, we need to extend the
1232                  * other half
1233                  */
1234                 if (e->target != list_first_entry(&t->text,
1235                                                   struct text_chunk, lst)) {
1236                         struct text_chunk *c = list_prev_entry(e->target, lst);
1237                         start->c = end->c = c;
1238                         start->o = end->o = c->end;
1239                         if (c->txt == e->target->txt &&
1240                             c->end == e->target->start &&
1241                             !e->at_start)
1242                                 c->end += e->len;
1243                 }
1244         }
1245 }
1246
1247 static void text_redo(struct text *t safe, struct text_edit *e safe,
1248                       struct doc_ref *start safe, struct doc_ref *end safe)
1249 {
1250         int is_split = 0;
1251
1252         if (e->len == 0)
1253                 /* no-op */
1254                 return;
1255
1256         if (e->target->end == e->target->start) {
1257                 /* need to re-link */
1258                 struct list_head *l = e->target->lst.prev;
1259                 if (e->target->lst.next != l->next) abort();
1260                 list_add(&e->target->lst, l);
1261                 /* If this is a split, need to truncate prior */
1262                 if (e->target != list_first_entry(&t->text,
1263                                                   struct text_chunk, lst)) {
1264                         struct text_chunk *c = list_prev_entry(e->target, lst);
1265                         if (c->txt == e->target->txt &&
1266                             c->end > e->target->start) {
1267                                 c->end = e->target->start;
1268                                 is_split = 1;
1269                         }
1270                 }
1271         }
1272         start->c = end->c = e->target;
1273         end->o = e->target->start; // incase is insertion at start
1274         start->o = e->target->end; // incase inserting at end
1275         if (e->at_start) {
1276                 e->target->start += e->len;
1277                 if (e->len > 0)
1278                         /* deletion at start */
1279                         start->o = end->o = e->target->start;
1280                 else
1281                         /* Insertion at start, not currently possible */
1282                         start->o = e->target->start;
1283         } else {
1284                 e->target->end += e->len;
1285                 if (is_split)
1286                         start->o = end->o = e->target->start;
1287                 else if (e->len > 0)
1288                         /* Insertion at end */
1289                         end->o = e->target->end;
1290                 else
1291                         /* Deletion at end */
1292                         start->o = end->o = e->target->end;
1293         }
1294         if (e->target->start == e->target->end) {
1295                 /* This chunk is deleted, so leave start/end pointing
1296                  * beyond it */
1297                 if (e->target->lst.next == &t->text) {
1298                         end->c = NULL;
1299                         end->o = 0;
1300                 } else {
1301                         end->c = list_next_entry(e->target, lst);
1302                         end->o = end->c->start;
1303                 }
1304                 *start = *end;
1305
1306                 __list_del(e->target->lst.prev, e->target->lst.next);
1307         }
1308 }
1309
1310 static bool check_readonly(const struct cmd_info *ci safe)
1311 {
1312         struct doc *d = ci->home->data;
1313         struct text *t = ci->home->doc_data;
1314
1315         if (t->undo == t->saved &&
1316             check_file_changed(ci->home) &&
1317             !d->readonly) {
1318                 call("doc:notify:doc:status-changed", ci->home);
1319                 d->readonly = 1;
1320         }
1321         if (!d->readonly)
1322                 return False;
1323         call("Message", ci->focus, 0, NULL, "Document is read-only");
1324         return True;
1325 }
1326
1327 DEF_CMD(text_reundo)
1328 {
1329         struct mark *m = ci->mark;
1330         struct doc_ref start, end;
1331         int last = 0;
1332         struct text_edit *ed = NULL;
1333         bool first = 1;
1334         int status;
1335         struct text *t = ci->home->doc_data;
1336
1337         if (!m)
1338                 return Enoarg;
1339
1340         if (check_readonly(ci))
1341                 return Efail;
1342
1343         if (!ci->num)
1344                 /* New undo sequence - do redo first */
1345                 t->prev_edit = Redo;
1346
1347         status = (t->undo == t->saved);
1348
1349         do {
1350                 struct mark *m2;
1351                 struct mark *early = NULL;
1352                 int where = 0;
1353                 int i;
1354
1355                 ed = NULL;
1356                 if (t->prev_edit <= Redo && t->redo) {
1357                         ed = t->redo;
1358                         text_redo(t, ed, &start, &end);
1359                         t->redo = ed->next;
1360                         ed->next = t->undo;
1361                         ed->alt_is_second = 0;
1362                         t->prev_edit = Redo;
1363                         t->undo = ed;
1364                         last = t->redo == NULL || t->redo->first;
1365                 } else if (t->prev_edit <= Undo &&
1366                            t->undo &&
1367                            t->undo->altnext && !t->undo->alt_is_second) {
1368                         ed = t->undo->altnext;
1369                         text_redo(t, ed, &start, &end);
1370                         t->prev_edit = Redo;
1371                         t->undo->altnext = t->redo;
1372                         t->undo->alt_is_second = 1;
1373                         t->redo = ed->next;
1374                         ed->next = t->undo;
1375                         ed->alt_is_second = 0;
1376                         t->undo = ed;
1377                         last = t->redo == NULL || t->redo->first;
1378                 } else if (t->undo) {
1379                         ed = t->undo;
1380                         text_undo(t, ed, &start, &end);
1381                         t->undo = ed->next;
1382                         if (ed->alt_is_second) {
1383                                 t->prev_edit = AltUndo;
1384                                 ed->next = ed->altnext;
1385                                 ed->altnext = t->redo;
1386                         } else {
1387                                 t->prev_edit = Undo;
1388                                 ed->next = t->redo;
1389                         }
1390                         t->redo = ed;
1391                         last = ed->first;
1392                 }
1393
1394                 if (!ed)
1395                         break;
1396                 if (ed->len == 0)
1397                         /* That was just a no-op, keep going */
1398                         continue;
1399
1400                 text_normalize(t, &start);
1401                 text_normalize(t, &end);
1402
1403                 if (!first)
1404                         where = text_locate(t, &m->ref, &end);
1405                 if (!where) {
1406                         /* Not nearby, look from the start */
1407                         mark_reset(ci->home, m, 0);
1408                         where = 1;
1409                         first = 0;
1410                 }
1411
1412                 t->revising_marks = True;
1413                 if (where == 1) {
1414                         mark_step(m, 1);
1415                         do {
1416                                 struct doc_ref tmp = m->ref;
1417                                 i = text_advance_towards(t, &tmp, &end);
1418                                 if (i == 0)
1419                                         break;
1420                                 while ((m2 = mark_next(m)) != NULL &&
1421                                        m2->ref.c == tmp.c &&
1422                                        m2->ref.o <= tmp.o)
1423                                         mark_to_mark_noref(m, m2);
1424                                 m->ref = tmp;
1425                         } while (i == 2);
1426                 } else {
1427                         mark_step(m, 0);
1428                         do {
1429                                 struct doc_ref tmp = m->ref;
1430                                 i = text_retreat_towards(t, &tmp, &end);
1431                                 if (i == 0)
1432                                         break;
1433                                 while ((m2 = mark_prev(m)) != NULL &&
1434                                        m2->ref.c == tmp.c &&
1435                                        m2->ref.o >= tmp.o)
1436                                         mark_to_mark_noref(m, m2);
1437                                 m->ref = tmp;
1438                         } while (i == 2);
1439                 }
1440                 t->revising_marks = False;
1441
1442                 if (!_text_ref_same(t, &m->ref, &end))
1443                         /* eek! */
1444                         break;
1445                 /* point is now at location of undo */
1446
1447                 m2 = m;
1448                 hlist_for_each_entry_continue_reverse(m2, all)
1449                         if (text_update_prior_after_change(t, &m2->ref,
1450                                                            &start, &end) == 0)
1451                                 break;
1452                 m2 = m;
1453                 hlist_for_each_entry_continue(m2, all)
1454                         if (text_update_following_after_change(t, &m2->ref,
1455                                                                &start,
1456                                                                &end) == 0)
1457                                 break;
1458
1459                 text_normalize(t, &m->ref);
1460                 if (text_ref_same(t, &start, &end))
1461                         early = m;
1462                 else {
1463                         early = mark_dup(m);
1464                         mark_step(early, 0);
1465                         /* There cannot be any mark between start and end,
1466                          * so it is safe to assign 'ref' here.
1467                          */
1468                         early->ref = start;
1469                 }
1470                 pane_notify("doc:replaced", ci->home,
1471                             0, early, NULL,
1472                             0, m);
1473                 if (early != m)
1474                         mark_free(early);
1475
1476                 text_check_consistent(t);
1477
1478         } while (ed && !last);
1479
1480         text_check_consistent(t);
1481
1482         if (status != (t->undo == t->saved))
1483                 call("doc:notify:doc:status-changed", ci->home);
1484         text_check_autosave(ci->home);
1485
1486         if (!ed)
1487                 t->prev_edit = Redo;
1488         return ed ? 1 : Efalse;
1489 }
1490
1491 #ifdef DEBUG
1492
1493 static int common_prefix(char *a, char *b, int l)
1494 {
1495         int i = 0;
1496         while (i < l &&
1497                a[i] == b[i])
1498                 i++;
1499         return i;
1500 }
1501
1502 /* Compare a string with the text.
1503  * Update the ref passed all matching chars.
1504  * Return length that was matched.
1505  */
1506 static int text_str_cmp(struct text *t, struct doc_ref *r, char *s)
1507 {
1508         struct text_chunk *c = r->c;
1509         int o = r->o;
1510         int matched = 0;
1511
1512         if (c == NULL)
1513                 return 0;
1514
1515         list_for_each_entry_from(c, &t->text, lst) {
1516                 int l = strlen(s);
1517                 if (o == 0)
1518                         o = c->start;
1519                 if (c->end - o < l)
1520                         l = c->end - o;
1521                 l = common_prefix(c->txt+o, s, l);
1522                 matched += l;
1523                 o += l;
1524                 if (s[l] == 0)
1525                         break;
1526                 if (l == c->end - o)
1527                         break;
1528                 s += l;
1529                 o = 0;
1530         }
1531         r->c = c;
1532         r->o = o;
1533         return matched;
1534 }
1535 #endif
1536
1537 static void text_normalize(struct text *t safe, struct doc_ref *r safe)
1538 {
1539         /* Adjust so at not at the end of a chunk - either ->o points
1540          * at a byte, or ->c is NULL.
1541          */
1542         while (r->c && r->o >= r->c->end) {
1543                 if (r->c->lst.next == &t->text) {
1544                         r->c = NULL;
1545                         r->o = 0;
1546                 } else {
1547                         r->c = list_next_entry(r->c, lst);
1548                         r->o = r->c->start;
1549                 }
1550         }
1551 }
1552
1553 static void text_denormalize(struct text *t safe, struct doc_ref *r safe)
1554 {
1555         /* Ensure r->o is after some byte, or at start of file. */
1556         if (r->c && r->o > r->c->start)
1557                 /* nothing to do */
1558                 return;
1559         if (r->c == NULL) {
1560                 if (list_empty(&t->text))
1561                         return;
1562                 r->c = list_entry(t->text.prev, struct text_chunk, lst);
1563                 r->o = r->c->end;
1564                 return;
1565         }
1566         if (r->c->lst.prev == &t->text)
1567                 return;
1568         r->c = list_prev_entry(r->c, lst);
1569         r->o = r->c->end;
1570 }
1571
1572 static void text_add_str(struct text *t safe, struct mark *pm safe,
1573                          const char *str safe, off_t size, bool *first safe)
1574 {
1575         struct doc_ref start;
1576         struct mark *m;
1577
1578         text_denormalize(t, &pm->ref);
1579         _text_add_str(t, &pm->ref, str, size, &start, first);
1580         text_normalize(t, &pm->ref);
1581         for (m = mark_prev(pm);
1582              m && text_update_prior_after_change(t, &m->ref,
1583                                                  &start, &pm->ref);
1584              m = mark_prev(m))
1585                 ;
1586         for (m = mark_next(pm);
1587              m && text_update_following_after_change(t, &m->ref,
1588                                                      &start, &pm->ref);
1589              m = mark_next(m))
1590                 ;
1591 }
1592
1593 static inline wint_t text_next(struct pane *p safe, struct doc_ref *r safe, bool bytes)
1594 {
1595         struct text *t = p->doc_data;
1596         wint_t ret = WERR;
1597         const char *c;
1598
1599         text_normalize(t, r);
1600         if (r->c == NULL)
1601                 return WEOF;
1602
1603         c = r->c->txt + r->o;
1604         if (!bytes)
1605                 ret = get_utf8(&c, r->c->txt + r->c->end);
1606         if (ret < WERR)
1607                 r->o = c - r->c->txt;
1608         else
1609                 ret = (unsigned char)r->c->txt[r->o++];
1610         text_normalize(t, r);
1611         return ret;
1612 }
1613
1614 static inline wint_t text_prev(struct pane *p safe, struct doc_ref *r safe, bool bytes)
1615 {
1616         struct text *t = p->doc_data;
1617         wint_t ret;
1618         const char *c;
1619
1620         text_denormalize(t, r);
1621         if (list_empty(&t->text))
1622                 return WEOF;
1623         if (r->c == NULL || r->o <= r->c->start)
1624                 // assert (r->c->lst.prev == &t->text)
1625                 return WEOF;
1626
1627         if (bytes)
1628                 r->o -= 1;
1629         else {
1630                 r->o = r->c->start +
1631                         utf8_round_len(r->c->txt+r->c->start,
1632                                        r->o - r->c->start - 1);
1633                 c = r->c->txt + r->o;
1634                 ret = get_utf8(&c, r->c->txt + r->c->end);
1635                 if (ret < WERR)
1636                         return ret;
1637         }
1638
1639         ret = (unsigned char)r->c->txt[r->o];
1640         return ret;
1641 }
1642
1643 DEF_CMD(text_char_byte)
1644 {
1645         return do_char_byte(ci);
1646 }
1647 static bool _text_ref_same(struct text *t safe, struct doc_ref *r1 safe,
1648                            struct doc_ref *r2 safe)
1649 {
1650         if (r1->c == r2->c) {
1651                 return r1->o == r2->o;
1652         }
1653         if (r1->c == NULL /*FIXME redundant*/ && r2->c != NULL) {
1654                 if (list_empty(&t->text))
1655                         return True;
1656                 return (r2->o == r2->c->end &&
1657                         r2->c->lst.next == &t->text);
1658         }
1659         if (r2->c == NULL /* FIXME redundant*/ && r1->c != NULL) {
1660                 if (list_empty(&t->text))
1661                         return True;
1662                 return (r1->o == r1->c->end &&
1663                         r1->c->lst.next == &t->text);
1664         }
1665         /* FIXME impossible */
1666         if (r1->c == NULL || r2->c == NULL) return False;
1667
1668         if (r1->o == r1->c->end &&
1669             r2->o == r2->c->start &&
1670             list_next_entry(r1->c, lst) == r2->c)
1671                 return True;
1672         if (r1->o == r1->c->start &&
1673             r2->o == r2->c->end &&
1674             list_prev_entry(r1->c, lst) == r2->c)
1675                 return True;
1676         return False;
1677 }
1678
1679 static bool text_ref_same(struct text *t safe, struct doc_ref *r1 safe,
1680                          struct doc_ref *r2 safe)
1681 {
1682         bool ret = _text_ref_same(t, r1, r2);
1683         ASSERT(ret == (r1->c == r2->c && r1->o == r2->o));
1684         return ret;
1685 }
1686
1687 DEF_LOOKUP_CMD(text_handle, text_map);
1688
1689 DEF_CMD(text_new)
1690 {
1691         struct text *t;
1692         struct pane *p;
1693
1694         p = doc_register(ci->home, &text_handle.c);
1695         if (!p)
1696                 return Efail;
1697         t = p->doc_data;
1698         t->alloc = safe_cast NULL;
1699         INIT_LIST_HEAD(&t->text);
1700         t->saved = t->undo = t->redo = NULL;
1701         t->prev_edit = Redo;
1702         t->fname = NULL;
1703         t->file_changed = 0;
1704         t->stat.st_ino = 0;
1705         t->as.changes = 0;
1706         t->as.timer_started = 0;
1707         t->as.last_change = 0;
1708         text_new_alloc(t, 0);
1709
1710         return comm_call(ci->comm2, "callback:doc", p);
1711 }
1712
1713 DEF_CMD(text_new2)
1714 {
1715         if (ci->num2 != S_IFREG)
1716                 return Efallthrough;
1717         return text_new_func(ci);
1718 }
1719
1720 static int count_bytes(struct text *t safe, struct mark *from, struct mark *to)
1721 {
1722         struct text_chunk *c, *first, *last;
1723         int l = 0, head, tail;
1724
1725         first = list_first_entry_or_null(&t->text, struct text_chunk, lst);
1726         head = 0;
1727         if (from && from->ref.c) {
1728                 first = from->ref.c;
1729                 head = from->ref.o - first->start;
1730         }
1731         last = NULL;
1732         tail = 0;
1733         if (to && to->ref.c) {
1734                 last = to->ref.c;
1735                 tail = last->end - to->ref.o;
1736         }
1737         c = first;
1738         list_for_each_entry_from(c, &t->text, lst) {
1739                 l += c->end - c->start;
1740                 if (c == first)
1741                         l -= head;
1742                 if (c == last) {
1743                         l -= tail;
1744                         break;
1745                 }
1746         }
1747         return l;
1748 }
1749
1750 DEF_CMD(text_content)
1751 {
1752         struct mark *from = ci->mark, *to = ci->mark2;
1753         struct mark *m;
1754         struct text *t = ci->home->doc_data;
1755         struct text_chunk *c, *first, *last;
1756         int bytes = strcmp(ci->key, "doc:content-bytes") == 0;
1757         int l = 0, head, tail;
1758         int size = 0;
1759
1760         if (!from)
1761                 return Enoarg;
1762         m = mark_dup(from);
1763         head = 0;
1764         first = from->ref.c;
1765         if (first)
1766                 head = from->ref.o - first->start;
1767         last = NULL;
1768         tail = 0;
1769         if (to) {
1770                 /* Calculate size so comm2 can pre-allocate */
1771                 if (to->ref.c) {
1772                         last = to->ref.c;
1773                         tail = last->end - to->ref.o;
1774                 }
1775                 c = first;
1776                 list_for_each_entry_from(c, &t->text, lst) {
1777                         l += c->end - c->start;
1778                         if (c == first)
1779                                 l -= head;
1780                         if (c == last) {
1781                                 l -= tail;
1782                                 break;
1783                         }
1784                 }
1785                 size = l;
1786         }
1787         l = 0;
1788         c = first;
1789         list_for_each_entry_from(c, &t->text, lst) {
1790                 struct mark *m2;
1791                 const char *s = c->txt + c->start;
1792                 int ln = c->end - c->start;
1793                 if (c == first) {
1794                         s += head;
1795                         ln -= head;
1796                 }
1797                 if (c == last)
1798                         ln -= tail;
1799                 if (m->ref.c != c) {
1800                         while ((m2 = mark_next(m)) &&
1801                                m2->ref.c == m->ref.c)
1802                                 mark_to_mark(m, m2);
1803                         m->ref.c = c;
1804                         m->ref.o = c->start;
1805                 }
1806                 while (ln > 0) {
1807                         int rv;
1808                         const char *ss = s;
1809                         wint_t wc;
1810
1811                         if (bytes)
1812                                 wc = *s++;
1813                         else {
1814                                 wc = get_utf8(&s, s+ln);
1815                                 if (wc >= WERR)
1816                                         wc = *s++;
1817                         }
1818
1819                         while ((m2 = mark_next(m)) &&
1820                                m2->ref.c == m->ref.c &&
1821                                m2->ref.o <= s - c->txt)
1822                                 mark_to_mark(m, m2);
1823                         m->ref.o = s - c->txt;
1824                         text_normalize(t, &m->ref);
1825
1826                         ln -= s - ss;
1827                         /* Interpreted can see " unterminated" and know
1828                          * than ->num2 is the length of ->str
1829                          */
1830                         rv = comm_call(ci->comm2, "consume unterminated",
1831                                        ci->focus,
1832                                        wc, m, s, ln, NULL, NULL, size, 0);
1833                         size = 0;
1834                         if (rv <= 0 || rv > ln + 1) {
1835                                 /* Time to stop */
1836                                 ln = 0;
1837                                 c = last;
1838                         } else if (rv > 1) {
1839                                 /* consumed (some of) str */
1840                                 s += rv - 1;
1841                                 ln -= rv - 1;
1842                         }
1843                 }
1844                 if (c == last)
1845                         break;
1846         }
1847         mark_free(m);
1848         return 1;
1849 }
1850
1851 DEF_CMD(text_debug_mark)
1852 {
1853         char *ret = NULL;
1854         struct text_chunk *c;
1855         struct mark *m = ci->mark;
1856
1857         if (!m || m->owner != ci->home || !ci->comm2)
1858                 return Enoarg;
1859         c = m->ref.c;
1860         if (!mark_valid(m))
1861                 ret = strdup("M:FREED");
1862         else if (!c)
1863                 ret = strdup("M:EOF");
1864         else {
1865                 unsigned int len = c->end - c->start;
1866                 unsigned int o = ci->mark->ref.o;
1867
1868                 if (o <= c->start + 4 || len <= 8) {
1869                         if (len > 8)
1870                                 len = 8;
1871                         asprintf(&ret, "M:(%.*s[%d])", len,
1872                                  c->txt + c->start, m->ref.o);
1873                 } else {
1874                         len = c->end - m->ref.o;
1875                         if (len > 4)
1876                                 len = 4;
1877                         asprintf(&ret, "M:(%.4s..[%d]%.*s)",
1878                                  c->txt + c->start,
1879                                  m->ref.o, len, c->txt + m->ref.o);
1880                 }
1881         }
1882         comm_call(ci->comm2, "cb", ci->focus, 0, NULL, ret);
1883         free(ret);
1884         return 1;
1885 }
1886
1887 DEF_CMD(text_val_marks)
1888 {
1889         struct text *t = ci->home->doc_data;
1890         struct text_chunk *c;
1891         int found;
1892
1893         if (!ci->mark || !ci->mark2)
1894                 return Enoarg;
1895
1896         if (t->revising_marks)
1897                 return 1;
1898
1899         if (ci->mark->ref.c == ci->mark2->ref.c) {
1900                 if (ci->mark->ref.o < ci->mark2->ref.o)
1901                         return 1;
1902                 LOG("text_val_marks: same buf, bad offset: %u, %u",
1903                     ci->mark->ref.o, ci->mark2->ref.o);
1904                 return Efalse;
1905         }
1906         found = 0;
1907         list_for_each_entry(c, &t->text, lst) {
1908                 if (ci->mark->ref.c == c)
1909                         found = 1;
1910                 if (ci->mark2->ref.c == c) {
1911                         if (found == 1)
1912                                 return 1;
1913                         LOG("text_val_marks: mark2.c found before mark1");
1914                         return Efalse;
1915                 }
1916         }
1917         if (ci->mark2->ref.c == NULL) {
1918                 if (found == 1)
1919                         return 1;
1920                 LOG("text_val_marks: mark2.c (NULL) found before mark1");
1921                 return Efalse;
1922         }
1923         if (found == 0)
1924                 LOG("text_val_marks: Neither mark found in chunk list");
1925         if (found == 1)
1926                 LOG("text_val_marks: mark2 not found in chunk list");
1927         return Efalse;
1928 }
1929
1930 DEF_CMD(text_set_ref)
1931 {
1932         struct mark *m = ci->mark;
1933         struct text *t = ci->home->doc_data;
1934
1935         if (!m)
1936                 return Enoarg;
1937         mark_to_end(ci->home, m, ci->num != 1);
1938         if (list_empty(&t->text) || ci->num != 1) {
1939                 m->ref.c = NULL;
1940                 m->ref.o = 0;
1941         } else {
1942                 m->ref.c = list_first_entry(&t->text, struct text_chunk, lst);
1943                 m->ref.o = m->ref.c->start;
1944         }
1945         return 1;
1946 }
1947
1948 static int text_advance_towards(struct text *t safe,
1949                                 struct doc_ref *ref safe,
1950                                 struct doc_ref *target safe)
1951 {
1952         /* Move 'ref' towards 'target'.
1953          * If at end of chunk, step to next chunk, then
1954          * advance to 'target' or to end of chunk, whichever comes first.
1955          * return:
1956          * 0 - reached end of text
1957          * 1 - found target
1958          * 2 - on a new chunk, keep looking.
1959          */
1960         if (ref->c && ref->o >= ref->c->end)
1961                 text_normalize(t, ref);
1962         if (ref->c == target->c) {
1963                 if (ref->o > target->o)
1964                         /* will never find it */
1965                         return 0;
1966                 ref->o = target->o;
1967                 return 1;
1968         }
1969         if (ref->c == NULL)
1970                 /* Reached EOF, haven't found */
1971                 return 0;
1972         ref->o = ref->c->end;
1973         return 2;
1974 }
1975
1976 static int text_retreat_towards(struct text *t safe, struct doc_ref *ref safe,
1977                                 struct doc_ref *target safe)
1978 {
1979         /* Move 'ref' towards 'target'.
1980          * If at start of chunk, step to previous chunk, then
1981          * retreat to 'target' or to start of chunk, whichever comes first.
1982          * return:
1983          * 0 - reached start of text
1984          * 1 - found target
1985          * 2 - on a new chunk, keep looking.
1986          */
1987
1988         if (ref->c != target->c && (!ref->c || ref->o <= ref->c->start))
1989                 if (text_prev(safe_cast container_of(t, struct pane, doc_data[0]), ref, 1) == WEOF)
1990                         return 0;
1991
1992         if (ref->c == target->c) {
1993                 if (ref->c == NULL)
1994                         return 1;
1995                 if (target->o > ref->o)
1996                         return 0;
1997                 ref->o = target->o;
1998                 return 1;
1999         }
2000         if (ref->c)
2001                 ref->o = ref->c->start;
2002         return 2;
2003 }
2004
2005 static int text_locate(struct text *t safe, struct doc_ref *r safe,
2006                        struct doc_ref *dest safe)
2007 {
2008         /* move back/forward a little from 'r' looking for 'dest'.
2009          * return 0 if not found, -1 if dest found before r.
2010          * return 1 if dest found after or at r.
2011          */
2012         struct text_chunk *next, *prev;
2013
2014         if (r->c == NULL) {
2015                 if (dest->c == NULL)
2016                         return 1;
2017                 else
2018                         return -1;
2019         }
2020         if (dest->c == NULL)
2021                 return 1;
2022         if (r->c == dest->c) {
2023                 if (dest->o < r->o)
2024                         return -1;
2025                 else
2026                         return 1;
2027         }
2028         next = (r->c->lst.next == &t->text) ? NULL : list_next_entry(r->c, lst);
2029         prev = (r->c->lst.prev == &t->text) ? NULL : list_prev_entry(r->c, lst);
2030         if (next == dest->c)
2031                 return 1;
2032         if (prev == dest->c)
2033                 return -1;
2034
2035         next = (next == NULL || next->lst.next == &t->text) ?
2036                 NULL : list_next_entry(next, lst);
2037         prev = (prev == NULL || prev->lst.prev == &t->text) ?
2038                 NULL : list_prev_entry(prev, lst);
2039         if (next == dest->c)
2040                 return 1;
2041         if (prev == dest->c)
2042                 return -1;
2043         return 0;
2044 }
2045
2046 static void check_allocated(struct text *t safe, char *buf safe, int len)
2047 {
2048         struct text_alloc *ta = t->alloc;
2049         for (ta = t->alloc; ta; ta = ta->prev) {
2050                 if (buf >= ta->text && buf+len <= ta->text + ta->free)
2051                         return;
2052         }
2053         abort();
2054 }
2055
2056 static void text_ref_consistent(struct text *t safe, struct doc_ref *r safe,
2057                                 int *loops safe)
2058 {
2059         struct text_chunk *c;
2060
2061         if (r->c == NULL) {
2062                 if (r->o)
2063                         abort();
2064                 return;
2065         }
2066         if (r->o >= r->c->end)
2067                 abort();
2068         if (r->o < r->c->start)
2069                 abort();
2070         list_for_each_entry(c, &t->text, lst) {
2071                 if (r->c == c || *loops <= 0)
2072                         return;
2073                 (*loops) -= 1;
2074         }
2075         abort();
2076 }
2077
2078 static void text_check_consistent(struct text *t safe)
2079 {
2080         /* make sure text is consistent, and abort if not.
2081          * - each chunk points to allocated space
2082          * - no two chunks overlap
2083          * - no chunks are empty
2084          * - every mark points to a valid chunk with valid offset
2085          * - all marks are in text order
2086          */
2087         struct text_chunk *c;
2088         struct mark *m, *prev;
2089         struct doc *d = &t->doc;
2090         int loops;
2091
2092         list_for_each_entry(c, &t->text, lst) {
2093                 check_allocated(t, c->txt, c->end);
2094                 if (c->start >= c->end)
2095                         abort();
2096         }
2097         list_for_each_entry(c, &t->text, lst) {
2098                 struct text_chunk *c2;
2099                 list_for_each_entry(c2, &t->text, lst) {
2100                         if (c2 == c ||
2101                             c2->txt != c->txt)
2102                                 continue;
2103                         if (c->start >= c2->end)
2104                                 continue;
2105                         if (c2->start >= c->end)
2106                                 continue;
2107                         abort();
2108                 }
2109         }
2110
2111         /* This test is quadratic in the number of marks, so let's
2112          * give up rather then annoy the users.
2113          */
2114         loops = 10000;
2115         for (m = mark_first(d); m; m = mark_next(m))
2116                 text_ref_consistent(t, &m->ref, &loops);
2117
2118         prev = NULL;
2119         for (m = mark_first(d); m; m = mark_next(m)) {
2120                 if (prev) {
2121                         struct doc_ref r = prev->ref;/* SMATCH Bug things prev
2122                                                       * has no state*/
2123                         int i;
2124                         struct doc_ref r2 = m->ref;
2125                         text_normalize(t, &r2);
2126                         while ((i = text_advance_towards(t, &r,
2127                                                          &r2)) != 1) {
2128                                 if (i == 0)
2129                                         abort();
2130                         }
2131                 }
2132                 prev = m;
2133         }
2134         doc_check_consistent(d);
2135 }
2136
2137 static void text_add_attrs(struct attrset **attrs safe,
2138                            const char *new safe, int o)
2139 {
2140         char sep = *new++;
2141         char *cpy = strdup(new);
2142         char *a = cpy;
2143
2144         while (a) {
2145                 char *k, *v;
2146                 k = a;
2147                 a = strchr(a, sep);
2148                 if (a)
2149                         *a++ = '\0';
2150                 v = strchr(k, '=');
2151                 if (v)
2152                         *v++ = '\0';
2153                 else
2154                         continue;
2155                 attr_set_str_key(attrs, k, v, o);
2156         }
2157
2158         free(cpy);
2159 }
2160
2161 DEF_CMD(text_replace)
2162 {
2163
2164         struct text *t = ci->home->doc_data;
2165         struct mark *pm = ci->mark2;
2166         struct mark *end = ci->mark;
2167         const char *str = ci->str;
2168         const char *newattrs = ci->str2;
2169         bool first = !ci->num2;
2170         struct mark *early = NULL;
2171         int status_change = 0;
2172
2173         if (check_readonly(ci))
2174                 return Efail;
2175
2176         if (!pm) {
2177                 /* Default to insert at end */
2178                 pm = point_new(ci->home);
2179                 if (!pm)
2180                         return Efail;
2181                 mark_reset(ci->home, pm, 1);
2182         }
2183
2184         /* First delete, then insert */
2185         if (end && !text_ref_same(t, &pm->ref, &end->ref)) {
2186                 struct mark *myend, *m;
2187                 int l;
2188
2189                 if (t->undo == t->saved)
2190                         status_change = 1;
2191
2192                 if (pm->seq >= end->seq) {
2193                         myend = mark_dup(pm);
2194                         mark_to_mark(pm, end);
2195                 } else
2196                         myend = mark_dup(end);
2197                 /* pm is at the start, myend is at the end */
2198                 l = count_bytes(t, pm, myend);
2199                 mark_free(myend);
2200                 text_del(t, &pm->ref, l, &first);
2201                 text_normalize(t, &pm->ref);
2202
2203                 for (m = mark_prev(pm);
2204                      m && text_update_prior_after_change(t, &m->ref,
2205                                                          &pm->ref, &pm->ref);
2206                      m = mark_prev(m))
2207                         ;
2208                 for (m = mark_next(pm);
2209                      m && text_update_following_after_change(t, &m->ref,
2210                                                              &pm->ref,
2211                                                              &pm->ref);
2212                      m = mark_next(m))
2213                         ;
2214                 text_check_consistent(t);
2215         }
2216         if (end && end != pm)
2217                 early = end;
2218         else
2219                 early = mark_dup(pm);
2220         /* leave "early" at the start of the insertion, and
2221          * pm moves to the end - they are both currently at
2222          * the same location in the doc.
2223          */
2224         mark_step(early, 0);
2225
2226         if (str && *str) {
2227                 if (t->undo == t->saved)
2228                         status_change = 1;
2229
2230                 text_add_str(t, pm, str, -1, &first);
2231                 if (newattrs && early->ref.c)
2232                         text_add_attrs(&early->ref.c->attrs, newattrs,
2233                                        early->ref.o);
2234                 text_check_consistent(t);
2235
2236         }
2237         text_check_autosave(ci->home);
2238         if (status_change)
2239                 call("doc:notify:doc:status-changed", ci->home);
2240         pane_notify("doc:replaced", ci->home, 0, early, NULL,
2241                     0, pm);
2242         if (early != end)
2243                 mark_free(early);
2244         if (!ci->mark2)
2245                 mark_free(pm);
2246         return first ? 1 : 2;
2247 }
2248
2249 static struct attrset *text_attrset(struct pane *p safe, struct mark *m safe,
2250                                     int *op safe)
2251 {
2252         struct text_chunk *c;
2253         struct text *t = p->doc_data;
2254         unsigned int o;
2255
2256         c = m->ref.c;
2257         o = m->ref.o;
2258
2259         if (!c)
2260                 /* EOF */
2261                 return NULL;
2262         if (o >= c->end) {
2263                 /* End of chunk, need to look at next */
2264                 if (c->lst.next == &t->text)
2265                         return NULL;
2266                 c = list_next_entry(c, lst);
2267                 o = c->start;
2268         }
2269
2270         *op = o;
2271         return c->attrs;
2272 }
2273
2274 DEF_CMD(text_doc_get_attr)
2275 {
2276         struct mark *m = ci->mark;
2277         const char *attr = ci->str;
2278         const char *val;
2279         struct attrset *a;
2280         int o = 0;
2281
2282         if (!m || !attr)
2283                 return Enoarg;
2284         a = text_attrset(ci->home, m, &o);
2285         val = attr_get_str(a, attr, o);
2286         if (!val && !ci->num2)
2287                 return Efallthrough;
2288         comm_call(ci->comm2, "callback:get_attr", ci->focus, 0, m, val,
2289                   0, NULL, attr);
2290         if (ci->num2 == 1) {
2291                 const char *key = attr;
2292                 int len = strlen(attr);
2293                 while ((key = attr_get_next_key(a, key, o, &val)) != NULL &&
2294                        strncmp(key, attr, len) == 0)
2295                         comm_call(ci->comm2, "callback:get_attr", ci->focus,
2296                                   0, m, val, 0,
2297                                   NULL, key);
2298         }
2299         return 1;
2300 }
2301
2302 DEF_CMD(text_get_attr)
2303 {
2304         struct text *t = ci->home->doc_data;
2305         const char *attr = ci->str;
2306         const char *val;
2307
2308         if (!attr)
2309                 return Enoarg;
2310
2311         if ((val = attr_find(ci->home->attrs, attr)) != NULL)
2312                 ;
2313         else if (strcmp(attr, "render-default") == 0)
2314                 val = "text";
2315         else if (strcmp(attr, "doc-type") == 0)
2316                 val = "text";
2317         else if (strcmp(attr, "doc:charset") == 0)
2318                 val = "utf-8";
2319         else if (strcmp(attr, "filename") == 0)
2320                 val = t->fname;
2321         else if (strcmp(attr, "doc-file-changed") == 0)
2322                 val = t->file_changed ? "yes" : "no";
2323         else if (strcmp(attr, "doc-modified") == 0)
2324                 val = (t->saved != t->undo) ? "yes" : "no";
2325         else if (strcmp(attr, "autosave-exists") == 0)
2326                 val = t->autosave_exists ? "yes" : "no";
2327         else if (strcmp(attr, "autosave-name") == 0) {
2328                 if (!t->autosave_name && t->fname)
2329                         t->autosave_name = autosave_name(t->fname);
2330                 val = t->autosave_name;
2331         } else if (strcmp(attr, "is_backup") == 0) {
2332                 const char *f = t->fname ?: "";
2333                 const char *base = strrchr(f, '/');
2334                 int l;
2335
2336                 if (base)
2337                         base += 1;
2338                 else
2339                         base = f;
2340                 l = strlen(base);
2341                 if (base[0] == '#' && base[l-1] == '#')
2342                         val = "yes";
2343                 else if (base[l-1] == '~' && strchr(base, '~') - base < l-1)
2344                         val = "yes";
2345                 else
2346                         val = "no";
2347         } else if (strcmp(attr, "base-name") == 0) {
2348                 char *f = strsave(ci->focus, t->fname ?: "");
2349                 char *base;
2350                 int l;
2351
2352                 if (!f)
2353                         return Efail;
2354                 base = strrchr(f, '/');
2355                 if (base)
2356                         base += 1;
2357                 else
2358                         base = f;
2359                 l = strlen(base);
2360                 val = f;
2361                 if (base[0] == '#' && base[l-1] == '#') {
2362                         base[l-1] = '\0';
2363                         strcpy(base, base+1);
2364                 } else if (base[l-1] == '~' && strchr(base, '~') - base < l-1) {
2365                         while (l > 1 && base[l-2] != '~')
2366                                 l -= 1;
2367                         base[l-2] = '\0';
2368                 } else
2369                         val = NULL;
2370         } else
2371                 return Efallthrough;
2372
2373         comm_call(ci->comm2, "callback:get_attr", ci->focus, 0, NULL, val);
2374         return 1;
2375 }
2376
2377 DEF_CMD(text_set_attr)
2378 {
2379         const char *attr = ci->str;
2380         const char *val = ci->str2;
2381         struct text_chunk *c, *c2;
2382         struct text *t = ci->home->doc_data;
2383         unsigned int o, o2;
2384
2385         if (!attr)
2386                 return Enoarg;
2387         if (!ci->mark)
2388                 return Efallthrough;
2389
2390         o = ci->mark->ref.o;
2391         c = ci->mark->ref.c;
2392         if (!c)
2393                 /* EOF */
2394                 return Efallthrough;
2395         if (o >= c->end) {
2396                 /* End of chunk, need to look at next */
2397                 if (c->lst.next == &t->text)
2398                         return Efallthrough;
2399                 c = list_next_entry(c, lst);
2400                 o = c->start;
2401         }
2402         pane_notify("doc:replaced-attr", ci->home, 1, ci->mark, NULL,
2403                     0, ci->mark2);
2404         attr_set_str_key(&c->attrs, attr, val, o);
2405         if (!ci->mark2 || ci->mark2->seq <= ci->mark->seq)
2406                 return Efallthrough;
2407         /* Delete all subsequent instances of attr */
2408         o += 1;
2409         o2 = ci->mark2->ref.o;
2410         c2 = ci->mark2->ref.c;
2411         while (c != c2) {
2412                 attr_del_all(&c->attrs, attr, o, c->end);
2413                 c = list_next_entry(c, lst);
2414                 o = c ? c->start : 0;
2415         }
2416         if (c && o < o2)
2417                 attr_del_all(&c->attrs, attr, o, o2);
2418         return Efallthrough;
2419 }
2420
2421 DEF_CMD(text_modified)
2422 {
2423         struct text *t = ci->home->doc_data;
2424
2425         if (ci->num == 0) {
2426                 /* toggle status */
2427                 if (t->saved == t->undo)
2428                         t->saved = NULL;
2429                 else
2430                         t->saved = t->undo;
2431         } else if (ci->num > 0)
2432                 /* Set "is modified" */
2433                 t->saved = NULL;
2434         else
2435                 /* Clear "is modified" */
2436                 t->saved = t->undo;
2437         text_check_autosave(ci->home);
2438         call("doc:notify:doc:status-changed", ci->home);
2439         return 1;
2440 }
2441
2442 DEF_CMD(text_revisited)
2443 {
2444         struct text *t = ci->home->doc_data;
2445
2446         if (ci->num <= 0)
2447                 /* Being buried, not visited */
2448                 return Efallthrough;
2449
2450         if (check_file_changed(ci->home) && t->saved == t->undo) {
2451                 call("doc:load-file", ci->home, 2, NULL, NULL, -1);
2452                 call("Message", ci->focus, 0, NULL, "File Reloaded");
2453         }
2454         return Efallthrough;
2455 }
2456
2457 static void text_cleanout(struct text *t safe)
2458 {
2459         struct text_alloc *ta;
2460         struct mark *m;
2461
2462         hlist_for_each_entry(m, &t->doc.marks, all) {
2463                 m->ref.c = NULL;
2464                 m->ref.o = 0;
2465         }
2466
2467         while (!list_empty(&t->text)) {
2468                 struct text_chunk *c = list_entry(t->text.next,
2469                                                   struct text_chunk, lst);
2470                 list_del(&c->lst);
2471                 attr_free(&c->attrs);
2472                 unalloc(c, text);
2473         }
2474         ta = t->alloc;
2475         while (ta) {
2476                 struct text_alloc *tmp = ta;
2477                 ta = tmp->prev;
2478                 unalloc_buf(tmp, sizeof(*tmp) + tmp->size, text);
2479         }
2480         t->alloc = safe_cast NULL;
2481         while (t->undo) {
2482                 struct text_edit *te = t->undo;
2483
2484                 if (te->altnext == NULL) {
2485                         t->undo = te->next;
2486                         unalloc(te, undo);
2487                 } else if (te->next == NULL) {
2488                         t->undo = te->altnext;
2489                         unalloc(te, undo);
2490                 } else {
2491                         /* Make the ->altnext link shorted, until it
2492                          * disappears
2493                          */
2494                         t->undo = te->altnext;
2495                         te->altnext = t->undo->next;
2496                         t->undo->next = te;
2497                 }
2498         }
2499         while (t->redo) {
2500                 struct text_edit *te = t->redo;
2501
2502                 if (te->altnext == NULL) {
2503                         t->redo = te->next;
2504                         unalloc(te, undo);
2505                 } else if (te->next == NULL) {
2506                         t->redo = te->altnext;
2507                         unalloc(te, undo);
2508                 } else {
2509                         /* Make the ->altnext link shorted, until it
2510                          * disappears
2511                          */
2512                         t->redo = te->altnext;
2513                         te->altnext = t->redo->next;
2514                         t->redo->next = te;
2515                 }
2516         }
2517 }
2518
2519 DEF_CMD(text_destroy)
2520 {
2521         struct text *t = ci->home->doc_data;
2522
2523         text_cleanout(t);
2524         free((void*)t->fname);
2525         t->fname = NULL;
2526         free((void*)t->autosave_name);
2527         t->autosave_name = NULL;
2528         return Efallthrough;
2529 }
2530
2531 DEF_CMD(text_clear)
2532 {
2533         /* Clear the document, including undo/redo records
2534          * i.e. free all text
2535          */
2536         struct text *t = ci->home->doc_data;
2537         struct mark *m;
2538
2539         text_cleanout(t);
2540         text_new_alloc(t, 0);
2541
2542         hlist_for_each_entry(m, &t->doc.marks, all) {
2543                 m->ref.c = NULL;
2544                 m->ref.o = 0;
2545         }
2546         pane_notify("doc:replaced", ci->home);
2547
2548         return 1;
2549 }
2550
2551 void edlib_init(struct pane *ed safe)
2552 {
2553         call_comm("global-set-command", ed, &text_new, 0, NULL,
2554                   "attach-doc-text");
2555         call_comm("global-set-command", ed, &text_new2, 0, NULL,
2556                   "open-doc-text");
2557
2558         text_map = key_alloc();
2559
2560         key_add_chain(text_map, doc_default_cmd);
2561         key_add(text_map, "doc:load-file", &text_load_file);
2562         key_add(text_map, "doc:insert-file", &text_insert_file);
2563         key_add(text_map, "doc:same-file", &text_same_file);
2564         key_add(text_map, "doc:content", &text_content);
2565         key_add(text_map, "doc:content-bytes", &text_content);
2566         key_add(text_map, "doc:set-ref", &text_set_ref);
2567         key_add(text_map, "doc:save-file", &text_save_file);
2568         key_add(text_map, "doc:write-file", &text_write_file);
2569         key_add(text_map, "doc:reundo", &text_reundo);
2570         key_add(text_map, "doc:set-attr", &text_set_attr);
2571         key_add(text_map, "doc:get-attr", &text_doc_get_attr);
2572         key_add(text_map, "doc:replace", &text_replace);
2573         key_add(text_map, "doc:char", &text_char_byte);
2574         key_add(text_map, "doc:byte", &text_char_byte);
2575         key_add(text_map, "doc:modified", &text_modified);
2576         key_add(text_map, "doc:set:readonly", &text_readonly);
2577         key_add(text_map, "doc:notify:doc:revisit", &text_revisited);
2578         key_add(text_map, "doc:clear", &text_clear);
2579         key_add(text_map, "doc:autosave-delete", &text_autosave_delete);
2580         key_add(text_map, "doc:debug:mark", &text_debug_mark);
2581         key_add(text_map, "debug:validate-marks", &text_val_marks);
2582
2583         key_add(text_map, "Close", &text_destroy);
2584         key_add(text_map, "get-attr", &text_get_attr);
2585 }