LCOV - code coverage report
Current view: top level - common/stream - stdio_stream.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 139 183 76.0 %
Date: 2024-04-26 00:35:57 Functions: 17 21 81.0 %

          Line data    Source code
       1             : /*
       2             :  * SPDX-License-Identifier: MPL-2.0
       3             :  *
       4             :  * This Source Code Form is subject to the terms of the Mozilla Public
       5             :  * License, v. 2.0.  If a copy of the MPL was not distributed with this
       6             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
       7             :  *
       8             :  * Copyright 2024 MonetDB Foundation;
       9             :  * Copyright August 2008 - 2023 MonetDB B.V.;
      10             :  * Copyright 1997 - July 2008 CWI.
      11             :  */
      12             : 
      13             : /* streams working on a disk file */
      14             : 
      15             : #include "monetdb_config.h"
      16             : #include "stream.h"
      17             : #include "stream_internal.h"
      18             : 
      19             : 
      20             : /* ------------------------------------------------------------------ */
      21             : /* streams working on a disk file */
      22             : 
      23             : 
      24             : /* should be static but isn't because there are some other parts of the
      25             :  * library that have a specific fast path for stdio streams.
      26             :  */
      27             : ssize_t
      28    26573075 : file_read(stream *restrict s, void *restrict buf, size_t elmsize, size_t cnt)
      29             : {
      30    26573075 :         FILE *fp = (FILE *) s->stream_data.p;
      31    26573075 :         size_t rc = 0;
      32             : 
      33    26573075 :         if (fp == NULL) {
      34           0 :                 mnstr_set_error(s, MNSTR_READ_ERROR, "file ended");
      35           0 :                 return -1;
      36             :         }
      37             : 
      38    26573075 :         if (elmsize && cnt) {
      39    26573075 :                 errno = 0;
      40    26573075 :                 if ((rc = fread(buf, elmsize, cnt, fp)) == 0 && ferror(fp)) {
      41           0 :                         mnstr_set_error_errno(s, errno == EINTR ? MNSTR_INTERRUPT : MNSTR_READ_ERROR, "read error");
      42           0 :                         return -1;
      43             :                 }
      44    26573074 :                 s->eof |= rc == 0 && feof(fp);
      45             :         }
      46    26573074 :         return (ssize_t) rc;
      47             : }
      48             : 
      49             : 
      50             : static ssize_t
      51    37076836 : file_write(stream *restrict s, const void *restrict buf, size_t elmsize, size_t cnt)
      52             : {
      53    37076836 :         FILE *fp = (FILE *) s->stream_data.p;
      54             : 
      55    37076836 :         if (fp == NULL) {
      56           0 :                 mnstr_set_error(s, MNSTR_WRITE_ERROR, "file ended");
      57           0 :                 return -1;
      58             :         }
      59             : 
      60    37076836 :         if (elmsize && cnt) {
      61    37074142 :                 size_t rc = fwrite(buf, elmsize, cnt, fp);
      62             : 
      63    37074142 :                 if (rc != cnt) {
      64             :                         // only happens if fwrite encountered an error.
      65           0 :                         mnstr_set_error_errno(s, MNSTR_WRITE_ERROR, "write error");
      66           0 :                         return -1;
      67             :                 }
      68    37074142 :                 return (ssize_t) rc;
      69             :         }
      70        2694 :         return (ssize_t) cnt;
      71             : }
      72             : 
      73             : 
      74             : static void
      75       51253 : file_close(stream *s)
      76             : {
      77       51253 :         FILE *fp = (FILE *) s->stream_data.p;
      78             : 
      79       51253 :         if (fp == NULL)
      80             :                 return;
      81       25769 :         if (fp != stdin && fp != stdout && fp != stderr) {
      82       24649 :                 if (s->name && *s->name == '|')
      83           0 :                         pclose(fp);
      84             :                 else
      85       24649 :                         fclose(fp);
      86        1120 :         } else if (!s->readonly)
      87         652 :                 fflush(fp);
      88       25770 :         s->stream_data.p = NULL;
      89             : }
      90             : 
      91             : 
      92             : static void
      93       25770 : file_destroy(stream *s)
      94             : {
      95       25770 :         file_close(s);
      96       25770 :         destroy_stream(s);
      97       25770 : }
      98             : 
      99             : 
     100             : static void
     101           0 : file_clrerr(stream *s)
     102             : {
     103           0 :         FILE *fp = (FILE *) s->stream_data.p;
     104             : 
     105           0 :         if (fp)
     106           0 :                 clearerr(fp);
     107           0 : }
     108             : 
     109             : 
     110             : static int
     111      168365 : file_flush(stream *s, mnstr_flush_level flush_level)
     112             : {
     113      168365 :         FILE *fp = (FILE *) s->stream_data.p;
     114             : 
     115      168365 :         if (fp == NULL || (!s->readonly && fflush(fp) < 0)) {
     116           0 :                         mnstr_set_error_errno(s, MNSTR_WRITE_ERROR, "flush error");
     117           0 :                 return -1;
     118             :         }
     119             :         (void) flush_level;
     120             :         return 0;
     121             : }
     122             : 
     123             : 
     124             : static int
     125           8 : file_fsync(stream *s)
     126             : {
     127             : 
     128           8 :         FILE *fp = (FILE *) s->stream_data.p;
     129             : 
     130           8 :         if (fp == NULL ||
     131           8 :             (!s->readonly
     132             : #ifdef NATIVE_WIN32
     133             :              && _commit(fileno(fp)) < 0
     134             : #else
     135             : #ifdef HAVE_FDATASYNC
     136           8 :              && fdatasync(fileno(fp)) < 0
     137             : #else
     138             : #ifdef HAVE_FSYNC
     139             :              && fsync(fileno(fp)) < 0
     140             : #endif
     141             : #endif
     142             : #endif
     143             :                     )) {
     144           0 :                 mnstr_set_error(s, MNSTR_WRITE_ERROR, "fsync failed");
     145           0 :                 return -1;
     146             :         }
     147             :         return 0;
     148             : }
     149             : 
     150             : 
     151             : static int
     152           0 : file_fgetpos(stream *restrict s, fpos_t *restrict p)
     153             : {
     154           0 :         FILE *fp = (FILE *) s->stream_data.p;
     155             : 
     156           0 :         if (fp == NULL || p == NULL)
     157             :                 return -1;
     158           0 :         return fgetpos(fp, p) ? -1 : 0;
     159             : }
     160             : 
     161             : 
     162             : static int
     163           0 : file_fsetpos(stream *restrict s, fpos_t *restrict p)
     164             : {
     165           0 :         FILE *fp = (FILE *) s->stream_data.p;
     166             : 
     167           0 :         if (fp == NULL || p == NULL)
     168             :                 return -1;
     169           0 :         return fsetpos(fp, p) ? -1 : 0;
     170             : }
     171             : 
     172             : /* convert a string from UTF-8 to wide characters; the return value is
     173             :  * freshly allocated */
     174             : #ifdef NATIVE_WIN32
     175             : static wchar_t *
     176             : utf8towchar(const char *src)
     177             : {
     178             :         wchar_t *dest;
     179             :         size_t i = 0;
     180             :         size_t j = 0;
     181             :         uint32_t c;
     182             : 
     183             :         /* count how many wchar_t's we need, while also checking for
     184             :          * correctness of the input */
     185             :         while (src[j]) {
     186             :                 i++;
     187             :                 if ((src[j+0] & 0x80) == 0) {
     188             :                         j += 1;
     189             :                 } else if ((src[j+0] & 0xE0) == 0xC0
     190             :                            && (src[j+1] & 0xC0) == 0x80
     191             :                            && (src[j+0] & 0x1E) != 0) {
     192             :                         j += 2;
     193             :                 } else if ((src[j+0] & 0xF0) == 0xE0
     194             :                            && (src[j+1] & 0xC0) == 0x80
     195             :                            && (src[j+2] & 0xC0) == 0x80
     196             :                            && ((src[j+0] & 0x0F) != 0
     197             :                                || (src[j+1] & 0x20) != 0)) {
     198             :                         j += 3;
     199             :                 } else if ((src[j+0] & 0xF8) == 0xF0
     200             :                            && (src[j+1] & 0xC0) == 0x80
     201             :                            && (src[j+2] & 0xC0) == 0x80
     202             :                            && (src[j+3] & 0xC0) == 0x80) {
     203             :                         c = (src[j+0] & 0x07) << 18
     204             :                                 | (src[j+1] & 0x3F) << 12
     205             :                                 | (src[j+2] & 0x3F) << 6
     206             :                                 | (src[j+3] & 0x3F);
     207             :                         if (c < 0x10000
     208             :                             || c > 0x10FFFF
     209             :                             || (c & 0x1FF800) == 0x00D800)
     210             :                                 return NULL;
     211             : #if SIZEOF_WCHAR_T == 2
     212             :                         i++;
     213             : #endif
     214             :                         j += 4;
     215             :                 } else {
     216             :                         return NULL;
     217             :                 }
     218             :         }
     219             :         dest = malloc((i + 1) * sizeof(wchar_t));
     220             :         if (dest == NULL)
     221             :                 return NULL;
     222             :         /* go through the source string again, this time we can skip
     223             :          * the correctness tests */
     224             :         i = j = 0;
     225             :         while (src[j]) {
     226             :                 if ((src[j+0] & 0x80) == 0) {
     227             :                         dest[i++] = src[j+0];
     228             :                         j += 1;
     229             :                 } else if ((src[j+0] & 0xE0) == 0xC0) {
     230             :                         dest[i++] = (src[j+0] & 0x1F) << 6
     231             :                                 | (src[j+1] & 0x3F);
     232             :                         j += 2;
     233             :                 } else if ((src[j+0] & 0xF0) == 0xE0) {
     234             :                         dest[i++] = (src[j+0] & 0x0F) << 12
     235             :                                 | (src[j+1] & 0x3F) << 6
     236             :                                 | (src[j+2] & 0x3F);
     237             :                         j += 3;
     238             :                 } else if ((src[j+0] & 0xF8) == 0xF0) {
     239             :                         c = (src[j+0] & 0x07) << 18
     240             :                                 | (src[j+1] & 0x3F) << 12
     241             :                                 | (src[j+2] & 0x3F) << 6
     242             :                                 | (src[j+3] & 0x3F);
     243             : #if SIZEOF_WCHAR_T == 2
     244             :                         dest[i++] = 0xD800 | ((c - 0x10000) >> 10);
     245             :                         dest[i++] = 0xDE00 | (c & 0x3FF);
     246             : #else
     247             :                         dest[i++] = c;
     248             : #endif
     249             :                         j += 4;
     250             :                 }
     251             :         }
     252             :         dest[i] = 0;
     253             :         return dest;
     254             : }
     255             : 
     256             : #else
     257             : 
     258             : static char *
     259       24803 : cvfilename(const char *filename)
     260             : {
     261             : #if defined(HAVE_NL_LANGINFO) && defined(HAVE_ICONV)
     262       24803 :         char *code_set = nl_langinfo(CODESET);
     263             : 
     264       24803 :         if (code_set != NULL && strcmp(code_set, "UTF-8") != 0) {
     265          20 :                 iconv_t cd = iconv_open("UTF-8", code_set);
     266             : 
     267          20 :                 if (cd != (iconv_t) -1) {
     268          20 :                         size_t len = strlen(filename);
     269          20 :                         size_t size = 4 * len;
     270          20 :                         char *from = (char *) filename;
     271          20 :                         char *r = malloc(size + 1);
     272          20 :                         char *p = r;
     273             : 
     274          20 :                         if (r) {
     275          20 :                                 if (iconv(cd, &from, &len, &p, &size) != (size_t) -1) {
     276          20 :                                         iconv_close(cd);
     277          20 :                                         *p = 0;
     278          20 :                                         return r;
     279             :                                 }
     280           0 :                                 free(r);
     281             :                         }
     282           0 :                         iconv_close(cd);
     283             :                 }
     284             :         }
     285             : #endif
     286             :         /* couldn't use iconv for whatever reason; alternative is to
     287             :          * use utf8towchar above to convert to a wide character string
     288             :          * (wcs) and convert that to the locale-specific encoding
     289             :          * using wcstombs or wcsrtombs (but preferably only if the
     290             :          * locale's encoding is not UTF-8) */
     291       24783 :         return strdup(filename);
     292             : }
     293             : #endif
     294             : 
     295             : 
     296             : stream *
     297       24803 : open_stream(const char *restrict filename, const char *restrict flags)
     298             : {
     299       24803 :         stream *s;
     300       24803 :         FILE *fp;
     301       24803 :         fpos_t pos;
     302       24803 :         char buf[UTF8BOMLENGTH + 1];
     303             : 
     304       24803 :         if ((s = create_stream(filename)) == NULL)
     305             :                 return NULL;
     306             : #ifdef NATIVE_WIN32
     307             :         {
     308             :                 wchar_t *wfname = utf8towchar(filename);
     309             :                 wchar_t *wflags = utf8towchar(flags);
     310             :                 if (wfname != NULL && wflags != NULL)
     311             :                         fp = _wfopen(wfname, wflags);
     312             :                 else
     313             :                         fp = NULL;
     314             :                 if (wfname)
     315             :                         free(wfname);
     316             :                 if (wflags)
     317             :                         free(wflags);
     318             :         }
     319             : #else
     320             :         {
     321       24803 :                 char *fname = cvfilename(filename);
     322       24803 :                 if (fname) {
     323       24803 :                         fp = fopen(fname, flags);
     324       24803 :                         free(fname);
     325             :                 } else
     326             :                         fp = NULL;
     327             :         }
     328             : #endif
     329       24803 :         if (fp == NULL) {
     330         152 :                 mnstr_set_open_error(filename, errno, "open failed");
     331         152 :                 destroy_stream(s);
     332         152 :                 return NULL;
     333             :         }
     334       24651 :         s->readonly = flags[0] == 'r';
     335       24651 :         s->binary = flags[1] == 'b';
     336       24651 :         s->read = file_read;
     337       24651 :         s->write = file_write;
     338       24651 :         s->close = file_close;
     339       24651 :         s->destroy = file_destroy;
     340       24651 :         s->clrerr = file_clrerr;
     341       24651 :         s->flush = file_flush;
     342       24651 :         s->fsync = file_fsync;
     343       24651 :         s->fgetpos = file_fgetpos;
     344       24651 :         s->fsetpos = file_fsetpos;
     345       24651 :         s->stream_data.p = (void *) fp;
     346             :         /* if a text file is opened for reading, and it starts with
     347             :          * the UTF-8 encoding of the Unicode Byte Order Mark, skip the
     348             :          * mark, and mark the stream as being a UTF-8 stream */
     349       24651 :         if (flags[0] == 'r' && flags[1] != 'b' && fgetpos(fp, &pos) == 0) {
     350           0 :                 if (file_read(s, buf, 1, UTF8BOMLENGTH) == UTF8BOMLENGTH &&
     351           0 :                     strncmp(buf, UTF8BOM, UTF8BOMLENGTH) == 0)
     352           0 :                         s->isutf8 = true;
     353           0 :                 else if (fsetpos(fp, &pos) != 0) {
     354             :                         /* unlikely: we couldn't seek the file back */
     355           0 :                         fclose(fp);
     356           0 :                         destroy_stream(s);
     357           0 :                         return NULL;
     358             :                 }
     359             :         }
     360             :         return s;
     361             : }
     362             : 
     363             : stream *
     364        1172 : file_stream(const char *name)
     365             : {
     366        1172 :         stream *s;
     367             : 
     368        1172 :         if ((s = create_stream(name)) == NULL)
     369             :                 return NULL;
     370        1172 :         s->read = file_read;
     371        1172 :         s->write = file_write;
     372        1172 :         s->close = file_close;
     373        1172 :         s->destroy = file_destroy;
     374        1172 :         s->flush = file_flush;
     375        1172 :         s->fsync = file_fsync;
     376        1172 :         s->fgetpos = file_fgetpos;
     377        1172 :         s->fsetpos = file_fsetpos;
     378        1172 :         return s;
     379             : }
     380             : 
     381             : stream *
     382         472 : file_rstream(FILE *restrict fp, bool binary, const char *restrict name)
     383             : {
     384         472 :         stream *s;
     385             : 
     386         472 :         if (fp == NULL)
     387             :                 return NULL;
     388             : #ifdef STREAM_DEBUG
     389             :         fprintf(stderr, "file_rstream %s\n", name);
     390             : #endif
     391         472 :         if ((s = file_stream(name)) == NULL)
     392             :                 return NULL;
     393         472 :         s->binary = binary;
     394         472 :         s->stream_data.p = (void *) fp;
     395         472 :         return s;
     396             : }
     397             : 
     398             : stream *
     399         700 : file_wstream(FILE *restrict fp, bool binary, const char *restrict name)
     400             : {
     401         700 :         stream *s;
     402             : 
     403         700 :         if (fp == NULL)
     404             :                 return NULL;
     405             : #ifdef STREAM_DEBUG
     406             :         fprintf(stderr, "file_wstream %s\n", name);
     407             : #endif
     408         700 :         if ((s = file_stream(name)) == NULL)
     409             :                 return NULL;
     410         700 :         s->readonly = false;
     411         700 :         s->binary = binary;
     412         700 :         s->stream_data.p = (void *) fp;
     413         700 :         return s;
     414             : }
     415             : 
     416             : 
     417             : stream *
     418         472 : stdin_rastream(void)
     419             : {
     420         472 :         const char *name = "<stdin>";
     421             :         // Make an attempt to skip a BOM marker.
     422             :         // It would be nice to integrate this with with the BOM removal code
     423             :         // in text_stream.c but that is complicated. In text_stream,
     424         472 :         struct stat stb;
     425         472 :         if (fstat(fileno(stdin), &stb) == 0 && (stb.st_mode & S_IFMT) == S_IFREG) {
     426          47 :                 fpos_t pos;
     427          47 :                 if (fgetpos(stdin, &pos) == 0) {
     428          47 :                         char bytes[UTF8BOMLENGTH];
     429          47 :                         size_t nread = fread(bytes, 1, UTF8BOMLENGTH, stdin);
     430          47 :                         if (nread != 3 || memcmp(bytes, UTF8BOM, UTF8BOMLENGTH) != 0) {
     431             :                                 // not a BOM, rewind
     432          47 :                                 if (nread > 0 && fsetpos(stdin, &pos) != 0) {
     433             :                                         // oops, bytes have been read but we can't rewind
     434           0 :                                         mnstr_set_error_errno(NULL, MNSTR_OPEN_ERROR, "while rewinding after checking for byte order mark");
     435           0 :                                         return NULL;
     436             :                                 }
     437             :                         }
     438             :                 }
     439             :         }
     440             : 
     441             : #ifdef _MSC_VER
     442             :         return win_console_in_stream(name);
     443             : #else
     444         472 :         return file_rstream(stdin, false, name);
     445             : #endif
     446             : }
     447             : 
     448             : stream *
     449         533 : stdout_wastream(void)
     450             : {
     451         533 :         const char *name = "<stdout>";
     452             : #ifdef _MSC_VER
     453             :         if (isatty(fileno(stdout)))
     454             :                 return win_console_out_stream(name);
     455             : #endif
     456         533 :         return file_wstream(stdout, false, name);
     457             : }
     458             : 
     459             : stream *
     460         167 : stderr_wastream(void)
     461             : {
     462         167 :         const char *name = "<stderr>";
     463             : #ifdef _MSC_VER
     464             :         if (isatty(fileno(stderr)))
     465             :                 return win_console_out_stream(name);
     466             : #endif
     467         167 :         return file_wstream(stderr, false, name);
     468             : }
     469             : 
     470             : /* some lower-level access functions */
     471             : FILE *
     472       63013 : getFile(stream *s)
     473             : {
     474       63016 :         for (; s != NULL; s = s->inner) {
     475             : #ifdef _MSC_VER
     476             :                 if (s->read == console_read)
     477             :                         return stdin;
     478             :                 if (s->write == console_write)
     479             :                         return stdout;
     480             : #endif
     481       63016 :                 if (s->read == file_read)
     482       63013 :                         return (FILE *) s->stream_data.p;
     483             :         }
     484             : 
     485             :         return NULL;
     486             : }
     487             : 
     488             : int
     489        1321 : getFileNo(stream *s)
     490             : {
     491        1321 :         FILE *f;
     492             : 
     493        1321 :         f = getFile(s);
     494        1321 :         if (f == NULL)
     495             :                 return -1;
     496        1321 :         return fileno(f);
     497             : }
     498             : 
     499             : size_t
     500         995 : getFileSize(stream *s)
     501             : {
     502         995 :         struct stat stb;
     503         995 :         int fd = getFileNo(s);
     504             : 
     505         995 :         if (fd >= 0 && fstat(fd, &stb) == 0)
     506         995 :                 return (size_t) stb.st_size;
     507             :         return 0;               /* unknown */
     508             : }
     509             : 
     510             : int
     511           0 : file_remove(const char *filename)
     512             : {
     513           0 :         int rc = -1;
     514             : 
     515             : #ifdef NATIVE_WIN32
     516             :         wchar_t *wfname = utf8towchar(filename);
     517             :         if (wfname != NULL) {
     518             :                 rc = _wremove(wfname);
     519             :                 free(wfname);
     520             :         }
     521             : #else
     522           0 :         char *fname = cvfilename(filename);
     523           0 :         if (fname) {
     524           0 :                 rc = remove(fname);
     525           0 :                 free(fname);
     526             :         }
     527             : #endif
     528           0 :         return rc;
     529             : }

Generated by: LCOV version 1.14