27#define _LARGEFILE64_SOURCE 1
43#pragma comment(lib, "shlwapi.lib")
48#if _POSIX_C_SOURCE >= 200112L
51#if _POSIX_MAPPED_FILES >= 200112L
70 HANDLE hFileMappingObject;
74 SIZE_T dwNumberOfBytesToMap;
75#elif _POSIX_C_SOURCE >= 200112L && !defined(__NEWLIB__)
114 void *ptr = malloc(
sizeof(
struct __fwbuf));
121__fwbuf_free(
void *ptr)
142 char dir[MAX_PATH - 14];
143 strncpy(dir, buf->
filename, MAX_PATH - 14 - 1);
144 dir[MAX_PATH - 14 - 1] =
'\0';
145 PathRemoveFileSpecA(dir);
151 if (!GetTempFileNameA(dir,
"tmp", 0, buf->
tmpname)) {
152 dwErrCode = GetLastError();
153 goto error_GetTempFileNameA;
156 buf->hFile = CreateFileA(buf->
tmpname, GENERIC_READ | GENERIC_WRITE, 0,
157 NULL, TRUNCATE_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
158 if (buf->hFile == INVALID_HANDLE_VALUE) {
159 dwErrCode = GetLastError();
160 goto error_CreateFileA;
165 buf->hFileMappingObject = INVALID_HANDLE_VALUE;
166 buf->lpBaseAddress = NULL;
167 buf->dwNumberOfBytesToMap = 0;
173error_GetTempFileNameA:
176 SetLastError(dwErrCode);
178#elif _POSIX_C_SOURCE >= 200112L && !defined(__NEWLIB__)
184 goto error_strdup_filename;
190 goto error_strdup_tmp;
192 char *dir = dirname(tmp);
193 size_t n = strlen(dir);
195 buf->
dirfd = open(dir, O_RDONLY | O_CLOEXEC | O_DIRECTORY);
196 if (buf->
dirfd == -1) {
198 goto error_open_dirfd;
204 goto error_malloc_tmpname;
208 if (!n || buf->
tmpname[n - 1] !=
'/')
209 strcat(buf->
tmpname,
"/.tmp-XXXXXX");
211 strcat(buf->
tmpname,
".tmp-XXXXXX");
217 buf->
fd = mkostemp(buf->
tmpname, O_CLOEXEC);
227 if (fcntl(buf->
fd, F_SETFD, FD_CLOEXEC) == -1) {
235 buf->
addr = MAP_FAILED;
252error_strdup_filename:
265 buf->stream = fopen(tmpnam(buf->
tmpname),
"w+b");
291__fwbuf_fini(
struct __fwbuf *buf)
298#if _POSIX_C_SOURCE >= 200112L && !defined(__NEWLIB__)
309 fwbuf_t *buf = __fwbuf_alloc();
312 goto error_alloc_buf;
345 LARGE_INTEGER FileSize;
346 if (!GetFileSizeEx(buf->hFile, &FileSize)) {
347 buf->dwErrCode = GetLastError();
350 return FileSize.QuadPart;
351#elif _POSIX_C_SOURCE >= 200112L && !defined(__NEWLIB__)
354 if (fstat64(buf->
fd, &stat) == -1) {
357 if (fstat(buf->
fd, &stat) == -1) {
382 if (!SetEndOfFile(buf->hFile)) {
383 buf->dwErrCode = GetLastError();
391#elif _POSIX_C_SOURCE >= 200112L && !defined(__NEWLIB__)
393 if (ftruncate64(buf->
fd, size) == -1) {
396 if (ftruncate(buf->
fd, size) == -1) {
403 if (size < buf->last) {
421 LARGE_INTEGER li = { .QuadPart = 0 };
422 if (!SetFilePointerEx(buf->hFile, li, &li, FILE_CURRENT)) {
423 buf->dwErrCode = GetLastError();
427#elif _POSIX_C_SOURCE >= 200112L && !defined(__NEWLIB__)
429 intmax_t pos = lseek64(buf->
fd, 0, SEEK_CUR);
431 intmax_t pos = lseek(buf->
fd, 0, SEEK_CUR);
437 long pos = ftell(buf->stream);
453 LARGE_INTEGER li = { .QuadPart = pos };
454 if (!SetFilePointerEx(buf->hFile, li, &li, FILE_BEGIN)) {
455 buf->dwErrCode = GetLastError();
459#elif _POSIX_C_SOURCE >= 200112L && !defined(__NEWLIB__)
461 pos = lseek64(buf->
fd, pos, SEEK_SET);
463 pos = lseek(buf->
fd, pos, SEEK_SET);
473 if (pos > LONG_MAX) {
478 if (fseek(buf->stream, pos, SEEK_SET)) {
491 assert(ptr || !size);
500 DWORD nNumberOfBytesWritten;
501 if (!WriteFile(buf->hFile, ptr, size, &nNumberOfBytesWritten, NULL)) {
502 buf->dwErrCode = GetLastError();
505 return nNumberOfBytesWritten;
506#elif _POSIX_C_SOURCE >= 200112L && !defined(__NEWLIB__)
509 result = write(buf->
fd, ptr, size);
510 while (result == -1 && errno == EINTR);
519 size_t result = fwrite(ptr, 1, size, buf->stream);
520 if (result != size && ferror(buf->stream)) {
528 if (buf->map && pos < buf->pos + (intmax_t)buf->
len
529 && pos + (intmax_t)size > buf->pos) {
530 size_t begin =
MAX(pos - buf->pos, 0);
531 size_t end =
MIN(pos + size - buf->pos, buf->
len);
532 memmove((
char *)buf->map + begin, ptr, end - begin);
535 buf->last =
MAX(buf->last, pos + (intmax_t)result);
536 buf->size =
MAX(buf->size, buf->last);
545 assert(ptr || !size);
558 buf->dwErrCode = ERROR_INVALID_PARAMETER;
568 DWORD nNumberOfBytesWritten;
569 OVERLAPPED Overlapped = { 0 };
570 ULARGE_INTEGER uli = { .QuadPart = pos };
571 Overlapped.Offset = uli.LowPart;
572 Overlapped.OffsetHigh = uli.HighPart;
574 if (!WriteFile(buf->hFile, ptr, size, &nNumberOfBytesWritten,
578 buf->dwErrCode = GetLastError();
579 goto error_WriteFile;
582 result = nNumberOfBytesWritten;
588 SetLastError(buf->dwErrCode);
590#elif _POSIX_C_SOURCE >= 200112L && !defined(__NEWLIB__)
594 result = pwrite64(buf->
fd, ptr, size, pos);
597 result = pwrite(buf->
fd, ptr, size, pos);
599 while (result == -1 && errno == EINTR);
621 if (buf->last < pos) {
626 while (buf->last < pos) {
627 if (fputc(0, buf->stream) == EOF) {
633 buf->size =
MAX(buf->size, buf->last);
643 if (result == -1 || (
size_t)result != size)
668 SetLastError(buf->dwErrCode = ERROR_INVALID_PARAMETER);
669#elif _POSIX_MAPPED_FILES >= 200112L
670 errno = buf->
errsv = EINVAL;
676 if (pos > (intmax_t)size) {
678 SetLastError(buf->dwErrCode = ERROR_INVALID_PARAMETER);
679#elif _POSIX_MAPPED_FILES >= 200112L
680 errno = buf->
errsv = EOVERFLOW;
689 size =
MIN((uintmax_t)size, *psize);
692 SYSTEM_INFO SystemInfo;
694 GetSystemInfo(&SystemInfo);
695 DWORD off = pos % SystemInfo.dwAllocationGranularity;
696 if ((uintmax_t)size > (uintmax_t)(SIZE_MAX - off)) {
697 buf->dwErrCode = ERROR_INVALID_PARAMETER;
701 ULARGE_INTEGER MaximumSize = { .QuadPart = pos + size };
702 buf->hFileMappingObject = CreateFileMapping(buf->hFile, NULL,
703 PAGE_READWRITE, MaximumSize.HighPart,
704 MaximumSize.LowPart, NULL);
705 if (buf->hFileMappingObject == INVALID_HANDLE_VALUE) {
706 buf->dwErrCode = GetLastError();
707 goto error_CreateFileMapping;
710 ULARGE_INTEGER FileOffset = { .QuadPart = pos - off };
711 buf->lpBaseAddress = MapViewOfFile(buf->hFileMappingObject,
712 FILE_MAP_WRITE, FileOffset.HighPart, FileOffset.LowPart,
713 (SIZE_T)(off + size));
714 if (!buf->lpBaseAddress) {
715 buf->dwErrCode = GetLastError();
716 goto error_MapViewOfFile;
720 *psize = (size_t)size;
722 return (
char *)buf->lpBaseAddress + off;
725 CloseHandle(buf->hFileMappingObject);
726 buf->hFileMappingObject = INVALID_HANDLE_VALUE;
727error_CreateFileMapping:
729 SetLastError(buf->dwErrCode);
731#elif _POSIX_MAPPED_FILES >= 200112L
732 long page_size = sysconf(_SC_PAGE_SIZE);
733 if (page_size <= 0) {
737 intmax_t off = pos % page_size;
738 if ((uintmax_t)size > (uintmax_t)(SIZE_MAX - off)) {
739 errno = buf->
errsv = EOVERFLOW;
744 buf->
addr = mmap64(NULL, off + size, PROT_READ | PROT_WRITE, MAP_SHARED,
748 buf->
addr = mmap(NULL, off + size, PROT_READ | PROT_WRITE, MAP_SHARED,
751 if (buf->
addr == MAP_FAILED) {
755 buf->
len = off + size;
760 return (
char *)buf->
addr + off;
764 if ((uintmax_t)size > SIZE_MAX) {
769 buf->map = calloc(size, 1);
773 goto error_malloc_map;
779 if (pos < buf->last) {
791 size_t nitems =
MIN(size, buf->last - pos);
792 if (fread(buf->map, 1, nitems, buf->stream) != nitems
793 && ferror(buf->stream)) {
834 if (buf->dwErrCode) {
836 dwErrCode = buf->dwErrCode;
839 if (buf->hFileMappingObject != INVALID_HANDLE_VALUE) {
841 if (!FlushViewOfFile(buf->lpBaseAddress,
842 buf->dwNumberOfBytesToMap) && !result) {
845 dwErrCode = GetLastError();
847 if (!UnmapViewOfFile(buf->lpBaseAddress) && !result) {
849 dwErrCode = GetLastError();
851 if (!CloseHandle(buf->hFileMappingObject) && !result) {
853 dwErrCode = GetLastError();
856 buf->hFileMappingObject = INVALID_HANDLE_VALUE;
857 buf->lpBaseAddress = NULL;
858 buf->dwNumberOfBytesToMap = 0;
863 buf->dwErrCode = dwErrCode;
864 SetLastError(dwErrCode);
866#elif _POSIX_MAPPED_FILES >= 200112L
873 if (buf->
addr != MAP_FAILED) {
874 if (msync(buf->
addr, buf->
len, MS_SYNC) == -1 && !result) {
878 if (munmap(buf->
addr, buf->
len) == -1 && !result) {
883 buf->
addr = MAP_FAILED;
903 void *map = buf->map;
934#elif _POSIX_C_SOURCE >= 200112L && !defined(__NEWLIB__)
948 SetLastError(buf->dwErrCode);
949 return !!buf->dwErrCode;
950#elif _POSIX_C_SOURCE >= 200112L && !defined(__NEWLIB__)
957 return !!buf->errnum;
968 buf->dwErrCode = ERROR_OPERATION_ABORTED;
969#elif _POSIX_C_SOURCE >= 200112L && !defined(__NEWLIB__)
971 buf->
errsv = ECANCELED;
985 DWORD dwErrCode = GetLastError();
987 if (buf->hFile == INVALID_HANDLE_VALUE)
991 if (!result && !FlushFileBuffers(buf->hFile)) {
993 dwErrCode = GetLastError();
996 if (!CloseHandle(buf->hFile) && !result) {
998 dwErrCode = GetLastError();
1000 buf->hFile = INVALID_HANDLE_VALUE;
1004 MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH)) {
1008 dwErrCode = GetLastError();
1014 SetLastError(buf->dwErrCode = dwErrCode);
1016#elif _POSIX_C_SOURCE >= 200112L && !defined(__NEWLIB__)
1023 if (!result && fsync(buf->
fd) == -1) {
1028 if (close(buf->
fd) == -1 && !result) {
1042 if (!result && fsync(buf->
dirfd) == -1) {
1047 if (close(buf->
dirfd) == -1 && !result) {
1054 errno = buf->
errsv = errsv;
1063 if (!result && buf->last < buf->size) {
1068 while (!result && buf->last < buf->size) {
1069 if (fputc(0, buf->stream) == EOF) {
1079 if (!result && fflush(buf->stream) == EOF) {
1085 if (fclose(buf->stream) == EOF && !result) {
This header file is part of the utilities library; it contains the native and platform-independent er...
errnum_t errno2num(int errnum)
Transforms a standard C error number to a platform-independent error number.
errnum_t errc2num(int errc)
Transforms a native error code to a platform-independent error number.
int errnum2c(errnum_t errnum)
Transforms a platform-independent error number to a native error code.
errnum
The platform-independent error numbers.
@ ERRNUM_INVAL
Invalid argument.
@ ERRNUM_OVERFLOW
Value too large to be stored in data type.
@ ERRNUM_CANCELED
Operation canceled.
int get_errc(void)
Returns the last (thread-specific) native error code set by a system call or library function.
void set_errc(int errc)
Sets the current (thread-specific) native error code to errc.
int errno2c(int errnum)
Transforms a standard C error number to a native error code.
void set_errnum(errnum_t errnum)
Sets the current (thread-specific) platform-independent error number to errnum.
int fwbuf_error(fwbuf_t *buf)
Returns 1 if the error indicator of a write file buffer is set, and 0 if not.
intmax_t fwbuf_set_pos(fwbuf_t *buf, intmax_t pos)
Sets the current offset (in bytes) of a write file buffer with respect to the beginning of the file.
int fwbuf_set_size(fwbuf_t *buf, intmax_t size)
Sets the new size (in bytes) of the a write file buffer.
void * fwbuf_map(fwbuf_t *buf, intmax_t pos, size_t *psize)
Maps (part of) the contents of a write file buffer to memory.
ssize_t fwbuf_write(fwbuf_t *buf, const void *ptr, size_t size)
Writes bytes to the current position in a write file buffer.
int fwbuf_unmap(fwbuf_t *buf)
Unmaps the current memory map of a write file buffer, if it exists, and writes the changes to disk.
void fwbuf_destroy(fwbuf_t *buf)
Destroys a write file buffer.
int fwbuf_commit(fwbuf_t *buf)
Commits all changes to a write file buffer to disk if all previous file operations were successful,...
intmax_t fwbuf_get_pos(fwbuf_t *buf)
Returns the current offset (in bytes) of a write file buffer with respect to the beginning of the fil...
ssize_t fwbuf_pwrite(fwbuf_t *buf, const void *ptr, size_t size, intmax_t pos)
Writes bytes to the specified position in a write file buffer.
void fwbuf_cancel(fwbuf_t *buf)
Cancels any further file operations by setting the error indicator of a write file buffer to ERRNUM_C...
intmax_t fwbuf_get_size(fwbuf_t *buf)
Returns the current size (in bytes) of the a write file buffer, or -1 on error.
fwbuf_t * fwbuf_create(const char *filename)
Creates a new (atomic) write file buffer.
void fwbuf_clearerr(fwbuf_t *buf)
Clears the error indicator of a write file buffer, allowing fwbuf_commit() to write the file to disk.
This header file is part of the utilities library; it contains the (atomic) write file buffer declara...
#define MIN(a, b)
Returns the minimum of a and b.
#define MAX(a, b)
Returns the maximum of a and b.
This is the internal header file of the utilities library.
This header file is part of the C11 and POSIX compatibility library; it includes <stdio....
This header file is part of the C11 and POSIX compatibility library; it includes <stdlib....
This header file is part of the C11 and POSIX compatibility library; it includes <string....
An (atomic) write file buffer struct.
int fd
The file descriptor.
void * addr
The base address of the current file mapping.
int errsv
The number of the first error that occurred during a file operation.
char * filename
A pointer to the name of the file.
int dirfd
The file descriptor of the directory containing the temporary file.
size_t len
The length (in bytes) of the mapping at addr.
char * tmpname
A pointer to the name of the temporary file.