* Move _PDCLIB_file_t to _PDCLIB_io.h, compertmentalizing dependencies.
Removes I/O specific structures from _PDCLIB_int.h. Allows _PDCLIB_io.h
to depend upon _PDCLIB_threadconfig.h without recursive dependencies
happening.
* Add "lock" member to _PDCLIB_file_t as the stream locking mutex
* Add flockfile/ftrylockfile/funlockfile as functions which manipulate the
locking mutex explicitly
* Add man page on flockfile/funlockfile/ftrylockfile
* Add flockfile/ftrylockfile/funlockfile and *_unlocked to <stdio.h> and
<wchar.h> as appropriate
* win32: Initialize mutex member correctly
#include <stddef.h>
#ifndef REGTEST
+#include <_PDCLIB_io.h>
/* Helper function that parses the C-style mode string passed to fopen() into
the PDCLib flags FREAD, FWRITE, FAPPEND, FRW (read-write) and FBIN (binary
mode).
--- /dev/null
+/* flockfile(FILE * )\r
+\r
+ This file is part of the Public Domain C Library (PDCLib).\r
+ Permission is granted to use, modify, and / or redistribute at will.\r
+*/\r
+\r
+#include <stdio.h>\r
+#include <stdarg.h>\r
+\r
+#ifndef REGTEST\r
+#include <threads.h>\r
+#include <stdlib.h>\r
+\r
+void flockfile( FILE * file )\r
+{\r
+ if( mtx_lock( &file->lock ) != thrd_success ) {\r
+ abort();\r
+ }\r
+}\r
+\r
+#endif\r
+\r
+#ifdef TEST\r
+#include <_PDCLIB_test.h>\r
+\r
+int main( void )\r
+{\r
+ // Not tested here - tested by other stdio test drivers\r
+ return TEST_RESULTS;\r
+}\r
+\r
+#endif\r
--- /dev/null
+/* ftrylockfile(FILE * )\r
+\r
+ This file is part of the Public Domain C Library (PDCLib).\r
+ Permission is granted to use, modify, and / or redistribute at will.\r
+*/\r
+\r
+#include <stdio.h>\r
+#include <stdarg.h>\r
+\r
+#ifndef REGTEST\r
+#include <threads.h>\r
+#include <stdlib.h>\r
+\r
+int ftrylockfile( FILE * file )\r
+{\r
+ int res = mtx_trylock( &file->lock );\r
+ switch(res) {\r
+ case thrd_success:\r
+ return 0;\r
+ case thrd_busy:\r
+ return 1;\r
+\r
+ default:\r
+ abort();\r
+ }\r
+}\r
+\r
+#endif\r
+\r
+#ifdef TEST\r
+#include <_PDCLIB_test.h>\r
+\r
+int main( void )\r
+{\r
+ // Not tested here - tested by other stdio test drivers\r
+ return TEST_RESULTS;\r
+}\r
+\r
+#endif\r
--- /dev/null
+/* funlockfile(FILE * )\r
+\r
+ This file is part of the Public Domain C Library (PDCLib).\r
+ Permission is granted to use, modify, and / or redistribute at will.\r
+*/\r
+\r
+#include <stdio.h>\r
+#include <stdarg.h>\r
+\r
+#ifndef REGTEST\r
+#include <threads.h>\r
+#include <stdlib.h>\r
+\r
+void funlockfile( FILE * file )\r
+{\r
+ int res = mtx_unlock( &file->lock );\r
+ switch(res) {\r
+ case thrd_success:\r
+ return;\r
+\r
+ default:\r
+ abort();\r
+ }\r
+}\r
+\r
+#endif\r
+\r
+#ifdef TEST\r
+#include <_PDCLIB_test.h>\r
+\r
+int main( void )\r
+{\r
+ // Not tested here - tested by other stdio test drivers\r
+ return TEST_RESULTS;\r
+}\r
+\r
+#endif\r
\r
int main( void )\r
{\r
+ // MinGW at least has a very nonconforming (different signature!) variety\r
+ // of wcstok\r
+#ifndef REGTEST\r
wchar_t s[] = L"_a_bc__d_";\r
wchar_t* state = NULL;\r
wchar_t* tokres;\r
TESTCASE( s[4] == L'd' );\r
TESTCASE( s[5] == L'\0' );\r
TESTCASE( ( tokres = wcstok( NULL, L"_", &state ) ) == NULL );\r
+#endif\r
return TEST_RESULTS;\r
}\r
#endif\r
#ifndef _PDCLIB_STDIO_H
#define _PDCLIB_STDIO_H _PDCLIB_STDIO_H
-#include <_PDCLIB_int.h>
+#include <_PDCLIB_io.h>
_PDCLIB_BEGIN_EXTERN_C
#ifndef _PDCLIB_SIZE_T_DEFINED
*/
void perror( const char * s ) _PDCLIB_nothrow;
+/* Unlocked I/O
+ *
+ * Since threading was introduced in C11, FILE objects have had implicit locks
+ * to prevent data races and inconsistent output.
+ *
+ * PDCLib provides these functions from POSIX as an extension in order to enable
+ * users to access the underlying unlocked functions.
+ *
+ * For each function defined in C11 where an _unlocked variant is defined below,
+ * the behaviour of the _unlocked variant is the same except that it will not
+ * take the lock associated with the stream.
+ *
+ * flockfile, ftrylockfile and funlockfile can be used to manually manipulate
+ * the stream locks. The behaviour of the _unlocked functions if called when the
+ * stream isn't locked by the calling thread is implementation defined.
+ */
+#if _PDCLIB_POSIX_MIN(200112L) || _PDCLIB_BSD_SOURCE || _PDCLIB_SVID_SOURCE
+void flockfile(FILE *file);
+int ftrylockfile(FILE *file);
+void funlockfile(FILE *file);
+
+int getc_unlocked(FILE *stream);
+int getchar_unlocked(void);
+int putc_unlocked(int c, FILE *stream);
+int putchar_unlocked(int c);
+#endif
+
+#if _PDCLIB_BSD_SOURCE || _PDCLIB_SVID_SOURCE
+void clearerr_unlocked(FILE *stream);
+int feof_unlocked(FILE *stream);
+int ferror_unlocked(FILE *stream);
+int fileno_unlocked(FILE *stream);
+int fflush_unlocked(FILE *stream);
+int fgetc_unlocked(FILE *stream);
+int fputc_unlocked(int c, FILE *stream);
+size_t fread_unlocked(void *ptr, size_t size, size_t n, FILE *stream);
+size_t fwrite_unlocked(const void *ptr, size_t size, size_t n, FILE *stream);
+#endif
+
+#if _PDCLIB_GNU_SOURCE
+char *fgets_unlocked(char *s, int n, FILE *stream);
+int fputs_unlocked(const char *s, FILE *stream);
+#endif
+
_PDCLIB_END_EXTERN_C
#endif
\r
#ifndef _PDCLIB_WCHAR_H\r
#define _PDCLIB_WCHAR_H\r
-#include <_PDCLIB_int.h>\r
+#include <_PDCLIB_io.h>\r
_PDCLIB_BEGIN_EXTERN_C\r
/* This is VASTLY incomplete. Functions being implemented as required by other\r
portions of the library\r
wint_t putwchar(wchar_t c);\r
wint_t ungetwc(wint_t c, struct _PDCLIB_file_t *stream);\r
\r
+#if _PDCLIB_GNU_SOURCE\r
+wint_t getwc_unlocked(struct _PDCLIB_file_t *stream);\r
+wint_t getwchar_unlocked(void);\r
+wint_t fgetwc_unlocked(struct _PDCLIB_file_t *stream);\r
+wint_t fputwc_unlocked(wchar_t wc, struct _PDCLIB_file_t *stream);\r
+wint_t putwc_unlocked(wchar_t wc, struct _PDCLIB_file_t *stream);\r
+wint_t putwchar_unlocked(wchar_t wc);\r
+wchar_t *fgetws_unlocked(wchar_t *ws, int n, struct _PDCLIB_file_t *stream);\r
+int fputws_unlocked(const wchar_t *ws, struct _PDCLIB_file_t *stream);\r
+#endif\r
+\r
/* Wide character <-> Numeric conversions */\r
#if 0\r
double wcstod(const wchar_t *_PDCLIB_restrict nptr, wchar_t **_PDCLIB_restrict endptr);\r
* XOPEN: X/Open System Interface (XSI)/Single Unix Specification
* 0 (XPG4), 500 (SUSv2/UNIX98), 600 (SUSv3/UNIX03), 700 (SUSv4)
*
- * PDCLib does not attempt or claim POSIX comformance, but makes available these
- * extensions as
- * (a) useful, and
- * (b)
+ * Additionally, the macros
+ * _BSD_SOURCE, _SVID_SOURCE and _GNU_SOURCE
+ * are adhered to. If _GNU_SOURCE is defined, _XOPEN_SOURCE and
+ * _POSIX_C_SOURCE are defined to their most recent values to match glibc
+ * behaviour
+ *
+ * The intention of supporting these feature test macros is to ease
+ * application portability from these systems to PDCLib systems; in addition,
+ * it eases support for these standards by systems supporting them which are
+ * using PDCLib as their default C library.
+ *
+ * Applications targetting purely PDClib/PDCLib based platforms may define
+ * just _PDCLIB_EXTENSIONS, which will enable all supported extensions, plus
+ * all features from all supported versions of C and C++.
+ *
*/
#define _PDCLIB_C_MIN(min) _PDCLIB_C_MINMAX(min, 3000)
#define _PDCLIB_CXX_MIN(min) _PDCLIB_CXX_MINMAX(min, 3000)
#define _PDCLIB_CXX_MINMAX(min, max) 1
#define _PDCLIB_POSIX_MINMAX(min, max) 1
#define _PDCLIB_XOPEN_MINMAX(min, max) 1
+
+ #undef _PDCLIB_EXTENSIONS
+ #undef _PDCLIB_BSD_SOURCE
+ #undef _PDCLIB_SVID_SOURCE
+ #undef _PDCLIB_GNU_SOURCE
+
+ #define _PDCLIB_EXTENSIONS 1
+ #define _PDCLIB_BSD_SOURCE 1
+ #define _PDCLIB_SVID_SOURCE 1
+ #define _PDCLIB_GNU_SOURCE 1
#else
#define _PDCLIB_C_MINMAX(min, max) \
(_PDCLIB_C_VERSION >= (min) && _PDCLIB_C_VERSION <= (max))
#define _XOPEN_SOURCE 0
#endif
+ #if defined(_GNU_SOURCE)
+ #define _PDCLIB_GNU_SOURCE 1
+ #define _PDCLIB_SVID_SOURCE 1
+ #define _PDCLIB_BSD_SOURCE 1
+ #undef _XOPEN_SOURCE
+ #define _XOPEN_SOURCE 700
+ #else
+ #define _PDCLIB_GNU_SOURCE 0
+ #endif
+
+ #if defined(_PDCLIB_BSD_SOURCE)
+ // pass
+ #elif defined(_BSD_SOURCE)
+ #define _PDCLIB_BSD_SOURCE 1
+ #else
+ #define _PDCLIB_BSD_SOURCE 0
+ #endif
+
+ #if defined(_PDCLIB_SVID_SOURCE)
+ // pass
+ #elif defined(_SVID_SOURCE)
+ #define _PDCLIB_SVID_SOURCE 1
+ #else
+ #define _PDCLIB_SVID_SOURCE 0
+ #endif
+
#if _PDCLIB_XOPEN_MIN(700) && !_PDCLIB_POSIX_MIN(200809L)
#undef _POSIX_C_SOURCE
#define _POSIX_C_SOURCE 2008098L
int tm_isdst; \
};
#endif
-
-/* -------------------------------------------------------------------------- */
-/* Various <stdio.h> internals */
-/* -------------------------------------------------------------------------- */
-
-/* Flags for representing mode (see fopen()). Note these must fit the same
- status field as the _IO?BF flags in <stdio.h> and the internal flags below.
-*/
-#define _PDCLIB_FREAD 8u
-#define _PDCLIB_FWRITE 16u
-#define _PDCLIB_FAPPEND 32u
-#define _PDCLIB_FRW 64u
-#define _PDCLIB_FBIN 128u
-
-/* Internal flags, made to fit the same status field as the flags above. */
-/* -------------------------------------------------------------------------- */
-/* free() the buffer memory on closing (false for user-supplied buffer) */
-#define _PDCLIB_FREEBUFFER 512u
-/* stream has encountered error / EOF */
-#define _PDCLIB_ERRORFLAG 1024u
-#define _PDCLIB_EOFFLAG 2048u
-/* stream is wide-oriented */
-#define _PDCLIB_WIDESTREAM 4096u
-/* stream is byte-oriented */
-#define _PDCLIB_BYTESTREAM 8192u
-/* file associated with stream should be remove()d on closing (tmpfile()) */
-#define _PDCLIB_DELONCLOSE 16384u
-/* stream handle should not be free()d on close (stdin, stdout, stderr) */
-#define _PDCLIB_STATIC 32768u
-
-/* Position / status structure for getpos() / fsetpos(). */
-struct _PDCLIB_fpos_t
-{
- _PDCLIB_uint64_t offset; /* File position offset */
- int status; /* Multibyte parsing state (unused, reserved) */
-};
-
-/* FILE structure */
-struct _PDCLIB_file_t
-{
- _PDCLIB_fd_t handle; /* OS file handle */
- char * buffer; /* Pointer to buffer memory */
- _PDCLIB_size_t bufsize; /* Size of buffer */
- _PDCLIB_size_t bufidx; /* Index of current position in buffer */
- _PDCLIB_size_t bufend; /* Index of last pre-read character in buffer */
- struct _PDCLIB_fpos_t pos; /* Offset and multibyte parsing state */
- _PDCLIB_size_t ungetidx; /* Number of ungetc()'ed characters */
- unsigned char * ungetbuf; /* ungetc() buffer */
- unsigned int status; /* Status flags; see above */
- /* multibyte parsing status to be added later */
- char * filename; /* Name the current stream has been opened with */
- struct _PDCLIB_file_t * next; /* Pointer to next struct (internal) */
-};
/* -------------------------------------------------------------------------- */
/* Internal data types */
--- /dev/null
+#ifndef _PDCLIB_IO_H\r
+#define _PDCLIB_IO_H\r
+#include "_PDCLIB_int.h"\r
+#include "_PDCLIB_threadconfig.h"\r
+\r
+/* PDCLib internal I/O logic <_PDCLIB_int.h>\r
+\r
+ This file is part of the Public Domain C Library (PDCLib).\r
+ Permission is granted to use, modify, and / or redistribute at will.\r
+*/\r
+\r
+/* Flags for representing mode (see fopen()). Note these must fit the same\r
+ status field as the _IO?BF flags in <stdio.h> and the internal flags below.\r
+*/\r
+#define _PDCLIB_FREAD 8u\r
+#define _PDCLIB_FWRITE 16u\r
+#define _PDCLIB_FAPPEND 32u \r
+#define _PDCLIB_FRW 64u\r
+#define _PDCLIB_FBIN 128u\r
+\r
+/* Internal flags, made to fit the same status field as the flags above. */\r
+/* -------------------------------------------------------------------------- */\r
+/* free() the buffer memory on closing (false for user-supplied buffer) */\r
+#define _PDCLIB_FREEBUFFER 512u\r
+/* stream has encountered error / EOF */\r
+#define _PDCLIB_ERRORFLAG 1024u\r
+#define _PDCLIB_EOFFLAG 2048u\r
+/* stream is wide-oriented */\r
+#define _PDCLIB_WIDESTREAM 4096u\r
+/* stream is byte-oriented */\r
+#define _PDCLIB_BYTESTREAM 8192u\r
+/* file associated with stream should be remove()d on closing (tmpfile()) */\r
+#define _PDCLIB_DELONCLOSE 16384u\r
+/* stream handle should not be free()d on close (stdin, stdout, stderr) */\r
+#define _PDCLIB_STATIC 32768u\r
+\r
+/* Position / status structure for getpos() / fsetpos(). */\r
+struct _PDCLIB_fpos_t\r
+{\r
+ _PDCLIB_uint64_t offset; /* File position offset */\r
+ int status; /* Multibyte parsing state (unused, reserved) */\r
+};\r
+\r
+/* FILE structure */\r
+struct _PDCLIB_file_t\r
+{\r
+ _PDCLIB_fd_t handle; /* OS file handle */\r
+ _PDCLIB_MTX_T lock; /* file lock */\r
+ char * buffer; /* Pointer to buffer memory */\r
+ _PDCLIB_size_t bufsize; /* Size of buffer */\r
+ _PDCLIB_size_t bufidx; /* Index of current position in buffer */\r
+ _PDCLIB_size_t bufend; /* Index of last pre-read character in buffer */\r
+ struct _PDCLIB_fpos_t pos; /* Offset and multibyte parsing state */\r
+ _PDCLIB_size_t ungetidx; /* Number of ungetc()'ed characters */\r
+ unsigned char * ungetbuf; /* ungetc() buffer */\r
+ unsigned int status; /* Status flags; see above */\r
+ /* multibyte parsing status to be added later */\r
+ char * filename; /* Name the current stream has been opened with */\r
+ struct _PDCLIB_file_t * next; /* Pointer to next struct (internal) */\r
+};\r
+\r
+\r
+#endif\r
--- /dev/null
+.\" This file is part of the Public Domain C Library (PDCLib).\r
+.\" Permission is granted to use, modify, and / or redistribute at will.\r
+.\"\r
+.Dd\r
+.Dt flockfile 3\r
+.Os\r
+\r
+.Sh NAME\r
+.Nm flockfile, ftrylockfile, funlockfile\r
+.Nd stdio file locking\r
+\r
+.Sh SYNOPSIS\r
+.Sy #define _POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600 || _SVID_SOURCE || _BSD_SOURCE\r
+\r
+.In stdio.h\r
+.Fn "void flockfile" "FILE *file"\r
+.Fn "int ftrylockfile" "FILE *file"\r
+.Fn "void funlockfile" "FILE *file"\r
+\r
+.Sh DESCRIPTION\r
+.Fn flockfile\r
+locks the passed FILE stream for access by the calling thread, potentially \r
+blocking. \r
+.Fn ftrylockfile\r
+attempts to lock the file for the calling thread, but will return failure if\r
+another thread has already locked the file.\r
+.Fn funlockfile\r
+releases the lock on the stream, allowing another thread in the process to \r
+access it.\r
+.\"\r
+.Pp\r
+The same stream may be locked multiple times by the calling thread; the number \r
+of calls to \r
+.Fn flockfile \r
+and \r
+.Fn ftrylockfile\r
+on a file must be equal to the number of calls to\r
+.Fn funlockfile .\r
+.\"\r
+.Pp\r
+No other thread may do I/O through the locked file while it is locked.\r
+\r
+.Sh IMPLEMENTATION NOTES\r
+PDCLib implements the file locking on top of the Mutex primitives added by C11\r
+\r
+.Sh SEE ALSO\r
+.Xr fopen 3\r
+.Xr fclose 3\r
+.Xr unlocked_stdio 3\r
+.Xr mtx_t 3\r
+\r
+.Sh STANDARDS\r
+The locked I/O routines were initially introduced in \r
+.St -svid4 ,\r
+and incorporated into POSIX in \r
+.St -p1003.1-2001 .\r
#include <stdlib.h>\r
#include <stdio.h>\r
#include <signal.h>\r
+#include <threads.h>\r
#include <wchar.h> // Watcom bug: winnt.h assumes string.h defines wchar_t\r
#include <windows.h>\r
\r
cl = GetCommandLineW();\r
wargv = CommandLineToArgvW(cl, &argc);\r
argv = argvToAnsi(wargv, argc);\r
+\r
+ if( mtx_init(&stdin->lock, mtx_recursive) != thrd_success \r
+ || mtx_init(&stdout->lock, mtx_recursive) != thrd_success\r
+ || mtx_init(&stderr->lock, mtx_recursive) != thrd_success ) {\r
+ fputs( "Error during C runtime initialization: "\r
+ "Unable to allocate stdio mutex", stderr );\r
+ }\r
+\r
atexit(freeArgs);\r
\r
int exitStatus = main(argc, argv, NULL);\r
static unsigned char _PDCLIB_sout_ungetbuf[_PDCLIB_UNGETCBUFSIZE];
static unsigned char _PDCLIB_serr_ungetbuf[_PDCLIB_UNGETCBUFSIZE];
-static struct _PDCLIB_file_t _PDCLIB_serr = { NULL, _PDCLIB_serr_buffer, BUFSIZ, 0, 0, { 0, 0 }, 0, _PDCLIB_serr_ungetbuf, _IONBF | _PDCLIB_FWRITE | _PDCLIB_STATIC, NULL, NULL };
-static struct _PDCLIB_file_t _PDCLIB_sout = { NULL, _PDCLIB_sout_buffer, BUFSIZ, 0, 0, { 0, 0 }, 0, _PDCLIB_sout_ungetbuf, _IOLBF | _PDCLIB_FWRITE | _PDCLIB_STATIC, NULL, &_PDCLIB_serr };
-static struct _PDCLIB_file_t _PDCLIB_sin = { NULL, _PDCLIB_sin_buffer, BUFSIZ, 0, 0, { 0, 0 }, 0, _PDCLIB_sin_ungetbuf, _IOLBF | _PDCLIB_FREAD | _PDCLIB_STATIC, NULL, &_PDCLIB_sout };
+static struct _PDCLIB_file_t _PDCLIB_serr = { NULL, { 0 }, _PDCLIB_serr_buffer, BUFSIZ, 0, 0, { 0, 0 }, 0, _PDCLIB_serr_ungetbuf, _IONBF | _PDCLIB_FWRITE | _PDCLIB_STATIC, NULL, NULL };
+static struct _PDCLIB_file_t _PDCLIB_sout = { NULL, { 0 }, _PDCLIB_sout_buffer, BUFSIZ, 0, 0, { 0, 0 }, 0, _PDCLIB_sout_ungetbuf, _IOLBF | _PDCLIB_FWRITE | _PDCLIB_STATIC, NULL, &_PDCLIB_serr };
+static struct _PDCLIB_file_t _PDCLIB_sin = { NULL, { 0 }, _PDCLIB_sin_buffer, BUFSIZ, 0, 0, { 0, 0 }, 0, _PDCLIB_sin_ungetbuf, _IOLBF | _PDCLIB_FREAD | _PDCLIB_STATIC, NULL, &_PDCLIB_sout };
struct _PDCLIB_file_t * stdin = &_PDCLIB_sin;
struct _PDCLIB_file_t * stdout = &_PDCLIB_sout;