1 /*
2  *      Record-oriented file access package.
3  *      -------------------------------------
4  *
5  *      REC *RecOpen (int fd, int mode)
6  *              - initialize Rec package for file fd,
7  *              for writing if mode!=0.
8  *
9  *      int RecSave (REC *r, char *filename)
10  *              - and write out changes,
11  *              returns 0 if ok or -1 if cannot write file.
12  *
13  *      int RecClose (REC *r)
14  *              - close Rec discarding changes.
15  *
16  *      RecBroken (REC *r)
17  *              - if Rec has broken lines, return 1, else 0.
18  *
19  *      LINE *RecGet (REC *r, int linenum)
20  *              - get line.
21  *
22  *      RecPut (LINE *l, int newlen)
23  *              - put line.
24  *
25  *      RecInsLine (REC *r, int linenum)
26  *              - insert empty line before line #linenum
27  *
28  *      RecDelLine (REC *r, int linenum)
29  *              - delete line #linenum
30  *
31  *      RecInsChar (REC *r, int line, int off, int sym)
32  *              - insert char into line
33  *
34  *      RecDelChar (REC *r, int line, int off)
35  *              - delete char from line
36  */
37 
38 #include <stdlib.h>
39 #include <string.h>
40 #if HAVE_UNISTD_H
41 #   include <unistd.h>
42 #endif
43 #if HAVE_FCNTL_H
44 #   include <fcntl.h>
45 #endif
46 #include "rec.h"
47 #include "deco.h"
48 
49 #define BUFSIZ          512
50 #define TEMPCELL        16              /* minimal size of temp space */
51 #define QUANT           512
52 #define MAXLINE         1024            /* maximum length of unbroken line */
53 #define NOINDEX         (POOLSZ+2)
54 
55 #define BAKSUFFIX       ".b"
56 
57 static char mask [8] = { 1, 2, 4, 8, 0x10, 0x20, 0x40, 0x80, };
58 static char tfilepattern [] = "/tmp/recXXXXXX";
59 
60 static int lenbuf;
61 static char *scanbuf, *pbuf;
62 static char *tfilename;
63 static int eoln;
64 static int MAXLEN = ((unsigned int) (int) -1 >> 1) / sizeof (struct index);
65 
ffcopy(int from,int to)66 static int ffcopy (int from, int to)
67 {
68 	char buf [512], *p;
69 	register n, k;
70 
71 	for (;;) {
72 		n = read (from, buf, sizeof (buf));
73 		if (n < 0) {
74 			error ("Cannot read file");
75 			return (-1);
76 		}
77 		if (n == 0)
78 			return (0);
79 		p = buf;
80 		while (n > 0) {
81 			k = write (to, p, (unsigned) n);
82 			if (k <= 0) {
83 				error ("Cannot write file");
84 				return (-1);
85 			}
86 			n -= k;
87 			p += k;
88 		}
89 	}
90 }
91 
tempfree(REC * r,long seek,int len)92 static void tempfree (REC *r, long seek, int len)  /* mark temp space free */
93 {
94 	register i;
95 
96 	len = (len + TEMPCELL - 1) / TEMPCELL;
97 	seek /= TEMPCELL;
98 	for (i=0; i<len; ++i, ++seek)
99 		r->tmap [seek>>3] &= ~mask [seek & 7];
100 }
101 
tempbusy(REC * r,int len)102 static long tempbusy (REC *r, int len)   /* search & mark temp space busy */
103 {
104 	register i, n;
105 	register long seek;
106 
107 	len = (len + TEMPCELL - 1) / TEMPCELL;
108 	for (seek=0; seek<MAXTEMP; ++seek) {
109 		for (n=0; n<len; ++n)
110 			if (r->tmap [seek>>3] & mask [seek & 7])
111 				break;
112 		if (n >= len)
113 			break;
114 	}
115 	if (seek >= MAXTEMP) {
116 		error ("No temp space");
117 		return (0);
118 	}
119 	for (n=seek, i=0; i<len; ++i, ++n)
120 		r->tmap [n>>3] |= mask [n & 7];
121 	return (seek * TEMPCELL);
122 }
123 
tempsave(REC * r,char * str,int len)124 static long tempsave (REC *r, char *str, int len) /* save string in temp file, return seek */
125 {
126 	register long seek;
127 
128 	if (! len)
129 		return (0);
130 	seek = tempbusy (r, len);
131 	if (lseek (r->tfd, seek, 0) < 0)
132 		error ("Cannot lseek on writing temp file");
133 	if (write (r->tfd, str, (unsigned) len) != len)
134 		error ("Cannot write temporary file");
135 	return (seek);
136 }
137 
scanline(int fd)138 static int scanline (int fd)
139 {
140 	register len;
141 
142 	len = 0;
143 	eoln = 0;
144 	for (;;) {
145 		if (pbuf >= &scanbuf[lenbuf]) {
146 			lenbuf = read (fd, pbuf = scanbuf, (unsigned) BUFSIZ);
147 			if (lenbuf <= 0)
148 				return (len ? len : -1);
149 		}
150 		if (*pbuf++ == '\n') {
151 			eoln = 1;
152 			return (len);
153 		}
154 		if (++len >= MAXLINE)
155 			return (len);
156 	}
157 }
158 
RecOpen(int fd,int wmode)159 REC *RecOpen (int fd, int wmode)
160 {
161 	register REC *r;
162 	register struct index *x;
163 	register i;
164 
165 	r = (REC*) malloc (sizeof (REC));
166 	r->fd = fd;
167 	r->bakfd = -1;
168 	if (wmode) {
169 		if (! tfilename)
170 			tfilename = tfilepattern;
171 		r->tfd = creat (tfilename, 0600);
172 		if (r->tfd < 0) {
173 			error ("Cannot open temporary file");
174 			return (0);
175 		}
176 		close (r->tfd);
177 		r->tfd = open (tfilename, 2);
178 		if (r->tfd < 0) {
179 			error ("Cannot reopen temporary file");
180 			return (0);
181 		}
182 		unlink (tfilename);
183 	} else
184 		r->tfd = -1;
185 
186 	for (i=0; i<TEMPSZ; ++i)
187 		r->tmap [i] = 0;
188 
189 	for (i=0; i<POOLSZ; ++i)
190 		r->map[i].busy = 0;
191 
192 	lseek (r->fd, 0L, 0);
193 	r->broken = 0;
194 	r->len = 0;
195 	r->lindex = 0;
196 	r->nindex = 0;
197 	r->size = 0;
198 	scanbuf = malloc (BUFSIZ);
199 	pbuf = scanbuf;
200 	lenbuf = 0;
201 	for (;;) {
202 		mcheck (r->lindex, struct index *, r->nindex, QUANT, r->len+1);
203 		x = &r->lindex[r->len];
204 		x->seek = r->size;              /* store seek of current line */
205 		x->poolindex = NOINDEX;         /* clear pool index of current line */
206 		x->flags = 0;                   /* clear flags */
207 		x->len = scanline (r->fd);      /* scan next line */
208 		if (x->len < 0)
209 			break;
210 		r->size += x->len;              /* increment seek by length of line */
211 		if (eoln)
212 			++r->size;              /* \n at end of line */
213 		else {
214 			x->flags |= XNOEOLN;    /* no end of line */
215 			++r->broken;
216 		}
217 		if (++r->len >= MAXLEN) {
218 			error ("file too long");
219 			return (0);
220 		}
221 	}
222 	free (scanbuf);
223 	return (r);
224 }
225 
RecClose(REC * r)226 void RecClose (REC *r)
227 {
228 	register i;
229 
230 	for (i=0; i<POOLSZ; ++i)
231 		if (r->map[i].busy) {
232 			if (r->pool[i].len)
233 				free (r->pool[i].ptr);
234 			r->lindex[r->map[i].index].poolindex = NOINDEX;
235 			r->map[i].busy = 0;
236 		}
237 	free (r->lindex);
238 	if (r->tfd >= 0)
239 		close (r->tfd);
240 	if (r->bakfd >= 0)
241 		close (r->bakfd);
242 }
243 
RecSave(REC * r,char * filename)244 int RecSave (REC *r, char *filename)
245 {
246 	register i, fd;
247 	register LINE *p;
248 	char bak [40];
249 
250 	if (r->bakfd < 0) {
251 		strcpy (bak, filename);
252 		strcat (bak, BAKSUFFIX);
253 		r->bakfd = creat (bak, 0600);
254 		if (r->bakfd < 0) {
255 			error ("Cannot create %s", bak);
256 			return (-1);
257 		}
258 		lseek (r->fd, 0L, 0);
259 		if (ffcopy (r->fd, r->bakfd) < 0) {
260 			close (r->bakfd);
261 			r->bakfd = -1;
262 			unlink (bak);
263 			return (-1);
264 		}
265 		close (r->bakfd);
266 		r->bakfd = open (bak, 0);
267 		if (r->bakfd < 0) {
268 			error ("Cannot open %s", bak);
269 			unlink (bak);
270 			return (-1);
271 		}
272 		close (r->fd);
273 		r->fd = r->bakfd;
274 	}
275 	fd = creat (filename, 0664);
276 	if (fd < 0) {
277 		error ("Cannot create %s", filename);
278 		unlink (bak);
279 		return (-1);
280 	}
281 	for (i=0; i<r->len; ++i) {
282 		p = RecGet (r, i);
283 		if (p->len)
284 			write (fd, p->ptr, (unsigned) p->len);
285 		write (fd, "\n", 1);
286 	}
287 	close (fd);
288 	return (0);
289 }
290 
RecBreak(REC * r)291 void RecBreak (REC *r)
292 {
293 	register i;
294 	register struct index *x;
295 
296 	x = r->lindex;
297 	for (i=0; i<r->len; ++i, ++x)
298 		x->flags &= ~XNOEOLN;
299 }
300 
readline(int fd,long seek,int len,LINE * rez)301 static void readline (int fd, long seek, int len, LINE *rez)
302 {
303 	register l, n;
304 	register char *s;
305 
306 	rez->len = len;
307 	rez->mod = 0;
308 	if (! len) {
309 		rez->ptr = "";
310 		return;
311 	}
312 	rez->ptr = malloc (rez->len);
313 	if (lseek (fd, seek, 0) < 0)
314 		error ("Cannot lseek on reading fd=%d, seek=%ld", fd, seek);
315 	for (l=rez->len, s=rez->ptr; l>0; l-=n, s+=n) {
316 		n = read (fd, s, (unsigned) l);
317 		if (n <= 0) {
318 			error ("Cannot read line");
319 			if (rez->len)
320 				free (rez->ptr);
321 			rez->len = 0;
322 			rez->ptr = "";
323 			return;
324 		}
325 	}
326 }
327 
freeline(REC * r)328 static int freeline (REC *r)
329 {
330 	register struct map *m;
331 	register struct index *x;
332 	register LINE *l;
333 	register mintime, minindex;
334 
335 	/* find free place in pool */
336 	for (m=r->map; m<r->map+POOLSZ; ++m)
337 		if (! m->busy)
338 			return (m - r->map);
339 	/* pool is full; find the oldest line */
340 	mintime = r->map[0].time;
341 	minindex = 0;
342 	for (m=r->map; m<r->map+POOLSZ; ++m)
343 		if (m->time < mintime)
344 			minindex = m - r->map;
345 	m = &r->map[minindex];
346 	l = &r->pool[minindex];
347 	x = &r->lindex[m->index];
348 	/* remove line from pool */
349 	if (l->mod) {           /* line is modified, save it in temp file */
350 		if ((x->flags & XTEMP) && l->oldlen)
351 			tempfree (r, x->seek, l->oldlen);
352 		x->seek = tempsave (r, l->ptr, l->len);
353 		x->len = l->len;
354 		x->flags |= XTEMP;
355 	}
356 	if (l->len)
357 		free (l->ptr);
358 	x->poolindex = NOINDEX;
359 	m->busy = 0;
360 	return (minindex);
361 }
362 
RecGet(REC * r,int n)363 LINE *RecGet (REC *r, int n)
364 {
365 	register struct index *x;
366 	register struct map *m;
367 	register LINE *p;
368 	static long timecount = 1;              /* time stamp */
369 
370 	if (n < 0 || n >= r->len)
371 		return (0);
372 	x = &r->lindex[n];
373 	if (x->poolindex != NOINDEX) {          /* line is in cache */
374 		r->map[x->poolindex].time = ++timecount;
375 		return (&r->pool[x->poolindex]);
376 	}
377 	x->poolindex = freeline (r);            /* get free pool index */
378 	p = &r->pool[x->poolindex];
379 	m = &r->map[x->poolindex];
380 	m->time = ++timecount;
381 	m->index = n;
382 	m->busy = 1;
383 	/* read line from file */
384 	readline ((x->flags & XTEMP) ? r->tfd : r->fd, x->seek, x->len, p);
385 	p->noeoln = (x->flags & XNOEOLN) != 0;
386 	return (p);
387 }
388 
RecPut(LINE * p,int newlen)389 void RecPut (LINE *p, int newlen)
390 {
391 	if (! p->mod)
392 		p->oldlen = p->len;
393 	p->mod = 1;
394 	p->len = newlen;
395 }
396 
RecDelChar(REC * r,int line,int off)397 void RecDelChar (REC *r, int line, int off)
398 {
399 	register LINE *p;
400 	char *s;
401 
402 	if (! (p = RecGet (r, line)))
403 		return;
404 	if (p->len <= off)
405 		return;
406 	s = malloc (p->len-1);
407 	if (off)
408 		memcpy (s, p->ptr, off);
409 	if (off <= p->len-1)
410 		memcpy (s+off, p->ptr+off+1, p->len-off-1);
411 	free (p->ptr);
412 	p->ptr = s;
413 	RecPut (p, p->len - 1);
414 }
415 
RecInsChar(REC * r,int line,int off,int sym)416 void RecInsChar (REC *r, int line, int off, int sym)
417 {
418 	register LINE *p;
419 	char *s;
420 
421 	if (! (p = RecGet (r, line)))
422 		return;
423 	s = malloc (p->len+1);
424 	if (off)
425 		memcpy (s, p->ptr, off);
426 	s [off] = sym;
427 	if (off < p->len)
428 		memcpy (s+off+1, p->ptr+off, p->len-off);
429 	if (p->len)
430 		free (p->ptr);
431 	p->ptr = s;
432 	RecPut (p, p->len + 1);
433 }
434 
RecInsLine(REC * r,int n)435 void RecInsLine (REC *r, int n)
436 {
437 	register struct index *x, *i;
438 	register struct map *m;
439 	register k;
440 
441 	if (n<0 || n>r->len)
442 		return;
443 	++r->len;
444 	mcheck (r->lindex, struct index *, r->nindex, QUANT, r->len+1);
445 	i = &r->lindex[n];
446 	for (x= &r->lindex[r->len-1]; x>i; --x)
447 		x[0] = x[-1];
448 	i->seek = 0;
449 	i->len = 0;
450 	i->poolindex = NOINDEX;
451 	i->flags = XTEMP;
452 	m = r->map;
453 	for (k=0; k<POOLSZ; ++k, ++m)
454 		if (m->index >= n)
455 			++m->index;
456 }
457 
RecDelLine(REC * r,int n)458 void RecDelLine (REC *r, int n)
459 {
460 	register struct index *x, *i;
461 	register struct map *m;
462 	register LINE *l;
463 	register k;
464 
465 	if (n<0 || n>=r->len)
466 		return;
467 	x = &r->lindex[n];
468 	m = r->map;
469 	if (x->poolindex != NOINDEX) {          /* exclude line from pool */
470 		l = &r->pool[x->poolindex];
471 		if (x->flags & XTEMP)
472 			if (l->mod) {
473 				if (l->oldlen)
474 					tempfree (r, x->seek, (long) l->oldlen);
475 			} else if (l->len)
476 				tempfree (r, x->seek, l->len);
477 		if (l->len)
478 			free (l->ptr);
479 		m[x->poolindex].busy = 0;
480 	}
481 	for (k=0; k<POOLSZ; ++k, ++m)
482 		if (m->index > n)
483 			--m->index;
484 	i = &r->lindex[r->len-1];
485 	for (x= &r->lindex[n]; x<i; ++x)
486 		x[0] = x[1];
487 	--r->len;
488 }
489