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