/* Copyright (C) 1992, 1995, 1996, 1997, 1998, 1999 artofcode LLC. All rights reserved. This program 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., 59 Temple Place, Suite 330, Boston, MA, 02111-1307. */ /*$Id: scfd.c,v 1.3.4.1.2.1 2003/01/17 00:49:05 giles Exp $ */ /* CCITTFax decoding filter */ #include "stdio_.h" /* includes std.h */ #include "memory_.h" #include "gdebug.h" #include "strimpl.h" #include "scf.h" #include "scfx.h" /* ------ CCITTFaxDecode ------ */ private_st_CFD_state(); /* Set default parameter values. */ private void s_CFD_set_defaults(register stream_state * st) { stream_CFD_state *const ss = (stream_CFD_state *) st; s_CFD_set_defaults_inline(ss); } /* Initialize CCITTFaxDecode filter */ private int s_CFD_init(stream_state * st) { stream_CFD_state *const ss = (stream_CFD_state *) st; int raster = ss->raster = ROUND_UP((ss->Columns + 7) >> 3, ss->DecodedByteAlign); byte white = (ss->BlackIs1 ? 0 : 0xff); s_hcd_init_inline(ss); /* Because skip_white_pixels can look as many as 4 bytes ahead, */ /* we need to allow 4 extra bytes at the end of the row buffers. */ ss->lbuf = gs_alloc_bytes(st->memory, raster + 4, "CFD lbuf"); ss->lprev = 0; if (ss->lbuf == 0) return ERRC; /****** WRONG ******/ if (ss->K != 0) { ss->lprev = gs_alloc_bytes(st->memory, raster + 4, "CFD lprev"); if (ss->lprev == 0) return ERRC; /****** WRONG ******/ /* Clear the initial reference line for 2-D encoding. */ memset(ss->lbuf, white, raster); /* Ensure that the scan of the reference line will stop. */ ss->lbuf[raster] = 0xa0; } ss->k_left = min(ss->K, 0); ss->run_color = 0; ss->damaged_rows = 0; ss->skipping_damage = false; ss->cbit = 0; ss->uncomp_run = 0; ss->rows_left = (ss->Rows <= 0 || ss->EndOfBlock ? -1 : ss->Rows + 1); ss->rpos = ss->wpos = raster - 1; ss->eol_count = 0; ss->invert = white; ss->min_left = 1; return 0; } /* Release the filter. */ private void s_CFD_release(stream_state * st) { stream_CFD_state *const ss = (stream_CFD_state *) st; gs_free_object(st->memory, ss->lprev, "CFD lprev(close)"); gs_free_object(st->memory, ss->lbuf, "CFD lbuf(close)"); } /* Declare the variables that hold the state. */ #define cfd_declare_state\ hcd_declare_state;\ register byte *q;\ int qbit /* Load the state from the stream. */ #define cfd_load_state()\ hcd_load_state(),\ q = ss->lbuf + ss->wpos, qbit = ss->cbit /* Store the state back in the stream. */ #define cfd_store_state()\ hcd_store_state(),\ ss->wpos = q - ss->lbuf, ss->cbit = qbit /* Macros to get blocks of bits from the input stream. */ /* Invariants: 0 <= bits_left <= bits_size; */ /* bits [bits_left-1..0] contain valid data. */ #define avail_bits(n) hcd_bits_available(n) #define ensure_bits(n, outl) hcd_ensure_bits(n, outl) #define peek_bits(n) hcd_peek_bits(n) #define peek_var_bits(n) hcd_peek_var_bits(n) #define skip_bits(n) hcd_skip_bits(n) /* Get a run from the stream. */ #ifdef DEBUG # define IF_DEBUG(expr) expr #else # define IF_DEBUG(expr) DO_NOTHING #endif #define get_run(decode, initial_bits, min_bits, runlen, str, locl, outl)\ BEGIN\ const cfd_node *np;\ int clen;\ \ HCD_ENSURE_BITS_ELSE(initial_bits) {\ /* We might still have enough bits for the specific code. */\ if (bits_left < min_bits) goto outl;\ np = &decode[hcd_peek_bits_left() << (initial_bits - bits_left)];\ if ((clen = np->code_length) > bits_left) goto outl;\ goto locl;\ }\ np = &decode[peek_bits(initial_bits)];\ if ((clen = np->code_length) > initial_bits) {\ IF_DEBUG(uint init_bits = peek_bits(initial_bits));\ if (!avail_bits(clen)) goto outl;\ clen -= initial_bits;\ skip_bits(initial_bits);\ ensure_bits(clen, outl); /* can't goto outl */\ np = &decode[np->run_length + peek_var_bits(clen)];\ if_debug4('W', "%s xcode=0x%x,%d rlen=%d\n", str,\ (init_bits << np->code_length) +\ peek_var_bits(np->code_length),\ initial_bits + np->code_length,\ np->run_length);\ skip_bits(np->code_length);\ } else {\ locl: if_debug4('W', "%s code=0x%x,%d rlen=%d\n", str,\ peek_var_bits(clen), clen, np->run_length);\ skip_bits(clen);\ }\ runlen = np->run_length;\ END /* Skip data bits for a white run. */ /* rlen is either less than 64, or a multiple of 64. */ #define skip_data(rlen, makeup_label)\ if ( (qbit -= rlen) < 0 )\ { q -= qbit >> 3, qbit &= 7;\ if ( rlen >= 64 ) goto makeup_label;\ } /* Invert data bits for a black run. */ /* If rlen >= 64, execute makeup_action: this is to handle */ /* makeup codes efficiently, since these are always a multiple of 64. */ #define invert_data(rlen, black_byte, makeup_action, d)\ if ( rlen > qbit )\ { *q++ ^= (1 << qbit) - 1;\ rlen -= qbit;\ switch ( rlen >> 3 )\ {\ case 7: /* original rlen possibly >= 64 */\ if ( rlen + qbit >= 64 ) goto d;\ *q++ = black_byte;\ case 6: *q++ = black_byte;\ case 5: *q++ = black_byte;\ case 4: *q++ = black_byte;\ case 3: *q++ = black_byte;\ case 2: *q++ = black_byte;\ case 1: *q = black_byte;\ rlen &= 7;\ if ( !rlen ) { qbit = 0; break; }\ q++;\ case 0: /* know rlen != 0 */\ qbit = 8 - rlen;\ *q ^= 0xff << qbit;\ break;\ default: /* original rlen >= 64 */\ d: memset(q, black_byte, rlen >> 3);\ q += rlen >> 3;\ rlen &= 7;\ if ( !rlen ) qbit = 0, q--;\ else qbit = 8 - rlen, *q ^= 0xff << qbit;\ makeup_action;\ }\ }\ else\ qbit -= rlen,\ *q ^= ((1 << rlen) - 1) << qbit /* Buffer refill for CCITTFaxDecode filter */ private int cf_decode_eol(P2(stream_CFD_state *, stream_cursor_read *)); private int cf_decode_1d(P2(stream_CFD_state *, stream_cursor_read *)); private int cf_decode_2d(P2(stream_CFD_state *, stream_cursor_read *)); private int cf_decode_uncompressed(P2(stream_CFD_state *, stream_cursor_read *)); private int s_CFD_process(stream_state * st, stream_cursor_read * pr, stream_cursor_write * pw, bool last) { stream_CFD_state *const ss = (stream_CFD_state *) st; int wstop = ss->raster - 1; int eol_count = ss->eol_count; int k_left = ss->k_left; int rows_left = ss->rows_left; int status = 0; #ifdef DEBUG const byte *rstart = pr->ptr; const byte *wstart = pw->ptr; #endif top: #ifdef DEBUG { hcd_declare_state; hcd_load_state(); if_debug8('w', "\ [w]CFD_process top: eol_count=%d, k_left=%d, rows_left=%d\n\ bits=0x%lx, bits_left=%d, read %u, wrote %u%s\n", eol_count, k_left, rows_left, (ulong) bits, bits_left, (uint) (p - rstart), (uint) (pw->ptr - wstart), (ss->skipping_damage ? ", skipping damage" : "")); } #endif if (ss->skipping_damage) { /* Skip until we reach an EOL. */ hcd_declare_state; int skip; status = 0; do { switch ((skip = cf_decode_eol(ss, pr))) { default: /* not EOL */ hcd_load_state(); skip_bits(-skip); hcd_store_state(); continue; case 0: /* need more input */ goto out; case 1: /* EOL */ { /* Back up over the EOL. */ hcd_load_state(); bits_left += run_eol_code_length; hcd_store_state(); } ss->skipping_damage = false; } } while (ss->skipping_damage); ss->damaged_rows++; } /* * Check for a completed input scan line. This isn't quite as * simple as it seems, because we could have run out of input data * between a makeup code and a 0-length termination code, or in a * 2-D line before a final horizontal code with a 0-length second * run. There's probably a way to think about this situation that * doesn't require a special check, but I haven't found it yet. */ if (ss->wpos == wstop && ss->cbit <= (-ss->Columns & 7) && (k_left == 0 ? !(ss->run_color & ~1) : ss->run_color == 0) ) { /* Check for completed data to be copied to the client. */ /* (We could avoid the extra copy step for 1-D, but */ /* it's simpler not to, and it doesn't cost much.) */ if (ss->rpos < ss->wpos) { stream_cursor_read cr; cr.ptr = ss->lbuf + ss->rpos; cr.limit = ss->lbuf + ss->wpos; status = stream_move(&cr, pw); ss->rpos = cr.ptr - ss->lbuf; if (status) goto out; } if (rows_left > 0 && --rows_left == 0) { status = EOFC; goto out; } if (ss->K != 0) { byte *prev_bits = ss->lprev; ss->lprev = ss->lbuf; ss->lbuf = prev_bits; if (ss->K > 0) k_left = (k_left == 0 ? ss->K : k_left) - 1; } ss->rpos = ss->wpos = -1; ss->eol_count = eol_count = 0; ss->cbit = 0; ss->invert = (ss->BlackIs1 ? 0 : 0xff); memset(ss->lbuf, ss->invert, wstop + 1); ss->run_color = 0; /* * If EndOfLine is true, we want to include the byte padding * in the string of initial zeros in the EOL. If EndOfLine * is false, we aren't sure what we should do.... */ if (ss->EncodedByteAlign & !ss->EndOfLine) ss->bits_left &= ~7; } /* If we're between scan lines, scan for EOLs. */ if (ss->wpos < 0) { while ((status = cf_decode_eol(ss, pr)) > 0) { if_debug0('w', "[w]EOL\n"); /* If we are in a Group 3 mixed regime, */ /* check the next bit for 1- vs. 2-D. */ if (ss->K > 0) { hcd_declare_state; hcd_load_state(); ensure_bits(1, out); /* can't fail */ k_left = (peek_bits(1) ? 0 : 1); skip_bits(1); hcd_store_state(); } ++eol_count; /* * According to Adobe, the decoder should always check for * the EOD sequence, regardless of EndOfBlock: the Red Book's * documentation of EndOfBlock is wrong. */ if (eol_count == (ss->K < 0 ? 2 : 6)) { status = EOFC; goto out; } } if (status == 0) /* input empty while scanning EOLs */ goto out; switch (eol_count) { case 0: if (ss->EndOfLine) { /* EOL is required, but none is present. */ status = ERRC; goto check; } case 1: break; default: status = ERRC; goto check; } } /* Now decode actual data. */ if (k_left < 0) { if_debug0('w', "[w2]new row\n"); status = cf_decode_2d(ss, pr); } else if (k_left == 0) { if_debug0('w', "[w1]new row\n"); status = cf_decode_1d(ss, pr); } else { if_debug1('w', "[w1]new 2-D row, %d left\n", k_left); status = cf_decode_2d(ss, pr); } if_debug3('w', "[w]CFD status = %d, wpos = %d, cbit = %d\n", status, ss->wpos, ss->cbit); check:switch (status) { case 1: /* output full */ goto top; case ERRC: /* Check for special handling of damaged rows. */ if (ss->damaged_rows >= ss->DamagedRowsBeforeError || !(ss->EndOfLine && ss->K >= 0) ) break; /* Substitute undamaged data if appropriate. */ /****** NOT IMPLEMENTED YET ******/ { ss->wpos = wstop; ss->cbit = -ss->Columns & 7; ss->run_color = 0; } ss->skipping_damage = true; goto top; default: ss->damaged_rows = 0; /* finished a good row */ } out:ss->k_left = k_left; ss->rows_left = rows_left; ss->eol_count = eol_count; return status; } /* * Decode a leading EOL, if any. * If an EOL is present, skip over it and return 1; * if no EOL is present, read no input and return -N, where N is the * number of initial bits that can be skipped in the search for an EOL; * if more input is needed, return 0. * Note that if we detected an EOL, we know that we can back up over it; * if we detected an N-bit non-EOL, we know that at least N bits of data * are available in the buffer. */ private int cf_decode_eol(stream_CFD_state * ss, stream_cursor_read * pr) { hcd_declare_state; int zeros; int look_ahead; hcd_load_state(); for (zeros = 0; zeros < run_eol_code_length - 1; zeros++) { ensure_bits(1, out); if (peek_bits(1)) return -(zeros + 1); skip_bits(1); } /* We definitely have an EOL. Skip further zero bits. */ look_ahead = (ss->K > 0 ? 2 : 1); for (;;) { ensure_bits(look_ahead, back); if (peek_bits(1)) break; skip_bits(1); } skip_bits(1); hcd_store_state(); return 1; back: /* * We ran out of data while skipping zeros. * We know we are at a byte boundary, and have just skipped * at least run_eol_code_length - 1 zeros. However, * bits_left may be 1 if look_ahead == 2. */ bits &= (1 << bits_left) - 1; bits_left += run_eol_code_length - 1; hcd_store_state(); out:return 0; } /* Decode a 1-D scan line. */ private int cf_decode_1d(stream_CFD_state * ss, stream_cursor_read * pr) { cfd_declare_state; byte black_byte = (ss->BlackIs1 ? 0xff : 0); int end_bit = -ss->Columns & 7; byte *stop = ss->lbuf - 1 + ss->raster; int run_color = ss->run_color; int status; int bcnt; cfd_load_state(); if_debug1('w', "[w1]entry run_color = %d\n", ss->run_color); if (ss->run_color > 0) goto db; else goto dw; #define q_at_stop() (q >= stop && (qbit <= end_bit || q > stop)) top:run_color = 0; if (q_at_stop()) goto done; dw: /* Decode a white run. */ get_run(cf_white_decode, cfd_white_initial_bits, cfd_white_min_bits, bcnt, "[w1]white", dwl, out0); if (bcnt < 0) { /* exceptional situation */ switch (bcnt) { case run_uncompressed: /* Uncompressed data. */ cfd_store_state(); bcnt = cf_decode_uncompressed(ss, pr); if (bcnt < 0) return bcnt; cfd_load_state(); if (bcnt) goto db; else goto dw; /*case run_error: */ /*case run_zeros: *//* Premature end-of-line. */ default: status = ERRC; goto out; } } skip_data(bcnt, dwx); if (q_at_stop()) { run_color = 0; /* not inside a run */ goto done; } run_color = 1; db: /* Decode a black run. */ get_run(cf_black_decode, cfd_black_initial_bits, cfd_black_min_bits, bcnt, "[w1]black", dbl, out1); if (bcnt < 0) { /* All exceptional codes are invalid here. */ /****** WRONG, uncompressed IS ALLOWED ******/ status = ERRC; goto out; } /* Invert bits designated by black run. */ invert_data(bcnt, black_byte, goto dbx, idb); goto top; dwx: /* If we run out of data after a makeup code, */ /* note that we are still processing a white run. */ run_color = -1; goto dw; dbx: /* If we run out of data after a makeup code, */ /* note that we are still processing a black run. */ run_color = 2; goto db; done:if (q > stop || qbit < end_bit) status = ERRC; else status = 1; out:cfd_store_state(); ss->run_color = run_color; if_debug1('w', "[w1]exit run_color = %d\n", run_color); return status; out0: /* We already set run_color to 0 or -1. */ status = 0; goto out; out1: /* We already set run_color to 1 or 2. */ status = 0; goto out; } /* Decode a 2-D scan line. */ private int cf_decode_2d(stream_CFD_state * ss, stream_cursor_read * pr) { cfd_declare_state; byte invert_white = (ss->BlackIs1 ? 0 : 0xff); byte black_byte = ~invert_white; byte invert = ss->invert; int end_count = -ss->Columns & 7; uint raster = ss->raster; byte *q0 = ss->lbuf; byte *prev_q01 = ss->lprev + 1; byte *endptr = q0 - 1 + raster; int init_count = raster << 3; register int count; int rlen; int status; cfd_load_state(); count = ((endptr - q) << 3) + qbit; endptr[1] = 0xa0; /* a byte with some 0s and some 1s, */ /* to ensure run scan will stop */ if_debug1('W', "[w2]raster=%d\n", raster); switch (ss->run_color) { case -2: ss->run_color = 0; goto hww; case -1: ss->run_color = 0; goto hbw; case 1: ss->run_color = 0; goto hwb; case 2: ss->run_color = 0; goto hbb; /*case 0: */ } top:if (count <= end_count) { status = (count < end_count ? ERRC : 1); goto out; } /* If invert == invert_white, white and black have their */ /* correct meanings; if invert == ~invert_white, */ /* black and white are interchanged. */ if_debug1('W', "[w2]%4d:\n", count); #ifdef DEBUG /* Check the invariant between q, qbit, and count. */ { int pcount = (endptr - q) * 8 + qbit; if (pcount != count) dlprintf2("[w2]Error: count=%d pcount=%d\n", count, pcount); } #endif /* * We could just use get_run here, but we can do better. However, * we must be careful to handle the case where the very last codes * in the input stream are 1-bit "vertical 0" codes: we can't just * use ensure_bits(3, ...) and go to get more data if it fails. */ ensure_bits(3, out3); #define vertical_0 (countof(cf2_run_vertical) / 2) switch (peek_bits(3)) { default /*4..7*/ : /* vertical(0) */ v0: skip_bits(1); rlen = vertical_0; break; case 2: /* vertical(+1) */ skip_bits(3); rlen = vertical_0 + 1; break; case 3: /* vertical(-1) */ skip_bits(3); rlen = vertical_0 - 1; break; case 1: /* horizontal */ skip_bits(3); if (invert == invert_white) goto hww; else goto hbb; case 0: /* everything else */ get_run(cf_2d_decode, cfd_2d_initial_bits, cfd_2d_min_bits, rlen, "[w2]", d2l, out0); /* rlen may be run2_pass, run_uncompressed, or */ /* 0..countof(cf2_run_vertical)-1. */ if (rlen < 0) switch (rlen) { case run2_pass: break; case run_uncompressed: { int which; cfd_store_state(); which = cf_decode_uncompressed(ss, pr); if (which < 0) { status = which; goto out; } cfd_load_state(); /****** ADJUST count ******/ invert = (which ? ~invert_white : invert_white); } goto top; default: /* run_error, run_zeros */ status = ERRC; goto out; } } /* Interpreting the run requires scanning the */ /* previous ('reference') line. */ { int prev_count = count; byte prev_data; int dlen; static const byte count_bit[8] = {0x80, 1, 2, 4, 8, 0x10, 0x20, 0x40}; byte *prev_q = prev_q01 + (q - q0); int plen; if (!(count & 7)) prev_q++; /* because of skip macros */ prev_data = prev_q[-1] ^ invert; /* Find the b1 transition. */ if ((prev_data & count_bit[prev_count & 7]) && (prev_count < init_count || invert != invert_white) ) { /* Look for changing white first. */ if_debug1('W', " data=0x%x", prev_data); skip_black_pixels(prev_data, prev_q, prev_count, invert, plen); if (prev_count < end_count) /* overshot */ prev_count = end_count; if_debug1('W', " b1 other=%d", prev_count); } if (prev_count != end_count) { if_debug1('W', " data=0x%x", prev_data); skip_white_pixels(prev_data, prev_q, prev_count, invert, plen); if (prev_count < end_count) /* overshot */ prev_count = end_count; if_debug1('W', " b1 same=%d", prev_count); } /* b1 = prev_count; */ if (rlen == run2_pass) { /* Pass mode. Find b2. */ if (prev_count != end_count) { if_debug1('W', " data=0x%x", prev_data); skip_black_pixels(prev_data, prev_q, prev_count, invert, plen); if (prev_count < end_count) /* overshot */ prev_count = end_count; } /* b2 = prev_count; */ if_debug2('W', " b2=%d, pass %d\n", prev_count, count - prev_count); } else { /* Vertical coding. */ /* Remember that count counts *down*. */ prev_count += rlen - vertical_0; /* a1 */ if_debug2('W', " vertical %d -> %d\n", rlen - vertical_0, prev_count); } /* Now either invert or skip from count */ /* to prev_count, and reset count. */ if (invert == invert_white) { /* Skip data bits. */ q = endptr - (prev_count >> 3); qbit = prev_count & 7; } else { /* Invert data bits. */ dlen = count - prev_count; invert_data(dlen, black_byte, DO_NOTHING, idd); } count = prev_count; if (rlen >= 0) /* vertical mode */ invert = ~invert; /* polarity changes */ } goto top; out3: if (bits_left > 0 && peek_bits(1)) { /* This is a 1-bit "vertical 0" code, which we can still process. */ goto v0; } /* falls through */ out0:status = 0; /* falls through */ out:cfd_store_state(); ss->invert = invert; return status; /* * We handle horizontal decoding here, so that we can * branch back into it if we run out of input data. */ /* White, then black. */ hww:get_run(cf_white_decode, cfd_white_initial_bits, cfd_white_min_bits, rlen, " white", wwl, outww); if ((count -= rlen) < end_count) { status = ERRC; goto out; } skip_data(rlen, hww); /* Handle the second half of a white-black horizontal code. */ hwb:get_run(cf_black_decode, cfd_black_initial_bits, cfd_black_min_bits, rlen, " black", wbl, outwb); if ((count -= rlen) < end_count) { status = ERRC; goto out; } invert_data(rlen, black_byte, goto hwb, ihwb); goto top; outww:ss->run_color = -2; goto out0; outwb:ss->run_color = 1; goto out0; /* Black, then white. */ hbb:get_run(cf_black_decode, cfd_black_initial_bits, cfd_black_min_bits, rlen, " black", bbl, outbb); if ((count -= rlen) < end_count) { status = ERRC; goto out; } invert_data(rlen, black_byte, goto hbb, ihbb); /* Handle the second half of a black-white horizontal code. */ hbw:get_run(cf_white_decode, cfd_white_initial_bits, cfd_white_min_bits, rlen, " white", bwl, outbw); if ((count -= rlen) < end_count) { status = ERRC; goto out; } skip_data(rlen, hbw); goto top; outbb:ss->run_color = 2; goto out0; outbw:ss->run_color = -1; goto out0; } #if 1 /*************** */ private int cf_decode_uncompressed(stream_CFD_state * ss, stream_cursor_read * pr) { return ERRC; } #else /*************** */ /* Decode uncompressed data. */ /* (Not tested: no sample data available!) */ /****** DOESN'T CHECK FOR OVERFLOWING SCAN LINE ******/ private int cf_decode_uncompressed(stream * s) { cfd_declare_state; const cfd_node *np; int clen, rlen; cfd_load_state(); while (1) { ensure_bits(cfd_uncompressed_initial_bits, NOOUT); np = &cf_uncompressed_decode[peek_bits(cfd_uncompressed_initial_bits)]; clen = np->code_length; rlen = np->run_length; if (clen > cfd_uncompressed_initial_bits) { /* Must be an exit code. */ break; } if (rlen == cfd_uncompressed_initial_bits) { /* Longest representable white run */ if_debug1('W', "[wu]%d\n", rlen); if ((qbit -= cfd_uncompressed_initial_bits) < 0) qbit += 8, q++; } else { if_debug1('W', "[wu]%d+1\n", rlen); if (qbit -= rlen < 0) qbit += 8, q++; *q ^= 1 << qbit; } skip_bits(clen); } clen -= cfd_uncompressed_initial_bits; skip_bits(cfd_uncompressed_initial_bits); ensure_bits(clen, NOOUT); np = &cf_uncompressed_decode[rlen + peek_var_bits(clen)]; rlen = np->run_length; skip_bits(np->code_length); if_debug1('w', "[wu]exit %d\n", rlen); if (rlen >= 0) { /* Valid exit code, rlen = 2 * run length + next polarity */ if ((qbit -= rlen >> 1) < 0) qbit += 8, q++; rlen &= 1; } out: /******* WRONG ******/ cfd_store_state(); return rlen; } #endif /*************** */ /* Stream template */ const stream_template s_CFD_template = {&st_CFD_state, s_CFD_init, s_CFD_process, 1, 1, s_CFD_release, s_CFD_set_defaults };