"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 }