--- /dev/null
+<?xml version="1.0"?>
+<!DOCTYPE fax-tests SYSTEM "./fax-tests.dtd">
+<fax-tests>
+<config>
+ <path type="IMAGE" value="../test-data/etsi/fax"/>
+</config>
+<messages>
+ <!-- TCF = 2700 bytes at 14400, 2250 at 12000, 1800 at 9600, 1350 at 7200, 900 at 4800 or 450 at 2400 -->
+ <!-- Bad TCF == 10101010.... -->
+ <!-- slow HDLC preamble == 37 flag bytes -->
+ <!-- slow HDLC inter-frame flag sequence == 1 flag byte -->
+ <!-- slow HDLC end flag sequence == 5 flag bytes -->
+ <!-- synchronisation sequence == 250ms of zeros. = 450 bytes at 14400, 375 at 12000, 300 at 9600, 225 at 7200, 150 at 4800 or 75 at 2400 -->
+ <!-- fast HDLC inter-frame flag sequence == 1 flag byte -->
+ <!-- fast HDLC end flag sequence == 10 flag bytes -->
+ <!-- STAIRSTEP image is 1728x1728 pixels. Its is about 15k, so an average of 68.2 bits per row. To
+ cook it as a 31k page requires a min_bits of 141. To cook it as a 63k page requires a min_bits of
+ 286. To cook it as a 64k page requires a min_bits of 291 -->
+</messages>
+<test-group name="Supplementary">
+ <test name="PPS-MPS-lost-PPS">
+ <!-- Tester calls DUT and sends one 31k byte STAIRSTEP page and one 15k byte STAIRSTEP page. -->
+ <step type="CALL"/>
+
+ <!--<step dir="T" type="CNG"/>-->
+
+ <step dir="R" type="CED"/>
+ <step dir="R" type="HDLC" modem="V.21" tag="DIS" value="FF C8 01 ..." timeout="60000"/>
+ <step dir="R" type="SILENCE"/>
+
+ <step type="WAIT" value="75"/>
+ <step dir="T" type="PREAMBLE" modem="V.21"/>
+ <step dir="T" type="HDLC" tag="DCS" value="FF C8 41 00 50 1F 30"/>
+ <step dir="T" type="POSTAMBLE"/>
+ <step type="WAIT" value="75"/>
+ <step dir="T" type="TCF" modem="V.27ter/4800" value="900"/>
+
+ <step dir="R" type="HDLC" modem="V.21" tag="CFR" value="FF C8 21"/>
+ <step dir="R" type="SILENCE"/>
+
+ <step type="WAIT" value="75"/>
+ <step dir="T" type="PREAMBLE" modem="V.27ter/4800"/>
+ <step dir="T" type="PP" value="etsi_300_242_a4_stairstep.tif" min_bits="141"/>
+ <step dir="T" type="POSTAMBLE"/>
+ <step type="WAIT" value="75"/>
+ <step dir="T" type="PREAMBLE" modem="V.21"/>
+ <step dir="T" type="HDLC" tag="PPS-NULL" value="FF C8 7D 00 00 00 08"/>
+ <step dir="T" type="POSTAMBLE"/>
+
+ <step dir="R" type="HDLC" modem="V.21" tag="MCF" value="FF C8 31"/>
+ <step dir="R" type="SILENCE"/>
+
+ <step type="WAIT" value="75"/>
+ <step dir="T" type="PREAMBLE" modem="V.27ter/4800"/>
+ <step dir="T" type="PP" value="etsi_300_242_a4_stairstep.tif"/>
+ <step dir="T" type="POSTAMBLE"/>
+ <step type="WAIT" value="75"/>
+ <step dir="T" type="PREAMBLE" modem="V.21"/>
+ <step dir="T" type="HDLC" tag="PPS-MPS" value="FF C8 7D 72 00 80 08"/>
+ <step dir="T" type="POSTAMBLE"/>
+
+ <step dir="R" type="HDLC" modem="V.21" tag="MCF" value="FF C8 31"/>
+ <step dir="R" type="SILENCE"/>
+
+ <!-- Repeat the last chunk, as though we missed the MCF -->
+ <step type="WAIT" value="75"/>
+ <step dir="T" type="PREAMBLE" modem="V.27ter/4800"/>
+ <step dir="T" type="PP" value="etsi_300_242_a4_stairstep.tif"/>
+ <step dir="T" type="POSTAMBLE"/>
+ <step type="WAIT" value="75"/>
+ <step dir="T" type="PREAMBLE" modem="V.21"/>
+ <step dir="T" type="HDLC" tag="PPS-MPS" value="FF C8 7D 72 00 80 08"/>
+ <step dir="T" type="POSTAMBLE"/>
+
+ <step dir="R" type="HDLC" modem="V.21" tag="MCF" value="FF C8 31"/>
+ <step dir="R" type="SILENCE"/>
+
+ <step type="WAIT" value="75"/>
+ <step dir="T" type="PREAMBLE" modem="V.27ter/4800"/>
+ <step dir="T" type="PP" value="etsi_300_242_a4_white.tif"/>
+ <step dir="T" type="POSTAMBLE"/>
+ <step type="WAIT" value="75"/>
+ <step dir="T" type="PREAMBLE" modem="V.21"/>
+ <step dir="T" type="HDLC" tag="PPS-MPS" value="FF C8 7D 72 80 00 08"/>
+ <step dir="T" type="POSTAMBLE"/>
+
+ <step dir="R" type="HDLC" modem="V.21" tag="MCF" value="FF C8 31"/>
+ <step dir="R" type="SILENCE"/>
+
+ <step type="WAIT" value="75"/>
+ <step dir="T" type="PREAMBLE" modem="V.21"/>
+ <step dir="T" type="HDLC" tag="DCN" value="FF C8 5F"/>
+ <step dir="T" type="POSTAMBLE"/>
+ </test>
+</test-group>
+</fax-tests>
--- /dev/null
+/*
+ * SpanDSP - a series of DSP components for telephony
+ *
+ * image_translate.c - Image translation routines for reworking colour
+ * and gray scale images to be bi-level images of an
+ * appropriate size to be FAX compatible.
+ *
+ * Written by Steve Underwood <steveu@coppice.org>
+ *
+ * Copyright (C) 2009 Steve Underwood
+ *
+ * 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 version 2, as
+ * published by the Free Software Foundation.
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*! \file */
+
+#if defined(HAVE_CONFIG_H)
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <time.h>
+#include <memory.h>
+#include <string.h>
+#if defined(HAVE_TGMATH_H)
+#include <tgmath.h>
+#endif
+#if defined(HAVE_MATH_H)
+#include <math.h>
+#endif
+#include "floating_fudge.h"
+#include <tiffio.h>
+#include <assert.h>
+
+#include "spandsp/telephony.h"
+#include "spandsp/fast_convert.h"
+#include "spandsp/logging.h"
+#include "spandsp/saturated.h"
+#include "spandsp/t4_rx.h"
+#include "spandsp/t4_tx.h"
+#if defined(SPANDSP_SUPPORT_T85)
+#include "spandsp/t81_t82_arith_coding.h"
+#include "spandsp/t85.h"
+#endif
+#include "spandsp/t4_t6_decode.h"
+#include "spandsp/t4_t6_encode.h"
+#include "spandsp/image_translate.h"
+
+#include "spandsp/private/logging.h"
+#if defined(SPANDSP_SUPPORT_T85)
+#include "spandsp/private/t81_t82_arith_coding.h"
+#include "spandsp/private/t85.h"
+#endif
+#include "spandsp/private/t4_t6_decode.h"
+#include "spandsp/private/t4_t6_encode.h"
+#include "spandsp/private/t4_rx.h"
+#include "spandsp/private/t4_tx.h"
+#include "spandsp/private/image_translate.h"
+
+static int image_colour16_to_gray8_row(uint8_t mono[], uint16_t colour[], int pixels)
+{
+ int i;
+ uint32_t gray;
+
+ for (i = 0; i < pixels; i++)
+ {
+ gray = colour[3*i]*19595 + colour[3*i + 1]*38469 + colour[3*i + 2]*7472;
+ mono[i] = saturateu8(gray >> 24);
+ }
+ return pixels;
+}
+/*- End of function --------------------------------------------------------*/
+
+static int image_colour8_to_gray8_row(uint8_t mono[], uint8_t colour[], int pixels)
+{
+ int i;
+ uint32_t gray;
+
+ for (i = 0; i < pixels; i++)
+ {
+ gray = colour[3*i]*19595 + colour[3*i + 1]*38469 + colour[3*i + 2]*7472;
+ mono[i] = saturateu8(gray >> 16);
+ }
+ return pixels;
+}
+/*- End of function --------------------------------------------------------*/
+
+static int image_gray16_to_gray8_row(uint8_t mono[], uint16_t gray[], int pixels)
+{
+ int i;
+
+ for (i = 0; i < pixels; i++)
+ mono[i] = gray[i] >> 8;
+ return pixels;
+}
+/*- End of function --------------------------------------------------------*/
+
+static int get_and_scrunch_row(image_translate_state_t *s, uint8_t buf[], size_t len)
+{
+ int row_len;
+
+ row_len = (*s->row_read_handler)(s->row_read_user_data, buf, s->input_width*s->bytes_per_pixel);
+ if (row_len != s->input_width*s->bytes_per_pixel)
+ return 0;
+ /* Scrunch colour down to gray, and scrunch 16 bit pixels down to 8 bit pixels */
+ switch (s->input_format)
+ {
+ case IMAGE_TRANSLATE_FROM_GRAY_16:
+ image_gray16_to_gray8_row(buf, (uint16_t *) buf, s->input_width);
+ break;
+ case IMAGE_TRANSLATE_FROM_COLOUR_16:
+ image_colour16_to_gray8_row(buf, (uint16_t *) buf, s->input_width);
+ break;
+ case IMAGE_TRANSLATE_FROM_COLOUR_8:
+ image_colour8_to_gray8_row(buf, buf, s->input_width);
+ break;
+ }
+ return row_len;
+}
+/*- End of function --------------------------------------------------------*/
+
+static int image_resize_row(image_translate_state_t *s, uint8_t buf[], size_t len)
+{
+ int i;
+ int output_width;
+ int output_length;
+ int input_width;
+ int input_length;
+ double c1;
+ double c2;
+ double int_part;
+ int x;
+#if defined(SPANDSP_USE_FIXED_POINT)
+ int frac_row;
+ int frac_col;
+#else
+ double frac_row;
+ double frac_col;
+#endif
+ int row_len;
+ int skip;
+ uint8_t *p;
+
+ if (s->raw_output_row < 0)
+ return 0;
+ output_width = s->output_width - 1;
+ output_length = s->output_length - 1;
+ input_width = s->input_width - 1;
+ input_length = s->input_length - 1;
+
+ skip = s->raw_output_row*input_length/output_length;
+ if (skip >= s->raw_input_row)
+ {
+ skip++;
+ while (skip >= s->raw_input_row)
+ {
+ if (s->raw_input_row >= s->input_length)
+ {
+ s->raw_output_row = -1;
+ break;
+ }
+ row_len = get_and_scrunch_row(s, s->raw_pixel_row[0], s->input_width*s->bytes_per_pixel);
+ if (row_len != s->input_width*s->bytes_per_pixel)
+ {
+ s->raw_output_row = -1;
+ return 0;
+ }
+ s->raw_input_row++;
+ p = s->raw_pixel_row[0];
+ s->raw_pixel_row[0] = s->raw_pixel_row[1];
+ s->raw_pixel_row[1] = p;
+ }
+ }
+
+#if defined(SPANDSP_USE_FIXED_POINT)
+ frac_row = s->raw_output_row*input_length/output_length;
+ frac_row = s->raw_output_row*input_length - frac_row*output_length;
+ for (i = 0; i < output_width; i++)
+ {
+ x = i*input_width/output_width;
+ frac_col = x - x*output_width;
+ c1 = s->raw_pixel_row[0][x] + (s->raw_pixel_row[0][x + 1] - s->raw_pixel_row[0][x])*frac_col;
+ c1 = s->raw_pixel_row[1][x] + (s->raw_pixel_row[1][x + 1] - s->raw_pixel_row[1][x])*frac_col;
+ buf[i] = saturateu8(c1 + (c2 - c1)*frac_row);
+ }
+#else
+ frac_row = modf((double) s->raw_output_row*input_length/output_length, &int_part);
+ for (i = 0; i < output_width; i++)
+ {
+ frac_col = modf((double) i*input_width/output_width, &int_part);
+ x = int_part;
+ c1 = s->raw_pixel_row[0][x] + (s->raw_pixel_row[0][x + 1] - s->raw_pixel_row[0][x])*frac_col;
+ c2 = s->raw_pixel_row[1][x] + (s->raw_pixel_row[1][x + 1] - s->raw_pixel_row[1][x])*frac_col;
+ buf[i] = saturateu8(c1 + (c2 - c1)*frac_row);
+ }
+#endif
+ if (++s->raw_output_row >= s->output_length)
+ s->raw_output_row = -1;
+ return len;
+}
+/*- End of function --------------------------------------------------------*/
+
+static __inline__ uint8_t find_closest_palette_color(int in)
+{
+ return (in >= 128) ? 255 : 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) image_translate_row(image_translate_state_t *s, uint8_t buf[], size_t len)
+{
+ int x;
+ int y;
+ int i;
+ int j;
+ int limit;
+ int old_pixel;
+ int new_pixel;
+ int quant_error;
+ uint8_t *p;
+ uint8_t xx;
+
+ if (s->output_row < 0)
+ return 0;
+ y = s->output_row++;
+ /* This algorithm works over two rows, and outputs the earlier of the two. To
+ make this work:
+ - At row 0 we grab and scrunch two rows.
+ - From row 1 up to the last row we grab one new additional row each time.
+ - At the last row we dither and output, without getting an extra row in. */
+ for (i = (y == 0) ? 0 : 1; i < 2; i++)
+ {
+ p = s->pixel_row[0];
+ s->pixel_row[0] = s->pixel_row[1];
+ s->pixel_row[1] = p;
+
+ /* If this is the end of the image just ignore that there is now rubbish in pixel_row[1].
+ Mark that the end has occurred. This row will be properly output, and the next one
+ will fail, with the end of image condition (i.e. returning zero length) */
+ if (s->resize)
+ {
+ if (image_resize_row(s, s->pixel_row[1], s->output_width*s->bytes_per_pixel) != s->output_width*s->bytes_per_pixel)
+ s->output_row = -1;
+ }
+ else
+ {
+ if (get_and_scrunch_row(s, s->pixel_row[1], s->output_width*s->bytes_per_pixel) != s->output_width*s->bytes_per_pixel)
+ s->output_row = -1;
+ }
+ }
+ /* Apply Floyd-Steinberg dithering to the 8 bit pixels, using a bustrophodontic
+ scan, to reduce the grayscale image to pure black and white */
+ /* The first and last pixels in each row need special treatment, so we do not
+ step outside the row. */
+ if ((y & 1))
+ {
+ x = s->output_width - 1;
+ old_pixel = s->pixel_row[0][x];
+ new_pixel = find_closest_palette_color(old_pixel);
+ quant_error = old_pixel - new_pixel;
+ s->pixel_row[0][x + 0] = new_pixel;
+ s->pixel_row[0][x - 1] = saturateu8(s->pixel_row[0][x - 1] + (7*quant_error)/16);
+ s->pixel_row[1][x + 0] = saturateu8(s->pixel_row[1][x + 0] + (5*quant_error)/16);
+ s->pixel_row[1][x - 1] = saturateu8(s->pixel_row[1][x - 1] + (1*quant_error)/16);
+ for ( ; x > 0; x--)
+ {
+ old_pixel = s->pixel_row[0][x];
+ new_pixel = find_closest_palette_color(old_pixel);
+ quant_error = old_pixel - new_pixel;
+ s->pixel_row[0][x + 0] = new_pixel;
+ s->pixel_row[0][x - 1] = saturateu8(s->pixel_row[0][x - 1] + (7*quant_error)/16);
+ s->pixel_row[1][x + 1] = saturateu8(s->pixel_row[1][x + 1] + (3*quant_error)/16);
+ s->pixel_row[1][x + 0] = saturateu8(s->pixel_row[1][x + 0] + (5*quant_error)/16);
+ s->pixel_row[1][x - 1] = saturateu8(s->pixel_row[1][x - 1] + (1*quant_error)/16);
+ }
+ old_pixel = s->pixel_row[0][x];
+ new_pixel = find_closest_palette_color(old_pixel);
+ quant_error = old_pixel - new_pixel;
+ s->pixel_row[0][x + 0] = new_pixel;
+ s->pixel_row[1][x + 1] = saturateu8(s->pixel_row[1][x + 1] + (3*quant_error)/16);
+ s->pixel_row[1][x + 0] = saturateu8(s->pixel_row[1][x + 0] + (5*quant_error)/16);
+ }
+ else
+ {
+ x = 0;
+ old_pixel = s->pixel_row[0][x];
+ new_pixel = find_closest_palette_color(old_pixel);
+ quant_error = old_pixel - new_pixel;
+ s->pixel_row[0][x + 0] = new_pixel;
+ s->pixel_row[0][x + 1] = saturateu8(s->pixel_row[0][x + 1] + (7*quant_error)/16);
+ s->pixel_row[1][x + 0] = saturateu8(s->pixel_row[1][x + 0] + (5*quant_error)/16);
+ s->pixel_row[1][x + 1] = saturateu8(s->pixel_row[1][x + 1] + (1*quant_error)/16);
+ for ( ; x < s->output_width - 1; x++)
+ {
+ old_pixel = s->pixel_row[0][x];
+ new_pixel = find_closest_palette_color(old_pixel);
+ quant_error = old_pixel - new_pixel;
+ s->pixel_row[0][x + 0] = new_pixel;
+ s->pixel_row[0][x + 1] = saturateu8(s->pixel_row[0][x + 1] + (7*quant_error)/16);
+ s->pixel_row[1][x - 1] = saturateu8(s->pixel_row[1][x - 1] + (3*quant_error)/16);
+ s->pixel_row[1][x + 0] = saturateu8(s->pixel_row[1][x + 0] + (5*quant_error)/16);
+ s->pixel_row[1][x + 1] = saturateu8(s->pixel_row[1][x + 1] + (1*quant_error)/16);
+ }
+ old_pixel = s->pixel_row[0][x];
+ new_pixel = find_closest_palette_color(old_pixel);
+ quant_error = old_pixel - new_pixel;
+ s->pixel_row[0][x + 0] = new_pixel;
+ s->pixel_row[1][x - 1] = saturateu8(s->pixel_row[1][x - 1] + (3*quant_error)/16);
+ s->pixel_row[1][x + 0] = saturateu8(s->pixel_row[1][x + 0] + (5*quant_error)/16);
+ }
+ /* Now bit pack the pixel per byte row into a pixel per bit row. */
+ for (i = 0, x = 0; x < s->output_width; i++, x += 8)
+ {
+ xx = 0;
+ /* Allow for the possibility that the width is not a multiple of 8 */
+ limit = (8 <= s->output_width - x) ? 8 : (s->output_width - x);
+ for (j = 0; j < limit; j++)
+ {
+ if (s->pixel_row[0][x + j] <= 128)
+ xx |= (1 << (7 - j));
+ }
+ buf[i] = xx;
+ }
+ return i;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) image_translate_get_output_width(image_translate_state_t *s)
+{
+ return s->output_width;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) image_translate_get_output_length(image_translate_state_t *s)
+{
+ return s->output_length;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(image_translate_state_t *) image_translate_init(image_translate_state_t *s,
+ int input_format,
+ int input_width,
+ int input_length,
+ int output_width,
+ t4_row_read_handler_t row_read_handler,
+ void *row_read_user_data)
+{
+ int i;
+
+ if (s == NULL)
+ {
+ if ((s = (image_translate_state_t *) malloc(sizeof(*s))) == NULL)
+ return NULL;
+ }
+ memset(s, 0, sizeof(*s));
+
+ s->input_format = input_format;
+
+ s->input_width = input_width;
+ s->input_length = input_length;
+
+ s->resize = (output_width > 0);
+ s->output_width = (s->resize) ? output_width : s->input_width;
+ s->output_length = (s->resize) ? s->input_length*s->output_width/s->input_width : s->input_length;
+
+ switch (s->input_format)
+ {
+ case IMAGE_TRANSLATE_FROM_GRAY_8:
+ s->bytes_per_pixel = 1;
+ break;
+ case IMAGE_TRANSLATE_FROM_GRAY_16:
+ s->bytes_per_pixel = 2;
+ break;
+ case IMAGE_TRANSLATE_FROM_COLOUR_8:
+ s->bytes_per_pixel = 3;
+ break;
+ case IMAGE_TRANSLATE_FROM_COLOUR_16:
+ s->bytes_per_pixel = 6;
+ break;
+ default:
+ s->bytes_per_pixel = 1;
+ break;
+ }
+
+ /* Allocate the two row buffers we need, using the space requirements we now have */
+ if (s->resize)
+ {
+ for (i = 0; i < 2; i++)
+ {
+ if ((s->raw_pixel_row[i] = (uint8_t *) malloc(s->input_width*s->bytes_per_pixel)) == NULL)
+ return NULL;
+ memset(s->raw_pixel_row[i], 0, s->input_width*s->bytes_per_pixel);
+ if ((s->pixel_row[i] = (uint8_t *) malloc(s->output_width*sizeof(uint8_t))) == NULL)
+ return NULL;
+ memset(s->pixel_row[i], 0, s->output_width*sizeof(uint8_t));
+ }
+ }
+ else
+ {
+ for (i = 0; i < 2; i++)
+ {
+ if ((s->pixel_row[i] = (uint8_t *) malloc(s->output_width*s->bytes_per_pixel)) == NULL)
+ return NULL;
+ memset(s->pixel_row[i], 0, s->output_width*s->bytes_per_pixel);
+ }
+ }
+
+ s->row_read_handler = row_read_handler;
+ s->row_read_user_data = row_read_user_data;
+
+ s->raw_input_row = 0;
+ s->raw_output_row = 0;
+ s->output_row = 0;
+
+ return s;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) image_translate_release(image_translate_state_t *s)
+{
+ int i;
+
+ for (i = 0; i < 2; i++)
+ {
+ if (s->raw_pixel_row[i])
+ {
+ free(s->raw_pixel_row[i]);
+ s->raw_pixel_row[i] = NULL;
+ }
+ if (s->pixel_row[i])
+ {
+ free(s->pixel_row[i]);
+ s->pixel_row[i] = NULL;
+ }
+ }
+ return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) image_translate_free(image_translate_state_t *s)
+{
+ int res;
+
+ res = image_translate_release(s);
+ free(s);
+ return res;
+}
+/*- End of function --------------------------------------------------------*/
+/*- End of file ------------------------------------------------------------*/
--- /dev/null
+/*
+ * SpanDSP - a series of DSP components for telephony
+ *
+ * image_translate.h - Image translation routines for reworking colour
+ * and gray scale images to be bi-level images of an
+ * appropriate size to be FAX compatible.
+ *
+ * Written by Steve Underwood <steveu@coppice.org>
+ *
+ * Copyright (C) 2009 Steve Underwood
+ *
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1,
+ * as published by the Free Software Foundation.
+ *
+ * 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*! \file */
+
+#if !defined(_SPANDSP_IMAGE_TRANSLATE_H_)
+#define _SPANDSP_IMAGE_TRANSLATE_H_
+
+/*! \page image_translate_page Image translation
+\section image_translate_page_sec_1 What does it do?
+
+\section image_translate_page_sec_2 How does it work?
+
+\section image_translate_page_sec_3 How do I use it?
+*/
+
+typedef struct image_translate_state_s image_translate_state_t;
+
+enum
+{
+ IMAGE_TRANSLATE_FROM_MONO = 1,
+ IMAGE_TRANSLATE_FROM_GRAY_8 = 2,
+ IMAGE_TRANSLATE_FROM_GRAY_16 = 3,
+ IMAGE_TRANSLATE_FROM_COLOUR_8 = 4,
+ IMAGE_TRANSLATE_FROM_COLOUR_16 = 5
+};
+
+#if defined(__cplusplus)
+extern "C"
+{
+#endif
+
+/*! \brief Get the next row of a translated image.
+ \param s The image translation context.
+ \return the length of the row buffer, in bytes */
+SPAN_DECLARE(int) image_translate_row(image_translate_state_t *s, uint8_t buf[], size_t len);
+
+/*! \brief Get the width of the image being produced by an image translation context.
+ \param s The image translation context.
+ \return The width of the output image, in pixel. */
+SPAN_DECLARE(int) image_translate_get_output_width(image_translate_state_t *s);
+
+/*! \brief Get the length of the image being produced by an image translation context.
+ \param s The image translation context.
+ \return The length of the output image, in pixel. */
+SPAN_DECLARE(int) image_translate_get_output_length(image_translate_state_t *s);
+
+/*! \brief Initialise an image translation context for rescaling and squashing a gray scale
+ or colour image to a bi-level FAX type image.
+ \param s The image translation context.
+ \param input_format x
+ \param input_width The width of the source image, in pixels.
+ \param input_length The length of the source image, in pixels.
+ \param output_width The width of the output image, in pixels. The length of the output image
+ will be derived automatically from this and the source image dimension, to main the
+ geometry of the original image.
+ \param row_read_handler A callback routine used to pull rows of pixels from the source image
+ into the translation process.
+ \param row_read_user_data An opaque point passed to read_row_handler
+ \return A pointer to the context, or NULL if there was a problem. */
+SPAN_DECLARE(image_translate_state_t *) image_translate_init(image_translate_state_t *s,
+ int input_format,
+ int input_width,
+ int input_length,
+ int output_width,
+ t4_row_read_handler_t row_read_handler,
+ void *row_read_user_data);
+
+/*! \brief Release the resources associated with an image translation context.
+ \param s The image translation context.
+ \return 0 for success, otherwise -1. */
+SPAN_DECLARE(int) image_translate_release(image_translate_state_t *s);
+
+/*! \brief Free the resources associated with an image translation context.
+ \param s The image translation context.
+ \return 0 for success, otherwise -1. */
+SPAN_DECLARE(int) image_translate_free(image_translate_state_t *s);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
+/*- End of file ------------------------------------------------------------*/
--- /dev/null
+/*
+ * SpanDSP - a series of DSP components for telephony
+ *
+ * private/image_translate.c - Image translation routines for reworking colour
+ * and gray scale images to be bi-level images of an
+ * appropriate size to be FAX compatible.
+ *
+ * Written by Steve Underwood <steveu@coppice.org>
+ *
+ * Copyright (C) 2009 Steve Underwood
+ *
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1,
+ * as published by the Free Software Foundation.
+ *
+ * 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#if !defined(_SPANDSP_PRIVATE_IMAGE_TRANSLATE_H_)
+#define _SPANDSP_PRIVATE_IMAGE_TRANSLATE_H_
+
+struct image_translate_state_s
+{
+ int input_format;
+ int input_width;
+ int input_length;
+ int output_width;
+ int output_length;
+ int resize;
+ int bytes_per_pixel;
+ int raw_input_row;
+ int raw_output_row;
+ int output_row;
+
+ uint8_t *raw_pixel_row[2];
+ uint8_t *pixel_row[2];
+
+ t4_row_read_handler_t row_read_handler;
+ void *row_read_user_data;
+};
+
+#endif
+/*- End of file ------------------------------------------------------------*/
--- /dev/null
+/*
+ * SpanDSP - a series of DSP components for telephony
+ *
+ * private/t4_t6_decode.h - definitions for T.4/T.6 fax decoding
+ *
+ * Written by Steve Underwood <steveu@coppice.org>
+ *
+ * Copyright (C) 2003, 2009 Steve Underwood
+ *
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1,
+ * as published by the Free Software Foundation.
+ *
+ * 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#if !defined(_SPANDSP_PRIVATE_T4_T6_DECODE_H_)
+#define _SPANDSP_PRIVATE_T4_T6_DECODE_H_
+
+/*!
+ T.4 1D, T4 2D and T6 decompressor state.
+*/
+struct t4_t6_decode_state_s
+{
+ /*! \brief The type of compression used between the FAX machines. */
+ //int encoding;
+ /*! \brief Width of the current page, in pixels. */
+ //int image_width;
+
+ /*! \brief Callback function to write a row of pixels to the image destination. */
+ t4_row_write_handler_t row_write_handler;
+ /*! \brief Opaque pointer passed to row_write_handler. */
+ void *row_write_user_data;
+
+ /*! \brief A pointer into the image buffer indicating where the last row begins */
+ int last_row_starts_at;
+
+ /*! \brief This variable is used to count the consecutive EOLS we have seen. If it
+ reaches six, this is the end of the image. It is initially set to -1 for
+ 1D and 2D decoding, as an indicator that we must wait for the first EOL,
+ before decoding any image data. */
+ int consecutive_eols;
+
+ /*! \brief The reference or starting changing element on the coding line. At the
+ start of the coding line, a0 is set on an imaginary white changing element
+ situated just before the first element on the line. During the coding of
+ the coding line, the position of a0 is defined by the previous coding mode.
+ (See T.4/4.2.1.3.2.). */
+ int a0;
+ /*! \brief The first changing element on the reference line to the right of a0 and of
+ opposite colour to a0. */
+ int b1;
+ /*! \brief The length of the in-progress run of black or white. */
+ int run_length;
+ /*! \brief 2D horizontal mode control. */
+ int black_white;
+ /*! \brief TRUE if the current run is black */
+ int its_black;
+
+ /*! \brief The current step into the current row run-lengths buffer. */
+ int a_cursor;
+ /*! \brief The current step into the reference row run-lengths buffer. */
+ int b_cursor;
+
+ /*! \brief Incoming bit buffer for decompression. */
+ uint32_t rx_bitstream;
+ /*! \brief The number of bits currently in rx_bitstream. */
+ int rx_bits;
+ /*! \brief The number of bits to be skipped before trying to match the next code word. */
+ int rx_skip_bits;
+
+ /*! \brief Decoded pixel buffer. */
+ //uint32_t pixel_stream;
+ /*! \brief The number of bits currently in pixel_stream. */
+ //int tx_bits;
+
+ /*! \brief Current pixel row number. */
+ //int row;
+
+ /*! \brief The current number of consecutive bad rows. */
+ int curr_bad_row_run;
+ /*! \brief The longest run of consecutive bad rows seen in the current page. */
+ int longest_bad_row_run;
+ /*! \brief The total number of bad rows in the current page. */
+ int bad_rows;
+
+ /*! \brief Error and flow logging control */
+ //logging_state_t logging;
+};
+
+#endif
+/*- End of file ------------------------------------------------------------*/
--- /dev/null
+/*
+ * SpanDSP - a series of DSP components for telephony
+ *
+ * private/t4_t6_encode.h - definitions for T.4/T.6 fax compression
+ *
+ * Written by Steve Underwood <steveu@coppice.org>
+ *
+ * Copyright (C) 2003 Steve Underwood
+ *
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1,
+ * as published by the Free Software Foundation.
+ *
+ * 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#if !defined(_SPANDSP_PRIVATE_T4_T6_ENCODE_H_)
+#define _SPANDSP_PRIVATE_T4_T6_ENCODE_H_
+
+/*!
+ T.4 1D, T4 2D and T6 compressor state.
+*/
+struct t4_t6_encode_state_s
+{
+ /*! \brief The minimum number of encoded bits per row. This is a timing thing
+ for hardware FAX machines. */
+ int min_bits_per_row;
+ /*! \brief The current maximum contiguous rows that may be 2D encoded. */
+ int max_rows_to_next_1d_row;
+
+ /*! \brief Number of rows left that can be 2D encoded, before a 1D encoded row
+ must be used. */
+ int rows_to_next_1d_row;
+
+ /*! \brief The number of runs currently in the reference row. */
+ int ref_steps;
+
+ /*! \brief Pointer to the byte containing the next image bit to transmit. */
+ int bit_pos;
+ /*! \brief Pointer to the bit within the byte containing the next image bit to transmit. */
+ int bit_ptr;
+
+ /*! \brief Callback function to read a row of pixels from the image source. */
+ t4_row_read_handler_t row_read_handler;
+ /*! \brief Opaque pointer passed to row_read_handler. */
+ void *row_read_user_data;
+};
+
+#endif
+/*- End of file ------------------------------------------------------------*/
--- /dev/null
+/*\r
+ * SpanDSP - a series of DSP components for telephony\r
+ *\r
+ * private/timezone.h - Timezone handling for time interpretation\r
+ *\r
+ * Written by Steve Underwood <steveu@coppice.org>\r
+ *\r
+ * Copyright (C) 2010 Steve Underwood\r
+ *\r
+ * All rights reserved.\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU Lesser General Public License version 2.1,\r
+ * as published by the Free Software Foundation.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ * GNU Lesser General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU Lesser General Public\r
+ * License along with this program; if not, write to the Free Software\r
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\r
+ */\r
+\r
+#if !defined(_SPANDSP_PRIVATE_TIMEZONE_H_)\r
+#define _SPANDSP_PRIVATE_TIMEZONE_H_\r
+\r
+#define TZ_MAX_CHARS 50 /* Maximum number of abbreviation characters */\r
+\r
+#define TZ_MAX_LEAPS 50 /* Maximum number of leap second corrections */\r
+\r
+#define TZNAME_MAX 255\r
+\r
+/* The TZ_MAX_TIMES value below is enough to handle a bit more than a\r
+ * year's worth of solar time (corrected daily to the nearest second) or\r
+ * 138 years of Pacific Presidential Election time\r
+ * (where there are three time zone transitions every fourth year). */\r
+#define TZ_MAX_TIMES 370\r
+\r
+#if !defined(NOSOLAR)\r
+#define TZ_MAX_TYPES 256 /* Limited by what (unsigned char)'s can hold */\r
+#else\r
+/* Must be at least 14 for Europe/Riga as of Jan 12 1995,\r
+ * as noted by Earl Chew <earl@hpato.aus.hp.com>. */\r
+#define TZ_MAX_TYPES 20 /* Maximum number of local time types */\r
+#endif\r
+\r
+#define TZ_BIGGEST(a, b) (((a) > (b)) ? (a) : (b))\r
+\r
+/* Time type information */\r
+struct tz_ttinfo_s\r
+{\r
+ int32_t gmtoff; /* UTC offset in seconds */\r
+ int isdst; /* Used to set tm_isdst */\r
+ int abbrind; /* Abbreviation list index */\r
+ int ttisstd; /* TRUE if transition is std time */\r
+ int ttisgmt; /* TRUE if transition is UTC */\r
+};\r
+\r
+/* Leap second information */\r
+struct tz_lsinfo_s\r
+{\r
+ time_t trans; /* Transition time */\r
+ int32_t corr; /* Correction to apply */\r
+};\r
+\r
+struct tz_state_s\r
+{\r
+ int leapcnt;\r
+ int timecnt;\r
+ int typecnt;\r
+ int charcnt;\r
+ time_t ats[TZ_MAX_TIMES];\r
+ uint8_t types[TZ_MAX_TIMES];\r
+ struct tz_ttinfo_s ttis[TZ_MAX_TYPES];\r
+ char chars[TZ_BIGGEST(TZ_MAX_CHARS + 1, (2*(TZNAME_MAX + 1)))];\r
+ struct tz_lsinfo_s lsis[TZ_MAX_LEAPS];\r
+};\r
+\r
+struct tz_s\r
+{\r
+ struct tz_state_s state;\r
+ char lcl_tzname[TZNAME_MAX + 1];\r
+ int lcl_is_set;\r
+ const char *tzname[2];\r
+};\r
+\r
+#endif\r
+/*- End of file ------------------------------------------------------------*/\r
--- /dev/null
+/*
+ * SpanDSP - a series of DSP components for telephony
+ *
+ * t4_t6_decode.h - definitions for T.4/T.6 fax decoding
+ *
+ * Written by Steve Underwood <steveu@coppice.org>
+ *
+ * Copyright (C) 2003, 2009 Steve Underwood
+ *
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1,
+ * as published by the Free Software Foundation.
+ *
+ * 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*! \file */
+
+#if !defined(_SPANDSP_T4_T6_DECODE_H_)
+#define _SPANDSP_T4_T6_DECODE_H_
+
+/*! \page t4_t6_decode_page T.4 and T.6 FAX image decompression
+
+\section t4_t6_decode_page_sec_1 What does it do?
+The T.4 image compression and decompression routines implement the 1D and 2D
+encoding methods defined in ITU specification T.4. They also implement the pure
+2D encoding method defined in T.6. These are image compression algorithms used
+for FAX transmission.
+
+\section t4_t6_decode_page_sec_1 How does it work?
+*/
+
+typedef struct t4_t6_decode_state_s t4_t6_decode_state_t;
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
+/*- End of file ------------------------------------------------------------*/
--- /dev/null
+/*
+ * SpanDSP - a series of DSP components for telephony
+ *
+ * t4_t6_encode.h - definitions for T.4/T.6 fax encoding
+ *
+ * Written by Steve Underwood <steveu@coppice.org>
+ *
+ * Copyright (C) 2003, 2009 Steve Underwood
+ *
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1,
+ * as published by the Free Software Foundation.
+ *
+ * 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*! \file */
+
+#if !defined(_SPANDSP_T4_T6_ENCODE_H_)
+#define _SPANDSP_T4_T6_ENCODE_H_
+
+typedef struct t4_t6_encode_state_s t4_t6_encode_state_t;
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
+/*- End of file ------------------------------------------------------------*/
--- /dev/null
+/*\r
+ * SpanDSP - a series of DSP components for telephony\r
+ *\r
+ * timezone.h - Timezone handling for time interpretation\r
+ *\r
+ * Written by Steve Underwood <steveu@coppice.org>\r
+ *\r
+ * Copyright (C) 2010 Steve Underwood\r
+ *\r
+ * All rights reserved.\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU Lesser General Public License version 2.1,\r
+ * as published by the Free Software Foundation.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ * GNU Lesser General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU Lesser General Public\r
+ * License along with this program; if not, write to the Free Software\r
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\r
+ */\r
+\r
+/*! \file */\r
+\r
+#if !defined(_SPANDSP_TIMEZONE_H_)\r
+#define _SPANDSP_TIMEZONE_H_\r
+\r
+/*! \page timezone_page Timezone handling\r
+\r
+\section timezone_sec_1 What does it do?\r
+\r
+\section timezone_sec_2 How does it work?\r
+\r
+*/\r
+\r
+typedef struct tz_s tz_t;\r
+\r
+enum\r
+{\r
+ TM_SUNDAY = 0,\r
+ TM_MONDAY,\r
+ TM_TUESDAY,\r
+ TM_WEDNESDAY,\r
+ TM_THURSDAY,\r
+ TM_FRIDAY,\r
+ TM_SATURDAY\r
+};\r
+\r
+enum\r
+{\r
+ TM_JANUARY = 0,\r
+ TM_FEBRUARY,\r
+ TM_MARCH,\r
+ TM_APRIL,\r
+ TM_MAY,\r
+ TM_JUNE,\r
+ TM_JULY,\r
+ TM_AUGUST,\r
+ TM_SEPTEMBER,\r
+ TM_OCTOBER,\r
+ TM_NOVEMBER,\r
+ TM_DECEMBER\r
+};\r
+\r
+#if defined(__cplusplus)\r
+extern "C"\r
+{\r
+#endif\r
+\r
+SPAN_DECLARE(tz_t *) tz_init(tz_t *tz, const char *tzstring);\r
+\r
+SPAN_DECLARE(int) tz_release(tz_t *tz);\r
+\r
+SPAN_DECLARE(int) tz_free(tz_t *tz);\r
+\r
+SPAN_DECLARE(int) tz_localtime(tz_t *tz, struct tm *tm, time_t t);\r
+\r
+SPAN_DECLARE(const char *) tz_tzname(tz_t *tz, int isdst);\r
+\r
+#if defined(__cplusplus)\r
+}\r
+#endif\r
+\r
+#endif\r
+/*- End of file ------------------------------------------------------------*/\r
--- /dev/null
+/*\r
+ * SpanDSP - a series of DSP components for telephony\r
+ *\r
+ * timezone.c - Timezone handling for time interpretation\r
+ *\r
+ * Written by Steve Underwood <steveu@coppice.org>\r
+ *\r
+ * Copyright (C) 2010 Steve Underwood\r
+ *\r
+ * All rights reserved.\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU Lesser General Public License version 2.1,\r
+ * as published by the Free Software Foundation.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ * GNU Lesser General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU Lesser General Public\r
+ * License along with this program; if not, write to the Free Software\r
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\r
+ */\r
+\r
+/*! \file */\r
+\r
+/* Timezone processing might not seem like a DSP activity, but getting the headers\r
+ right on FAXes demands it. We need to handle multiple time zones within a process,\r
+ for FAXes related to different parts of the globe, so the system timezone handling\r
+ is not adequate. */\r
+\r
+/* This timezone handling is derived from public domain software by Arthur David Olson\r
+ <arthur_david_olson@nih.gov> which you may download from ftp://elsie.nci.nih.gov/pub\r
+ at the time of writing. */\r
+\r
+#if defined(HAVE_CONFIG_H)\r
+#include "config.h"\r
+#endif\r
+\r
+#include <stdlib.h>\r
+#include <inttypes.h>\r
+#include <stdio.h>\r
+#include <string.h>\r
+#include <time.h>\r
+#include <stdlib.h>\r
+#include <assert.h>\r
+\r
+#include "spandsp/telephony.h"\r
+#include "spandsp/timezone.h"\r
+\r
+#include "spandsp/private/timezone.h"\r
+\r
+#if !defined(FALSE)\r
+#define FALSE 0\r
+#endif\r
+\r
+#if !defined(TRUE)\r
+#define TRUE (!FALSE)\r
+#endif\r
+\r
+#define SECS_PER_MIN 60\r
+#define MINS_PER_HOUR 60\r
+#define HOURS_PER_DAY 24\r
+#define DAYS_PER_WEEK 7\r
+#define DAYS_PER_NON_LEAP_YEAR 365\r
+#define DAYS_PER_LEAP_YEAR 366\r
+#define SECS_PER_HOUR (SECS_PER_MIN*MINS_PER_HOUR)\r
+#define SECS_PER_DAY ((long int) SECS_PER_HOUR*HOURS_PER_DAY)\r
+#define MONTHS_PER_YEAR 12\r
+\r
+#define TM_YEAR_BASE 1900\r
+\r
+#define EPOCH_YEAR 1970\r
+#define EPOCH_WDAY TM_THURSDAY\r
+\r
+#define isleap(y) (((y)%4) == 0 && (((y)%100) != 0 || ((y)%400) == 0))\r
+\r
+#define isleap_sum(a, b) isleap((a)%400 + (b)%400)\r
+\r
+/* Unlike <ctype.h>'s isdigit, this also works if c < 0 | c > UCHAR_MAX. */\r
+#define is_digit(c) ((unsigned int) (c) - '0' <= 9)\r
+\r
+#define TZ_DEF_RULE_STRING ",M4.1.0,M10.5.0"\r
+\r
+#define JULIAN_DAY 0 /* Jn - Julian day */\r
+#define DAY_OF_YEAR 1 /* n - day of year */\r
+#define MONTH_NTH_DAY_OF_WEEK 2 /* Mm.n.d - month, week, day of week */\r
+\r
+static const char wildabbr[] = " ";\r
+\r
+static const char gmt[] = "GMT";\r
+\r
+struct tz_rule_s\r
+{\r
+ int r_type; /* Type of rule--see below */\r
+ int r_day; /* Day number of rule */\r
+ int r_week; /* Week number of rule */\r
+ int r_mon; /* Month number of rule */\r
+ long int r_time; /* Transition time of rule */\r
+};\r
+\r
+static const int mon_lengths[2][MONTHS_PER_YEAR] =\r
+{\r
+ {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},\r
+ {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}\r
+};\r
+\r
+static const int year_lengths[2] =\r
+{\r
+ DAYS_PER_NON_LEAP_YEAR,\r
+ DAYS_PER_LEAP_YEAR\r
+};\r
+\r
+static int increment_overflow(int *number, int delta)\r
+{\r
+ int number0;\r
+\r
+ number0 = *number;\r
+ *number += delta;\r
+ return (*number < number0) != (delta < 0);\r
+}\r
+/*- End of function --------------------------------------------------------*/\r
+\r
+static void set_tzname(tz_t *tz)\r
+{\r
+ struct tz_state_s *sp;\r
+ const struct tz_ttinfo_s *ttisp;\r
+ int i;\r
+\r
+ sp = &tz->state;\r
+ tz->tzname[0] = wildabbr;\r
+ tz->tzname[1] = wildabbr;\r
+ for (i = 0; i < sp->typecnt; i++)\r
+ {\r
+ ttisp = &sp->ttis[i];\r
+ tz->tzname[ttisp->isdst] = &sp->chars[ttisp->abbrind];\r
+ }\r
+ for (i = 0; i < sp->timecnt; i++)\r
+ {\r
+ ttisp = &sp->ttis[sp->types[i]];\r
+ tz->tzname[ttisp->isdst] = &sp->chars[ttisp->abbrind];\r
+ }\r
+}\r
+/*- End of function --------------------------------------------------------*/\r
+\r
+/* Return the number of leap years through the end of the given year\r
+ where, to make the math easy, the answer for year zero is defined as zero. */\r
+static int leaps_thru_end_of(const int y)\r
+{\r
+ return (y >= 0) ? (y/4 - y/100 + y/400) : -(leaps_thru_end_of(-(y + 1)) + 1);\r
+}\r
+/*- End of function --------------------------------------------------------*/\r
+\r
+static struct tm *time_sub(const time_t * const timep, const long int offset, const struct tz_state_s * const sp, struct tm * const tmp)\r
+{\r
+ const struct tz_lsinfo_s *lp;\r
+ time_t tdays;\r
+ const int *ip;\r
+ int32_t corr;\r
+ int32_t seconds;\r
+ int32_t rem;\r
+ int idays;\r
+ int y;\r
+ int hit;\r
+ int i;\r
+\r
+ corr = 0;\r
+ hit = 0;\r
+ i = sp->leapcnt;\r
+ while (--i >= 0)\r
+ {\r
+ lp = &sp->lsis[i];\r
+ if (*timep >= lp->trans)\r
+ {\r
+ if (*timep == lp->trans)\r
+ {\r
+ hit = ((i == 0 && lp->corr > 0) || lp->corr > sp->lsis[i - 1].corr);\r
+ if (hit)\r
+ {\r
+ while (i > 0\r
+ &&\r
+ sp->lsis[i].trans == sp->lsis[i - 1].trans + 1\r
+ &&\r
+ sp->lsis[i].corr == sp->lsis[i - 1].corr + 1)\r
+ {\r
+ hit++;\r
+ --i;\r
+ }\r
+ }\r
+ }\r
+ corr = lp->corr;\r
+ break;\r
+ }\r
+ }\r
+ y = EPOCH_YEAR;\r
+ tdays = *timep/SECS_PER_DAY;\r
+ rem = *timep - tdays*SECS_PER_DAY;\r
+ while (tdays < 0 || tdays >= year_lengths[isleap(y)])\r
+ {\r
+ int newy;\r
+ time_t tdelta;\r
+ int idelta;\r
+ int leapdays;\r
+\r
+ tdelta = tdays / DAYS_PER_LEAP_YEAR;\r
+ idelta = tdelta;\r
+ if (tdelta - idelta >= 1 || idelta - tdelta >= 1)\r
+ return NULL;\r
+ if (idelta == 0)\r
+ idelta = (tdays < 0) ? -1 : 1;\r
+ newy = y;\r
+ if (increment_overflow(&newy, idelta))\r
+ return NULL;\r
+ leapdays = leaps_thru_end_of(newy - 1) - leaps_thru_end_of(y - 1);\r
+ tdays -= ((time_t) newy - y)*DAYS_PER_NON_LEAP_YEAR;\r
+ tdays -= leapdays;\r
+ y = newy;\r
+ }\r
+ seconds = tdays*SECS_PER_DAY;\r
+ tdays = seconds/SECS_PER_DAY;\r
+ rem += seconds - tdays*SECS_PER_DAY;\r
+ /* Given the range, we can now fearlessly cast... */\r
+ idays = tdays;\r
+ rem += (offset - corr);\r
+ while (rem < 0)\r
+ {\r
+ rem += SECS_PER_DAY;\r
+ idays--;\r
+ }\r
+ while (rem >= SECS_PER_DAY)\r
+ {\r
+ rem -= SECS_PER_DAY;\r
+ idays++;\r
+ }\r
+ while (idays < 0)\r
+ {\r
+ if (increment_overflow(&y, -1))\r
+ return NULL;\r
+ idays += year_lengths[isleap(y)];\r
+ }\r
+ while (idays >= year_lengths[isleap(y)])\r
+ {\r
+ idays -= year_lengths[isleap(y)];\r
+ if (increment_overflow(&y, 1))\r
+ return NULL;\r
+ }\r
+ tmp->tm_year = y;\r
+ if (increment_overflow(&tmp->tm_year, -TM_YEAR_BASE))\r
+ return NULL;\r
+ tmp->tm_yday = idays;\r
+ /* The "extra" mods below avoid overflow problems. */\r
+ tmp->tm_wday = EPOCH_WDAY\r
+ + ((y - EPOCH_YEAR) % DAYS_PER_WEEK)*(DAYS_PER_NON_LEAP_YEAR % DAYS_PER_WEEK)\r
+ + leaps_thru_end_of(y - 1)\r
+ - leaps_thru_end_of(EPOCH_YEAR - 1)\r
+ + idays;\r
+ tmp->tm_wday %= DAYS_PER_WEEK;\r
+ if (tmp->tm_wday < 0)\r
+ tmp->tm_wday += DAYS_PER_WEEK;\r
+ tmp->tm_hour = (int) (rem/SECS_PER_HOUR);\r
+ rem %= SECS_PER_HOUR;\r
+ tmp->tm_min = (int) (rem/SECS_PER_MIN);\r
+ /* A positive leap second requires a special\r
+ * representation. This uses "... ??:59:60" et seq. */\r
+ tmp->tm_sec = (int) (rem%SECS_PER_MIN) + hit;\r
+ ip = mon_lengths[isleap(y)];\r
+ for (tmp->tm_mon = 0; idays >= ip[tmp->tm_mon]; (tmp->tm_mon)++)\r
+ idays -= ip[tmp->tm_mon];\r
+ tmp->tm_mday = (int) (idays + 1);\r
+ tmp->tm_isdst = 0;\r
+ return tmp;\r
+}\r
+/*- End of function --------------------------------------------------------*/\r
+\r
+/* Given a pointer into a time zone string, scan until a character that is not\r
+ * a valid character in a zone name is found. Return a pointer to that\r
+ * character. */\r
+static const char *get_tzname(const char *strp)\r
+{\r
+ char c;\r
+\r
+ while ((c = *strp) != '\0' && !is_digit(c) && c != ',' && c != '-' && c != '+')\r
+ strp++;\r
+ return strp;\r
+}\r
+/*- End of function --------------------------------------------------------*/\r
+\r
+/* Given a pointer into a time zone string, extract a number from that string.\r
+ * Check that the number is within a specified range; if it is not, return\r
+ * NULL.\r
+ * Otherwise, return a pointer to the first character not part of the number. */\r
+static const char *get_num(const char *strp, int * const nump, const int min, const int max)\r
+{\r
+ char c;\r
+ int num;\r
+\r
+ if (strp == NULL || !is_digit(c = *strp))\r
+ return NULL;\r
+ num = 0;\r
+ do\r
+ {\r
+ num = num*10 + (c - '0');\r
+ if (num > max)\r
+ return NULL; /* Illegal value */\r
+ c = *++strp;\r
+ }\r
+ while (is_digit(c));\r
+ if (num < min)\r
+ return NULL; /* Illegal value */\r
+ *nump = num;\r
+ return strp;\r
+}\r
+/*- End of function --------------------------------------------------------*/\r
+\r
+/* Given a pointer into a time zone string, extract a number of seconds,\r
+ * in hh[:mm[:ss]] form, from the string.\r
+ * If any error occurs, return NULL.\r
+ * Otherwise, return a pointer to the first character not part of the number\r
+ * of seconds. */\r
+static const char *get_secs(const char *strp, long int * const secsp)\r
+{\r
+ int num;\r
+\r
+ /* HOURS_PER_DAY*DAYS_PER_WEEK - 1 allows quasi-Posix rules like\r
+ * "M10.4.6/26", which does not conform to Posix,\r
+ * but which specifies the equivalent of\r
+ * "02:00 on the first Sunday on or after 23 Oct". */\r
+ strp = get_num(strp, &num, 0, HOURS_PER_DAY*DAYS_PER_WEEK - 1);\r
+ if (strp == NULL)\r
+ return NULL;\r
+ *secsp = num*(long int) SECS_PER_HOUR;\r
+ if (*strp == ':')\r
+ {\r
+ strp = get_num(strp + 1, &num, 0, MINS_PER_HOUR - 1);\r
+ if (strp == NULL)\r
+ return NULL;\r
+ *secsp += num*SECS_PER_MIN;\r
+ if (*strp == ':')\r
+ {\r
+ /* SECS_PER_MIN allows for leap seconds. */\r
+ strp = get_num(strp + 1, &num, 0, SECS_PER_MIN);\r
+ if (strp == NULL)\r
+ return NULL;\r
+ *secsp += num;\r
+ }\r
+ }\r
+ return strp;\r
+}\r
+/*- End of function --------------------------------------------------------*/\r
+\r
+/* Given a pointer into a time zone string, extract an offset, in\r
+ * [+-]hh[:mm[:ss]] form, from the string.\r
+ * If any error occurs, return NULL.\r
+ * Otherwise, return a pointer to the first character not part of the time. */\r
+static const char *get_offset(const char *strp, long int * const offsetp)\r
+{\r
+ int neg = 0;\r
+\r
+ if (*strp == '-')\r
+ {\r
+ neg = 1;\r
+ strp++;\r
+ }\r
+ else if (*strp == '+')\r
+ {\r
+ strp++;\r
+ }\r
+ strp = get_secs(strp, offsetp);\r
+ if (strp == NULL)\r
+ return NULL; /* Illegal time */\r
+ if (neg)\r
+ *offsetp = -*offsetp;\r
+ return strp;\r
+}\r
+/*- End of function --------------------------------------------------------*/\r
+\r
+/* Given a pointer into a time zone string, extract a rule in the form\r
+ * date[/time]. See POSIX section 8 for the format of "date" and "time".\r
+ * If a valid rule is not found, return NULL.\r
+ * Otherwise, return a pointer to the first character not part of the rule. */\r
+static const char *get_rule(const char *strp, struct tz_rule_s * const rulep)\r
+{\r
+ if (*strp == 'J')\r
+ {\r
+ /* Julian day. */\r
+ rulep->r_type = JULIAN_DAY;\r
+ strp = get_num(strp + 1, &rulep->r_day, 1, DAYS_PER_NON_LEAP_YEAR);\r
+ }\r
+ else if (*strp == 'M')\r
+ {\r
+ /* Month, week, day. */\r
+ rulep->r_type = MONTH_NTH_DAY_OF_WEEK;\r
+ strp = get_num(strp + 1, &rulep->r_mon, 1, MONTHS_PER_YEAR);\r
+ if (strp == NULL || *strp++ != '.')\r
+ return NULL;\r
+ strp = get_num(strp, &rulep->r_week, 1, 5);\r
+ if (strp == NULL || *strp++ != '.')\r
+ return NULL;\r
+ strp = get_num(strp, &rulep->r_day, 0, DAYS_PER_WEEK - 1);\r
+ }\r
+ else if (is_digit(*strp))\r
+ {\r
+ /* Day of the year. */\r
+ rulep->r_type = DAY_OF_YEAR;\r
+ strp = get_num(strp, &rulep->r_day, 0, DAYS_PER_LEAP_YEAR - 1);\r
+ }\r
+ else\r
+ {\r
+ /* Invalid format */\r
+ return NULL;\r
+ }\r
+ if (strp == NULL)\r
+ return NULL;\r
+ if (*strp == '/')\r
+ {\r
+ /* Time specified. */\r
+ strp = get_secs(strp + 1, &rulep->r_time);\r
+ }\r
+ else\r
+ {\r
+ /* Default = 2:00:00 */\r
+ rulep->r_time = 2*SECS_PER_HOUR;\r
+ }\r
+ return strp;\r
+}\r
+/*- End of function --------------------------------------------------------*/\r
+\r
+/* Given the Epoch-relative time of January 1, 00:00:00 UTC, in a year, the\r
+ * year, a rule, and the offset from UTC at the time that rule takes effect,\r
+ * calculate the Epoch-relative time that rule takes effect. */\r
+static time_t trans_time(const time_t janfirst, const int year, const struct tz_rule_s * const rulep, const long int offset)\r
+{\r
+ int leapyear;\r
+ time_t value;\r
+ int i;\r
+ int d;\r
+ int m1;\r
+ int yy0;\r
+ int yy1;\r
+ int yy2;\r
+ int dow;\r
+\r
+ value = 0;\r
+ leapyear = isleap(year);\r
+ switch (rulep->r_type)\r
+ {\r
+ case JULIAN_DAY:\r
+ /* Jn - Julian day, 1 == January 1, 60 == March 1 even in leap\r
+ * years.\r
+ * In non-leap years, or if the day number is 59 or less, just\r
+ * add SECS_PER_DAY times the day number-1 to the time of\r
+ * January 1, midnight, to get the day. */\r
+ value = janfirst + (rulep->r_day - 1)*SECS_PER_DAY;\r
+ if (leapyear && rulep->r_day >= 60)\r
+ value += SECS_PER_DAY;\r
+ break;\r
+ case DAY_OF_YEAR:\r
+ /* n - day of year.\r
+ * Just add SECS_PER_DAY times the day number to the time of\r
+ * January 1, midnight, to get the day. */\r
+ value = janfirst + rulep->r_day * SECS_PER_DAY;\r
+ break;\r
+ case MONTH_NTH_DAY_OF_WEEK:\r
+ /* Mm.n.d - nth "dth day" of month m. */\r
+ value = janfirst;\r
+ for (i = 0; i < rulep->r_mon - 1; i++)\r
+ value += mon_lengths[leapyear][i]*SECS_PER_DAY;\r
+\r
+ /* Use Zeller's Congruence to get day-of-week of first day of month. */\r
+ m1 = (rulep->r_mon + 9)%12 + 1;\r
+ yy0 = (rulep->r_mon <= 2) ? (year - 1) : year;\r
+ yy1 = yy0/100;\r
+ yy2 = yy0%100;\r
+ dow = ((26*m1 - 2)/10 + 1 + yy2 + yy2/4 + yy1/4 - 2*yy1)%7;\r
+ if (dow < 0)\r
+ dow += DAYS_PER_WEEK;\r
+\r
+ /* "dow" is the day-of-week of the first day of the month. Get\r
+ * the day-of-month (zero-origin) of the first "dow" day of the\r
+ * month. */\r
+ d = rulep->r_day - dow;\r
+ if (d < 0)\r
+ d += DAYS_PER_WEEK;\r
+ for (i = 1; i < rulep->r_week; i++)\r
+ {\r
+ if (d + DAYS_PER_WEEK >= mon_lengths[leapyear][rulep->r_mon - 1])\r
+ break;\r
+ d += DAYS_PER_WEEK;\r
+ }\r
+\r
+ /* "d" is the day-of-month (zero-origin) of the day we want. */\r
+ value += d*SECS_PER_DAY;\r
+ break;\r
+ }\r
+\r
+ /* "value" is the Epoch-relative time of 00:00:00 UTC on the day in\r
+ * question. To get the Epoch-relative time of the specified local\r
+ * time on that day, add the transition time and the current offset\r
+ * from UTC. */\r
+ return value + rulep->r_time + offset;\r
+}\r
+/*- End of function --------------------------------------------------------*/\r
+\r
+/* Given a POSIX section 8-style TZ string, fill in the rule tables as\r
+ appropriate. */\r
+static int tzparse(const char *name, struct tz_state_s * const sp, const int lastditch)\r
+{\r
+ const char *stdname;\r
+ const char *dstname;\r
+ size_t stdlen;\r
+ size_t dstlen;\r
+ long int stdoffset;\r
+ long int dstoffset;\r
+ long int theirstdoffset;\r
+ long int theirdstoffset;\r
+ long int theiroffset;\r
+ unsigned char *typep;\r
+ char *cp;\r
+ int load_result;\r
+ int isdst;\r
+ int i;\r
+ int j;\r
+ int year;\r
+ struct tz_rule_s start;\r
+ struct tz_rule_s end;\r
+ time_t *atp;\r
+ time_t janfirst;\r
+ time_t starttime;\r
+ time_t endtime;\r
+\r
+ dstname = NULL;\r
+ stdname = name;\r
+ if (lastditch)\r
+ {\r
+ stdlen = strlen(name); /* Length of standard zone name */\r
+ name += stdlen;\r
+ if (stdlen >= sizeof(sp->chars))\r
+ stdlen = sizeof(sp->chars) - 1;\r
+ stdoffset = 0;\r
+ }\r
+ else\r
+ {\r
+ name = get_tzname(name);\r
+ stdlen = name - stdname;\r
+ if (stdlen < 3)\r
+ return -1;\r
+ if (*name == '\0')\r
+ return -1;\r
+ name = get_offset(name, &stdoffset);\r
+ if (name == NULL)\r
+ return -1;\r
+ }\r
+ load_result = -1;\r
+ if (load_result != 0)\r
+ sp->leapcnt = 0; /* So, we're off a little */\r
+ if (*name != '\0')\r
+ {\r
+ dstname = name;\r
+ name = get_tzname(name);\r
+ dstlen = name - dstname; /* Length of DST zone name */\r
+ if (dstlen < 3)\r
+ return -1;\r
+ if (*name != '\0' && *name != ',' && *name != ';')\r
+ {\r
+ if ((name = get_offset(name, &dstoffset)) == NULL)\r
+ return -1;\r
+ }\r
+ else\r
+ {\r
+ dstoffset = stdoffset - SECS_PER_HOUR;\r
+ }\r
+ if (*name == '\0' && load_result != 0)\r
+ name = TZ_DEF_RULE_STRING;\r
+ if (*name == ',' || *name == ';')\r
+ {\r
+ if ((name = get_rule(name + 1, &start)) == NULL)\r
+ return -1;\r
+ if (*name++ != ',')\r
+ return -1;\r
+ if ((name = get_rule(name, &end)) == NULL)\r
+ return -1;\r
+ if (*name != '\0')\r
+ return -1;\r
+ sp->typecnt = 2; /* Standard time and DST */\r
+ /* Two transitions per year, from EPOCH_YEAR to 2037. */\r
+ sp->timecnt = 2*(2037 - EPOCH_YEAR + 1);\r
+ if (sp->timecnt > TZ_MAX_TIMES)\r
+ return -1;\r
+ sp->ttis[0].gmtoff = -dstoffset;\r
+ sp->ttis[0].isdst = 1;\r
+ sp->ttis[0].abbrind = stdlen + 1;\r
+ sp->ttis[1].gmtoff = -stdoffset;\r
+ sp->ttis[1].isdst = 0;\r
+ sp->ttis[1].abbrind = 0;\r
+ atp = sp->ats;\r
+ typep = sp->types;\r
+ janfirst = 0;\r
+ for (year = EPOCH_YEAR; year <= 2037; year++)\r
+ {\r
+ starttime = trans_time(janfirst, year, &start, stdoffset);\r
+ endtime = trans_time(janfirst, year, &end, dstoffset);\r
+ if (starttime > endtime)\r
+ {\r
+ *atp++ = endtime;\r
+ *typep++ = 1; /* DST ends */\r
+ *atp++ = starttime;\r
+ *typep++ = 0; /* DST begins */\r
+ }\r
+ else\r
+ {\r
+ *atp++ = starttime;\r
+ *typep++ = 0; /* DST begins */\r
+ *atp++ = endtime;\r
+ *typep++ = 1; /* DST ends */\r
+ }\r
+ janfirst += year_lengths[isleap(year)]*SECS_PER_DAY;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ if (*name != '\0')\r
+ return -1;\r
+ /* Initial values of theirstdoffset and theirdstoffset. */\r
+ theirstdoffset = 0;\r
+ for (i = 0; i < sp->timecnt; i++)\r
+ {\r
+ j = sp->types[i];\r
+ if (!sp->ttis[j].isdst)\r
+ {\r
+ theirstdoffset = -sp->ttis[j].gmtoff;\r
+ break;\r
+ }\r
+ }\r
+ theirdstoffset = 0;\r
+ for (i = 0; i < sp->timecnt; i++)\r
+ {\r
+ j = sp->types[i];\r
+ if (sp->ttis[j].isdst)\r
+ {\r
+ theirdstoffset = -sp->ttis[j].gmtoff;\r
+ break;\r
+ }\r
+ }\r
+ /* Initially we're assumed to be in standard time. */\r
+ isdst = FALSE;\r
+ theiroffset = theirstdoffset;\r
+ /* Now juggle transition times and types tracking offsets as you do. */\r
+ for (i = 0; i < sp->timecnt; i++)\r
+ {\r
+ j = sp->types[i];\r
+ sp->types[i] = sp->ttis[j].isdst;\r
+ if (sp->ttis[j].ttisgmt)\r
+ {\r
+ /* No adjustment to transition time */\r
+ }\r
+ else\r
+ {\r
+ /* If summer time is in effect, and the\r
+ * transition time was not specified as\r
+ * standard time, add the summer time\r
+ * offset to the transition time;\r
+ * otherwise, add the standard time\r
+ * offset to the transition time. */\r
+ /* Transitions from DST to DDST\r
+ * will effectively disappear since\r
+ * POSIX provides for only one DST\r
+ * offset. */\r
+ if (isdst && !sp->ttis[j].ttisstd)\r
+ sp->ats[i] += (dstoffset - theirdstoffset);\r
+ else\r
+ sp->ats[i] += (stdoffset - theirstdoffset);\r
+ }\r
+ theiroffset = -sp->ttis[j].gmtoff;\r
+ if (sp->ttis[j].isdst)\r
+ theirdstoffset = theiroffset;\r
+ else\r
+ theirstdoffset = theiroffset;\r
+ }\r
+ /* Finally, fill in ttis. ttisstd and ttisgmt need not be handled. */\r
+ sp->ttis[0].gmtoff = -stdoffset;\r
+ sp->ttis[0].isdst = FALSE;\r
+ sp->ttis[0].abbrind = 0;\r
+ sp->ttis[1].gmtoff = -dstoffset;\r
+ sp->ttis[1].isdst = TRUE;\r
+ sp->ttis[1].abbrind = stdlen + 1;\r
+ sp->typecnt = 2;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ dstlen = 0;\r
+ sp->typecnt = 1; /* Only standard time */\r
+ sp->timecnt = 0;\r
+ sp->ttis[0].gmtoff = -stdoffset;\r
+ sp->ttis[0].isdst = 0;\r
+ sp->ttis[0].abbrind = 0;\r
+ }\r
+ sp->charcnt = stdlen + 1;\r
+ if (dstlen != 0)\r
+ sp->charcnt += dstlen + 1;\r
+ if ((size_t) sp->charcnt > sizeof(sp->chars))\r
+ return -1;\r
+ cp = sp->chars;\r
+ strncpy(cp, stdname, stdlen);\r
+ cp += stdlen;\r
+ *cp++ = '\0';\r
+ if (dstlen != 0)\r
+ {\r
+ strncpy(cp, dstname, dstlen);\r
+ cp[dstlen] = '\0';\r
+ }\r
+ return 0;\r
+}\r
+/*- End of function --------------------------------------------------------*/\r
+\r
+static void tz_set(tz_t *tz, const char *tzstring)\r
+{\r
+ const char *name = "";\r
+ struct tz_state_s *lclptr = &tz->state;\r
+\r
+ if (tzstring)\r
+ name = tzstring;\r
+\r
+ /* See if we are already set OK */\r
+ if (tz->lcl_is_set > 0 && strcmp(tz->lcl_tzname, name) == 0)\r
+ return;\r
+ tz->lcl_is_set = strlen(name) < sizeof(tz->lcl_tzname);\r
+ if (tz->lcl_is_set)\r
+ strcpy(tz->lcl_tzname, name);\r
+\r
+ if (name[0] == '\0')\r
+ {\r
+ /* User wants it fast rather than right, so, we're off a little. */\r
+ lclptr->leapcnt = 0;\r
+ lclptr->timecnt = 0;\r
+ lclptr->typecnt = 0;\r
+ lclptr->ttis[0].isdst = 0;\r
+ lclptr->ttis[0].gmtoff = 0;\r
+ lclptr->ttis[0].abbrind = 0;\r
+ strcpy(lclptr->chars, gmt);\r
+ }\r
+ else if (name[0] == ':' || tzparse(name, lclptr, FALSE) != 0)\r
+ {\r
+ tzparse(gmt, lclptr, TRUE);\r
+ }\r
+ set_tzname(tz);\r
+}\r
+/*- End of function --------------------------------------------------------*/\r
+\r
+SPAN_DECLARE(int) tz_localtime(tz_t *tz, struct tm *tmp, time_t t)\r
+{\r
+ struct tz_state_s *sp;\r
+ const struct tz_ttinfo_s *ttisp;\r
+ int i;\r
+\r
+ sp = &tz->state;\r
+\r
+ if (sp->timecnt == 0 || t < sp->ats[0])\r
+ {\r
+ i = 0;\r
+ while (sp->ttis[i].isdst)\r
+ {\r
+ if (++i >= sp->typecnt)\r
+ {\r
+ i = 0;\r
+ break;\r
+ }\r
+ }\r
+ }\r
+ else\r
+ {\r
+ for (i = 1; i < sp->timecnt; i++)\r
+ {\r
+ if (t < sp->ats[i])\r
+ break;\r
+ }\r
+ i = (int) sp->types[i - 1];\r
+ }\r
+ ttisp = &sp->ttis[i];\r
+ time_sub(&t, ttisp->gmtoff, sp, tmp);\r
+ tmp->tm_isdst = ttisp->isdst;\r
+ tz->tzname[tmp->tm_isdst] = &sp->chars[ttisp->abbrind];\r
+ return 0;\r
+}\r
+/*- End of function --------------------------------------------------------*/\r
+\r
+SPAN_DECLARE(const char *) tz_tzname(tz_t *tz, int isdst)\r
+{\r
+ return tz->tzname[(!isdst) ? 0 : 1];\r
+}\r
+/*- End of function --------------------------------------------------------*/\r
+\r
+SPAN_DECLARE(tz_t *) tz_init(tz_t *tz, const char *tzstring)\r
+{\r
+ if (tz == NULL)\r
+ {\r
+ if ((tz = (tz_t *) malloc(sizeof(*tz))) == NULL)\r
+ return NULL;\r
+ }\r
+ memset(tz, 0, sizeof(*tz));\r
+ tz->tzname[0] =\r
+ tz->tzname[1] = wildabbr;\r
+ tz_set(tz, tzstring);\r
+ return tz;\r
+}\r
+/*- End of function --------------------------------------------------------*/\r
+\r
+SPAN_DECLARE(int) tz_release(tz_t *tz)\r
+{\r
+ return 0;\r
+}\r
+/*- End of function --------------------------------------------------------*/\r
+\r
+SPAN_DECLARE(int) tz_free(tz_t *tz)\r
+{\r
+ if (tz)\r
+ free(tz);\r
+ return 0;\r
+}\r
+/*- End of function --------------------------------------------------------*/\r
+/*- End of file ------------------------------------------------------------*/\r
--- /dev/null
+/*
+ * SpanDSP - a series of DSP components for telephony
+ *
+ * generate_striped_pages.c
+ *
+ * Written by Steve Underwood <steveu@coppice.org>
+ *
+ * Copyright (C) 2010 Steve Underwood
+ *
+ * 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 version 2, as
+ * published by the Free Software Foundation.
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*! \file */
+
+/*
+ This program generates an TIFF image as a number of small image striped, rather than
+ the usual all in one page FAX images usually consist of in TIFF files.
+ */
+
+#if defined(HAVE_CONFIG_H)
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <time.h>
+#include <memory.h>
+#include <string.h>
+#if defined(HAVE_TGMATH_H)
+#include <tgmath.h>
+#endif
+#if defined(HAVE_MATH_H)
+#include <math.h>
+#endif
+#include <tiffio.h>
+
+#include "spandsp.h"
+
+#define IMAGE_WIDTH 1728
+#define IMAGE_LENGTH 2600
+#define ROWS_PER_STRIPE 37
+
+int main(int argc, char *argv[])
+{
+ TIFF *tiff_file;
+ uint8_t image_buffer[10000];
+ int image_size;
+ time_t now;
+ struct tm *tm;
+ char buf[256 + 1];
+ int i;
+
+ if ((tiff_file = TIFFOpen("striped.tif", "w")) == NULL)
+ return -1;
+
+ TIFFSetField(tiff_file, TIFFTAG_COMPRESSION, COMPRESSION_CCITT_T6);
+ TIFFSetField(tiff_file, TIFFTAG_BITSPERSAMPLE, 1);
+ TIFFSetField(tiff_file, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
+ TIFFSetField(tiff_file, TIFFTAG_SAMPLESPERPIXEL, 1);
+ TIFFSetField(tiff_file, TIFFTAG_ROWSPERSTRIP, (int32_t) ROWS_PER_STRIPE);
+ TIFFSetField(tiff_file, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
+ TIFFSetField(tiff_file, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISWHITE);
+ TIFFSetField(tiff_file, TIFFTAG_FILLORDER, FILLORDER_LSB2MSB);
+ TIFFSetField(tiff_file, TIFFTAG_XRESOLUTION, 204.0f);
+ TIFFSetField(tiff_file, TIFFTAG_YRESOLUTION, 196.0f);
+ TIFFSetField(tiff_file, TIFFTAG_RESOLUTIONUNIT, RESUNIT_INCH);
+ TIFFSetField(tiff_file, TIFFTAG_SOFTWARE, "Spandsp");
+ TIFFSetField(tiff_file, TIFFTAG_HOSTCOMPUTER, "host");
+ TIFFSetField(tiff_file, TIFFTAG_FAXSUBADDRESS, "1111");
+ TIFFSetField(tiff_file, TIFFTAG_IMAGEDESCRIPTION, "Image in stripes");
+ TIFFSetField(tiff_file, TIFFTAG_MAKE, "spandsp");
+ TIFFSetField(tiff_file, TIFFTAG_MODEL, "testy");
+
+ time(&now);
+ tm = localtime(&now);
+ sprintf(buf,
+ "%4d/%02d/%02d %02d:%02d:%02d",
+ tm->tm_year + 1900,
+ tm->tm_mon + 1,
+ tm->tm_mday,
+ tm->tm_hour,
+ tm->tm_min,
+ tm->tm_sec);
+ TIFFSetField(tiff_file, TIFFTAG_DATETIME, buf);
+ TIFFSetField(tiff_file, TIFFTAG_FAXRECVTIME, 10);
+ TIFFSetField(tiff_file, TIFFTAG_IMAGEWIDTH, IMAGE_WIDTH);
+ TIFFSetField(tiff_file, TIFFTAG_IMAGELENGTH, IMAGE_LENGTH);
+ TIFFSetField(tiff_file, TIFFTAG_PAGENUMBER, 0, 1);
+
+ image_size = IMAGE_WIDTH*ROWS_PER_STRIPE/8;
+ memset(image_buffer, 0x18, image_size);
+
+ for (i = 0; i < IMAGE_LENGTH/ROWS_PER_STRIPE; i++)
+ {
+ if (IMAGE_LENGTH > (i + 1)*ROWS_PER_STRIPE)
+ image_size = IMAGE_WIDTH*ROWS_PER_STRIPE/8;
+ else
+ image_size = IMAGE_WIDTH*(IMAGE_LENGTH - i*ROWS_PER_STRIPE)/8;
+ if (TIFFWriteEncodedStrip(tiff_file, i, image_buffer, image_size) < 0)
+ return -1;
+ }
+
+ TIFFWriteDirectory(tiff_file);
+ TIFFClose(tiff_file);
+ return 0;
+}
--- /dev/null
+/*
+ * SpanDSP - a series of DSP components for telephony
+ *
+ * bitstream_tests.c
+ *
+ * Written by Steve Underwood <steveu@coppice.org>
+ *
+ * Copyright (C) 2007 Steve Underwood
+ *
+ * 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 version 2, as
+ * published by the Free Software Foundation.
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*! \page bitstream_tests_page Bitstream tests
+\section bitstream_tests_page_sec_1 What does it do?
+
+\section bitstream_tests_page_sec_2 How is it used?
+*/
+
+#if defined(HAVE_CONFIG_H)
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <string.h>
+#include <assert.h>
+
+//#if defined(WITH_SPANDSP_INTERNALS)
+#define SPANDSP_EXPOSE_INTERNAL_STRUCTURES
+//#endif
+
+#include "spandsp.h"
+
+uint8_t buffer[256];
+
+#define PATTERN 0x11111111
+#define SEQUENCE_LENGTH 17
+
+uint8_t left[] =
+{
+ 0x28, /* 2 of 4, 3, 2, 1 */
+ 0xC8, /* 1 of 6, 5, 2 of 4 */
+ 0xAE, /* 3 of 7, 5 of 6 */
+ 0x67, /* 4 of 8, 4 of 7 */
+ 0x74, /* 4 of 9, 4 of 8 */
+ 0x43, /* 3 of 10, 5 of 9 */
+ 0x32, /* 1 of 11, 7 of 10 */
+ 0xAA, /* 8 of 11 */
+ 0xAE, /* 6 of 12, 2 of 11 */
+ 0xED, /* 2 of 13, 6 of 12 */
+ 0x99, /* 8 of 13 */
+ 0x8E, /* 5 of 14, 3 of 13 */
+ 0xEE, /* 8 of 14 */
+ 0xEE, /* 7 of 15, 1 of 14 */
+ 0xEE, /* 8 of 15 */
+ 0xFF, /* 8 of 16 */
+ 0xFF, /* 8 of 16 */
+ 0x88, /* 8 of 17 */
+ 0x88, /* 8 of 17 */
+ 0x00 /* 1 of 17 */
+};
+uint8_t right[] =
+{
+ 0xD2, /* 1, 2, 3, 2 of 4 */
+ 0x90, /* 2 of 4, 5, 1 of 6 */
+ 0xCA, /* 5 of 6, 3 of 7 */
+ 0x7C, /* 4 of 7, 4 of 8 */
+ 0x87, /* 4 of 8, 4 of 9 */
+ 0x28, /* 5 of 9, 3 of 10 */
+ 0x33, /* 7 of 10, 1 of 11 */
+ 0x55, /* 8 of 11 */
+ 0xED, /* 2 of 11, 6 of 12 */
+ 0x2E, /* 6 of 12, 2 of 13 */
+ 0x33, /* 8 of 13 */
+ 0xEB, /* 3 of 13, 5 of 14 */
+ 0xEE, /* 8 of 14 */
+ 0xDC, /* 1 of 14, 7 of 15 */
+ 0xDD, /* 8 of 15 */
+ 0xFF, /* 8 of 16 */
+ 0xFF, /* 8 of 16 */
+ 0x10, /* 8 of 17 */
+ 0x11, /* 8 of 17 */
+ 0x01 /* 1 of 17 */
+};
+
+int main(int argc, char *argv[])
+{
+ int i;
+ bitstream_state_t state;
+ bitstream_state_t *s;
+ const uint8_t *r;
+ uint8_t *w;
+ uint8_t *cc;
+ unsigned int x;
+ int total_bits;
+
+ s = bitstream_init(&state, TRUE);
+ w = buffer;
+ total_bits = 0;
+ for (i = 0; i < SEQUENCE_LENGTH; i++)
+ {
+ bitstream_put(s, &w, PATTERN*i, i + 1);
+ total_bits += (i + 1);
+ }
+ bitstream_flush(s, &w);
+ printf("%d bits written\n", total_bits);
+
+ for (cc = buffer; cc < w; cc++)
+ printf("%02X ", *cc);
+ printf("\n");
+ for (cc = right; cc < right + sizeof(right); cc++)
+ printf("%02X ", *cc);
+ printf("\n");
+
+ if ((w - buffer) != sizeof(right) || memcmp(buffer, right, sizeof(right)))
+ {
+ printf("Test failed\n");
+ exit(2);
+ }
+
+ s = bitstream_init(&state, TRUE);
+ r = buffer;
+ for (i = 0; i < SEQUENCE_LENGTH; i++)
+ {
+ x = bitstream_get(s, &r, i + 1);
+ if (x != ((PATTERN*i) & ((1 << (i + 1)) - 1)))
+ {
+ printf("Error 0x%X 0x%X\n", x, ((PATTERN*i) & ((1 << (i + 1)) - 1)));
+ printf("Test failed\n");
+ exit(2);
+ }
+ }
+
+ s = bitstream_init(&state, FALSE);
+ w = buffer;
+ total_bits = 0;
+ for (i = 0; i < SEQUENCE_LENGTH; i++)
+ {
+ bitstream_put(s, &w, PATTERN*i, i + 1);
+ total_bits += (i + 1);
+ }
+ bitstream_flush(s, &w);
+ printf("%d bits written\n", total_bits);
+
+ for (cc = buffer; cc < w; cc++)
+ printf("%02X ", *cc);
+ printf("\n");
+ for (cc = left; cc < left + sizeof(left); cc++)
+ printf("%02X ", *cc);
+ printf("\n");
+
+ if ((w - buffer) != sizeof(left) || memcmp(buffer, left, sizeof(left)))
+ {
+ printf("Test failed\n");
+ exit(2);
+ }
+
+ s = bitstream_init(&state, FALSE);
+ r = buffer;
+ for (i = 0; i < SEQUENCE_LENGTH; i++)
+ {
+ x = bitstream_get(s, &r, i + 1);
+ if (x != ((PATTERN*i) & ((1 << (i + 1)) - 1)))
+ {
+ printf("Error 0x%X 0x%X\n", x, ((PATTERN*i) & ((1 << (i + 1)) - 1)));
+ printf("Test failed\n");
+ exit(2);
+ }
+ }
+
+ printf("Tests passed.\n");
+ return 0;
+}
+/*- End of function --------------------------------------------------------*/
+/*- End of file ------------------------------------------------------------*/
--- /dev/null
+/*
+ * SpanDSP - a series of DSP components for telephony
+ *
+ * image_translate_tests.c - Tests for the image translation routines.
+ *
+ * Written by Steve Underwood <steveu@coppice.org>
+ *
+ * Copyright (C) 2009 Steve Underwood
+ *
+ * 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 version 2, as
+ * published by the Free Software Foundation.
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*! \file */
+
+/*! \page image_translate_tests_page Image translation tests
+\section image_translate_tests_page_sec_1 What does it do?
+*/
+
+#if defined(HAVE_CONFIG_H)
+#include "config.h"
+#endif
+
+#include <inttypes.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <assert.h>
+#include <math.h>
+#include <errno.h>
+
+//#if defined(WITH_SPANDSP_INTERNALS)
+#define SPANDSP_EXPOSE_INTERNAL_STRUCTURES
+//#endif
+
+#include "spandsp.h"
+
+#define INPUT_TIFF_FILE_NAME "../test-data/local/lenna-colour.tif"
+
+typedef struct
+{
+ const uint8_t *image;
+ int width;
+ int length;
+ int current_row;
+ int bytes_per_pixel;
+} image_descriptor_t;
+
+static void display_row(int row, int width, uint8_t buf[])
+{
+ int i;
+ int test_pixel;
+
+ printf("%3d: ", row);
+ for (i = 0; i < width; i++)
+ {
+ test_pixel = (buf[i >> 3] >> (7 - (i & 7))) & 0x01;
+ printf("%c", (test_pixel) ? ' ' : '@');
+ }
+ printf("\n");
+}
+/*- End of function --------------------------------------------------------*/
+
+static int test_dithered_50_by_50(int row, int width, uint8_t buf[])
+{
+ static const char *image[50] =
+ {
+ " 0: @ @ @ @ @ @ @ @@ @@@@@@@@@@@@@@",
+ " 1: @ @ @ @ @ @ @ @ @ @@ @@ @ @ @ @ @@@@",
+ " 2: @ @ @ @ @ @ @ @@ @ @ @@@ @@@@@@ @@",
+ " 3: @ @ @ @ @ @ @ @ @ @ @@@ @@@@ @@@@@@",
+ " 4: @ @ @ @ @ @ @ @ @ @@@ @ @@ @@@@ @@@@",
+ " 5: @ @ @ @ @ @ @ @ @ @ @ @ @@ @@@ @@@@@@",
+ " 6: @ @ @ @ @ @ @@ @@@@ @@@ @@@ @@ @",
+ " 7: @ @ @ @ @ @ @ @ @ @@ @@ @ @@ @@@@@@",
+ " 8: @ @ @ @ @ @ @ @ @@@ @@@@@@@@@ @@@",
+ " 9: @ @ @ @ @ @ @ @ @ @ @ @ @ @@ @ @ @ @@@@@",
+ " 10: @ @ @ @ @ @@ @ @@ @@@@@@@@@@@@@",
+ " 11: @ @ @ @ @ @ @ @ @ @@ @@ @ @ @@ @@ @@",
+ " 12: @ @ @ @ @ @ @ @ @ @@ @@@@@@ @@@@@@@",
+ " 13: @ @ @ @ @ @ @ @ @@ @ @ @ @ @@@@@ @@@",
+ " 14: @ @ @ @ @ @ @ @@ @@@ @@@ @ @@@@@@",
+ " 15: @ @ @ @ @ @ @ @ @ @ @ @ @@ @@@@@@@ @@",
+ " 16: @ @ @ @ @ @ @ @ @@ @@ @@ @@ @@@@@",
+ " 17: @ @ @ @ @ @ @ @ @ @@ @@ @@ @@ @@@@@@@",
+ " 18: @ @ @ @ @ @ @ @ @ @ @ @@ @@@@@ @ @@@",
+ " 19: @ @ @ @ @ @ @ @ @@@ @@@ @ @@@@@@@",
+ " 20: @ @ @ @ @ @ @ @ @ @@ @ @@ @@@@@@ @@@ @",
+ " 21: @ @ @ @ @ @ @ @@ @@ @ @ @@@@@@@",
+ " 22: @ @ @ @ @ @@ @ @ @ @ @@ @@@@@@@ @@ @@",
+ " 23: @ @ @ @ @ @ @ @@ @@ @@ @ @ @@@@@@@",
+ " 24: @ @ @ @ @@ @ @ @@ @ @ @@@@@@ @@@@@",
+ " 25: @ @ @ @ @ @ @ @ @ @ @@@@@ @ @@@@ @@",
+ " 26: @ @ @ @ @ @ @ @ @ @@ @ @ @@@@ @@@@@",
+ " 27: @ @ @ @ @ @ @ @ @ @@ @ @@ @@@ @@@@@@@",
+ " 28: @ @ @ @ @ @ @ @ @@@ @@@ @@@ @@ @@",
+ " 29: @ @ @ @ @ @ @ @ @@ @ @@ @ @@ @@@@@@@",
+ " 30: @ @ @ @ @ @ @ @ @ @ @ @@@@@@@ @@@@@",
+ " 31: @ @ @ @ @ @ @ @ @ @@@@ @ @ @@@@ @@",
+ " 32: @ @ @ @ @ @ @ @ @ @@ @ @ @@@@@@@ @@@@@",
+ " 33: @ @ @ @ @ @ @ @@@ @@ @ @ @@@@@@@",
+ " 34: @ @ @ @ @ @ @ @ @ @ @ @ @@ @@@@@@ @ @@@",
+ " 35: @ @ @ @ @ @ @ @ @ @@ @ @ @@@@@@@",
+ " 36: @ @ @ @ @ @ @ @ @@ @@@ @@@@@@@ @@@ @",
+ " 37: @ @ @ @ @ @ @ @ @ @ @@ @ @ @@@@@@@",
+ " 38: @ @ @ @ @ @ @ @ @@ @@ @@@@@@@ @@ @@",
+ " 39: @ @ @ @ @ @ @ @ @ @ @@ @@ @ @ @@@@@@@",
+ " 40: @ @ @ @ @ @ @ @ @ @@ @@ @@@@ @@@@@",
+ " 41: @ @ @ @ @ @ @ @ @ @@ @ @@ @@@ @@@@ @@",
+ " 42: @ @ @ @ @ @ @@ @@ @@ @@ @@@ @@@@@",
+ " 43: @ @ @ @ @ @ @ @ @ @ @@ @@@ @@@@@@@",
+ " 44: @ @ @ @ @ @ @ @ @ @ @@@ @@@ @@@ @@ @@",
+ " 45: @ @ @ @ @ @ @@ @ @ @ @@ @@@@@@@",
+ " 46: @ @ @ @ @ @ @ @ @ @ @ @@ @@@@@@@@@ @@@@@",
+ " 47: @ @ @ @ @ @ @ @ @ @@ @ @ @ @@@@ @@",
+ " 48: @ @ @ @ @ @ @@ @ @@ @@@@@@ @@@@@",
+ " 49: @ @ @ @ @ @ @ @ @ @ @ @@ @@ @ @ @@@@@@@"
+ };
+ int i;
+ int match;
+ int ref_pixel;
+ int test_pixel;
+
+ match = 0;
+ for (i = 0; i < width; i++)
+ {
+ ref_pixel = (image[row][i + 5] == ' ');
+ test_pixel = (buf[i >> 3] >> (7 - (i & 7))) & 0x01;
+ if (ref_pixel != test_pixel)
+ match = -1;
+ }
+ return match;
+}
+/*- End of function --------------------------------------------------------*/
+
+static int row_read(void *user_data, uint8_t buf[], size_t len)
+{
+ image_descriptor_t *im;
+
+ im = (image_descriptor_t *) user_data;
+ if (im->current_row >= im->length)
+ return 0;
+ memcpy(buf, &im->image[im->current_row*im->width*im->bytes_per_pixel], len);
+ im->current_row++;
+ return len;
+}
+/*- End of function --------------------------------------------------------*/
+
+static void get_flattened_image(image_translate_state_t *s, int compare)
+{
+ int i;
+ int len;
+ uint8_t row_buf[5000];
+
+ for (i = 0; i < s->output_length; i++)
+ {
+ if ((len = image_translate_row(s, row_buf, (s->output_width + 7)/8)) != (s->output_width + 7)/8)
+ {
+ printf("Image finished early - %d %d\n", len, (s->output_width + 7)/8);
+ exit(2);
+ }
+ display_row(i, s->output_width, row_buf);
+ if (compare)
+ {
+ if (test_dithered_50_by_50(i, s->output_width, row_buf))
+ {
+ printf("Dithered image mismatch at row %d\n", i);
+ printf("Test failed\n");
+ exit(2);
+ }
+ }
+ }
+ if ((len = image_translate_row(s, row_buf, (s->output_width + 7)/8)) != 0)
+ {
+ printf("Image finished late - %d %d\n", len, (s->output_width + 7)/8);
+ exit(2);
+ }
+}
+/*- End of function --------------------------------------------------------*/
+
+static void dither_tests_gray16(void)
+{
+ int i;
+ int j;
+ image_translate_state_t bw;
+ image_translate_state_t *s = &bw;
+ uint16_t image[50*50];
+ image_descriptor_t im;
+
+ printf("Dithering from a 16 bit per sample gray scale to bi-level\n");
+ im.image = (const uint8_t *) image;
+ im.width = 50;
+ im.length = 50;
+ im.bytes_per_pixel = 2;
+ im.current_row = 0;
+
+ for (i = 0; i < im.length; i++)
+ {
+ for (j = 0; j < im.width; j++)
+ image[i*im.width + j] = j*1200;
+ }
+
+ s = image_translate_init(s, IMAGE_TRANSLATE_FROM_GRAY_16, im.width, im.length, -1, row_read, &im);
+ get_flattened_image(s, TRUE);
+}
+/*- End of function --------------------------------------------------------*/
+
+static void dither_tests_gray8(void)
+{
+ int i;
+ int j;
+ image_translate_state_t bw;
+ image_translate_state_t *s = &bw;
+ uint8_t image[50*50];
+ image_descriptor_t im;
+
+ printf("Dithering from a 8 bit per sample gray scale to bi-level\n");
+ im.image = image;
+ im.width = 50;
+ im.length = 50;
+ im.bytes_per_pixel = 1;
+ im.current_row = 0;
+
+ for (i = 0; i < im.length; i++)
+ {
+ for (j = 0; j < im.width; j++)
+ image[i*im.width + j] = j*1200/256;
+ }
+ s = image_translate_init(s, IMAGE_TRANSLATE_FROM_GRAY_8, im.width, im.length, -1, row_read, &im);
+ get_flattened_image(s, TRUE);
+}
+/*- End of function --------------------------------------------------------*/
+
+static void dither_tests_colour16(void)
+{
+ int i;
+ int j;
+ image_translate_state_t bw;
+ image_translate_state_t *s = &bw;
+ uint16_t image[50*50*3];
+ image_descriptor_t im;
+
+ printf("Dithering from a 3x16 bit per sample colour to bi-level\n");
+ im.image = (const uint8_t *) image;
+ im.width = 50;
+ im.length = 50;
+ im.bytes_per_pixel = 6;
+ im.current_row = 0;
+
+ for (i = 0; i < im.length; i++)
+ {
+ for (j = 0; j < im.width; j++)
+ {
+ image[i*3*im.width + 3*j + 0] = j*1200;
+ image[i*3*im.width + 3*j + 1] = j*1200;
+ image[i*3*im.width + 3*j + 2] = j*1200;
+ }
+ }
+ s = image_translate_init(s, IMAGE_TRANSLATE_FROM_COLOUR_16, im.width, im.length, -1, row_read, &im);
+ get_flattened_image(s, TRUE);
+}
+/*- End of function --------------------------------------------------------*/
+
+static void dither_tests_colour8(void)
+{
+ int i;
+ int j;
+ image_translate_state_t bw;
+ image_translate_state_t *s = &bw;
+ uint8_t image[50*50*3];
+ image_descriptor_t im;
+
+ printf("Dithering from a 3x8 bit per sample colour to bi-level\n");
+ im.image = image;
+ im.width = 50;
+ im.length = 50;
+ im.bytes_per_pixel = 3;
+ im.current_row = 0;
+
+ for (i = 0; i < im.length; i++)
+ {
+ for (j = 0; j < im.width; j++)
+ {
+ image[i*3*im.width + 3*j + 0] = j*1200/256;
+ image[i*3*im.width + 3*j + 1] = j*1200/256;
+ image[i*3*im.width + 3*j + 2] = j*1200/256;
+ }
+ }
+
+ s = image_translate_init(s, IMAGE_TRANSLATE_FROM_COLOUR_8, im.width, im.length, -1, row_read, &im);
+ get_flattened_image(s, TRUE);
+}
+/*- End of function --------------------------------------------------------*/
+
+static void grow_tests_colour8(void)
+{
+ int i;
+ int j;
+ image_translate_state_t resize;
+ image_translate_state_t *s1 = &resize;
+ uint8_t image[50*50*3];
+ image_descriptor_t im;
+
+ printf("Image growth tests\n");
+ im.image = image;
+ im.width = 50;
+ im.length = 50;
+ im.bytes_per_pixel = 3;
+ im.current_row = 0;
+
+ for (i = 0; i < im.length; i++)
+ {
+ for (j = 0; j < im.width; j++)
+ {
+ image[i*3*im.width + 3*j + 0] = j*1200/256;
+ image[i*3*im.width + 3*j + 1] = j*1200/256;
+ image[i*3*im.width + 3*j + 2] = j*1200/256;
+ }
+ }
+
+ s1 = image_translate_init(s1, IMAGE_TRANSLATE_FROM_COLOUR_8, im.width, im.length, 200, row_read, &im);
+
+ get_flattened_image(s1, FALSE);
+}
+/*- End of function --------------------------------------------------------*/
+
+static void lenna_tests(int output_width, const char *file)
+{
+ TIFF *in_file;
+ TIFF *out_file;
+ int image_width;
+ int image_length;
+ int output_length;
+ uint8_t *image;
+ uint8_t *image2;
+ int len;
+ int total;
+ int16_t bits_per_sample;
+ int16_t samples_per_pixel;
+ int i;
+ int n;
+ image_translate_state_t bw;
+ image_translate_state_t *s = &bw;
+ image_descriptor_t im;
+
+ printf("Dithering Lenna from colour to bi-level test\n");
+ if ((in_file = TIFFOpen(INPUT_TIFF_FILE_NAME, "r")) == NULL)
+ return;
+ image_width = 0;
+ TIFFGetField(in_file, TIFFTAG_IMAGEWIDTH, &image_width);
+ if (image_width <= 0)
+ return;
+ image_length = 0;
+ TIFFGetField(in_file, TIFFTAG_IMAGELENGTH, &image_length);
+ if (image_length <= 0)
+ return;
+ bits_per_sample = 0;
+ TIFFGetField(in_file, TIFFTAG_BITSPERSAMPLE, &bits_per_sample);
+ samples_per_pixel = 0;
+ TIFFGetField(in_file, TIFFTAG_SAMPLESPERPIXEL, &samples_per_pixel);
+ printf("Original image is %d x %d, %d bits per sample, %d samples per pixel\n", image_width, image_length, bits_per_sample, samples_per_pixel);
+ if ((image = malloc(image_width*image_length*samples_per_pixel)) == NULL)
+ return;
+ for (total = 0, i = 0; i < 1000; i++)
+ {
+ len = TIFFReadEncodedStrip(in_file, i, &image[total], image_width*image_length*samples_per_pixel - total);
+ if (len <= 0)
+ break;
+ total += len;
+ if (total == image_width*image_length*samples_per_pixel)
+ {
+ printf("Done\n");
+ break;
+ }
+ }
+ printf("Image size %d %d\n", total, image_width*image_length*samples_per_pixel);
+ TIFFClose(in_file);
+
+ im.image = image;
+ im.width = image_width;
+ im.length = image_length;
+ im.current_row = 0;
+ im.bytes_per_pixel = samples_per_pixel;
+
+ s = image_translate_init(s, IMAGE_TRANSLATE_FROM_COLOUR_8, image_width, image_length, output_width, row_read, &im);
+ output_width = image_translate_get_output_width(s);
+ output_length = image_translate_get_output_length(s);
+
+ if ((out_file = TIFFOpen(file, "w")) == NULL)
+ return;
+ TIFFSetField(out_file, TIFFTAG_IMAGEWIDTH, output_width);
+ TIFFSetField(out_file, TIFFTAG_IMAGELENGTH, output_length);
+ TIFFSetField(out_file, TIFFTAG_BITSPERSAMPLE, 1);
+ TIFFSetField(out_file, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
+ TIFFSetField(out_file, TIFFTAG_SAMPLESPERPIXEL, 1);
+ TIFFSetField(out_file, TIFFTAG_ROWSPERSTRIP, -1);
+ TIFFSetField(out_file, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
+ TIFFSetField(out_file, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISWHITE);
+ TIFFSetField(out_file, TIFFTAG_FILLORDER, FILLORDER_LSB2MSB);
+ TIFFSetField(out_file, TIFFTAG_PAGENUMBER, 0, 1);
+ TIFFSetField(out_file, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISWHITE);
+ TIFFSetField(out_file, TIFFTAG_FILLORDER, FILLORDER_LSB2MSB);
+
+ printf("Input %d x %d, output %d x %d\n", image_width, image_length, output_width, output_length);
+
+ if ((image2 = malloc(output_width*output_length/8)) == NULL)
+ return;
+ memset(image2, 0, output_width*output_length/8);
+ n = 0;
+ for (i = 0; i < output_length; i++)
+ n += image_translate_row(s, &image2[n], output_width/8);
+
+ TIFFWriteEncodedStrip(out_file, 0, image2, output_width*output_length/8);
+ TIFFWriteDirectory(out_file);
+ TIFFClose(out_file);
+}
+/*- End of function --------------------------------------------------------*/
+
+int main(int argc, char **argv)
+{
+#if 1
+ dither_tests_gray16();
+ dither_tests_gray8();
+ dither_tests_colour16();
+ dither_tests_colour8();
+#endif
+#if 1
+ grow_tests_colour8();
+#endif
+#if 1
+ lenna_tests(0, "lenna-bw.tif");
+ lenna_tests(1728, "lenna-bw-1728.tif");
+ lenna_tests(200, "lenna-bw-200.tif");
+#endif
+ printf("Tests passed.\n");
+ return 0;
+}
+/*- End of function --------------------------------------------------------*/
+/*- End of file ------------------------------------------------------------*/
--- /dev/null
+/*
+ * SpanDSP - a series of DSP components for telephony
+ *
+ * saturated_tests.c
+ *
+ * Written by Steve Underwood <steveu@coppice.org>
+ *
+ * Copyright (C) 2004 Steve Underwood
+ *
+ * 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 version 2, as
+ * published by the Free Software Foundation.
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*! \page saturated_tests_page Saturated arithmetic function tests
+\section saturated_tests_page_sec_1 What does it do?
+???.
+
+\section saturated_tests_page_sec_2 How does it work?
+???.
+*/
+
+#if defined(HAVE_CONFIG_H)
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#define SPANDSP_EXPOSE_INTERNAL_STRUCTURES
+#include "spandsp.h"
+
+int main(int argc, char *argv[])
+{
+ printf("Testing 16 bit saturation\n");
+ if (saturate16(10000) != 10000
+ ||
+ saturate16(-10000) != -10000
+ ||
+ saturate16(32767) != 32767
+ ||
+ saturate16(-32768) != -32768
+ ||
+ saturate16(32768) != 32767
+ ||
+ saturate16(-32769) != -32768)
+ {
+ printf("Test failed.\n");
+ exit(2);
+ }
+ printf("Testing 15 bit saturation\n");
+ if (saturate15(10000) != 10000
+ ||
+ saturate15(-10000) != -10000
+ ||
+ saturate15(16383) != 16383
+ ||
+ saturate15(-16384) != -16384
+ ||
+ saturate15(16384) != 16383
+ ||
+ saturate15(-16385) != -16384)
+ {
+ printf("Test failed.\n");
+ exit(2);
+ }
+ printf("Testing 16 bit unsigned saturation\n");
+ if (saturateu16(10000) != 10000
+ ||
+ saturateu16(32767) != 32767
+ ||
+ saturateu16(65535) != 65535
+ ||
+ saturateu16(65536) != 65535)
+ {
+ printf("Test failed.\n");
+ exit(2);
+ }
+ printf("Testing 8 bit unsigned saturation\n");
+ if (saturateu8(100) != 100
+ ||
+ saturateu8(127) != 127
+ ||
+ saturateu8(255) != 255
+ ||
+ saturateu8(256) != 255)
+ {
+ printf("Test failed.\n");
+ exit(2);
+ }
+ printf("Testing 16 bit saturation from float\n");
+ if (fsaturatef(10000.0f) != 10000
+ ||
+ fsaturatef(-10000.0f) != -10000
+ ||
+ fsaturatef(32767.0f) != 32767
+ ||
+ fsaturatef(-32768.0f) != -32768
+ ||
+ fsaturatef(32768.0f) != 32767
+ ||
+ fsaturatef(-32769.0f) != -32768)
+ {
+ printf("Test failed.\n");
+ exit(2);
+ }
+ printf("Testing 16 bit saturation from double\n");
+ if (fsaturate(10000.0) != 10000
+ ||
+ fsaturate(-10000.0) != -10000
+ ||
+ fsaturate(32767.0) != 32767
+ ||
+ fsaturate(-32768.0) != -32768
+ ||
+ fsaturate(32768.0) != 32767
+ ||
+ fsaturate(-32769.0) != -32768)
+ {
+ printf("Test failed.\n");
+ exit(2);
+ }
+ printf("Testing 16 bit fast saturation from float\n");
+ if (ffastsaturatef(10000.0f) != 10000
+ ||
+ ffastsaturatef(-10000.0f) != -10000
+ ||
+ ffastsaturatef(32767.0f) != 32767
+ ||
+ ffastsaturatef(-32768.0f) != -32768
+ ||
+ ffastsaturatef(32768.0f) != 32767
+ ||
+ ffastsaturatef(-32769.0f) != -32768)
+ {
+ printf("Test failed.\n");
+ exit(2);
+ }
+ printf("Testing 16 bit fast saturation from double\n");
+ if (ffastsaturate(10000.0) != 10000
+ ||
+ ffastsaturate(-10000.0) != -10000
+ ||
+ ffastsaturate(32767.0) != 32767
+ ||
+ ffastsaturate(-32768.0) != -32768
+ ||
+ ffastsaturate(32768.0) != 32767
+ ||
+ ffastsaturate(-32769.0) != -32768)
+ {
+ printf("Test failed.\n");
+ exit(2);
+ }
+ printf("Testing 16 bit float saturation from float\n");
+ if (ffsaturatef(10000.0f) != 10000.0f
+ ||
+ ffsaturatef(-10000.0f) != -10000.0f
+ ||
+ ffsaturatef(32767.0f) != 32767.0f
+ ||
+ ffsaturatef(-32768.0f) != -32768.0f
+ ||
+ ffsaturatef(32768.0f) != 32767.0f
+ ||
+ ffsaturatef(-32769.0f) != -32768.0f)
+ {
+ printf("Test failed.\n");
+ exit(2);
+ }
+ printf("Testing 16 bit double saturation from double\n");
+ if (ffsaturate(10000.0) != 10000.0
+ ||
+ ffsaturate(-10000.0) != -10000.0
+ ||
+ ffsaturate(32767.0) != 32767.0
+ ||
+ ffsaturate(-32768.0) != -32768.0
+ ||
+ ffsaturate(32768.0) != 32767.0
+ ||
+ ffsaturate(-32769.0) != -32768.0)
+ {
+ printf("Test failed.\n");
+ exit(2);
+ }
+ printf("Testing 16 bit add\n");
+ if (saturated_add16(10000, 10000) != 20000
+ ||
+ saturated_add16(10000, -10000) != 0
+ ||
+ saturated_add16(-10000, 10000) != 0
+ ||
+ saturated_add16(-10000, -10000) != -20000
+ ||
+ saturated_add16(-30000, -30000) != INT16_MIN
+ ||
+ saturated_add16(30000, 30000) != INT16_MAX)
+ {
+ printf("Test failed.\n");
+ exit(2);
+ }
+ printf("Testing 32 bit add\n");
+ if (saturated_add32(10000, 10000) != 20000
+ ||
+ saturated_add32(10000, -10000) != 0
+ ||
+ saturated_add32(-10000, 10000) != 0
+ ||
+ saturated_add32(-10000, -10000) != -20000
+ ||
+ saturated_add32(-2000000000, -2000000000) != INT32_MIN
+ ||
+ saturated_add32(2000000000, 2000000000) != INT32_MAX)
+ {
+ printf("Test failed.\n");
+ exit(2);
+ }
+ printf("Testing 16 bit subtract\n");
+ if (saturated_sub16(10000, 10000) != 0
+ ||
+ saturated_sub16(10000, -10000) != 20000
+ ||
+ saturated_sub16(-10000, 10000) != -20000
+ ||
+ saturated_sub16(-10000, -10000) != 0
+ ||
+ saturated_sub16(-30000, 30000) != INT16_MIN
+ ||
+ saturated_sub16(30000, -30000) != INT16_MAX)
+ {
+ printf("Test failed.\n");
+ exit(2);
+ }
+ printf("Testing 32 bit subtract\n");
+ if (saturated_sub32(10000, 10000) != 0
+ ||
+ saturated_sub32(10000, -10000) != 20000
+ ||
+ saturated_sub32(-10000, 10000) != -20000
+ ||
+ saturated_sub32(-10000, -10000) != 0
+ ||
+ saturated_sub32(-2000000000, 2000000000) != INT32_MIN
+ ||
+ saturated_sub32(2000000000, -2000000000) != INT32_MAX)
+ {
+ printf("Test failed.\n");
+ exit(2);
+ }
+ printf("Testing 16 x 16 => 16 bit multiply\n");
+ if (saturated_mul16(100, 100) != 0
+ ||
+ saturated_mul16(255, 255) != 1
+ ||
+ saturated_mul16(32767, -32768) != -32767
+ ||
+ saturated_mul16(-32768, 32767) != -32767
+ ||
+ saturated_mul16(32767, 32767) != 32766
+ ||
+ saturated_mul16(-32768, -32768) != 32767)
+ {
+ printf("Test failed.\n");
+ exit(2);
+ }
+ printf("Testing 16 x 16 => 32 bit multiply\n");
+ if (saturated_mul16_32(100, 100) != 20000
+ ||
+ saturated_mul16_32(-100, 100) != -20000
+ ||
+ saturated_mul16_32(32767, -32768) != -2147418112
+ ||
+ saturated_mul16_32(-32768, 32767) != -2147418112
+ ||
+ saturated_mul16_32(32767, 32767) != 2147352578
+ ||
+ saturated_mul16_32(-32768, -32768) != -2147483648)
+ {
+ printf("Test failed.\n");
+ exit(2);
+ }
+ printf("Testing 16 bit absolute\n");
+ if (saturated_abs16(10000) != 10000
+ ||
+ saturated_abs16(-10000) != 10000
+ ||
+ saturated_abs16(32767) != 32767
+ ||
+ saturated_abs16(-32768) != 32767)
+ {
+ printf("Test failed.\n");
+ exit(2);
+ }
+ printf("Tests passed.\n");
+ return 0;
+}
+/*- End of function --------------------------------------------------------*/
+/*- End of file ------------------------------------------------------------*/
--- /dev/null
+/*\r
+ * SpanDSP - a series of DSP components for telephony\r
+ *\r
+ * timezone_tests.c - Timezone handling for time interpretation\r
+ *\r
+ * Written by Steve Underwood <steveu@coppice.org>\r
+ *\r
+ * Copyright (C) 2010 Steve Underwood\r
+ *\r
+ * All rights reserved.\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU Lesser General Public License version 2.1,\r
+ * as published by the Free Software Foundation.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ * GNU Lesser General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU Lesser General Public\r
+ * License along with this program; if not, write to the Free Software\r
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\r
+ */\r
+\r
+/*! \page timezone_tests_page Timezone handling tests\r
+\section timezone_tests_page_sec_1 What does it do?\r
+*/\r
+\r
+#if defined(HAVE_CONFIG_H)\r
+#include "config.h"\r
+#endif\r
+\r
+#include <stdlib.h>\r
+#include <inttypes.h>\r
+#include <stdio.h>\r
+#include <string.h>\r
+#include <time.h>\r
+\r
+//#if defined(WITH_SPANDSP_INTERNALS)\r
+#define SPANDSP_EXPOSE_INTERNAL_STRUCTURES\r
+//#endif\r
+\r
+#include "spandsp.h"\r
+\r
+#ifndef FALSE\r
+#define FALSE 0\r
+#endif\r
+\r
+#ifndef TRUE\r
+#define TRUE (!FALSE)\r
+#endif\r
+\r
+int main(int argc, char *argv[])\r
+{\r
+ struct tm tms;\r
+ struct tm *tmp = &tms;\r
+ time_t ltime;\r
+ tz_t *tz;\r
+\r
+ /* Get the current time */\r
+ ltime = time(NULL);\r
+\r
+ /* Compute the local current time now for several localities, based on Posix tz strings */\r
+\r
+ tz = tz_init(NULL, "GMT0GMT0,M10.5.0,M3.5.0");\r
+ tz_localtime(tz, tmp, ltime);\r
+ printf("Local time is %02d:%02d:%02d\n", tmp->tm_hour, tmp->tm_min, tmp->tm_sec);\r
+ printf("Time zone is %s\n", tz_tzname(tz, tmp->tm_isdst));\r
+\r
+ tz_init(tz, "CST-8CST-8,M10.5.0,M3.5.0");\r
+ tz_localtime(tz, tmp, ltime);\r
+ printf("Local time is %02d:%02d:%02d\n", tmp->tm_hour, tmp->tm_min, tmp->tm_sec);\r
+ printf("Time zone is %s\n", tz_tzname(tz, tmp->tm_isdst));\r
+\r
+ tz_init(tz, "AEST-10AEDT-11,M10.5.0,M3.5.0");\r
+ tz_localtime(tz, tmp, ltime);\r
+ printf("Local time is %02d:%02d:%02d\n", tmp->tm_hour, tmp->tm_min, tmp->tm_sec);\r
+ printf("Time zone is %s\n", tz_tzname(tz, tmp->tm_isdst));\r
+\r
+ tz_free(tz);\r
+\r
+ return 0;\r
+}\r
+/*- End of function --------------------------------------------------------*/\r
+/*- End of file ------------------------------------------------------------*/\r
--- /dev/null
+#!/bin/sh
+#
+# spandsp fax tests
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License version 2.1,
+# as published by the Free Software Foundation.
+#
+# 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 Lesser General Public
+# License along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+run_tsb85_test()
+{
+ rm -f fax_tests_1.tif
+ echo ./tsb85_tests ${TEST}
+ ./tsb85_tests -x ../spandsp/fax-tests.xml ${TEST} 2>xyzzy2
+ RETVAL=$?
+ if [ $RETVAL != 0 ]
+ then
+ echo tsb85_tests ${TEST} failed!
+ exit $RETVAL
+ fi
+}
+
+for TEST in PPS-MPS-lost-PPS ; do
+ run_tsb85_test
+done