master ohmu/acme2k / src / cmd / acme / file.c
ohmu  ·  2026-09-21
  1#include <u.h>
  2#include <libc.h>
  3#include <draw.h>
  4#include <thread.h>
  5#include <cursor.h>
  6#include <mouse.h>
  7#include <keyboard.h>
  8#include <frame.h>
  9#include <fcall.h>
 10#include <plumb.h>
 11#include <libsec.h>
 12#include "dat.h"
 13#include "fns.h"
 14
 15/*
 16 * Structure of Undo list:
 17 * 	The Undo structure follows any associated data, so the list
 18 *	can be read backwards: read the structure, then read whatever
 19 *	data is associated (insert string, file name) and precedes it.
 20 *	The structure includes the previous value of the modify bit
 21 *	and a sequence number; successive Undo structures with the
 22 *	same sequence number represent simultaneous changes.
 23 */
 24
 25typedef struct Undo Undo;
 26struct Undo
 27{
 28	short	type;		/* Delete, Insert, Filename */
 29	short	mod;	/* modify bit */
 30	uint		seq;		/* sequence number */
 31	uint		p0;		/* location of change (unused in f) */
 32	uint		n;		/* # runes in string or file name */
 33};
 34
 35enum
 36{
 37	Undosize = sizeof(Undo)/sizeof(Rune)
 38};
 39
 40File*
 41fileaddtext(File *f, Text *t)
 42{
 43	if(f == nil){
 44		f = emalloc(sizeof(File));
 45		f->unread = TRUE;
 46	}
 47	f->text = realloc(f->text, (f->ntext+1)*sizeof(Text*));
 48	f->text[f->ntext++] = t;
 49	f->curtext = t;
 50	return f;
 51}
 52
 53void
 54filedeltext(File *f, Text *t)
 55{
 56	int i;
 57
 58	for(i=0; i<f->ntext; i++)
 59		if(f->text[i] == t)
 60			goto Found;
 61	error("can't find text in filedeltext");
 62
 63    Found:
 64	f->ntext--;
 65	if(f->ntext == 0){
 66		fileclose(f);
 67		return;
 68	}
 69	memmove(f->text+i, f->text+i+1, (f->ntext-i)*sizeof(Text*));
 70	if(f->curtext == t)
 71		f->curtext = f->text[0];
 72}
 73
 74void
 75fileinsert(File *f, uint p0, Rune *s, uint ns)
 76{
 77	if(p0 > f->b.nc)
 78		error("internal error: fileinsert");
 79	if(f->seq > 0)
 80		fileuninsert(f, &f->delta, p0, ns);
 81	bufinsert(&f->b, p0, s, ns);
 82	if(ns)
 83		f->mod = TRUE;
 84}
 85
 86void
 87fileuninsert(File *f, Buffer *delta, uint p0, uint ns)
 88{
 89	Undo u;
 90
 91	/* undo an insertion by deleting */
 92	u.type = Delete;
 93	u.mod = f->mod;
 94	u.seq = f->seq;
 95	u.p0 = p0;
 96	u.n = ns;
 97	bufinsert(delta, delta->nc, (Rune*)&u, Undosize);
 98}
 99
