The POSIX function fmemopen [1] is the way to create an input stream that reads from a string in memory. Unfortunately, it is not portable [2], and there is no way to make it portable (especially to native Windows) [3][4].
But some applications (e.g. GNU gettext) can make good use of such a construct. So, here is a module 'sf-istream' that implements an input stream abstraction that is either based on a 'FILE *' or on a string in memory. Another module 'sfl-istream' is essentially a sample: It adds line number tracking to 'sf-istream'. One can think of many "input stream" implementations, each with a different focus. Unfortunately, formulating such an abstraction requires an object- oriented programming language; it's hard to do in C. This is why here I concentrate on the practically most useful case. [1] https://pubs.opengroup.org/onlinepubs/9799919799/functions/fmemopen.html [2] https://www.gnu.org/software/gnulib/manual/html_node/fmemopen.html [3] https://lists.gnu.org/archive/html/bug-gnulib/2021-02/msg00077.html [4] https://lists.gnu.org/archive/html/bug-gnulib/2021-02/msg00079.html 2024-09-24 Bruno Haible <br...@clisp.org> sfl-istream: Add tests. * tests/test-sfl-istream.c: New file, based on tests/test-sf-istream.c. * modules/sfl-istream-tests: New file. sfl-istream: New module. * lib/sfl-istream.h: New file. * lib/sfl-istream.c: New file. * modules/sfl-istream: New file. 2024-09-24 Bruno Haible <br...@clisp.org> sf-istream: Add tests. * tests/test-sf-istream.c: New file. * modules/sf-istream-tests: New file. sf-istream: New module. * lib/sf-istream.h: New file. * lib/sf-istream.c: New file. * modules/sf-istream: New file. * doc/posix-functions/fmemopen.texi: Mention the new module.
>From 336a4d57d75c6349e9040383eb098d1112d31edd Mon Sep 17 00:00:00 2001 From: Bruno Haible <br...@clisp.org> Date: Tue, 24 Sep 2024 12:30:54 +0200 Subject: [PATCH 1/4] sf-istream: New module. * lib/sf-istream.h: New file. * lib/sf-istream.c: New file. * modules/sf-istream: New file. * doc/posix-functions/fmemopen.texi: Mention the new module. --- ChangeLog | 8 +++ doc/posix-functions/fmemopen.texi | 3 + lib/sf-istream.c | 96 +++++++++++++++++++++++++++++++ lib/sf-istream.h | 77 +++++++++++++++++++++++++ modules/sf-istream | 23 ++++++++ 5 files changed, 207 insertions(+) create mode 100644 lib/sf-istream.c create mode 100644 lib/sf-istream.h create mode 100644 modules/sf-istream diff --git a/ChangeLog b/ChangeLog index c870d9dd1c..2b0ffa4da0 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +2024-09-24 Bruno Haible <br...@clisp.org> + + sf-istream: New module. + * lib/sf-istream.h: New file. + * lib/sf-istream.c: New file. + * modules/sf-istream: New file. + * doc/posix-functions/fmemopen.texi: Mention the new module. + 2024-09-23 Bruno Haible <br...@clisp.org> getopt-posix: Fix compilation error in C++ mode (regression 2024-09-21). diff --git a/doc/posix-functions/fmemopen.texi b/doc/posix-functions/fmemopen.texi index da92ba2d72..7f66b100ba 100644 --- a/doc/posix-functions/fmemopen.texi +++ b/doc/posix-functions/fmemopen.texi @@ -16,3 +16,6 @@ This function is missing on many non-glibc platforms: Mac OS X 10.5, FreeBSD 6.0, NetBSD 5.0, OpenBSD 3.8, Minix 3.1.8, AIX 5.1, HP-UX 11, Solaris 11.3, Cygwin 1.5.x, mingw, MSVC 14, Android 5.1. @end itemize + +An alternative to the @code{fmemopen} function is the Gnulib module +@code{sf-istream}. diff --git a/lib/sf-istream.c b/lib/sf-istream.c new file mode 100644 index 0000000000..1bc4fa15e5 --- /dev/null +++ b/lib/sf-istream.c @@ -0,0 +1,96 @@ +/* A string or file based input stream. + Copyright (C) 2024 Free Software Foundation, Inc. + + This file is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation, either version 3 of the + License, or (at your option) any later version. + + This file 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, see <https://www.gnu.org/licenses/>. */ + +/* Written by Bruno Haible <br...@clisp.org>, 2024. */ + +#include <config.h> + +/* Specification. */ +#include "sf-istream.h" + +#include <stdlib.h> +#include <string.h> + +void +sf_istream_init_from_file (sf_istream_t *stream, FILE *fp) +{ + stream->fp = fp; + stream->input = NULL; + stream->input_end = NULL; +} + +void +sf_istream_init_from_string (sf_istream_t *stream, + const char *input) +{ + stream->fp = NULL; + stream->input = input; + stream->input_end = input + strlen (input); +} + +void +sf_istream_init_from_string_desc (sf_istream_t *stream, + string_desc_t input) +{ + stream->fp = NULL; + stream->input = string_desc_data (input); + stream->input_end = stream->input + string_desc_length (input); +} + +int +sf_getc (sf_istream_t *stream) +{ + int c; + + if (stream->fp != NULL) + c = getc (stream->fp); + else + { + if (stream->input == stream->input_end) + return EOF; + c = (unsigned char) *(stream->input++); + } + + return c; +} + +int +sf_ferror (sf_istream_t *stream) +{ + return (stream->fp != NULL && ferror (stream->fp)); +} + +void +sf_ungetc (sf_istream_t *stream, int c) +{ + if (c != EOF) + { + if (stream->fp != NULL) + ungetc (c, stream->fp); + else + { + stream->input--; + if (!(c == (unsigned char) *stream->input)) + /* C was incorrect. */ + abort (); + } + } +} + +void +sf_free (sf_istream_t *stream) +{ +} diff --git a/lib/sf-istream.h b/lib/sf-istream.h new file mode 100644 index 0000000000..e45c561708 --- /dev/null +++ b/lib/sf-istream.h @@ -0,0 +1,77 @@ +/* A string or file based input stream. + Copyright (C) 2024 Free Software Foundation, Inc. + + This file is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation, either version 3 of the + License, or (at your option) any later version. + + This file 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, see <https://www.gnu.org/licenses/>. */ + +/* Written by Bruno Haible <br...@clisp.org>, 2024. */ + +#ifndef _SF_ISTREAM_H +#define _SF_ISTREAM_H + +#include <stdio.h> + +#include "string-desc.h" + +/* An input stream type that can read from a file or from a string. */ +typedef struct sf_istream sf_istream_t; +struct sf_istream +{ + /* The input file stream, when reading from a file. */ + FILE *fp; + /* The input area, when reading from a string. */ + const char *input; + const char *input_end; +}; + +#ifdef __cplusplus +extern "C" { +#endif + +/* Initializes STREAM to read from FP. + FP must be a FILE stream open for reading. */ +extern void sf_istream_init_from_file (sf_istream_t *stream, FILE *fp); +/* Initializes STREAM to read from a NUL-terminated string INPUT. + The contents of INPUT must stay available and unchanged as long as STREAM + is in use. */ +extern void sf_istream_init_from_string (sf_istream_t *stream, + const char *input); +/* Initializes STREAM to read from a string INPUT. + The contents of INPUT must stay available and unchanged as long as STREAM + is in use. Operations on STREAM will not modify the contents of INPUT. */ +extern void sf_istream_init_from_string_desc (sf_istream_t *stream, + string_desc_t input); + +/* Reads a single 'char' from STREAM, and returns it as an 'unsigned char'. + Returns EOF when the end of stream was already reached. */ +extern int sf_getc (sf_istream_t *stream); + +/* Tests whether STREAM has encountered an error. + You may want to call this function after sf_getc (stream) has + returned EOF; in other situations it is guaranteed to return 0. */ +extern int sf_ferror (sf_istream_t *stream); + +/* Assuming that C was the last value returned by sf_getc (stream), + this call pushes back C onto the stream. + Only 1 character of pushback is guaranteed. */ +extern void sf_ungetc (sf_istream_t *stream, int c); + +/* Frees all memory held by STREAM. + This call has no effect on the arguments provided to sf_istream_init_*. */ +extern void sf_free (sf_istream_t *stream); + +#ifdef __cplusplus +} +#endif + +#endif /* _SF_ISTREAM_H */ diff --git a/modules/sf-istream b/modules/sf-istream new file mode 100644 index 0000000000..ab1289d7c4 --- /dev/null +++ b/modules/sf-istream @@ -0,0 +1,23 @@ +Description: +A string or file based input stream. + +Files: +lib/sf-istream.h +lib/sf-istream.c + +Depends-on: +string-desc + +configure.ac: + +Makefile.am: +lib_SOURCES += sf-istream.c + +Include: +"sf-istream.h" + +License: +LGPL + +Maintainer: +all -- 2.34.1
>From 357c5d521d7af3c56fb23cb2a98db562017468ed Mon Sep 17 00:00:00 2001 From: Bruno Haible <br...@clisp.org> Date: Tue, 24 Sep 2024 12:30:58 +0200 Subject: [PATCH 2/4] sf-istream: Add tests. * tests/test-sf-istream.c: New file. * modules/sf-istream-tests: New file. --- ChangeLog | 4 ++ modules/sf-istream-tests | 12 +++++ tests/test-sf-istream.c | 109 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 125 insertions(+) create mode 100644 modules/sf-istream-tests create mode 100644 tests/test-sf-istream.c diff --git a/ChangeLog b/ChangeLog index 2b0ffa4da0..6012c6e7e0 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,9 @@ 2024-09-24 Bruno Haible <br...@clisp.org> + sf-istream: Add tests. + * tests/test-sf-istream.c: New file. + * modules/sf-istream-tests: New file. + sf-istream: New module. * lib/sf-istream.h: New file. * lib/sf-istream.c: New file. diff --git a/modules/sf-istream-tests b/modules/sf-istream-tests new file mode 100644 index 0000000000..dc0f80d541 --- /dev/null +++ b/modules/sf-istream-tests @@ -0,0 +1,12 @@ +Files: +tests/test-sf-istream.c +tests/macros.h + +Depends-on: +unlink + +configure.ac: + +Makefile.am: +TESTS += test-sf-istream +check_PROGRAMS += test-sf-istream diff --git a/tests/test-sf-istream.c b/tests/test-sf-istream.c new file mode 100644 index 0000000000..61d096f55f --- /dev/null +++ b/tests/test-sf-istream.c @@ -0,0 +1,109 @@ +/* Test of string or file based input stream. + Copyright (C) 2024 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see <https://www.gnu.org/licenses/>. */ + +/* Written by Bruno Haible <br...@clisp.org>, 2024. */ + +#include <config.h> + +/* Specification. */ +#include "sf-istream.h" + +#include <unistd.h> + +#include "macros.h" + +#define CONTENTS_LEN 7 +#define CONTENTS "Hello\377\n" + +static void +test_open_stream (sf_istream_t *stream) +{ + int c; + + c = sf_getc (stream); + ASSERT (c == 'H'); + c = sf_getc (stream); + ASSERT (c == 'e'); + c = sf_getc (stream); + ASSERT (c == 'l'); + c = sf_getc (stream); + ASSERT (c == 'l'); + c = sf_getc (stream); + ASSERT (c == 'o'); + sf_ungetc (stream, c); + c = sf_getc (stream); + ASSERT (c == 'o'); + c = sf_getc (stream); + ASSERT (c == 0xff); + sf_ungetc (stream, c); + c = sf_getc (stream); + ASSERT (c == 0xff); + c = sf_getc (stream); + ASSERT (c == '\n'); + c = sf_getc (stream); + ASSERT (c == EOF); + c = sf_getc (stream); + ASSERT (c == EOF); + sf_ungetc (stream, c); + c = sf_getc (stream); + ASSERT (c == EOF); + ASSERT (!sf_ferror (stream)); +} + +int +main () +{ + char const contents[CONTENTS_LEN] = CONTENTS; + + /* Test reading from a file. */ + { + const char *filename = "test-sf-istream.tmp"; + unlink (filename); + { + FILE *fp = fopen (filename, "wb"); + ASSERT (fwrite (contents, 1, CONTENTS_LEN, fp) == CONTENTS_LEN); + ASSERT (fclose (fp) == 0); + } + { + FILE *fp = fopen (filename, "rb"); + sf_istream_t stream; + sf_istream_init_from_file (&stream, fp); + test_open_stream (&stream); + sf_free (&stream); + } + unlink (filename); + } + + /* Test reading from a string in memory. */ + { + sf_istream_t stream; + sf_istream_init_from_string_desc (&stream, + string_desc_new_addr (CONTENTS_LEN, + (char *) contents)); + test_open_stream (&stream); + sf_free (&stream); + } + + /* Test reading from a NUL-terminated string in memory. */ + { + sf_istream_t stream; + sf_istream_init_from_string (&stream, CONTENTS); + test_open_stream (&stream); + sf_free (&stream); + } + + return 0; +} -- 2.34.1
>From a61bea3aa85dbfd44dd6d5ef1d006f9ccc431af6 Mon Sep 17 00:00:00 2001 From: Bruno Haible <br...@clisp.org> Date: Tue, 24 Sep 2024 12:31:03 +0200 Subject: [PATCH 3/4] sfl-istream: New module. * lib/sfl-istream.h: New file. * lib/sfl-istream.c: New file. * modules/sfl-istream: New file. --- ChangeLog | 7 ++++ lib/sfl-istream.c | 84 +++++++++++++++++++++++++++++++++++++++++++++ lib/sfl-istream.h | 79 ++++++++++++++++++++++++++++++++++++++++++ modules/sfl-istream | 23 +++++++++++++ 4 files changed, 193 insertions(+) create mode 100644 lib/sfl-istream.c create mode 100644 lib/sfl-istream.h create mode 100644 modules/sfl-istream diff --git a/ChangeLog b/ChangeLog index 6012c6e7e0..52a498383f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +2024-09-24 Bruno Haible <br...@clisp.org> + + sfl-istream: New module. + * lib/sfl-istream.h: New file. + * lib/sfl-istream.c: New file. + * modules/sfl-istream: New file. + 2024-09-24 Bruno Haible <br...@clisp.org> sf-istream: Add tests. diff --git a/lib/sfl-istream.c b/lib/sfl-istream.c new file mode 100644 index 0000000000..ed6dd16646 --- /dev/null +++ b/lib/sfl-istream.c @@ -0,0 +1,84 @@ +/* A string or file based input stream, that keeps track of a line number. + Copyright (C) 2024 Free Software Foundation, Inc. + + This file is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation, either version 3 of the + License, or (at your option) any later version. + + This file 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, see <https://www.gnu.org/licenses/>. */ + +/* Written by Bruno Haible <br...@clisp.org>, 2024. */ + +#include <config.h> + +/* Specification. */ +#include "sfl-istream.h" + +void +sfl_istream_init_from_file (sfl_istream_t *stream, FILE *fp) +{ + sf_istream_init_from_file (&stream->istream, fp); + stream->line_number = 1; +} + +void +sfl_istream_init_from_string (sfl_istream_t *stream, const char *input) +{ + sf_istream_init_from_string (&stream->istream, input); + stream->line_number = 1; +} + +void +sfl_istream_init_from_string_desc (sfl_istream_t *stream, string_desc_t input) +{ + sf_istream_init_from_string_desc (&stream->istream, input); + stream->line_number = 1; +} + +void +sfl_set_line_number (sfl_istream_t *stream, size_t line_number) +{ + stream->line_number = line_number; +} + +size_t +sfl_get_line_number (sfl_istream_t *stream) +{ + return stream->line_number; +} + +int +sfl_getc (sfl_istream_t *stream) +{ + int c = sf_getc (&stream->istream); + if (c == '\n') + stream->line_number++; + return c; +} + +int +sfl_ferror (sfl_istream_t *stream) +{ + return sf_ferror (&stream->istream); +} + +void +sfl_ungetc (sfl_istream_t *stream, int c) +{ + if (c == '\n') + stream->line_number--; + sf_ungetc (&stream->istream, c); +} + +void +sfl_free (sfl_istream_t *stream) +{ + sf_free (&stream->istream); +} diff --git a/lib/sfl-istream.h b/lib/sfl-istream.h new file mode 100644 index 0000000000..7b487abbba --- /dev/null +++ b/lib/sfl-istream.h @@ -0,0 +1,79 @@ +/* A string or file based input stream, that keeps track of a line number. + Copyright (C) 2024 Free Software Foundation, Inc. + + This file is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation, either version 3 of the + License, or (at your option) any later version. + + This file 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, see <https://www.gnu.org/licenses/>. */ + +/* Written by Bruno Haible <br...@clisp.org>, 2024. */ + +#ifndef _SFL_ISTREAM_H +#define _SFL_ISTREAM_H + +#include "sf-istream.h" + +/* An input stream type that can read from a file or from a string + and that keeps track of a line number. */ +typedef struct sfl_istream sfl_istream_t; +struct sfl_istream +{ + sf_istream_t istream; + size_t line_number; +}; + +#ifdef __cplusplus +extern "C" { +#endif + +/* Initializes STREAM to read from FP. + FP must be a FILE stream open for reading. */ +extern void sfl_istream_init_from_file (sfl_istream_t *stream, FILE *fp); +/* Initializes STREAM to read from a NUL-terminated string INPUT. + The contents of INPUT must stay available and unchanged as long as STREAM + is in use. */ +extern void sfl_istream_init_from_string (sfl_istream_t *stream, + const char *input); +/* Initializes STREAM to read from a string INPUT. + The contents of INPUT must stay available and unchanged as long as STREAM + is in use. Operations on STREAM will not modify the contents of INPUT. */ +extern void sfl_istream_init_from_string_desc (sfl_istream_t *stream, + string_desc_t input); + +/* Sets the current line number of STREAM. */ +extern void sfl_set_line_number (sfl_istream_t *stream, size_t line_number); + +/* Returns the current line number of STREAM. */ +extern size_t sfl_get_line_number (sfl_istream_t *stream); + +/* Reads a single 'char' from STREAM, and returns it as an 'unsigned char'. + Returns EOF when the end of stream was already reached. */ +extern int sfl_getc (sfl_istream_t *stream); + +/* Tests whether STREAM has encountered an error. + You may want to call this function after sf_getc (stream) has + returned EOF; in other situations it is guaranteed to return 0. */ +extern int sfl_ferror (sfl_istream_t *stream); + +/* Assuming that C was the last value returned by sf_getc (stream), + this call pushes back C onto the stream. + Only 1 character of pushback is guaranteed. */ +extern void sfl_ungetc (sfl_istream_t *stream, int c); + +/* Frees all memory held by STREAM. + This call has no effect on the arguments provided to sfl_istream_init_*. */ +extern void sfl_free (sfl_istream_t *stream); + +#ifdef __cplusplus +} +#endif + +#endif /* _SFL_ISTREAM_H */ diff --git a/modules/sfl-istream b/modules/sfl-istream new file mode 100644 index 0000000000..800f81e5ed --- /dev/null +++ b/modules/sfl-istream @@ -0,0 +1,23 @@ +Description: +A string or file based input stream, that keeps track of a line number. + +Files: +lib/sfl-istream.h +lib/sfl-istream.c + +Depends-on: +sf-istream + +configure.ac: + +Makefile.am: +lib_SOURCES += sfl-istream.c + +Include: +"sfl-istream.h" + +License: +LGPL + +Maintainer: +all -- 2.34.1
>From 5a7754d11ecaf369987b7074333f2b392238d04a Mon Sep 17 00:00:00 2001 From: Bruno Haible <br...@clisp.org> Date: Tue, 24 Sep 2024 12:31:07 +0200 Subject: [PATCH 4/4] sfl-istream: Add tests. * tests/test-sfl-istream.c: New file, based on tests/test-sf-istream.c. * modules/sfl-istream-tests: New file. --- ChangeLog | 4 ++ modules/sfl-istream-tests | 12 ++++ tests/test-sfl-istream.c | 133 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 149 insertions(+) create mode 100644 modules/sfl-istream-tests create mode 100644 tests/test-sfl-istream.c diff --git a/ChangeLog b/ChangeLog index 52a498383f..934ed7233d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,9 @@ 2024-09-24 Bruno Haible <br...@clisp.org> + sfl-istream: Add tests. + * tests/test-sfl-istream.c: New file, based on tests/test-sf-istream.c. + * modules/sfl-istream-tests: New file. + sfl-istream: New module. * lib/sfl-istream.h: New file. * lib/sfl-istream.c: New file. diff --git a/modules/sfl-istream-tests b/modules/sfl-istream-tests new file mode 100644 index 0000000000..eba2aa0abb --- /dev/null +++ b/modules/sfl-istream-tests @@ -0,0 +1,12 @@ +Files: +tests/test-sfl-istream.c +tests/macros.h + +Depends-on: +unlink + +configure.ac: + +Makefile.am: +TESTS += test-sfl-istream +check_PROGRAMS += test-sfl-istream diff --git a/tests/test-sfl-istream.c b/tests/test-sfl-istream.c new file mode 100644 index 0000000000..bf8eeeccb6 --- /dev/null +++ b/tests/test-sfl-istream.c @@ -0,0 +1,133 @@ +/* Test of string or file based input stream with line number. + Copyright (C) 2024 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see <https://www.gnu.org/licenses/>. */ + +/* Written by Bruno Haible <br...@clisp.org>, 2024. */ + +#include <config.h> + +/* Specification. */ +#include "sfl-istream.h" + +#include <unistd.h> + +#include "macros.h" + +#define CONTENTS_LEN 9 +#define CONTENTS "Hello\377\n\nW" + +static void +test_open_stream (sfl_istream_t *stream) +{ + int c; + + ASSERT (sfl_get_line_number (stream) == 1); + c = sfl_getc (stream); + ASSERT (c == 'H'); + ASSERT (sfl_get_line_number (stream) == 1); + c = sfl_getc (stream); + ASSERT (c == 'e'); + ASSERT (sfl_get_line_number (stream) == 1); + c = sfl_getc (stream); + ASSERT (c == 'l'); + ASSERT (sfl_get_line_number (stream) == 1); + c = sfl_getc (stream); + ASSERT (c == 'l'); + ASSERT (sfl_get_line_number (stream) == 1); + c = sfl_getc (stream); + ASSERT (c == 'o'); + ASSERT (sfl_get_line_number (stream) == 1); + sfl_ungetc (stream, c); + c = sfl_getc (stream); + ASSERT (c == 'o'); + ASSERT (sfl_get_line_number (stream) == 1); + c = sfl_getc (stream); + ASSERT (c == 0xff); + ASSERT (sfl_get_line_number (stream) == 1); + sfl_ungetc (stream, c); + c = sfl_getc (stream); + ASSERT (c == 0xff); + ASSERT (sfl_get_line_number (stream) == 1); + c = sfl_getc (stream); + ASSERT (c == '\n'); + ASSERT (sfl_get_line_number (stream) == 2); + c = sfl_getc (stream); + ASSERT (c == '\n'); + ASSERT (sfl_get_line_number (stream) == 3); + sfl_ungetc (stream, c); + ASSERT (sfl_get_line_number (stream) == 2); + c = sfl_getc (stream); + ASSERT (c == '\n'); + ASSERT (sfl_get_line_number (stream) == 3); + c = sfl_getc (stream); + ASSERT (c == 'W'); + ASSERT (sfl_get_line_number (stream) == 3); + c = sfl_getc (stream); + ASSERT (c == EOF); + ASSERT (sfl_get_line_number (stream) == 3); + c = sfl_getc (stream); + ASSERT (c == EOF); + ASSERT (sfl_get_line_number (stream) == 3); + sfl_ungetc (stream, c); + c = sfl_getc (stream); + ASSERT (c == EOF); + ASSERT (sfl_get_line_number (stream) == 3); + ASSERT (!sfl_ferror (stream)); +} + +int +main () +{ + char const contents[CONTENTS_LEN] = CONTENTS; + + /* Test reading from a file. */ + { + const char *filename = "test-sfl-istream.tmp"; + unlink (filename); + { + FILE *fp = fopen (filename, "wb"); + ASSERT (fwrite (contents, 1, CONTENTS_LEN, fp) == CONTENTS_LEN); + ASSERT (fclose (fp) == 0); + } + { + FILE *fp = fopen (filename, "rb"); + sfl_istream_t stream; + sfl_istream_init_from_file (&stream, fp); + test_open_stream (&stream); + sfl_free (&stream); + } + unlink (filename); + } + + /* Test reading from a string in memory. */ + { + sfl_istream_t stream; + sfl_istream_init_from_string_desc (&stream, + string_desc_new_addr (CONTENTS_LEN, + (char *) contents)); + test_open_stream (&stream); + sfl_free (&stream); + } + + /* Test reading from a NUL-terminated string in memory. */ + { + sfl_istream_t stream; + sfl_istream_init_from_string (&stream, CONTENTS); + test_open_stream (&stream); + sfl_free (&stream); + } + + return 0; +} -- 2.34.1