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

Generated by: LCOV version 1.14