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