"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. For more information about "udiff.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes reports: 2021-08-14_vs_2021-09-18 or 2021-07-29_vs_2021-09-18.

    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 }