"Fossies" - the Fresh Open Source Software Archive 
Member "schily-2021-09-18/udiff/udiff.c" (20 Aug 2021, 19313 Bytes) of package /linux/privat/schily-2021-09-18.tar.bz2:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
1 /* @(#)udiff.c 1.42 21/08/20 Copyright 1985-2021 J. Schilling */
2 #include <schily/mconfig.h>
3 #ifndef lint
4 static UConst char sccsid[] =
5 "@(#)udiff.c 1.42 21/08/20 Copyright 1985-2021 J. Schilling";
6 #endif
7 /*
8 * line by line diff for two files
9 *
10 * Copyright (c) 1985-2021 J. Schilling
11 */
12 /*
13 * The contents of this file are subject to the terms of the
14 * Common Development and Distribution License, Version 1.0 only
15 * (the "License"). You may not use this file except in compliance
16 * with the License.
17 *
18 * See the file CDDL.Schily.txt in this distribution for details.
19 * A copy of the CDDL is also available via the Internet at
20 * http://www.opensource.org/licenses/cddl1.txt
21 *
22 * When distributing Covered Code, include this CDDL HEADER in each
23 * file and include the License file CDDL.Schily.txt from this distribution.
24 */
25
26 /*
27 * Remarks:
28 *
29 * The amount of memory "in use" largely depends on the allocation
30 * algorithm as the code intensively uses realloc().
31 *
32 * If the files differ, we always allocate
33 * (sizeof (off_t) + sizeof (long)) * (lines(old) + lines(new))
34 * In largefile mode we typically allocate ~ 50% more
35 * than in non-largefile mode. It seems that in largefile mode, the
36 * amount of space need is typically ~
37 * (sizeof (long long) + sizeof (long)) *
38 * (lines(old) + lines(new)).
39 *
40 * Largefile mode is also neeeded in order to be able to deal with files
41 * in the 64 bit inode # range on ZFS.
42 *
43 * If the code currently uses "long" for line numbers, which is sufficient
44 * It would be possible to have real large file mode in case that udiff is
45 * compiled in 64 bit mode.
46 * In 32 bit mode, there is no need to check for an integer overflow
47 * as the process will run "out of memory" before.
48 */
49
50 #include <schily/stdio.h>
51 #include <schily/stdlib.h>
52 #include <schily/unistd.h> /* Include sys/types.h, makes off_t available */
53 #include <schily/standard.h>
54 #include <schily/string.h>
55 #include <schily/stat.h>
56 #define GT_COMERR /* #define comerr gtcomerr */
57 #define GT_ERROR /* #define error gterror */
58 #include <schily/schily.h>
59 #include <schily/nlsdefs.h>
60
61 #define MAXLINE 32768
62
63 typedef struct line {
64 off_t off;
65 long hash;
66 } line;
67
68 LOCAL char *olbf; /* "old" line buffer */
69 LOCAL char *nlbf; /* "new" line buffer */
70 LOCAL size_t olsz; /* "old" line buffer size */
71 LOCAL size_t nlsz; /* "new" line buffer size */
72
73 LOCAL BOOL posix; /* -posix flag */
74 LOCAL int nmatch = 2;
75 LOCAL BOOL bdiffmode = FALSE;
76
77 LOCAL line *oldfile; /* Offsets and hashes for old file */
78 LOCAL char *oldname; /* Old file name */
79 LOCAL FILE *of = 0; /* File pointer for old file */
80 LOCAL long olc = 0; /* Line count for old file */
81 LOCAL line *newfile; /* Offsets and hashes for new file */
82 LOCAL char *newname; /* New file name */
83 LOCAL FILE *nf = 0; /* File pointer for new file */
84 LOCAL long nlc = 0; /* Line count for new file */
85 LOCAL long clc = 0; /* Common line count */
86 LOCAL off_t ooff; /* Saved seek offset for old file */
87 LOCAL off_t noff; /* Saved seek offset for new file */
88
89 #define glinep(i, a) (&((a)[(i)]))
90 #define compare(o, n) (linecmp(glinep((o), oldfile), glinep((n), newfile)))
91
92 LOCAL void usage __PR((int exitcode));
93 EXPORT int main __PR((int ac, char **av));
94 LOCAL const char *filename __PR((const char *name));
95 LOCAL off_t readcommon __PR((FILE *ofp, char *oname,
96 FILE *nfp, char *nname));
97 LOCAL long readfile __PR((FILE *f, char *fname, line **hline));
98 LOCAL void addline __PR((char *s, size_t ll, off_t loff, long lc,
99 line *hline));
100 LOCAL long hash __PR((char *s, size_t ll));
101 LOCAL BOOL linecmp __PR((line *olp, line *nlp));
102 LOCAL int diff __PR((void));
103 LOCAL void showdel __PR((long o, long n, long c));
104 LOCAL void showadd __PR((long o, long n, long c));
105 LOCAL void showchange __PR((long o, long n, long c));
106 LOCAL void showxchange __PR((long o, long n, long oc, long nc));
107
108 LOCAL FILE *xfileopen __PR((int idx, char *name, char *mode));
109 LOCAL int xfileseek __PR((int idx, off_t off));
110 LOCAL off_t xfilepos __PR((int idx));
111 LOCAL ssize_t xgetdelim __PR((char **lineptr, size_t *n,
112 int delim, int idx));
113 LOCAL ssize_t xfileread __PR((int idx, char **lineptr, size_t n));
114
115 LOCAL void
116 usage(exitcode)
117 int exitcode;
118 {
119 error("Usage: udiff [options] file1 file2\n");
120 error(" -help Print this help.\n");
121 error(" -version Print version number.\n");
122 error(" -posix Print diffs in POSIX mode.\n");
123 error(" nmatch=# Set number of matching lines for resync (default = %d).\n",
124 nmatch);
125 exit(exitcode);
126 }
127
128
129 EXPORT int
130 main(ac, av)
131 int ac;
132 char **av;
133 {
134 char *options = "posix,nmatch#,help,version";
135 BOOL help = FALSE;
136 BOOL prversion = FALSE;
137 int ret = 0;
138 int fac;
139 char * const *fav;
140 const char *av0 = filename(av[0]);
141 struct stat sb1;
142 struct stat sb2;
143 off_t lineoff;
144
145 save_args(ac, av);
146 if (av0[0] != 'u') {
147 nmatch = 1;
148 posix = 1;
149 }
150 if (av0[0] == 'f' && av0[1] == 's') {
151 bdiffmode = TRUE;
152 }
153
154 (void) setlocale(LC_ALL, "");
155
156 #ifdef USE_NLS
157 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
158 #define TEXT_DOMAIN "udiff" /* Use this only if it weren't */
159 #endif
160 { char *dir;
161 dir = searchfileinpath("share/locale", F_OK,
162 SIP_ANY_FILE|SIP_NO_PATH, NULL);
163 if (dir)
164 (void) bindtextdomain(TEXT_DOMAIN, dir);
165 else
166 #if defined(PROTOTYPES) && defined(INS_BASE)
167 (void) bindtextdomain(TEXT_DOMAIN, INS_BASE "/share/locale");
168 #else
169 (void) bindtextdomain(TEXT_DOMAIN, "/usr/share/locale");
170 #endif
171 (void) textdomain(TEXT_DOMAIN);
172 }
173 #endif /* USE_NLS */
174
175 fac = --ac;
176 fav = ++av;
177 if (getallargs(&fac, &fav, options, &posix, &nmatch,
178 &help, &prversion) < 0) {
179 errmsgno(EX_BAD, "Bad option: '%s'\n", fav[0]);
180 usage(EX_BAD);
181 }
182 if (help)
183 usage(0);
184 if (nmatch <= 0) {
185 errmsgno(EX_BAD, "Bad nmatch value: %d\n", nmatch);
186 usage(EX_BAD);
187 }
188 if (prversion) {
189 gtprintf("Udiff release %s %s (%s-%s-%s) Copyright (C) 1985-2021 %s\n",
190 "1.42", "2021/08/20",
191 HOST_CPU, HOST_VENDOR, HOST_OS,
192 _("Jörg Schilling"));
193 exit(0);
194 }
195
196 fac = ac;
197 fav = av;
198 if (getfiles(&fac, &fav, options) <= 0) {
199 error("No files given.\n");
200 usage(EX_BAD);
201 }
202 oldname = fav[0];
203 if ((of = xfileopen(0, fav[0], "rb")) == NULL)
204 comerr("Cannot open file %s\n", fav[0]);
205 fac--, fav++;
206
207 if (getfiles(&fac, &fav, options) <= 0) {
208 error("Only one file given.\n");
209 usage(EX_BAD);
210 }
211 newname = fav[0];
212 if ((nf = xfileopen(1, fav[0], "rb")) == NULL)
213 comerr("Cannot open file %s\n", fav[0]);
214 fac--, fav++;
215
216 if (getfiles(&fac, &fav, options) > 0) {
217 error("Too many files given.\n");
218 usage(EX_BAD);
219 }
220 fac--, fav++;
221
222 if (filestat(of, &sb1) < 0)
223 comerr("Cannot stat '%s'\n", oldname);
224 if (filestat(nf, &sb2) < 0)
225 comerr("Cannot stat '%s'\n", newname);
226 if (sb1.st_ino == sb2.st_ino && sb1.st_dev == sb2.st_dev)
227 goto same;
228
229 #ifdef HAVE_SETVBUF
230 setvbuf(of, NULL, _IOFBF, MAXLINE);
231 setvbuf(nf, NULL, _IOFBF, MAXLINE);
232 #endif
233 if (sb1.st_size == sb2.st_size) {
234 long ob[MAXLINE / sizeof (long)];
235 long nb[MAXLINE / sizeof (long)];
236 char *op = (char *)ob;
237 char *np = (char *)nb;
238 int n1;
239 int n2;
240
241 while ((n1 = xfileread(0, &op, sizeof (ob))) > 0) {
242 n2 = xfileread(1, &np, sizeof (nb));
243
244 if (n1 != n2)
245 goto notsame;
246 if (cmpbytes(op, np, n1) < n1)
247 goto notsame;
248 }
249 if (n1 < 0)
250 goto notsame;
251 goto same;
252 }
253 notsame:
254 if (!bdiffmode)
255 ret = 1;
256 if (xfileseek(0, (off_t)0) == (off_t)-1 ||
257 xfileseek(1, (off_t)0) == (off_t)-1)
258 comerr("Cannot seek.\n");
259
260 lineoff = readcommon(of, oldname, nf, newname);
261 xfileseek(0, lineoff);
262 xfileseek(1, lineoff);
263
264 olc = readfile(of, oldname, &oldfile);
265 nlc = readfile(nf, newname, &newfile);
266
267 (void) diff();
268 same:
269 fclose(of);
270 fclose(nf);
271 return (ret);
272 }
273
274
275 LOCAL const char *
276 filename(name)
277 const char *name;
278 {
279 char *p;
280
281 if ((p = strrchr(name, '/')) == NULL)
282 return (name);
283 return (++p);
284 }
285
286 LOCAL off_t
287 readcommon(ofp, oname, nfp, nname)
288 FILE *ofp;
289 char *oname;
290 FILE *nfp;
291 char *nname;
292 {
293 off_t lineoff = 0;
294 off_t readoff = 0;
295
296 clc = 0;
297 for (;;) {
298 long ob[MAXLINE / sizeof (long)];
299 long nb[MAXLINE / sizeof (long)];
300 char *op = (char *)ob;
301 char *np = (char *)nb;
302 int n;
303 int n1;
304 int n2;
305 char *oop;
306 char *nl;
307
308 n1 = xfileread(0, &op, sizeof (ob));
309 n2 = xfileread(1, &np, sizeof (nb));
310 if (n1 <= 0 || n2 <= 0)
311 break;
312 if (n2 < n1)
313 n1 = n2;
314 n = n2 = cmpbytes(op, np, n1);
315
316 /*
317 * Count newlines in common part.
318 */
319 oop = op;
320 while (n > 0 && (nl = findbytes(op, n, '\n'))) {
321 lineoff = readoff + 1 + (nl - (char *)oop);
322 n -= nl - op + 1;
323 op = nl + 1;
324 clc++;
325 }
326 if (n2 != n1) /* cmpbytes() signalled a difference */
327 break; /* or n1 was less than n2 */
328 readoff += n2;
329 }
330 return (lineoff);
331 }
332
333 LOCAL long
334 readfile(f, fname, hlinep)
335 register FILE *f;
336 char *fname;
337 line **hlinep;
338 {
339 register ssize_t len;
340 register int lch = -1; /* Last character from last read line */
341 off_t loff = 0;
342 long lc = 0;
343 long lsize = 0;
344 static int xgrow = 1024;
345 #define MAX_GROW 16384
346
347 /*
348 * Get current posision as we skipped over the common parts.
349 */
350 loff = xfilepos(f == of ? 0:1);
351
352 /*
353 * Use getdelim() to include the newline in the hash.
354 * This allows to correctly deal with files that do not end in a newline
355 * and this allows to handle nul bytes in the line.
356 */
357 if (olsz == 0) /* If first file is mmap()d and 2nd is not */
358 olbf = NULL; /* could be != NULL from mmap() space */
359 for (;;) {
360 if ((len = xgetdelim(&olbf, &olsz, '\n', f == of ? 0:1)) < 0) {
361 if (ferror(f))
362 comerr("Cannot read '%s'.\n", fname);
363 break;
364 }
365 lch = ((unsigned char *)olbf)[len-1];
366 if (lc >= lsize) {
367 lsize += xgrow;
368 *hlinep = ___realloc(*hlinep,
369 (lsize) * sizeof (line),
370 "new line");
371 if (xgrow < MAX_GROW)
372 xgrow *= 2;
373 }
374 addline(olbf, len, loff, lc++, *hlinep);
375 #ifdef USE_CRLF
376 loff = filepos(f);
377 #else /* USE_CRLF */
378 loff += len;
379 #endif /* USE_CRLF */
380 }
381 if (lch >= 0 && lch != '\n') {
382 error("Warning: missing newline at end of file %s\n", fname);
383 }
384 return (lc);
385 }
386
387 LOCAL void
388 addline(s, ll, loff, lc, hline)
389 register char *s;
390 size_t ll;
391 off_t loff;
392 long lc;
393 line *hline;
394 {
395 line *lp;
396
397 lp = glinep(lc, hline);
398 lp->off = loff;
399 lp->hash = hash(s, ll);
400 }
401
402
403 LOCAL long
404 hash(s, ll)
405 register char *s;
406 register size_t ll;
407 {
408 register long h;
409
410 for (h = 0; ll-- > 0; ) {
411 if (h < 0) {
412 h <<= 1;
413 h += (*s++ & 255)+1; /* rotate sign bit */
414 } else {
415 h <<= 1;
416 h += (*s++ & 255);
417 }
418 }
419 return (h);
420 }
421
422
423 LOCAL BOOL
424 linecmp(olp, nlp)
425 line *olp;
426 line *nlp;
427 {
428 ssize_t lo;
429 ssize_t ln;
430
431 if (olp->hash != nlp->hash)
432 return (FALSE);
433
434 if (ooff != olp->off) {
435 xfileseek(0, olp->off);
436 ooff = olp->off;
437 }
438 lo = xgetdelim(&olbf, &olsz, '\n', 0);
439 if (lo < 0)
440 return (FALSE);
441 ooff += lo;
442 #ifdef USE_CRLF
443 ooff = filepos(of);
444 #endif
445
446 if (noff != nlp->off) {
447 xfileseek(1, nlp->off);
448 noff = nlp->off;
449 }
450 ln = xgetdelim(&nlbf, &nlsz, '\n', 1);
451 if (ln < 0)
452 return (FALSE);
453 noff += ln;
454 #ifdef USE_CRLF
455 ooff = filepos(nf);
456 #endif
457
458 if (lo != ln)
459 return (FALSE);
460 return (cmpbytes(olbf, nlbf, lo) >= lo);
461 }
462
463
464 LOCAL int
465 diff()
466 {
467 register long oln;
468 register long nln;
469 register long k;
470 register long l;
471 register long i;
472 register long j;
473 long mx;
474 BOOL b;
475 BOOL m;
476 int ret = 0;
477
478 ooff = noff = -1;
479 oln = 0, nln = 0;
480 while (oln < olc && nln < nlc) {
481 if (compare(oln, nln)) {
482 oln++;
483 nln++;
484 continue; /* Nothing changed */
485 }
486
487 ret = 1;
488 mx = (olc-oln)+(nlc-nln);
489 m = FALSE;
490 for (k = 1; k < mx; k++)
491 for (l = 0; l <= k; l++) {
492 if (oln+l >= olc)
493 break;
494 if (nln+k-l >= nlc) {
495 l = nln+k - nlc;
496 continue;
497 }
498 if (compare((long)(oln+l), (long)(nln+k-l))) {
499 for (j = 1, b = FALSE;
500 (j < nmatch) &&
501 (oln+l+j < olc) && (nln+k-l+j < nlc);
502 j++) {
503 if (!compare((long)(oln+l+j), (long)(nln+k-l+j))) {
504 b = TRUE;
505 break;
506 }
507 }
508 if (!b) {
509 if (l == 0)
510 showadd(oln, nln, k);
511 else if (k-l == 0)
512 showdel(oln, nln, l);
513 else if (l == k-l)
514 showchange(oln, nln, l);
515 else
516 showxchange(oln, nln, l, (long)(k-l));
517 oln += l;
518 nln += k-l;
519 m = TRUE;
520 goto out;
521 }
522 }
523 }
524 out:
525 if (!m)
526 break;
527 }
528 i = olc-oln;
529 j = nlc-nln;
530
531 if (i == 0 && j == 0)
532 return (ret);
533 else if (i == j)
534 showchange(oln, nln, i);
535 else if (j && i)
536 showxchange(oln, nln, i, j);
537 else if (i)
538 showdel(oln, nln, i);
539 else if (j)
540 showadd(oln, nln, j);
541 ret = 1;
542 return (ret);
543 }
544
545
546 LOCAL void
547 showdel(o, n, c)
548 long o;
549 long n;
550 long c;
551 {
552 long i;
553 line *lp;
554 ssize_t lo;
555
556 o += clc;
557 n += clc;
558 if (posix) {
559 if (c == 1)
560 printf("%ldd%ld\n", o+1, n);
561 else
562 printf("%ld,%ldd%ld\n", o+1, o+c, n);
563 } else if (c == 1)
564 gtprintf("\n-------- 1 line deleted at %ld:\n", o);
565 else
566 gtprintf("\n-------- %ld lines deleted at %ld:\n", c, o);
567 o -= clc;
568 n -= clc;
569
570 for (i = 0; i < c; i++) {
571 lp = glinep((long)(o+i), oldfile);
572 if (ooff != lp->off) {
573 xfileseek(0, lp->off);
574 ooff = lp->off;
575 }
576 lo = xgetdelim(&olbf, &olsz, '\n', 0);
577 ooff += lo;
578 #ifdef USE_CRLF
579 ooff = filepos(of);
580 #endif
581 if (posix)
582 printf("< ");
583 filewrite(stdout, olbf, lo);
584 if (olbf[lo-1] != '\n')
585 putchar('\n');
586 }
587 }
588
589
590 LOCAL void
591 showadd(o, n, c)
592 long o;
593 long n;
594 long c;
595 {
596 long i;
597 line *lp;
598 ssize_t ln;
599
600 o += clc;
601 n += clc;
602 if (posix) {
603 if (c == 1)
604 printf("%lda%ld\n", o, n+1);
605 else
606 printf("%lda%ld,%ld\n", o, n+1, n+c);
607 } else if (c == 1)
608 gtprintf("\n-------- 1 line added at %ld:\n", o);
609 else
610 gtprintf("\n-------- %ld lines added at %ld:\n", c, o);
611 o -= clc;
612 n -= clc;
613
614 for (i = 0; i < c; i++) {
615 lp = glinep((long)(n+i), newfile);
616 if (noff != lp->off) {
617 xfileseek(1, lp->off);
618 noff = lp->off;
619 }
620 ln = xgetdelim(&nlbf, &nlsz, '\n', 1);
621 noff += ln;
622 #ifdef USE_CRLF
623 noff = filepos(nf);
624 #endif
625 if (posix)
626 printf("> ");
627 filewrite(stdout, nlbf, ln);
628 if (nlbf[ln-1] != '\n')
629 putchar('\n');
630 }
631 }
632
633
634 LOCAL void
635 showchange(o, n, c)
636 long o;
637 long n;
638 long c;
639 {
640 long i;
641 line *lp;
642 ssize_t lo;
643 ssize_t ln;
644
645 o += clc;
646 n += clc;
647 if (posix) {
648 if (c == 1)
649 printf("%ldc%ld\n", o+1, n+1);
650 else
651 printf("%ld,%ldc%ld,%ld\n", o+1, o+c, n+1, n+c);
652 } else if (c == 1)
653 gtprintf("\n-------- 1 line changed at %ld from:\n", o);
654 else
655 gtprintf("\n-------- %ld lines changed at %ld-%ld from:\n",
656 c, o, o+c-1);
657 o -= clc;
658 n -= clc;
659
660 for (i = 0; i < c; i++) {
661 lp = glinep((long)(o+i), oldfile);
662 if (ooff != lp->off) {
663 xfileseek(0, lp->off);
664 ooff = lp->off;
665 }
666 lo = xgetdelim(&olbf, &olsz, '\n', 0);
667 ooff += lo;
668 #ifdef USE_CRLF
669 ooff = filepos(of);
670 #endif
671 if (posix)
672 printf("< ");
673 filewrite(stdout, olbf, lo);
674 if (olbf[lo-1] != '\n')
675 putchar('\n');
676 }
677 if (posix)
678 printf("---\n");
679 else
680 gtprintf("-------- to:\n");
681 for (i = 0; i < c; i++) {
682 lp = glinep((long)(n+i), newfile);
683 if (noff != lp->off) {
684 xfileseek(1, lp->off);
685 noff = lp->off;
686 }
687 ln = xgetdelim(&nlbf, &nlsz, '\n', 1);
688 noff += ln;
689 #ifdef USE_CRLF
690 noff = filepos(nf);
691 #endif
692 if (posix)
693 printf("> ");
694 filewrite(stdout, nlbf, ln);
695 if (nlbf[ln-1] != '\n')
696 putchar('\n');
697 }
698 }
699
700
701 LOCAL void
702 showxchange(o, n, oc, nc)
703 long o;
704 long n;
705 long oc;
706 long nc;
707 {
708 long i;
709 line *lp;
710 ssize_t lo;
711 ssize_t ln;
712
713 o += clc;
714 n += clc;
715 if (posix) {
716 if (oc == 1)
717 printf("%ldc%ld,%ld\n", o+1, n+1, n+nc);
718 else if (nc == 1)
719 printf("%ld,%ldc%ld\n", o+1, o+oc, n+1);
720 else
721 printf("%ld,%ldc%ld,%ld\n", o+1, o+oc, n+1, n+nc);
722 } else if (oc == 1)
723 gtprintf("\n-------- 1 line changed to %ld lines at %ld from:\n",
724 nc, o);
725 else if (nc == 1)
726 gtprintf("\n-------- %ld lines changed to 1 line at %ld-%ld from:\n",
727 oc, o, o+oc-1);
728 else
729 gtprintf("\n-------- %ld lines changed to %ld lines at %ld-%ld from:\n",
730 oc, nc, o, o+oc-1);
731 o -= clc;
732 n -= clc;
733
734 for (i = 0; i < oc; i++) {
735 lp = glinep((long)(o+i), oldfile);
736 if (ooff != lp->off) {
737 xfileseek(0, lp->off);
738 ooff = lp->off;
739 }
740 lo = xgetdelim(&olbf, &olsz, '\n', 0);
741 ooff += lo;
742 #ifdef USE_CRLF
743 ooff = filepos(of);
744 #endif
745 if (posix)
746 printf("< ");
747 filewrite(stdout, olbf, lo);
748 if (olbf[lo-1] != '\n')
749 putchar('\n');
750 }
751 if (posix)
752 printf("---\n");
753 else
754 gtprintf("-------- to:\n");
755 for (i = 0; i < nc; i++) {
756 lp = glinep((long)(n+i), newfile);
757 if (noff != lp->off) {
758 xfileseek(1, lp->off);
759 noff = lp->off;
760 }
761 ln = xgetdelim(&nlbf, &nlsz, '\n', 1);
762 noff += ln;
763 #ifdef USE_CRLF
764 noff = filepos(nf);
765 #endif
766 if (posix)
767 printf("> ");
768 filewrite(stdout, nlbf, ln);
769 if (nlbf[ln-1] != '\n')
770 putchar('\n');
771 }
772 }
773
774 #include <schily/mman.h>
775 #include <schily/errno.h>
776
777 LOCAL FILE *xf[2];
778 LOCAL off_t mmsize[2];
779 LOCAL char *mmbase[2];
780 LOCAL char *mmend[2];
781 LOCAL char *mmnext[2];
782
783 LOCAL FILE *
784 xfileopen(idx, name, mode)
785 int idx;
786 char *name;
787 char *mode;
788 {
789 FILE *f;
790 struct stat sb;
791
792 f = fileopen(name, mode);
793 if (f == NULL)
794 return (NULL);
795 xf[idx] = f;
796
797 #ifdef HAVE_MMAP
798 if (fstat(fileno(f), &sb) < 0) {
799 fclose(f);
800 return (NULL);
801 }
802 mmsize[idx] = sb.st_size;
803 if (sb.st_size > (64*1024*1024))
804 return (f);
805
806 if (!S_ISREG(sb.st_mode)) /* FIFO has st_size == 0 */
807 return (f); /* so cannot use mmap() */
808
809 if (sb.st_size == 0)
810 mmbase[idx] = "";
811 else
812 mmbase[idx] = mmap((void *)0, mmsize[idx],
813 PROT_READ, MAP_PRIVATE, fileno(f), (off_t)0);
814
815 if (mmbase[idx] == MAP_FAILED) {
816 /*
817 * Silently fall back to the read method.
818 */
819 mmbase[idx] = NULL;
820 } else if (mmbase[idx]) {
821 mmnext[idx] = mmbase[idx];
822 mmend[idx] = mmbase[idx] + mmsize[idx];
823 }
824 #endif
825 return (f);
826 }
827
828 LOCAL int
829 xfileseek(idx, off)
830 int idx;
831 off_t off;
832 {
833 #ifndef HAVE_MMAP
834 return (fileseek(xf[idx], off));
835 #else
836 if (mmbase[idx] == NULL)
837 return (fileseek(xf[idx], off));
838
839 mmnext[idx] = mmbase[idx] + off;
840
841 if (mmnext[idx] > (mmbase[idx] + mmsize[idx])) {
842 mmnext[idx] = (mmbase[idx] + mmsize[idx]);
843 seterrno(EINVAL);
844 return (-1);
845 }
846 if (mmnext[idx] < mmbase[idx]) {
847 mmnext[idx] = mmbase[idx];
848 seterrno(EINVAL);
849 return (-1);
850 }
851 return (0);
852 #endif
853 }
854
855 LOCAL off_t
856 xfilepos(idx)
857 int idx;
858 {
859 #ifndef HAVE_MMAP
860 return (filepos(xf[idx]));
861 #else
862 if (mmbase[idx] == NULL)
863 return (filepos(xf[idx]));
864
865 return (mmnext[idx] - mmbase[idx]);
866 #endif
867 }
868
869 LOCAL ssize_t
870 xgetdelim(lineptr, n, delim, idx)
871 char **lineptr;
872 size_t *n;
873 int delim;
874 int idx;
875 {
876 #ifndef HAVE_MMAP
877 return (getdelim(lineptr, n, delim, xf[idx]));
878 #else
879 char *p;
880 ssize_t siz;
881 ssize_t amt;
882
883 if (mmbase[idx] == NULL)
884 return (getdelim(lineptr, n, delim, xf[idx]));
885
886 *lineptr = mmnext[idx];
887 siz = mmend[idx] - mmnext[idx];
888 if (siz == 0) {
889 *lineptr = "";
890 return (-1);
891 }
892 p = findbytes(mmnext[idx], siz, delim);
893 if (p == NULL) {
894 amt = siz;
895 mmnext[idx] = mmend[idx];
896 } else {
897 amt = ++p - mmnext[idx];
898 mmnext[idx] = p;
899 }
900 if (amt == 0) {
901 *lineptr = "";
902 return (-1);
903 }
904 return (amt);
905 #endif
906 }
907
908 LOCAL ssize_t
909 xfileread(idx, lineptr, n)
910 int idx;
911 char **lineptr;
912 size_t n;
913 {
914 #ifndef HAVE_MMAP
915 return (fileread(xf[idx], *lineptr, n));
916 #else
917 ssize_t siz;
918 ssize_t amt;
919
920 if (mmbase[idx] == NULL)
921 return (fileread(xf[idx], *lineptr, n));
922
923 *lineptr = mmnext[idx];
924 siz = mmend[idx] - mmnext[idx];
925 amt = n;
926 if (amt > siz) {
927 amt = siz;
928 mmnext[idx] = mmend[idx];
929 } else {
930 mmnext[idx] += amt;
931 }
932 if (amt == 0)
933 *lineptr = "";
934 return (amt);
935 #endif
936 }