LCOV - code coverage report
Current view: top level - common/stream - stdio_stream.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 110 165 66.7 %
Date: 2021-10-13 02:24:04 Functions: 17 21 81.0 %

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

Generated by: LCOV version 1.14