From 47d455dd55be855e4cc691c32f687f723d9247ee Mon Sep 17 00:00:00 2001 From: toma Date: Wed, 25 Nov 2009 17:56:58 +0000 Subject: Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features. BUG:215923 git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdegraphics@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da --- kfaxview/libkfaximage/faxexpand.cpp | 745 ++++++++++++++++++++++++++++++++++++ 1 file changed, 745 insertions(+) create mode 100644 kfaxview/libkfaximage/faxexpand.cpp (limited to 'kfaxview/libkfaximage/faxexpand.cpp') diff --git a/kfaxview/libkfaximage/faxexpand.cpp b/kfaxview/libkfaximage/faxexpand.cpp new file mode 100644 index 00000000..9c4b4082 --- /dev/null +++ b/kfaxview/libkfaximage/faxexpand.cpp @@ -0,0 +1,745 @@ +/* Expand one page of fax data + Copyright (C) 1990, 1995 Frank D. Cringle. + +This file is part of viewfax - g3/g4 fax processing software. + +viewfax is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 2 of the License, or (at your +option) any later version. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#include + +#include +#include +#include + +#include + +#include "faxexpand.h" + +//Uncomment this for verbose debug output +//#define DEBUG_FAX +#define verbose false + +pagenode::pagenode() +{ +} + +/* Note that NeedBits() only works for n <= 16 */ +#define NeedBits(n) do { \ + if (BitsAvail < (n)) { \ + BitAcc |= *sp++ << BitsAvail; \ + BitsAvail += 16; \ + } \ +} while (0) +#define GetBits(n) (BitAcc & ((1<<(n))-1)) +#define ClrBits(n) do { \ + BitAcc >>= (n); \ + BitsAvail -= (n); \ +} while (0) + +#ifdef DEBUG_FAX +#define DEBUG_SHOW putchar(BitAcc & (1 << t) ? '1' : '0') +#define LOOKUP(wid,tab) do { \ + int t; \ + NeedBits(wid); \ + TabEnt = tab + GetBits(wid); \ + printf("%08lX/%d: %s%5d\t", BitAcc, BitsAvail, \ + StateNames[TabEnt->State], TabEnt->Param); \ + for (t = 0; t < TabEnt->Width; t++) \ + DEBUG_SHOW; \ + putchar('\n'); \ + fflush(stdout); \ + ClrBits(TabEnt->Width); \ +} while (0) + +#define SETVAL(x) do { \ + *pa++ = RunLength + (x); \ + printf("SETVAL: %d\t%d\n", RunLength + (x), a0); \ + a0 += x; \ + RunLength = 0; \ +} while (0) + +const char *StateNames[] = { + "Null ", + "Pass ", + "Horiz ", + "V0 ", + "VR ", + "VL ", + "Ext ", + "TermW ", + "TermB ", + "MakeUpW", + "MakeUpB", + "MakeUp ", + "EOL ", +}; + +#else +#define LOOKUP(wid,tab) do { \ + NeedBits(wid); \ + TabEnt = tab + GetBits(wid); \ + ClrBits(TabEnt->Width); \ +} while (0) + +#define SETVAL(x) do { \ + *pa++ = RunLength + (x); \ + a0 += x; \ + RunLength = 0; \ +} while (0) +#endif + +#define dumpruns(runs) do { \ + printf("-------------------- %d\n", LineNum); \ + for (pa = runs, a0 = 0; a0 < lastx; a0 += *pa++) \ + printf("%4d %d\n", a0, *pa); \ +} while (0) + +#define EndOfData(pn) (sp >= pn->data + pn->length/sizeof(*pn->data)) + +/* This macro handles coding errors in G3 data. + We redefine it below for the G4 case */ +#define SKIP_EOL do { \ + while (!EndOfData(pn)) { \ + NeedBits(11); \ + if (GetBits(11) == 0) \ + break; \ + ClrBits(1); \ + } \ + ClrBits(11); \ + goto EOL; \ +} while (0) +#define eol2lab EOL2: + +/* the line expanders are written as macros so that they can be reused + (twice each) but still have direct access to the local variables of + the "calling" function */ +#define expand1d() do { \ + while (a0 < lastx) { \ + int done = 0; \ + while (!done) { /* white first */ \ + LOOKUP(12, WhiteTable); \ + switch (TabEnt->State) { \ + case S_EOL: \ + EOLcnt = 1; \ + goto EOL; \ + case S_TermW: \ + SETVAL(TabEnt->Param); \ + done = 1; \ + break; \ + case S_MakeUpW: \ + case S_MakeUp: \ + a0 += TabEnt->Param; \ + RunLength += TabEnt->Param; \ + break; \ + case S_Ext: \ + unexpected("Extension code", LineNum); \ + SKIP_EOL; \ + break; \ + default: \ + unexpected("WhiteTable", LineNum); \ + SKIP_EOL; \ + break; \ + } \ + } \ + done = a0 >= lastx; \ + while (!done) { /* then black */ \ + LOOKUP(13, BlackTable); \ + switch (TabEnt->State) { \ + case S_EOL: \ + EOLcnt = 1; \ + goto EOL; \ + case S_TermB: \ + SETVAL(TabEnt->Param); \ + done = 1; \ + break; \ + case S_MakeUpB: \ + case S_MakeUp: \ + a0 += TabEnt->Param; \ + RunLength += TabEnt->Param; \ + break; \ + case S_Ext: \ + unexpected("Extension code", LineNum); \ + SKIP_EOL; \ + break; \ + default: \ + unexpected("BlackTable", LineNum); \ + SKIP_EOL; \ + break; \ + } \ + } \ + } \ + EOL: ; \ +} while (0) + +#define CHECK_b1 do { \ + if (pa != thisrun) while (b1 <= a0 && b1 < lastx) { \ + b1 += pb[0] + pb[1]; \ + pb += 2; \ + } \ +} while (0) + +#define expand2d(eolab) do { \ + while (a0 < lastx) { \ + LOOKUP(7, MainTable); \ + switch (TabEnt->State) { \ + case S_Pass: \ + CHECK_b1; \ + b1 += *pb++; \ + RunLength += b1 - a0; \ + a0 = b1; \ + b1 += *pb++; \ + break; \ + case S_Horiz: \ + if ((pa-run0)&1) { \ + int done = 0; \ + while (!done) { /* black first */ \ + LOOKUP(13, BlackTable); \ + switch (TabEnt->State) { \ + case S_TermB: \ + SETVAL(TabEnt->Param); \ + done = 1; \ + break; \ + case S_MakeUpB: \ + case S_MakeUp: \ + a0 += TabEnt->Param; \ + RunLength += TabEnt->Param; \ + break; \ + default: \ + unexpected("BlackTable", LineNum); \ + SKIP_EOL; \ + break; \ + } \ + } \ + done = 0; \ + while (!done) { /* then white */ \ + LOOKUP(12, WhiteTable); \ + switch (TabEnt->State) { \ + case S_TermW: \ + SETVAL(TabEnt->Param); \ + done = 1; \ + break; \ + case S_MakeUpW: \ + case S_MakeUp: \ + a0 += TabEnt->Param; \ + RunLength += TabEnt->Param; \ + break; \ + default: \ + unexpected("WhiteTable", LineNum); \ + SKIP_EOL; \ + break; \ + } \ + } \ + } \ + else { \ + int done = 0; \ + while (!done) { /* white first */ \ + LOOKUP(12, WhiteTable); \ + switch (TabEnt->State) { \ + case S_TermW: \ + SETVAL(TabEnt->Param); \ + done = 1; \ + break; \ + case S_MakeUpW: \ + case S_MakeUp: \ + a0 += TabEnt->Param; \ + RunLength += TabEnt->Param; \ + break; \ + default: \ + unexpected("WhiteTable", LineNum); \ + SKIP_EOL; \ + break; \ + } \ + } \ + done = 0; \ + while (!done) { /* then black */ \ + LOOKUP(13, BlackTable); \ + switch (TabEnt->State) { \ + case S_TermB: \ + SETVAL(TabEnt->Param); \ + done = 1; \ + break; \ + case S_MakeUpB: \ + case S_MakeUp: \ + a0 += TabEnt->Param; \ + RunLength += TabEnt->Param; \ + break; \ + default: \ + unexpected("BlackTable", LineNum); \ + SKIP_EOL; \ + break; \ + } \ + } \ + } \ + CHECK_b1; \ + break; \ + case S_V0: \ + CHECK_b1; \ + SETVAL(b1 - a0); \ + b1 += *pb++; \ + break; \ + case S_VR: \ + CHECK_b1; \ + SETVAL(b1 - a0 + TabEnt->Param); \ + b1 += *pb++; \ + break; \ + case S_VL: \ + CHECK_b1; \ + SETVAL(b1 - a0 - TabEnt->Param); \ + b1 -= *--pb; \ + break; \ + case S_Ext: \ + *pa++ = lastx - a0; \ + if (verbose) \ + kdDebug() << "Line " << LineNum << ": extension code\n";\ + SKIP_EOL; \ + break; \ + case S_EOL: \ + *pa++ = lastx - a0; \ + NeedBits(4); \ + if (GetBits(4) && verbose) /* already seen 7 zeros */ \ + kdDebug() << "Line " << LineNum << ": Bad EOL\n"; \ + ClrBits(4); \ + EOLcnt = 1; \ + goto eolab; \ + break; \ + default: \ + unexpected("MainTable", LineNum); \ + SKIP_EOL; \ + break; \ + } \ + } \ + if (RunLength) { \ + if (RunLength + a0 < lastx) { \ + /* expect a final V0 */ \ + NeedBits(1); \ + if (!GetBits(1)) { \ + unexpected("MainTable", LineNum); \ + SKIP_EOL; \ + } \ + ClrBits(1); \ + } \ + SETVAL(0); \ + } \ + eol2lab ; \ +} while (0) + +static void +unexpected(const char *what, int LineNum) +{ + if (verbose) + kdError() << "Line " << LineNum << ": Unexpected state in " + << what << endl; +} + +/* Expand tiff modified huffman data (g3-1d without EOLs) */ +void +MHexpand(struct pagenode *pn, drawfunc df) +{ + int a0; /* reference element */ + int lastx; /* copy line width to register */ + t32bits BitAcc; /* bit accumulator */ + int BitsAvail; /* # valid bits in BitAcc */ + int RunLength; /* Length of current run */ + t16bits *sp; /* pointer into compressed data */ + pixnum *pa; /* pointer into new line */ + int EOLcnt; /* number of consecutive EOLs */ + int LineNum; /* line number */ + pixnum *runs; /* list of run lengths */ + struct tabent *TabEnt; + + sp = pn->data; + BitAcc = 0; + BitsAvail = 0; + lastx = pn->size.width(); + runs = (pixnum *) malloc(lastx * sizeof(pixnum)); + for (LineNum = 0; LineNum < pn->rowsperstrip; ) { +#ifdef DEBUG_FAX + printf("\nBitAcc=%08lX, BitsAvail = %d\n", BitAcc, BitsAvail); + printf("-------------------- %d\n", LineNum); + fflush(stdout); +#endif + RunLength = 0; + pa = runs; + a0 = 0; + EOLcnt = 0; + if (BitsAvail & 7) /* skip to byte boundary */ + ClrBits(BitsAvail & 7); + expand1d(); + if (RunLength) + SETVAL(0); + if (a0 != lastx) { + if (verbose) + kdWarning() << "Line " << LineNum << ": length is " + << a0 << " (expected "<< lastx << ")\n"; + while (a0 > lastx) + a0 -= *--pa; + if (a0 < lastx) { + if ((pa - runs) & 1) + SETVAL(0); + SETVAL(lastx - a0); + } + } + (*df)(runs, LineNum++, pn); + } + free(runs); +} + +/* Expand group-3 1-dimensional data */ +void +g31expand(struct pagenode *pn, drawfunc df) +{ + int a0; /* reference element */ + int lastx; /* copy line width to register */ + t32bits BitAcc; /* bit accumulator */ + int BitsAvail; /* # valid bits in BitAcc */ + int RunLength; /* Length of current run */ + t16bits *sp; /* pointer into compressed data */ + pixnum *pa; /* pointer into new line */ + int EOLcnt; /* number of consecutive EOLs */ + int LineNum; /* line number */ + pixnum *runs; /* list of run lengths */ + struct tabent *TabEnt; + + sp = pn->data; + BitAcc = 0; + BitsAvail = 0; + lastx = pn->size.width(); + runs = (pixnum *) malloc(lastx * sizeof(pixnum)); + EOLcnt = 0; + for (LineNum = 0; LineNum < pn->rowsperstrip; ) { +#ifdef DEBUG_FAX + printf("\nBitAcc=%08lX, BitsAvail = %d\n", BitAcc, BitsAvail); + printf("-------------------- %d\n", LineNum); + fflush(stdout); +#endif + if (EOLcnt == 0) + while (!EndOfData(pn)) { + /* skip over garbage after a coding error */ + NeedBits(11); + if (GetBits(11) == 0) + break; + ClrBits(1); + } + for (EOLcnt = 1; !EndOfData(pn); EOLcnt++) { + /* we have seen 11 zeros, which implies EOL, + skip possible fill bits too */ + while (1) { + NeedBits(8); + if (GetBits(8)) + break; + ClrBits(8); + } + while (GetBits(1) == 0) + ClrBits(1); + ClrBits(1); /* the eol flag */ + NeedBits(11); + if (GetBits(11)) + break; + ClrBits(11); + } + if (EOLcnt > 1 && EOLcnt != 6 && verbose) + kdError() << "Line " << LineNum << ": bad RTC (" << EOLcnt << " EOLs)\n"; + if (EOLcnt >= 6 || EndOfData(pn)) { + free(runs); + return; + } + RunLength = 0; + pa = runs; + a0 = 0; + EOLcnt = 0; + expand1d(); + if (RunLength) + SETVAL(0); + if (a0 != lastx) { + if (verbose) + kdWarning() << "Line " << LineNum << ": length is " + << a0 << " (expected "<< lastx << ")\n"; + while (a0 > lastx) + a0 -= *--pa; + if (a0 < lastx) { + if ((pa - runs) & 1) + SETVAL(0); + SETVAL(lastx - a0); + } + } + (*df)(runs, LineNum++, pn); + } + free(runs); +} + +/* Expand group-3 2-dimensional data */ +void +g32expand(struct pagenode *pn, drawfunc df) +{ + int RunLength; /* Length of current run */ + int a0; /* reference element */ + int b1; /* next change on previous line */ + int lastx = pn->size.width();/* copy line width to register */ + pixnum *run0, *run1; /* run length arrays */ + pixnum *thisrun, *pa, *pb; /* pointers into runs */ + t16bits *sp; /* pointer into compressed data */ + t32bits BitAcc; /* bit accumulator */ + int BitsAvail; /* # valid bits in BitAcc */ + int EOLcnt; /* number of consecutive EOLs */ + int refline = 0; /* 1D encoded reference line */ + int LineNum; /* line number */ + struct tabent *TabEnt; + + sp = pn->data; + BitAcc = 0; + BitsAvail = 0; + /* allocate space for 2 runlength arrays */ + run0 = (pixnum *) malloc(2 * ((lastx+5)&~1) * sizeof(pixnum)); + run1 = run0 + ((lastx+5)&~1); + run1[0] = lastx; + run1[1] = 0; + EOLcnt = 0; + for (LineNum = 0; LineNum < pn->rowsperstrip; ) { +#ifdef DEBUG_FAX + printf("\nBitAcc=%08lX, BitsAvail = %d\n", BitAcc, BitsAvail); + printf("-------------------- %d\n", LineNum); + fflush(stdout); +#endif + if (EOLcnt == 0) + while (!EndOfData(pn)) { + /* skip over garbage after a coding error */ + NeedBits(11); + if (GetBits(11) == 0) + break; + ClrBits(1); + } + for (EOLcnt = 1; !EndOfData(pn); EOLcnt++) { + /* we have seen 11 zeros, which implies EOL, + skip possible fill bits too */ + while (1) { + NeedBits(8); + if (GetBits(8)) + break; + ClrBits(8); + } + while (GetBits(1) == 0) + ClrBits(1); + ClrBits(1); /* the eol flag */ + NeedBits(12); + refline = GetBits(1); /* 1D / 2D flag */ + ClrBits(1); + if (GetBits(11)) + break; + ClrBits(11); + } + if (EOLcnt > 1 && EOLcnt != 6 && verbose) + kdError() << "Line " << LineNum << ": bad RTC (" << EOLcnt << " EOLs)\n"; + if (EOLcnt >= 6 || EndOfData(pn)) { + free(run0); + return; + } + if (LineNum == 0 && refline == 0 && verbose) + kdDebug() << "First line is 2-D encoded\n"; + RunLength = 0; + if (LineNum & 1) { + pa = run1; + pb = run0; + } + else { + pa = run0; + pb = run1; + } + thisrun = pa; + EOLcnt = 0; + a0 = 0; + b1 = *pb++; + + if (refline) { + expand1d(); + } + else { + expand2d(EOL2); + } + if (RunLength) + SETVAL(0); + if (a0 != lastx) { + if (verbose) + kdWarning() << "Line " << LineNum << ": length is " + << a0 << " (expected "<< lastx << ")\n"; + while (a0 > lastx) + a0 -= *--pa; + if (a0 < lastx) { + if ((pa - run0) & 1) + SETVAL(0); + SETVAL(lastx - a0); + } + } + SETVAL(0); /* imaginary change at end of line for reference */ + (*df)(thisrun, LineNum++, pn); + } + free(run0); +} + +/* Redefine the "skip to eol" macro. We cannot recover from coding + errors in G4 data */ +#undef SKIP_EOL +#undef eol2lab +#define SKIP_EOL do { \ + if (verbose) \ + kdError() << "Line " << LineNum << ": G4 coding error\n"; \ + free(run0); \ + return; \ +} while (0) +#define eol2lab + +/* Expand group-4 data */ +void +g4expand(struct pagenode *pn, drawfunc df) +{ + int RunLength; /* Length of current run */ + int a0; /* reference element */ + int b1; /* next change on previous line */ + int lastx = pn->size.width();/* copy line width to register */ + pixnum *run0, *run1; /* run length arrays */ + pixnum *thisrun, *pa, *pb; /* pointers into runs */ + t16bits *sp; /* pointer into compressed data */ + t32bits BitAcc; /* bit accumulator */ + int BitsAvail; /* # valid bits in BitAcc */ + int LineNum; /* line number */ + int EOLcnt; + struct tabent *TabEnt; + + sp = pn->data; + BitAcc = 0; + BitsAvail = 0; + /* allocate space for 2 runlength arrays */ + run0 = (pixnum *) malloc(2 * ((lastx+5)&~1) * sizeof(pixnum)); + run1 = run0 + ((lastx+5)&~1); + run1[0] = lastx; /* initial reference line */ + run1[1] = 0; + + for (LineNum = 0; LineNum < pn->rowsperstrip; ) { +#ifdef DEBUG_FAX + printf("\nBitAcc=%08lX, BitsAvail = %d\n", BitAcc, BitsAvail); + printf("-------------------- %d\n", LineNum); + fflush(stdout); +#endif + RunLength = 0; + if (LineNum & 1) { + pa = run1; + pb = run0; + } + else { + pa = run0; + pb = run1; + } + thisrun = pa; + a0 = 0; + b1 = *pb++; + expand2d(EOFB); + if (a0 < lastx) { + if ((pa - run0) & 1) + SETVAL(0); + SETVAL(lastx - a0); + } + SETVAL(0); /* imaginary change at end of line for reference */ + (*df)(thisrun, LineNum++, pn); + continue; + EOFB: + NeedBits(13); + if (GetBits(13) != 0x1001 && verbose) + kdError() << "Bad RTC\n"; + break; + } + free(run0); +} + +static const unsigned char zerotab[256] = { + 0x88, 0x07, 0x16, 0x06, 0x25, 0x05, 0x15, 0x05, + 0x34, 0x04, 0x14, 0x04, 0x24, 0x04, 0x14, 0x04, + 0x43, 0x03, 0x13, 0x03, 0x23, 0x03, 0x13, 0x03, + 0x33, 0x03, 0x13, 0x03, 0x23, 0x03, 0x13, 0x03, + 0x52, 0x02, 0x12, 0x02, 0x22, 0x02, 0x12, 0x02, + 0x32, 0x02, 0x12, 0x02, 0x22, 0x02, 0x12, 0x02, + 0x42, 0x02, 0x12, 0x02, 0x22, 0x02, 0x12, 0x02, + 0x32, 0x02, 0x12, 0x02, 0x22, 0x02, 0x12, 0x02, + 0x61, 0x01, 0x11, 0x01, 0x21, 0x01, 0x11, 0x01, + 0x31, 0x01, 0x11, 0x01, 0x21, 0x01, 0x11, 0x01, + 0x41, 0x01, 0x11, 0x01, 0x21, 0x01, 0x11, 0x01, + 0x31, 0x01, 0x11, 0x01, 0x21, 0x01, 0x11, 0x01, + 0x51, 0x01, 0x11, 0x01, 0x21, 0x01, 0x11, 0x01, + 0x31, 0x01, 0x11, 0x01, 0x21, 0x01, 0x11, 0x01, + 0x41, 0x01, 0x11, 0x01, 0x21, 0x01, 0x11, 0x01, + 0x31, 0x01, 0x11, 0x01, 0x21, 0x01, 0x11, 0x01, + 0x70, 0x00, 0x10, 0x00, 0x20, 0x00, 0x10, 0x00, + 0x30, 0x00, 0x10, 0x00, 0x20, 0x00, 0x10, 0x00, + 0x40, 0x00, 0x10, 0x00, 0x20, 0x00, 0x10, 0x00, + 0x30, 0x00, 0x10, 0x00, 0x20, 0x00, 0x10, 0x00, + 0x50, 0x00, 0x10, 0x00, 0x20, 0x00, 0x10, 0x00, + 0x30, 0x00, 0x10, 0x00, 0x20, 0x00, 0x10, 0x00, + 0x40, 0x00, 0x10, 0x00, 0x20, 0x00, 0x10, 0x00, + 0x30, 0x00, 0x10, 0x00, 0x20, 0x00, 0x10, 0x00, + 0x60, 0x00, 0x10, 0x00, 0x20, 0x00, 0x10, 0x00, + 0x30, 0x00, 0x10, 0x00, 0x20, 0x00, 0x10, 0x00, + 0x40, 0x00, 0x10, 0x00, 0x20, 0x00, 0x10, 0x00, + 0x30, 0x00, 0x10, 0x00, 0x20, 0x00, 0x10, 0x00, + 0x50, 0x00, 0x10, 0x00, 0x20, 0x00, 0x10, 0x00, + 0x30, 0x00, 0x10, 0x00, 0x20, 0x00, 0x10, 0x00, + 0x40, 0x00, 0x10, 0x00, 0x20, 0x00, 0x10, 0x00, + 0x30, 0x00, 0x10, 0x00, 0x20, 0x00, 0x10, 0x00 +}; + +#define check(v) do { \ + prezeros = zerotab[v]; \ + postzeros = prezeros & 15; \ + prezeros >>= 4; \ + if (prezeros == 8) { \ + zeros += 8; \ + continue; \ + } \ + if (zeros + prezeros < 11) { \ + empty = 0; \ + zeros = postzeros; \ + continue; \ + } \ + zeros = postzeros; \ + if (empty) \ + EOLcnt++; \ + lines++; \ + empty = 1; \ +} while (0) + +/* count fax lines */ +int +G3count(struct pagenode *pn, int twoD) +{ + t16bits *p = pn->data; + t16bits *end = p + pn->length/sizeof(*p); + int lines = 0; /* lines seen so far */ + int zeros = 0; /* number of consecutive zero bits seen */ + int EOLcnt = 0; /* number of consecutive EOLs seen */ + int empty = 1; /* empty line */ + int prezeros, postzeros; + + while (p < end && EOLcnt < 6) { + t16bits bits = *p++; + check(bits&255); + if (twoD && (prezeros + postzeros == 7)) { + if (postzeros || ((bits & 0x100) == 0)) + zeros--; + } + check(bits>>8); + if (twoD && (prezeros + postzeros == 7)) { + if (postzeros || ((p < end) && ((*p & 1) == 0))) + zeros--; + } + } + return lines - EOLcnt; /* don't count trailing EOLs */ +} -- cgit v1.2.3