100void
101filedelete(File *f, uint p0, uint p1)
102{
103	if(!(p0<=p1 && p0<=f->b.nc && p1<=f->b.nc))
104		error("internal error: filedelete");
105	if(f->seq > 0)
106		fileundelete(f, &f->delta, p0, p1);
107	bufdelete(&f->b, p0, p1);
108	if(p1 > p0)
109		f->mod = TRUE;
110}
111
112void
113fileundelete(File *f, Buffer *delta, uint p0, uint p1)
114{
115	Undo u;
116	Rune *buf;
117	uint i, n;
118
119	/* undo a deletion by inserting */
120	u.type = Insert;
121	u.mod = f->mod;
122	u.seq = f->seq;
123	u.p0 = p0;
124	u.n = p1-p0;
125	buf = fbufalloc();
126	for(i=p0; i<p1; i+=n){
127		n = p1 - i;
128		if(n > RBUFSIZE)
129			n = RBUFSIZE;
130		bufread(&f->b, i, buf, n);
131		bufinsert(delta, delta->nc, buf, n);
132	}
133	fbuffree(buf);
134	bufinsert(delta, delta->nc, (Rune*)&u, Undosize);
135
136}
137
138void
139filesetname(File *f, Rune *name, int n)
140{
141	if(f->seq > 0)
142		fileunsetname(f, &f->delta);
143	free(f->name);
144	f->name = runemalloc(n);
145	runemove(f->name, name, n);
146	f->nname = n;
147	f->unread = TRUE;
148}
149
150void
151fileunsetname(File *f, Buffer *delta)
152{
153	Undo u;
154
155	/* undo a file name change by restoring old name */
156	u.type = Filename;
157	u.mod = f->mod;
158	u.seq = f->seq;
159	u.p0 = 0;	/* unused */
160	u.n = f->nname;
161	if(f->nname)
162		bufinsert(delta, delta->nc, f->name, f->nname);
163	bufinsert(delta, delta->nc, (Rune*)&u, Undosize);
164}
165
166uint
167fileload(File *f, uint p0, int fd, int *nulls, DigestState *h)
168{
169	if(f->seq > 0)
170		error("undo in file.load unimplemented");
171	return bufload(&f->b, p0, fd, nulls, h);
172}
173
174/* return sequence number of pending redo */
175uint
176fileredoseq(File *f)
177{
178	Undo u;
179	Buffer *delta;
180
181	delta = &f->epsilon;
182	if(delta->nc == 0)
183		return 0;
184	bufread(delta, delta->nc-Undosize, (Rune*)&u, Undosize);
185	return u.seq;
186}
187
188void
189fileundo(File *f, int isundo, uint *q0p, uint *q1p)
190{
191	Undo u;
192	Rune *buf;
193	uint i, j, n, up;
194	uint stop;
195	Buffer *delta, *epsilon;
196
197	if(isundo){
198		/* undo; reverse delta onto epsilon, seq decreases */
199		delta = &f->delta;
200		epsilon = &f->epsilon;
201		stop = f->seq;
202	}else{
203		/* redo; reverse epsilon onto delta, seq increases */
204		delta = &f->epsilon;
205		epsilon = &f->delta;
206		stop = 0;	/* don't know yet */
207	}
208
209	buf = fbufalloc();
210	while(delta->nc > 0){
211		up = delta->nc-Undosize;
212		bufread(delta, up, (Rune*)&u, Undosize);
213		if(isundo){
214			if(u.seq < stop){
215				f->seq = u.seq;
216				goto Return;
217			}
218		}else{
219			if(stop == 0)
220				stop = u.seq;
221			if(u.seq > stop)
222				goto Return;
223		}
224		switch(u.type){
225		default:
226			fprint(2, "undo: 0x%ux\n", u.type);
227			abort();
228			break;
229
230		case Delete:
231			f->seq = u.seq;
232			fileundelete(f, epsilon, u.p0, u.p0+u.n);
233			f->mod = u.mod;
234			bufdelete(&f->b, u.p0, u.p0+u.n);
235			for(j=0; j<f->ntext; j++)
236				textdelete(f->text[j], u.p0, u.p0+u.n, FALSE);
237			*q0p = u.p0;
238			*q1p = u.p0;
239			break;
240
241		case Insert:
242			f->seq = u.seq;
243			fileuninsert(f, epsilon, u.p0, u.n);
244			f->mod = u.mod;
245			up -= u.n;
246			for(i=0; i<u.n; i+=n){
247				n = u.n - i;
248				if(n > RBUFSIZE)
249					n = RBUFSIZE;
250				bufread(delta, up+i, buf, n);
251				bufinsert(&f->b, u.p0+i, buf, n);
252				for(j=0; j<f->ntext; j++)
253					textinsert(f->text[j], u.p0+i, buf, n, FALSE);
254			}
255			*q0p = u.p0;
256			*q1p = u.p0+u.n;
257			break;
258
259		case Filename:
260			f->seq = u.seq;
261			fileunsetname(f, epsilon);
262			f->mod = u.mod;
263			up -= u.n;
264			free(f->name);
265			if(u.n == 0)
266				f->name = nil;
267			else
268				f->name = runemalloc(u.n);
269			bufread(delta, up, f->name, u.n);
270			f->nname = u.n;
271			break;
272		}
273		bufdelete(delta, up, delta->nc);
274	}
275	if(isundo)
276		f->seq = 0;
277    Return:
278	fbuffree(buf);
279}
280
281void
282filereset(File *f)
283{
284	bufreset(&f->delta);
285	bufreset(&f->epsilon);
286	f->seq = 0;
287}
288
289void
290fileclose(File *f)
291{
292	free(f->name);
293	f->nname = 0;
294	f->name = nil;
295	free(f->text);
296	f->ntext = 0;
297	f->text = nil;
298	bufclose(&f->b);
299	bufclose(&f->delta);
300	bufclose(&f->epsilon);
301	elogclose(f);
302	free(f);
303}
304
305void
306filemark(File *f)
307{
308	if(f->epsilon.nc)
309		bufdelete(&f->epsilon, 0, f->epsilon.nc);
310	f->seq = seq;
311}