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 : /* stream
14 : * ======
15 : * Niels Nes
16 : * An simple interface to streams
17 : *
18 : * Processing files, streams, and sockets is quite different on Linux
19 : * and Windows platforms. To improve portability between both, we advise
20 : * to replace the stdio actions with the stream functionality provided
21 : * here.
22 : *
23 : * This interface can also be used to open 'non compressed, gzipped,
24 : * bz2zipped' data files and sockets. Using this interface one could
25 : * easily switch between the various underlying storage types.
26 : *
27 : * buffered streams
28 : * ----------------
29 : *
30 : * The bstream (or buffered_stream) can be used for efficient reading of
31 : * a stream. Reading can be done in large chunks and access can be done
32 : * in smaller bits, by directly accessing the underlying buffer.
33 : *
34 : * Beware that a flush on a buffered stream emits an empty block to
35 : * synchronize with the other side, telling it has reached the end of
36 : * the sequence and can close its descriptors.
37 : *
38 : * bstream functions
39 : * -----------------
40 : *
41 : * The bstream_create gets a read stream (rs) as input and the initial
42 : * chunk size and creates a buffered stream from this. A spare byte is
43 : * kept at the end of the buffer. The bstream_read will at least read
44 : * the next 'size' bytes. If the not read data (aka pos < len) together
45 : * with the new data will not fit in the current buffer it is resized.
46 : * The spare byte is kept.
47 : *
48 : * tee streams
49 : * -----------
50 : *
51 : * A tee stream is a write stream that duplicates all output to two
52 : * write streams of the same type (txt/bin).
53 : */
54 :
55 : /* Generic stream handling code such as init and close */
56 :
57 : #include "monetdb_config.h"
58 : #include "stream.h"
59 : #include "stream_internal.h"
60 : #include <stdio.h>
61 :
62 :
63 : #ifdef HAVE_PTHREAD_H
64 : #include <pthread.h>
65 : #endif
66 :
67 : struct tl_error_buf {
68 : char msg[1024];
69 : };
70 :
71 : static int tl_error_init(void);
72 : static struct tl_error_buf *get_tl_error_buf(void);
73 :
74 : #ifdef HAVE_PTHREAD_H
75 :
76 : static pthread_key_t tl_error_key;
77 :
78 : static void
79 563 : clear_main_tl_error_buf(void)
80 : {
81 563 : void *p = pthread_getspecific(tl_error_key);
82 563 : if (p != NULL) {
83 559 : pthread_setspecific(tl_error_key, NULL);
84 559 : free(p);
85 : }
86 563 : }
87 :
88 : static int
89 563 : tl_error_init(void)
90 : {
91 563 : if (pthread_key_create(&tl_error_key, free) != 0)
92 : return -1;
93 : // Turns out the destructor registered with pthread_key_create() does not
94 : // always run for the main thread. This atexit hook clears the main thread's
95 : // error buffer to avoid this being reported as a memory leak.
96 563 : atexit(clear_main_tl_error_buf);
97 563 : return 0;
98 : }
99 :
100 : static struct tl_error_buf*
101 397407 : get_tl_error_buf(void)
102 : {
103 397407 : struct tl_error_buf *p = pthread_getspecific(tl_error_key);
104 397407 : if (p == NULL) {
105 4088 : p = malloc(sizeof(*p));
106 4088 : if (p == NULL)
107 : return NULL;
108 4088 : *p = (struct tl_error_buf) { .msg = {0} };
109 4088 : pthread_setspecific(tl_error_key, p);
110 4088 : struct tl_error_buf *second_attempt = pthread_getspecific(tl_error_key);
111 4088 : assert(p == second_attempt && "maybe mnstr_init has not been called?");
112 : (void) second_attempt; // suppress warning if asserts disabled
113 : }
114 : return p;
115 : }
116 :
117 : #elif defined(WIN32)
118 :
119 : static DWORD tl_error_key = 0;
120 :
121 : static int
122 : tl_error_init(void)
123 : {
124 : DWORD key = TlsAlloc();
125 : if (key == TLS_OUT_OF_INDEXES)
126 : return -1;
127 : else {
128 : tl_error_key = key;
129 : return 0;
130 : }
131 : }
132 :
133 : static struct tl_error_buf*
134 : get_tl_error_buf(void)
135 : {
136 : struct tl_error_buf *p = TlsGetValue(tl_error_key);
137 :
138 : if (p == NULL) {
139 : if (GetLastError() != ERROR_SUCCESS)
140 : return NULL; // something went terribly wrong
141 :
142 : // otherwise, initialize
143 : p = malloc(sizeof(*p));
144 : if (p == NULL)
145 : return NULL;
146 : *p = (struct tl_error_buf) { .msg = 0 };
147 : if (!TlsSetValue(tl_error_key, p)) {
148 : free(p);
149 : return NULL;
150 : }
151 :
152 : struct tl_error_buf *second_attempt = TlsGetValue(tl_error_key);
153 : assert(p == second_attempt /* maybe mnstr_init has not been called? */);
154 : (void) second_attempt; // suppress warning if asserts disabled
155 : }
156 : return p;
157 : }
158 :
159 : #else
160 :
161 : #error "no pthreads and no Windows, don't know what to do"
162 :
163 : #endif
164 :
165 : static const char *mnstr_error_kind_description(mnstr_error_kind kind);
166 :
167 : int
168 765 : mnstr_init(void)
169 : {
170 765 : static ATOMIC_FLAG inited = ATOMIC_FLAG_INIT;
171 :
172 765 : if (ATOMIC_TAS(&inited))
173 : return 0;
174 :
175 563 : if (tl_error_init()< 0)
176 : return -1;
177 :
178 : #ifdef NATIVE_WIN32
179 : WSADATA w;
180 : if (WSAStartup(0x0101, &w) != 0)
181 : return -1;
182 : #endif
183 :
184 : return 0;
185 : }
186 :
187 : const char *
188 0 : mnstr_version(void)
189 : {
190 0 : return STREAM_VERSION;
191 : }
192 :
193 : /* Read at most cnt elements of size elmsize from the stream. Returns
194 : * the number of elements actually read or < 0 on failure. */
195 : ssize_t
196 17264138 : mnstr_read(stream *restrict s, void *restrict buf, size_t elmsize, size_t cnt)
197 : {
198 17264138 : if (s == NULL || buf == NULL)
199 : return -1;
200 : #ifdef STREAM_DEBUG
201 : fprintf(stderr, "read %s %zu %zu\n",
202 : s->name ? s->name : "<unnamed>", elmsize, cnt);
203 : #endif
204 17264138 : assert(s->readonly);
205 17264138 : if (s->errkind != MNSTR_NO__ERROR)
206 : return -1;
207 17264138 : return s->read(s, buf, elmsize, cnt);
208 : }
209 :
210 :
211 : /* Write cnt elements of size elmsize to the stream. Returns the
212 : * number of elements actually written. If elmsize or cnt equals zero,
213 : * returns cnt. */
214 : ssize_t
215 37113069 : mnstr_write(stream *restrict s, const void *restrict buf, size_t elmsize, size_t cnt)
216 : {
217 37113069 : if (s == NULL || buf == NULL)
218 : return -1;
219 : #ifdef STREAM_DEBUG
220 : fprintf(stderr, "write %s %zu %zu\n",
221 : s->name ? s->name : "<unnamed>", elmsize, cnt);
222 : #endif
223 37113067 : assert(!s->readonly);
224 37113067 : if (s->errkind != MNSTR_NO__ERROR)
225 : return -1;
226 37113067 : return s->write(s, buf, elmsize, cnt);
227 : }
228 :
229 :
230 : /* Read one line (separated by \n) of at most maxcnt-1 characters from
231 : * the stream. Returns the number of characters actually read,
232 : * includes the trailing \n; terminated by a NULL byte. */
233 : ssize_t
234 110953 : mnstr_readline(stream *restrict s, void *restrict buf, size_t maxcnt)
235 : {
236 110953 : char *b = buf, *start = buf;
237 :
238 110953 : if (s == NULL || buf == NULL)
239 : return -1;
240 : #ifdef STREAM_DEBUG
241 : fprintf(stderr, "readline %s %zu\n",
242 : s->name ? s->name : "<unnamed>", maxcnt);
243 : #endif
244 110953 : assert(s->readonly);
245 110953 : if (s->errkind != MNSTR_NO__ERROR)
246 : return -1;
247 110953 : if (maxcnt == 0)
248 : return 0;
249 110953 : if (maxcnt == 1) {
250 0 : *start = 0;
251 0 : return 0;
252 : }
253 18505530 : for (;;) {
254 18505530 : switch (s->read(s, start, 1, 1)) {
255 18505264 : case 1:
256 : /* successfully read a character,
257 : * check whether it is the line
258 : * separator and whether we have space
259 : * left for more */
260 18505264 : if (*start++ == '\n' || --maxcnt == 1) {
261 110687 : *start = 0;
262 110687 : return (ssize_t) (start - b);
263 : }
264 : break;
265 0 : case -1:
266 : /* error: if we didn't read anything yet,
267 : * return the error, otherwise return what we
268 : * have */
269 0 : if (start == b)
270 : return -1;
271 : /* fall through */
272 : case 0:
273 : /* end of file: return what we have */
274 266 : *start = 0;
275 266 : return (ssize_t) (start - b);
276 : }
277 : }
278 : }
279 :
280 :
281 : void
282 38434 : mnstr_settimeout(stream *s, unsigned int ms, bool (*func)(void *), void *data)
283 : {
284 38434 : if (s) {
285 38434 : s->timeout = ms;
286 38434 : s->timeout_func = func;
287 38434 : s->timeout_data = data;
288 38434 : if (s->update_timeout)
289 38434 : s->update_timeout(s);
290 : }
291 38436 : }
292 :
293 :
294 : void
295 80585 : mnstr_close(stream *s)
296 : {
297 80585 : if (s) {
298 : #ifdef STREAM_DEBUG
299 : fprintf(stderr, "close %s\n", s->name ? s->name : "<unnamed>");
300 : #endif
301 80585 : s->close(s);
302 : }
303 80585 : }
304 :
305 :
306 : void
307 1437 : mnstr_destroy(stream *s)
308 : {
309 1437 : if (s) {
310 : #ifdef STREAM_DEBUG
311 : fprintf(stderr, "destroy %s\n",
312 : s->name ? s->name : "<unnamed>");
313 : #endif
314 1435 : s->destroy(s);
315 : }
316 1437 : }
317 :
318 : void
319 181 : mnstr_va_set_error(stream *s, mnstr_error_kind kind, const char *fmt, va_list ap)
320 : {
321 181 : if (s == NULL)
322 : return;
323 :
324 181 : if (s->errkind != MNSTR_NO__ERROR && kind != MNSTR_NO__ERROR) {
325 : /* keep the first error */
326 : return;
327 : }
328 :
329 181 : s->errkind = kind;
330 :
331 181 : if (kind == MNSTR_NO__ERROR) {
332 0 : s->errmsg[0] = '\0';
333 0 : return;
334 : }
335 :
336 181 : char *start = &s->errmsg[0];
337 181 : char *end = start + sizeof(s->errmsg);
338 181 : if (s->name != NULL)
339 181 : start += snprintf(start, end - start, "stream %s: ", s->name);
340 :
341 181 : if (start >= end - 1)
342 : return;
343 :
344 181 : if (fmt == NULL)
345 19 : fmt = mnstr_error_kind_description(kind);
346 :
347 : // Complicated pointer dance in order to shut up 'might be a candidate
348 : // for gnu_printf format attribute' warning from gcc.
349 : // It's really eager to trace where the vsnprintf ends up, we need
350 : // the ? : to throw it off its scent.
351 : // Similarly, the parentheses around the 1 serve to suppress a Clang
352 : // warning about dead code (the atoi).
353 181 : void *f1 = (1) ? (void*)&vsnprintf : (void*)&atoi;
354 181 : int (*f)(char *str, size_t size, const char *format, va_list ap) = f1;
355 181 : f(start, end - start, fmt, ap);
356 : }
357 :
358 : void
359 18 : mnstr_set_error(stream *s, mnstr_error_kind kind, const char *fmt, ...)
360 : {
361 18 : va_list ap;
362 18 : va_start(ap, fmt);
363 18 : mnstr_va_set_error(s, kind, fmt, ap);
364 18 : va_end(ap);
365 18 : }
366 :
367 : static size_t my_strerror_r(int error_nr, char *buf, size_t len);
368 :
369 : void
370 163 : mnstr_set_error_errno(stream *s, mnstr_error_kind kind, const char *fmt, ...)
371 : {
372 163 : int e = errno;
373 163 : va_list ap;
374 163 : va_start(ap, fmt);
375 163 : mnstr_va_set_error(s, kind, fmt, ap);
376 163 : va_end(ap);
377 :
378 : /* append as much as fits of the system error message */
379 163 : char *start = &s->errmsg[0] + strlen(s->errmsg);
380 163 : char *end = &s->errmsg[0] + sizeof(s->errmsg);
381 163 : if (end - start >= 3) {
382 163 : start = stpcpy(start, ": ");
383 163 : start += my_strerror_r(e, start, end - start);
384 : }
385 163 : }
386 :
387 :
388 : void
389 397404 : mnstr_set_open_error(const char *name, int errnr, const char *fmt, ...)
390 : {
391 397404 : va_list ap;
392 :
393 397404 : struct tl_error_buf *buf = get_tl_error_buf();
394 397404 : if (buf == NULL)
395 397267 : return; // hopeless
396 :
397 397404 : if (errnr == 0 && fmt == NULL) {
398 397267 : buf->msg[0] = '\0';
399 397267 : return;
400 : }
401 :
402 137 : char *start = &buf->msg[0];
403 137 : char *end = start + sizeof(buf->msg);
404 :
405 137 : if (name != NULL)
406 137 : start += snprintf(start, end - start, "when opening %s: ", name);
407 137 : if (start >= end - 1)
408 : return;
409 :
410 137 : if (fmt != NULL) {
411 137 : va_start(ap, fmt);
412 137 : start += vsnprintf(start, end - start, fmt, ap);
413 137 : va_end(ap);
414 : }
415 137 : if (start >= end - 1)
416 : return;
417 :
418 137 : if (errnr != 0 && end - start >= 3) {
419 135 : start = stpcpy(start, ": ");
420 135 : start += my_strerror_r(errno, start, end - start);
421 : }
422 137 : if (start >= end - 1)
423 : return;
424 : }
425 :
426 : static size_t
427 298 : my_strerror_r(int error_nr, char *buf, size_t buflen)
428 : {
429 : // Four cases:
430 : // 1. strerror_s
431 : // 2. gnu strerror_r (returns char* and does not always fill buffer)
432 : // 3. xsi strerror_r (returns int and always fills the buffer)
433 : // 4. no strerror_r and no strerror_s
434 298 : char *to_move;
435 : #ifdef HAVE_STRERROR_S
436 : int result_code = strerror_s(buf, buflen, error_nr);
437 : if (result_code == 0)
438 : to_move = NULL;
439 : else
440 : to_move = "<failed to retrieve error message>";
441 : #elif defined(HAVE_STRERROR_R)
442 : #ifdef STRERROR_R_CHARP
443 : // gnu strerror_r sometimes only returns static string, needs copy
444 298 : to_move = strerror_r(error_nr, buf, buflen);
445 : #else
446 : int result_code = strerror_r(error_nr, buf, buflen);
447 : if (result_code == 0)
448 : to_move = NULL;
449 : else
450 : to_move = "<failed to retrieve error message>";
451 : #endif
452 : #else
453 : // Hope for the best
454 : to_move = strerror(error_nr);
455 : #endif
456 298 : if (to_move != NULL) {
457 : // move to buffer
458 298 : size_t size = strlen(to_move) + 1;
459 298 : assert(size <= buflen);
460 : // strerror_r may have return a pointer to/into the buffer
461 298 : memmove(buf, to_move, size);
462 298 : return size - 1;
463 : } else {
464 0 : return strlen(buf);
465 : }
466 : }
467 :
468 :
469 :
470 181 : void mnstr_copy_error(stream *dst, stream *src)
471 : {
472 181 : if (dst->errkind == MNSTR_NO__ERROR) {
473 181 : dst->errkind = src->errkind;
474 181 : memcpy(dst->errmsg, src->errmsg, sizeof(dst->errmsg));
475 : }
476 181 : }
477 :
478 : char *
479 0 : mnstr_error(const stream *s)
480 : {
481 0 : const char *msg = mnstr_peek_error(s);
482 0 : if (msg != NULL)
483 0 : return strdup(msg);
484 : else
485 : return NULL;
486 : }
487 :
488 : const char*
489 5 : mnstr_peek_error(const stream *s)
490 : {
491 5 : if (s == NULL) {
492 3 : struct tl_error_buf *b = get_tl_error_buf();
493 3 : if (b != NULL)
494 3 : return b->msg;
495 : else
496 : return "unknown error";
497 : }
498 :
499 2 : if (s->errkind == MNSTR_NO__ERROR)
500 : return "no error";
501 :
502 2 : if (s->errmsg[0] != '\0')
503 2 : return s->errmsg;
504 :
505 0 : return mnstr_error_kind_description(s->errkind);
506 : }
507 :
508 : static const char *
509 19 : mnstr_error_kind_description(mnstr_error_kind kind)
510 : {
511 19 : switch (kind) {
512 : case MNSTR_NO__ERROR:
513 : /* unreachable */
514 0 : assert(0);
515 : return NULL;
516 : case MNSTR_OPEN_ERROR:
517 : return "error could not open";
518 1 : case MNSTR_READ_ERROR:
519 1 : return "error reading";
520 0 : case MNSTR_WRITE_ERROR:
521 0 : return "error writing";
522 0 : case MNSTR_INTERRUPT:
523 0 : return "interrupted";
524 : case MNSTR_TIMEOUT:
525 : return "timeout";
526 : case MNSTR_UNEXPECTED_EOF:
527 : return "timeout";
528 : }
529 :
530 0 : return "Unknown error";
531 : }
532 :
533 : /* flush buffer, return 0 on success, non-zero on failure */
534 : int
535 782974 : mnstr_flush(stream *s, mnstr_flush_level flush_level)
536 : {
537 782974 : if (s == NULL)
538 : return -1;
539 : #ifdef STREAM_DEBUG
540 : fprintf(stderr, "flush %s\n", s->name ? s->name : "<unnamed>");
541 : #endif
542 782973 : assert(!s->readonly);
543 782973 : if (s->errkind != MNSTR_NO__ERROR)
544 : return -1;
545 782973 : if (s->flush)
546 781472 : return s->flush(s, flush_level);
547 : return 0;
548 : }
549 :
550 :
551 : /* sync file to disk, return 0 on success, non-zero on failure */
552 : int
553 36 : mnstr_fsync(stream *s)
554 : {
555 36 : if (s == NULL)
556 : return -1;
557 : #ifdef STREAM_DEBUG
558 : fprintf(stderr, "fsync %s (%d)\n",
559 : s->name ? s->name : "<unnamed>", s->errnr);
560 : #endif
561 36 : assert(!s->readonly);
562 36 : if (s->errkind != MNSTR_NO__ERROR)
563 : return -1;
564 36 : if (s->fsync)
565 36 : return s->fsync(s);
566 : return 0;
567 : }
568 :
569 :
570 : int
571 0 : mnstr_fgetpos(stream *restrict s, fpos_t *restrict p)
572 : {
573 0 : if (s == NULL || p == NULL)
574 : return -1;
575 : #ifdef STREAM_DEBUG
576 : fprintf(stderr, "fgetpos %s\n", s->name ? s->name : "<unnamed>");
577 : #endif
578 0 : if (s->errkind != MNSTR_NO__ERROR)
579 : return -1;
580 0 : if (s->fgetpos)
581 0 : return s->fgetpos(s, p);
582 : return 0;
583 : }
584 :
585 :
586 : int
587 0 : mnstr_fsetpos(stream *restrict s, fpos_t *restrict p)
588 : {
589 0 : if (s == NULL)
590 : return -1;
591 : #ifdef STREAM_DEBUG
592 : fprintf(stderr, "fsetpos %s\n", s->name ? s->name : "<unnamed>");
593 : #endif
594 0 : if (s->errkind != MNSTR_NO__ERROR)
595 : return -1;
596 0 : if (s->fsetpos)
597 0 : return s->fsetpos(s, p);
598 : return 0;
599 : }
600 :
601 :
602 : int
603 0 : mnstr_isalive(const stream *s)
604 : {
605 0 : if (s == NULL)
606 : return 0;
607 0 : if (s->errkind != MNSTR_NO__ERROR)
608 : return -1;
609 0 : if (s->isalive)
610 0 : return s->isalive(s);
611 : return 1;
612 : }
613 :
614 : int
615 10670287 : mnstr_getoob(const stream *s)
616 : {
617 10670287 : if (s->getoob)
618 9826166 : return s->getoob(s);
619 : return 0;
620 : }
621 :
622 : int
623 0 : mnstr_putoob(const stream *s, char val)
624 : {
625 0 : if (s->putoob)
626 0 : return s->putoob(s, val);
627 : return -1;
628 : }
629 :
630 :
631 : bool
632 233795 : mnstr_eof(const stream *s)
633 : {
634 233795 : return s->eof;
635 : }
636 :
637 : const char *
638 26720 : mnstr_name(const stream *s)
639 : {
640 26720 : if (s == NULL)
641 : return "connection terminated";
642 26720 : return s->name;
643 : }
644 :
645 :
646 : mnstr_error_kind
647 5212187 : mnstr_errnr(const stream *s)
648 : {
649 5212187 : if (s == NULL)
650 : return MNSTR_READ_ERROR;
651 5212187 : return s->errkind;
652 : }
653 :
654 : const char *
655 0 : mnstr_error_kind_name(mnstr_error_kind k)
656 : {
657 0 : switch (k) {
658 : case MNSTR_NO__ERROR:
659 : return "MNSTR_NO__ERROR";
660 0 : case MNSTR_OPEN_ERROR:
661 0 : return "MNSTR_OPEN_ERROR";
662 0 : case MNSTR_READ_ERROR:
663 0 : return "MNSTR_READ_ERROR";
664 0 : case MNSTR_WRITE_ERROR:
665 0 : return "MNSTR_WRITE_ERROR";
666 0 : case MNSTR_INTERRUPT:
667 0 : return "MNSTR_INTERRUPT";
668 0 : case MNSTR_TIMEOUT:
669 0 : return "MNSTR_TIMEOUT";
670 0 : default:
671 0 : return "<UNKNOWN_ERROR>";
672 : }
673 :
674 : }
675 :
676 : static void
677 0 : clearerror(stream *s)
678 : {
679 0 : if (s != NULL) {
680 0 : s->errkind = MNSTR_NO__ERROR;
681 0 : s->errmsg[0] = '\0';
682 : }
683 0 : }
684 :
685 : void
686 0 : mnstr_clearerr(stream *s)
687 : {
688 0 : clearerror(s);
689 0 : if (s != NULL && s->clrerr)
690 0 : s->clrerr(s);
691 0 : }
692 :
693 :
694 : bool
695 602 : mnstr_isbinary(const stream *s)
696 : {
697 602 : if (s == NULL)
698 : return false;
699 602 : return s->binary;
700 : }
701 :
702 :
703 : bool
704 0 : mnstr_get_swapbytes(const stream *s)
705 : {
706 0 : if (s == NULL)
707 : return 0;
708 0 : return s->swapbytes;
709 : }
710 :
711 :
712 : /* set stream to big-endian/little-endian byte order; the default is
713 : * native byte order */
714 : void
715 39873 : mnstr_set_bigendian(stream *s, bool bigendian)
716 : {
717 39873 : if (s == NULL)
718 : return;
719 : #ifdef STREAM_DEBUG
720 : fprintf(stderr, "mnstr_set_bigendian %s %s\n",
721 : s->name ? s->name : "<unnamed>",
722 : swapbytes ? "true" : "false");
723 : #endif
724 39873 : assert(s->readonly);
725 39873 : s->binary = true;
726 : #ifdef WORDS_BIGENDIAN
727 : s->swapbytes = !bigendian;
728 : #else
729 39873 : s->swapbytes = bigendian;
730 : #endif
731 : }
732 :
733 :
734 : void
735 69382 : close_stream(stream *s)
736 : {
737 69382 : if (s) {
738 69379 : if (s->close)
739 69379 : s->close(s);
740 69379 : if (s->destroy)
741 69379 : s->destroy(s);
742 : }
743 69382 : }
744 :
745 :
746 : void
747 397185 : destroy_stream(stream *s)
748 : {
749 397185 : if (s->name)
750 397185 : free(s->name);
751 397185 : free(s);
752 397185 : }
753 :
754 :
755 : stream *
756 397267 : create_stream(const char *name)
757 : {
758 397267 : stream *s;
759 :
760 397267 : if (name == NULL) {
761 0 : mnstr_set_open_error(NULL, 0, "internal error: name not set");
762 0 : return NULL;
763 : }
764 397267 : if ((s = (stream *) malloc(sizeof(*s))) == NULL) {
765 0 : mnstr_set_open_error(name, errno, "malloc failed");
766 0 : return NULL;
767 : }
768 397267 : *s = (stream) {
769 : .swapbytes = false,
770 : .readonly = true,
771 : .isutf8 = false, /* not known for sure */
772 : .binary = false,
773 : .eof = false,
774 397267 : .name = strdup(name),
775 : .errkind = MNSTR_NO__ERROR,
776 : .errmsg = {0},
777 : .destroy = destroy_stream,
778 : .clrerr = clearerror,
779 : };
780 397267 : if(s->name == NULL) {
781 0 : free(s);
782 0 : mnstr_set_open_error(name, errno, "malloc failed");
783 0 : return NULL;
784 : }
785 : #ifdef STREAM_DEBUG
786 : fprintf(stderr, "create_stream %s -> %p\n",
787 : name ? name : "<unnamed>", s);
788 : #endif
789 397267 : mnstr_set_open_error(NULL, 0, NULL); // clear the error
790 397267 : return s;
791 : }
792 :
793 :
794 : static ssize_t
795 0 : wrapper_read(stream *restrict s, void *restrict buf, size_t elmsize, size_t cnt)
796 : {
797 0 : ssize_t ret = s->inner->read(s->inner, buf, elmsize, cnt);
798 0 : s->eof |= s->inner->eof;
799 0 : return ret;
800 : }
801 :
802 :
803 : static ssize_t
804 0 : wrapper_write(stream *restrict s, const void *restrict buf, size_t elmsize, size_t cnt)
805 : {
806 0 : return s->inner->write(s->inner, buf, elmsize, cnt);
807 : }
808 :
809 :
810 : static void
811 0 : wrapper_close(stream *s)
812 : {
813 0 : s->inner->close(s->inner);
814 0 : }
815 :
816 :
817 : static void
818 0 : wrapper_clrerr(stream *s)
819 : {
820 0 : s->inner->clrerr(s->inner);
821 0 : }
822 :
823 :
824 : static void
825 0 : wrapper_destroy(stream *s)
826 : {
827 0 : s->inner->destroy(s->inner);
828 0 : destroy_stream(s);
829 0 : }
830 :
831 :
832 : static int
833 0 : wrapper_flush(stream *s, mnstr_flush_level flush_level)
834 : {
835 0 : return s->inner->flush(s->inner, flush_level);
836 : }
837 :
838 :
839 : static int
840 4 : wrapper_fsync(stream *s)
841 : {
842 4 : return s->inner->fsync(s->inner);
843 : }
844 :
845 :
846 : static int
847 0 : wrapper_fgetpos(stream *restrict s, fpos_t *restrict p)
848 : {
849 0 : return s->inner->fgetpos(s->inner, p);
850 : }
851 :
852 :
853 : static int
854 0 : wrapper_fsetpos(stream *restrict s, fpos_t *restrict p)
855 : {
856 0 : return s->inner->fsetpos(s->inner, p);
857 : }
858 :
859 :
860 : static void
861 38435 : wrapper_update_timeout(stream *s)
862 : {
863 38435 : s->inner->timeout = s->timeout;
864 38435 : s->inner->timeout_func = s->timeout_func;
865 38435 : s->inner->timeout_data = s->timeout_data;
866 38435 : s->inner->update_timeout(s->inner);
867 38436 : }
868 :
869 :
870 : static int
871 0 : wrapper_isalive(const stream *s)
872 : {
873 0 : return s->inner->isalive(s->inner);
874 : }
875 :
876 :
877 : static int
878 9825706 : wrapper_getoob(const stream *s)
879 : {
880 9825706 : return s->inner->getoob(s->inner);
881 : }
882 :
883 :
884 : static int
885 0 : wrapper_putoob(const stream *s, char val)
886 : {
887 0 : return s->inner->putoob(s->inner, val);
888 : }
889 :
890 :
891 : stream *
892 81181 : create_wrapper_stream(const char *name, stream *inner)
893 : {
894 81181 : if (inner == NULL)
895 : return NULL;
896 81181 : if (name == NULL)
897 81181 : name = inner->name;
898 81181 : stream *s = create_stream(name);
899 81181 : if (s == NULL)
900 : return NULL;
901 :
902 :
903 81181 : s->swapbytes = inner->swapbytes;
904 81181 : s->readonly = inner->readonly;
905 81181 : s->isutf8 = inner->isutf8;
906 81181 : s->binary = inner->binary;
907 81181 : s->timeout = inner->timeout;
908 81181 : s->inner = inner;
909 :
910 81181 : s->read = inner->read == NULL ? NULL : wrapper_read;
911 81181 : s->write = inner->write == NULL ? NULL : wrapper_write;
912 81181 : s->close = inner->close == NULL ? NULL : wrapper_close;
913 81181 : s->clrerr = inner->clrerr == NULL ? NULL : wrapper_clrerr;
914 81181 : s->destroy = wrapper_destroy;
915 81181 : s->flush = inner->flush == NULL ? NULL : wrapper_flush;
916 81181 : s->fsync = inner->fsync == NULL ? NULL : wrapper_fsync;
917 81181 : s->fgetpos = inner->fgetpos == NULL ? NULL : wrapper_fgetpos;
918 81181 : s->fsetpos = inner->fsetpos == NULL ? NULL : wrapper_fsetpos;
919 81181 : s->isalive = inner->isalive == NULL ? NULL : wrapper_isalive;
920 81181 : s->getoob = inner->getoob == NULL ? NULL : wrapper_getoob;
921 81181 : s->putoob = inner->putoob == NULL ? NULL : wrapper_putoob;
922 81181 : s->update_timeout = inner->update_timeout == NULL ? NULL : wrapper_update_timeout;
923 :
924 81181 : return s;
925 : }
926 :
927 : /* ------------------------------------------------------------------ */
928 : /* streams working on a disk file, compressed or not */
929 :
930 : stream *
931 14064 : open_rstream(const char *filename)
932 : {
933 14064 : if (filename == NULL)
934 : return NULL;
935 : #ifdef STREAM_DEBUG
936 : fprintf(stderr, "open_rstream %s\n", filename);
937 : #endif
938 :
939 14064 : stream *s = open_stream(filename, "rb");
940 14064 : if (s == NULL)
941 : return NULL;
942 :
943 13929 : stream *c = compressed_stream(s, 0);
944 13929 : if (c == NULL)
945 0 : close_stream(s);
946 :
947 : return c;
948 : }
949 :
950 : stream *
951 12790 : open_wstream(const char *filename)
952 : {
953 12790 : if (filename == NULL)
954 : return NULL;
955 : #ifdef STREAM_DEBUG
956 : fprintf(stderr, "open_wstream %s\n", filename);
957 : #endif
958 :
959 12790 : stream *s = open_stream(filename, "wb");
960 12790 : if (s == NULL)
961 : return NULL;
962 :
963 12790 : stream *c = compressed_stream(s, 0);
964 12790 : if (c == NULL) {
965 0 : close_stream(s);
966 0 : (void) file_remove(filename);
967 : }
968 :
969 : return c;
970 : }
971 :
972 : stream *
973 364 : open_rastream(const char *filename)
974 : {
975 364 : if (filename == NULL)
976 : return NULL;
977 : #ifdef STREAM_DEBUG
978 : fprintf(stderr, "open_rastream %s\n", filename);
979 : #endif
980 364 : stream *s = open_rstream(filename);
981 364 : if (s == NULL)
982 : return NULL;
983 :
984 330 : stream *t = create_text_stream(s);
985 330 : if (t == NULL)
986 0 : close_stream(s);
987 :
988 : return t;
989 : }
990 :
991 : stream *
992 26 : open_wastream(const char *filename)
993 : {
994 26 : if (filename == NULL)
995 : return NULL;
996 : #ifdef STREAM_DEBUG
997 : fprintf(stderr, "open_wastream %s\n", filename);
998 : #endif
999 26 : stream *s = open_wstream(filename);
1000 26 : if (s == NULL)
1001 : return NULL;
1002 :
1003 26 : stream *t = create_text_stream(s);
1004 26 : if (t == NULL) {
1005 0 : close_stream(s);
1006 0 : (void) file_remove(filename);
1007 : }
1008 :
1009 : return t;
1010 : }
1011 :
1012 :
1013 : /* put here because it depends on both bs_read AND bs2_read */
1014 : bool
1015 509560 : isa_block_stream(const stream *s)
1016 : {
1017 509560 : assert(s != NULL);
1018 1019120 : return s &&
1019 509560 : ((s->read == bs_read ||
1020 3412 : s->write == bs_write));
1021 : }
1022 :
1023 :
1024 : /* Put here because I need to think very carefully about this
1025 : * mnstr_read(,, 0, 0). What would that mean?
1026 : */
1027 : ssize_t
1028 40036 : mnstr_read_block(stream *restrict s, void *restrict buf, size_t elmsize, size_t cnt)
1029 : {
1030 40036 : ssize_t len = 0;
1031 40036 : char x = 0;
1032 :
1033 40036 : if (s == NULL || buf == NULL)
1034 : return -1;
1035 40036 : assert(s->read == bs_read || s->write == bs_write);
1036 80071 : if ((len = mnstr_read(s, buf, elmsize, cnt)) < 0 ||
1037 40035 : mnstr_read(s, &x, 0, 0) < 0 /* read prompt */ ||
1038 40035 : x > 0)
1039 1 : return -1;
1040 : return len;
1041 : }
|