1 /*  $Id$
2 
3     Part of SWI-Prolog
4 
5     Author:        Jan Wielemaker
6     E-mail:        J.Wielemaker@uva.nl
7     WWW:           http://www.swi-prolog.org
8     Copyright (C): 1985-2009, University of Amsterdam
9 
10     This library is free software; you can redistribute it and/or
11     modify it under the terms of the GNU Lesser General Public
12     License as published by the Free Software Foundation; either
13     version 2.1 of the License, or (at your option) any later version.
14 
15     This library is distributed in the hope that it will be useful,
16     but WITHOUT ANY WARRANTY; without even the implied warranty of
17     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18     Lesser General Public License for more details.
19 
20     You should have received a copy of the GNU Lesser General Public
21     License along with this library; if not, write to the Free Software
22     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23 */
24 
25 #ifdef __MINGW32__
26 #define __WINDOWS__ 1
27 #endif
28 
29 #ifdef __WINDOWS__
30 #include <uxnt/uxnt.h>
31 #ifdef __MINGW32__
32 #include "config.h"
33 #include <windows.h>
34 #else
35 #ifdef WIN64
36 #define MD "config/win64.h"
37 #else
38 #define MD "config/win32.h"
39 #endif
40 #endif
41 #include <winsock2.h>
42 #include "pl-mswchar.h"
43 #define CRLF_MAPPING 1
44 #endif
45 
46 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
47 This modules defines the  SWI-Prolog  I/O   streams.  These  streams are
48 provided to gain common access to  any   type  of character data: files,
49 stdio streams, but also resources, strings, XPCE objects, etc.
50 
51 MT:
52 
53 Multithreading is supported through  Slock()   and  Sunlock(). These are
54 recursive locks. If a stream handle  might   be  known to another thread
55 locking is required.
56 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
57 
58 #ifdef MD
59 #include MD
60 #else
61 #include <config.h>
62 #endif
63 
64 #if _FILE_OFFSET_BITS == 64 || defined(_LARGE_FILES)
65 #define O_LARGEFILES 1		/* use for conditional code in Prolog */
66 #else
67 #undef O_LARGEFILES
68 #endif
69 
70 #define PL_KERNEL 1
71 #include <wchar.h>
72 typedef wchar_t pl_wchar_t;
73 #include "SWI-Stream.h"
74 #include "pl-utf8.h"
75 #include <sys/types.h>
76 #ifdef HAVE_SYS_TIME_H
77 #include <sys/time.h>
78 #else
79 #include <time.h>
80 #endif
81 #include <errno.h>
82 #ifdef HAVE_MALLOC_H
83 #include <malloc.h>
84 #else
85 #ifdef HAVE_SYS_MALLOC_H
86 #include <sys/malloc.h>
87 #endif
88 #endif
89 #include <memory.h>
90 #include <string.h>
91 #include <fcntl.h>
92 #include <stdlib.h>
93 #include <limits.h>
94 #include <stdarg.h>
95 #include <ctype.h>
96 #include <sys/stat.h>
97 #ifdef HAVE_SYS_SELECT_H
98 #include <sys/select.h>
99 #endif
100 #ifdef HAVE_UNISTD_H
101 #include <unistd.h>
102 #endif
103 #include <stdio.h>			/* sprintf() for numeric values */
104 #include <assert.h>
105 #ifdef SYSLIB_H
106 #include SYSLIB_H
107 #endif
108 
109 #ifndef MB_LEN_MAX
110 #define MB_LEN_MAX 6
111 #endif
112 
113 #define ROUND(p, n) ((((p) + (n) - 1) & ~((n) - 1)))
114 #define UNDO_SIZE ROUND(MB_LEN_MAX, sizeof(wchar_t))
115 
116 #ifndef FALSE
117 #define FALSE 0
118 #endif
119 #ifndef TRUE
120 #define TRUE 1
121 #endif
122 
123 #define char_to_int(c)	(0xff & (int)(c))
124 
125 #define TMPBUFSIZE 256			/* Serror bufsize for Svfprintf() */
126 
127 int Slinesize = SIO_LINESIZE;		/* Sgets() buffer size */
128 
129 static ssize_t	S__flushbuf(IOSTREAM *s);
130 static void	run_close_hooks(IOSTREAM *s);
131 static int	S__removebuf(IOSTREAM *s);
132 static int	S__seterror(IOSTREAM *s);
133 
134 #ifdef O_PLMT
135 #define SLOCK(s)    if ( s->mutex ) recursiveMutexLock(s->mutex)
136 #define SUNLOCK(s)  if ( s->mutex ) recursiveMutexUnlock(s->mutex)
137 inline int
STRYLOCK(IOSTREAM * s)138 STRYLOCK(IOSTREAM *s)
139 { if ( s->mutex &&
140        recursiveMutexTryLock(s->mutex) == EBUSY )
141     return FALSE;
142 
143   return TRUE;
144 }
145 #else
146 #define SLOCK(s)
147 #define SUNLOCK(s)
148 #define STRYLOCK(s) (TRUE)
149 #endif
150 
151 #include "pl-error.h"
152 typedef void *record_t;
153 typedef intptr_t term_t;
154 
155 extern int 			fatalError(const char *fm, ...);
156 extern int 			PL_error(const char *pred, int arity,
157 					 const char *msg, int id, ...);
158 extern int			PL_handle_signals(void);
159 extern IOENC			initEncoding(void);
160 extern int			reportStreamError(IOSTREAM *s);
161 extern record_t			PL_record(term_t t);
162 extern int			PL_thread_self(void);
163 
164 
165 		 /*******************************
166 		 *	      BUFFER		*
167 		 *******************************/
168 
169 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
170 Note that the  buffer  is  allocated   from  s->unbuffer,  which  starts
171 UNDO_SIZE before s->buffer, so we can always push-back a wide
172 character into a multibyte stream. We do not do this for SIO_USERBUF
173 case, but this is only used by the output stream Svfprintf() where it is
174 not needed.
175 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
176 
177 static size_t
S__setbuf(IOSTREAM * s,char * buffer,size_t size)178 S__setbuf(IOSTREAM *s, char *buffer, size_t size)
179 { char *newbuf, *newunbuf;
180   int newflags = s->flags;
181 
182   if ( size == 0 )
183     size = SIO_BUFSIZE;
184 
185   if ( (s->flags & SIO_OUTPUT) )
186   { if ( S__removebuf(s) < 0 )
187       return -1;
188   }
189 
190   if ( buffer )
191   { newunbuf = newbuf = buffer;
192     newflags |= SIO_USERBUF;
193   } else
194   { if ( !(newunbuf = malloc(size+UNDO_SIZE)) )
195     { errno = ENOMEM;
196       return -1;
197     }
198     newflags &= ~SIO_USERBUF;
199     newbuf = newunbuf + UNDO_SIZE;
200   }
201 
202   if ( (s->flags & SIO_INPUT) )
203   { size_t buffered = s->limitp - s->bufp;
204     size_t copy = (buffered < size ? buffered : size);
205 
206     if ( size < buffered )
207     { size_t offset = size - buffered;
208       int64_t newpos;
209 
210       if ( s->functions->seek64 )
211       { newpos = (*s->functions->seek64)(s->handle, offset, SIO_SEEK_CUR);
212       } else if ( s->functions->seek )
213       { newpos = (*s->functions->seek)(s->handle, (long)offset, SIO_SEEK_CUR);
214       } else
215       { newpos = -1;
216 	errno = ESPIPE;
217       }
218 
219       if ( newpos == -1 )
220       { if ( !(newflags & SIO_USERBUF) )
221 	{ int oldeno = errno;
222 
223 	  free(newunbuf);
224 	  errno = oldeno;
225 	  S__seterror(s);
226 	  return -1;
227 	}
228       }
229     }
230 
231     memcpy(newbuf, s->bufp, copy);
232     S__removebuf(s);
233     s->unbuffer = newunbuf;
234     s->bufp = s->buffer = newbuf;
235     s->limitp = s->buffer+copy;
236   } else
237   { s->unbuffer = newunbuf;
238     s->bufp = s->buffer = newbuf;
239     s->limitp = &s->buffer[size];
240   }
241   s->bufsize = (int)size;
242   s->flags = newflags;
243 
244   return size;
245 }
246 
247 
248 void
Ssetbuffer(IOSTREAM * s,char * buffer,size_t size)249 Ssetbuffer(IOSTREAM *s, char *buffer, size_t size)
250 { if ( S__setbuf(s, buffer, size) != (size_t)-1 )
251     s->flags &= ~SIO_USERBUF;
252 }
253 
254 
255 static int
S__removebuf(IOSTREAM * s)256 S__removebuf(IOSTREAM *s)
257 { if ( s->buffer && s->unbuffer )
258   { int rval = 0;
259 
260     if ( (s->flags & SIO_OUTPUT) && S__flushbuf(s) < 0 )
261       rval = -1;
262 
263     if ( !(s->flags & SIO_USERBUF) )
264       free(s->unbuffer);
265     s->bufp = s->limitp = s->buffer = s->unbuffer = NULL;
266     s->bufsize = 0;
267 
268     return rval;
269   }
270 
271   return 0;
272 }
273 
274 
275 #ifdef DEBUG_IO_LOCKS
276 static char *
Sname(IOSTREAM * s)277 Sname(IOSTREAM *s)
278 { if ( s == Serror ) return "error";
279   if ( s == Sinput ) return "input";
280   if ( s == Soutput ) return "output";
281   return "?";
282 }
283 
284 
285 #include <execinfo.h>
286 #include <string.h>
287 
288 static void
print_trace(void)289 print_trace(void)
290 { void *array[7];
291   size_t size;
292   char **strings;
293   size_t i;
294 
295   size = backtrace(array, sizeof(array)/sizeof(void *));
296   strings = backtrace_symbols(array, size);
297 
298   printf(" Stack:");
299   for(i = 1; i < size; i++)
300   { printf("\n\t[%ld] %s", (long)i, strings[i]);
301   }
302   printf("\n");
303 
304   free(strings);
305 }
306 #endif /*DEBUG_IO_LOCKS*/
307 
308 
309 int
Slock(IOSTREAM * s)310 Slock(IOSTREAM *s)
311 { SLOCK(s);
312 
313 #ifdef DEBUG_IO_LOCKS
314   if ( s->locks > 2 )
315   { printf("  Lock [%d]: %s: %d locks", PL_thread_self(), Sname(s), s->locks+1);
316     print_trace();
317   }
318 #endif
319 
320   if ( !s->locks++ )
321   { if ( (s->flags & (SIO_NBUF|SIO_OUTPUT)) == (SIO_NBUF|SIO_OUTPUT) )
322       return S__setbuf(s, NULL, TMPBUFSIZE) == (size_t)-1 ? -1 : 0;
323   }
324 
325   return 0;
326 }
327 
328 
329 int
StryLock(IOSTREAM * s)330 StryLock(IOSTREAM *s)
331 { if ( !STRYLOCK(s) )
332     return -1;
333 
334   if ( !s->locks++ )
335   { if ( (s->flags & (SIO_NBUF|SIO_OUTPUT)) == (SIO_NBUF|SIO_OUTPUT) )
336       return S__setbuf(s, NULL, TMPBUFSIZE) == (size_t)-1 ? -1 : 0;
337   }
338 
339   return 0;
340 }
341 
342 
343 int
Sunlock(IOSTREAM * s)344 Sunlock(IOSTREAM *s)
345 { int rval = 0;
346 
347 #ifdef DEBUG_IO_LOCKS
348   if ( s->locks > 3 )
349   { printf("Unlock [%d]: %s: %d locks", PL_thread_self(), Sname(s), s->locks-1);
350     print_trace();
351   }
352 #endif
353 
354   if ( s->locks )
355   { if ( --s->locks == 0 )
356     { if ( (s->flags & (SIO_NBUF|SIO_OUTPUT)) == (SIO_NBUF|SIO_OUTPUT) )
357 	rval = S__removebuf(s);
358     }
359   } else
360   { assert(0);
361   }
362 
363   SUNLOCK(s);
364   return rval;
365 }
366 
367 
368 		 /*******************************
369 		 *	     FLUSH/FILL		*
370 		 *******************************/
371 
372 /* return values: -1: error, else #bytes written */
373 
374 static ssize_t
S__flushbuf(IOSTREAM * s)375 S__flushbuf(IOSTREAM *s)
376 { char *from, *to;
377   ssize_t rc;
378 
379   SLOCK(s);
380   from = s->buffer;
381   to   = s->bufp;
382 
383   while ( from < to )
384   { size_t size = (size_t)(to - from);
385     ssize_t n = (*s->functions->write)(s->handle, from, size);
386 
387     if ( n > 0 )			/* wrote some */
388     { from += n;
389     } else if ( n < 0 )			/* error */
390     { S__seterror(s);
391       rc = -1;
392       goto out;
393     } else				/* wrote nothing? */
394     { break;
395     }
396   }
397 
398   if ( to == from )			/* full flush */
399   { rc = s->bufp - s->buffer;
400     s->bufp = s->buffer;
401   } else				/* partial flush */
402   { size_t left = to - from;
403 
404     rc = from - s->buffer;
405     memmove(s->buffer, from, left);
406     s->bufp = s->buffer + left;
407   }
408 
409 out:
410   SUNLOCK(s);
411   return rc;
412 }
413 
414 
415 static int
S__flushbufc(int c,IOSTREAM * s)416 S__flushbufc(int c, IOSTREAM *s)
417 { if ( s->buffer )
418   { if ( S__flushbuf(s) <= 0 )		/* == 0: no progress!? */
419       c = -1;
420     else
421       *s->bufp++ = (c & 0xff);
422   } else
423   { if ( s->flags & SIO_NBUF )
424     { char chr = (char)c;
425 
426       if ( (*s->functions->write)(s->handle, &chr, 1) != 1 )
427       { S__seterror(s);
428 	c = -1;
429       }
430     } else
431     { if ( S__setbuf(s, NULL, 0) == (size_t)-1 )
432 	c = -1;
433       else
434 	*s->bufp++ = (char)c;
435     }
436   }
437 
438   return c;
439 }
440 
441 
442 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
443 S__fillbuf() fills the read-buffer, returning the first character of it.
444 It also realises the SWI-Prolog timeout facility.
445 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
446 
447 int
S__fillbuf(IOSTREAM * s)448 S__fillbuf(IOSTREAM *s)
449 { int c;
450 
451   if ( s->flags & (SIO_FEOF|SIO_FERR) )
452   { s->flags |= SIO_FEOF2;		/* reading past eof */
453     return -1;
454   }
455 
456 #ifdef HAVE_SELECT
457   s->flags &= ~SIO_TIMEOUT;
458 
459   if ( s->timeout >= 0 )
460   { int fd = Sfileno(s);
461 
462     if ( fd >= 0 )
463     { fd_set wait;
464       struct timeval time;
465       int rc;
466 
467       time.tv_sec  = s->timeout / 1000;
468       time.tv_usec = (s->timeout % 1000) * 1000;
469       FD_ZERO(&wait);
470 #ifdef __WINDOWS__
471       FD_SET((SOCKET)fd, &wait);
472 #else
473       FD_SET(fd, &wait);
474 #endif
475 
476       for(;;)
477       { rc = select(fd+1, &wait, NULL, NULL, &time);
478 
479 	if ( rc < 0 && errno == EINTR )
480 	{ if ( PL_handle_signals() < 0 )
481 	  { errno = EPLEXCEPTION;
482 	    return -1;
483 	  }
484 
485 	  continue;
486 	}
487 
488 	break;
489       }
490 
491       if ( rc == 0 )
492       { s->flags |= (SIO_TIMEOUT|SIO_FERR);
493 	return -1;
494       }
495     } else
496     { errno = EPERM;			/* no permission to select */
497       s->flags |= SIO_FERR;
498       return -1;
499     }
500   }
501 #endif
502 
503 
504   if ( s->flags & SIO_NBUF )
505   { char chr;
506     ssize_t n;
507 
508     if ( (n=(*s->functions->read)(s->handle, &chr, 1)) == 1 )
509     { c = char_to_int(chr);
510       return c;
511     } else if ( n == 0 )
512     { if ( !(s->flags & SIO_NOFEOF) )
513 	s->flags |= SIO_FEOF;
514       return -1;
515     } else
516     { S__seterror(s);
517       return -1;
518     }
519   } else
520   { ssize_t n;
521     size_t len;
522 
523     if ( !s->buffer )
524     { if ( S__setbuf(s, NULL, 0) == (size_t)-1 )
525 	return -1;
526       s->bufp = s->limitp = s->buffer;
527       len = s->bufsize;
528     } else if ( s->bufp < s->limitp )
529     { len = s->limitp - s->bufp;
530       memmove(s->buffer, s->bufp, s->limitp - s->bufp);
531       s->bufp = s->buffer;
532       s->limitp = &s->bufp[len];
533       len = s->bufsize - len;
534     } else
535     { s->bufp = s->limitp = s->buffer;
536       len = s->bufsize;
537     }
538 
539     if ( (n=(*s->functions->read)(s->handle, s->limitp, len)) > 0 )
540     { s->limitp += n;
541       c = char_to_int(*s->bufp++);
542       return c;
543     } else
544     { if ( n == 0 )
545       { if ( !(s->flags & SIO_NOFEOF) )
546 	  s->flags |= SIO_FEOF;
547 	return -1;
548 #ifdef EWOULDBLOCK
549       } else if ( errno == EWOULDBLOCK )
550       { s->bufp = s->buffer;
551 	s->limitp = s->buffer;
552 	S__seterror(s);
553 	return -1;
554 #endif
555       } else
556       { S__seterror(s);
557 	return -1;
558       }
559     }
560   }
561 }
562 
563 		 /*******************************
564 		 *	   CHARACTER I/O	*
565 		 *******************************/
566 
567 
568 static inline void
update_linepos(IOSTREAM * s,int c)569 update_linepos(IOSTREAM *s, int c)
570 { IOPOS *p = s->position;
571 
572   if ( c > '\r' )			/* speedup the 99% case a bit */
573   { p->linepos++;
574     return;
575   }
576 
577   switch(c)
578   { case '\n':
579       p->lineno++;
580       p->linepos = 0;
581       s->flags &= ~SIO_NOLINEPOS;
582       break;
583     case '\r':
584       p->linepos = 0;
585       s->flags &= ~SIO_NOLINEPOS;
586       break;
587     case '\b':
588       if ( p->linepos > 0 )
589 	p->linepos--;
590       break;
591     case EOF:
592       break;
593     case '\t':
594       p->linepos |= 7;
595     default:
596       p->linepos++;
597   }
598 }
599 
600 
601 
602 int
S__fcheckpasteeof(IOSTREAM * s,int c)603 S__fcheckpasteeof(IOSTREAM *s, int c)
604 { S__checkpasteeof(s, c);
605 
606   return c;
607 }
608 
609 
610 int
S__fupdatefilepos_getc(IOSTREAM * s,int c)611 S__fupdatefilepos_getc(IOSTREAM *s, int c)
612 { IOPOS *p = s->position;
613 
614   update_linepos(s, c);
615   p->byteno++;
616   p->charno++;
617 
618   return c;
619 }
620 
621 
622 static inline int
S__updatefilepos(IOSTREAM * s,int c)623 S__updatefilepos(IOSTREAM *s, int c)
624 { IOPOS *p = s->position;
625 
626   if ( p )
627   { update_linepos(s, c);
628     p->charno++;
629   }
630   S__checkpasteeof(s,c);
631 
632   return c;
633 }
634 
635 
636 static inline int
get_byte(IOSTREAM * s)637 get_byte(IOSTREAM *s)
638 { int c = Snpgetc(s);
639 
640   if ( s->position )
641     s->position->byteno++;
642 
643   return c;
644 }
645 
646 
647 static int
put_byte(int c,IOSTREAM * s)648 put_byte(int c, IOSTREAM *s)
649 { c &= 0xff;
650 
651   if ( s->bufp < s->limitp )
652   { *s->bufp++ = c;
653   } else
654   { if ( S__flushbufc(c, s) < 0 )
655     { s->lastc = EOF;
656       return -1;
657     }
658   }
659 
660   if ( s->position )
661     s->position->byteno++;
662 
663   return c;
664 }
665 
666 
667 int
Sputc(int c,IOSTREAM * s)668 Sputc(int c, IOSTREAM *s)
669 { c &= 0xff;
670 
671   if ( put_byte(c, s) < 0 )
672     return -1;
673 
674   s->lastc = c;
675 
676   if ( c == '\n' && (s->flags & SIO_LBUF) )
677   { if ( S__flushbuf(s) < 0 )
678       return -1;
679   }
680 
681   return S__updatefilepos(s, c);
682 }
683 
684 
685 int
Sfgetc(IOSTREAM * s)686 Sfgetc(IOSTREAM *s)
687 { return Sgetc(s);
688 }
689 
690 
691 static inline void
unget_byte(int c,IOSTREAM * s)692 unget_byte(int c, IOSTREAM *s)
693 { IOPOS *p = s->position;
694 
695   *--s->bufp = c;
696   if ( p )
697   { p->charno--;			/* FIXME: not correct */
698     p->byteno--;
699     if ( c == '\n' )
700       p->lineno--;
701     s->flags |= SIO_NOLINEPOS;
702   }
703 }
704 
705 
706 int
Sungetc(int c,IOSTREAM * s)707 Sungetc(int c, IOSTREAM *s)
708 { if ( s->bufp > s->unbuffer )
709   { unget_byte(c, s);
710 
711     return c;
712   }
713 
714   return -1;
715 }
716 
717 
718 static int
reperror(int c,IOSTREAM * s)719 reperror(int c, IOSTREAM *s)
720 { if ( c >= 0 && (s->flags & (SIO_REPXML|SIO_REPPL)) )
721   { char buf[16];
722     const char *q;
723 
724     if ( (s->flags & SIO_REPPL) )
725     { if ( c <= 0xffff )
726 	sprintf(buf, "\\u%04X", c);
727       else
728 	sprintf(buf, "\\U%08X", c);
729     } else
730       sprintf(buf, "&#%d;", c);
731 
732     for(q = buf; *q; q++)
733     { if ( put_byte(*q, s) < 0 )
734 	return -1;
735     }
736 
737     return c;
738   }
739 
740   Sseterr(s, SIO_FERR|SIO_CLEARERR, "Encoding cannot represent character");
741   return -1;
742 }
743 
744 
745 
746 static int
put_code(int c,IOSTREAM * s)747 put_code(int c, IOSTREAM *s)
748 { switch(s->encoding)
749   { case ENC_OCTET:
750     case ENC_ISO_LATIN_1:
751       if ( c >= 256 )
752       { if ( reperror(c, s) < 0 )
753 	  return -1;
754 	break;
755       }
756     simple:
757       if ( put_byte(c, s) < 0 )
758 	return -1;
759       break;
760     case ENC_ASCII:
761       if ( c >= 128 )
762       { if ( reperror(c, s) < 0 )
763 	  return -1;
764 	break;
765       }
766       goto simple;
767     case ENC_ANSI:
768     { char b[MB_LEN_MAX];
769       size_t n;
770 
771       if ( !s->mbstate )
772       { if ( !(s->mbstate = malloc(sizeof(*s->mbstate))) )
773 	  return EOF;			/* out of memory */
774 	memset(s->mbstate, 0, sizeof(*s->mbstate));
775       }
776 
777       if ( (n = wcrtomb(b, (wchar_t)c, s->mbstate)) == (size_t)-1 )
778       { if ( reperror(c, s) < 0 )
779 	  return -1;
780       } else
781       { size_t i;
782 
783 	for(i=0; i<n; i++)
784 	{ if ( put_byte(b[i]&0xff, s) < 0 )
785 	    return -1;
786 	}
787       }
788 
789       break;
790     }
791     case ENC_UTF8:
792     { char buf[6];
793       char *p, *end;
794 
795       if ( c < 128 )
796 	goto simple;
797 
798       end = utf8_put_char(buf, c);
799       for(p=buf; p<end; p++)
800       { if ( put_byte(*p&0xff, s) < 0 )
801 	  return -1;
802       }
803 
804       break;
805     }
806     case ENC_UNICODE_BE:
807       if ( put_byte(c>>8, s) < 0 )
808 	return -1;
809       if ( put_byte(c&0xff, s) < 0 )
810 	return -1;
811       break;
812     case ENC_UNICODE_LE:
813       if ( put_byte(c&0xff, s) < 0 )
814 	return -1;
815       if ( put_byte(c>>8, s) < 0 )
816 	return -1;
817       break;
818     case ENC_WCHAR:
819     { pl_wchar_t chr = c;
820       unsigned char *q = (unsigned char *)&chr;
821       unsigned char *e = &q[sizeof(pl_wchar_t)];
822 
823       while(q<e)
824       { if ( put_byte(*q++, s) < 0 )
825 	  return -1;
826       }
827 
828       break;
829     }
830     case ENC_UNKNOWN:
831       return -1;
832   }
833 
834 
835   s->lastc = c;
836 
837   if ( c == '\n' && (s->flags & SIO_LBUF) )
838   { if ( S__flushbuf(s) < 0 )
839       return -1;
840   }
841 
842   return S__updatefilepos(s, c);
843 }
844 
845 
846 int
Sputcode(int c,IOSTREAM * s)847 Sputcode(int c, IOSTREAM *s)
848 { if ( c < 0 )
849     return reperror(c, s);
850 
851   if ( s->tee && s->tee->magic == SIO_MAGIC )
852     Sputcode(c, s->tee);
853 
854   if ( c == '\n' && (s->flags&SIO_TEXT) && s->newline == SIO_NL_DOS )
855   { if ( put_code('\r', s) < 0 )
856       return -1;
857   }
858 
859   return put_code(c, s);
860 }
861 
862 
863 int
Scanrepresent(int c,IOSTREAM * s)864 Scanrepresent(int c, IOSTREAM *s)
865 { switch(s->encoding)
866   { case ENC_OCTET:
867     case ENC_ISO_LATIN_1:
868       if ( c <= 0xff )
869 	return 0;
870       return -1;
871     case ENC_ASCII:
872       if ( c < 0x7f )
873 	return 0;
874       return -1;
875     case ENC_ANSI:
876     { mbstate_t state;
877       char b[MB_LEN_MAX];
878 
879       memset(&state, 0, sizeof(state));
880       if ( wcrtomb(b, (wchar_t)c, &state) != (size_t)-1 )
881 	return 0;
882       return -1;
883     }
884     case ENC_WCHAR:
885       if ( sizeof(wchar_t) > 2 )
886 	return 0;
887     /*FALLTHROUGH*/
888     case ENC_UNICODE_BE:
889     case ENC_UNICODE_LE:
890       if ( c <= 0xffff )
891 	return 0;
892       return -1;
893     case ENC_UTF8:
894       return 0;
895     default:
896       assert(0);
897       return -1;
898   }
899 }
900 
901 
902 int
Sgetcode(IOSTREAM * s)903 Sgetcode(IOSTREAM *s)
904 { int c;
905 
906 retry:
907   switch(s->encoding)
908   { case ENC_OCTET:
909     case ENC_ISO_LATIN_1:
910       c = get_byte(s);
911       break;
912     case ENC_ASCII:
913     { c = get_byte(s);
914       if ( c > 128 )
915 	Sseterr(s, SIO_WARN, "non-ASCII character");
916       break;
917     }
918     case ENC_ANSI:
919     { char b[1];
920       size_t rc, n = 0;
921       wchar_t wc;
922 
923       if ( !s->mbstate )
924       { if ( !(s->mbstate = malloc(sizeof(*s->mbstate))) )
925 	  return EOF;			/* out of memory */
926 	memset(s->mbstate, 0, sizeof(*s->mbstate));
927       }
928 
929       for(;;)
930       { if ( (c = get_byte(s)) == EOF )
931 	{ if ( n == 0 )
932 	  { goto out;
933 	  } else
934 	  { Sseterr(s, SIO_WARN, "EOF in multibyte Sequence");
935 	    goto mberr;
936 	  }
937 	}
938 	b[0] = c;
939 
940 	if ( (rc=mbrtowc(&wc, b, 1, s->mbstate)) == 1 )
941 	{ c = wc;
942 	  goto out;
943 	} else if ( rc == (size_t)-1 )
944 	{ Sseterr(s, SIO_WARN, "Illegal multibyte Sequence");
945 	  goto mberr;
946 	}				/* else -2: incomplete */
947       }
948 
949     mberr:
950       c = UTF8_MALFORMED_REPLACEMENT;
951       goto out;
952     }
953     case ENC_UTF8:
954     { c = get_byte(s);
955       if ( c == EOF )
956 	break;
957 
958       if ( c & 0x80 )
959       { int extra = UTF8_FBN(c);
960 	int code;
961 
962 	if ( extra < 0 )
963 	{ Sseterr(s, SIO_WARN, "Illegal UTF-8 start");
964 	  c = UTF8_MALFORMED_REPLACEMENT;
965 	  goto out;
966 	}
967 
968 	code = UTF8_FBV(c,extra);
969 	for( ; extra > 0; extra-- )
970 	{ int c2 = get_byte(s);
971 
972 	  if ( !ISUTF8_CB(c2) )
973 	  { Sseterr(s, SIO_WARN, "Illegal UTF-8 continuation");
974 	    c = UTF8_MALFORMED_REPLACEMENT;
975 	    Sungetc(c2, s);
976 	    goto out;
977 	  }
978 	  code = (code<<6)+(c2&0x3f);
979 	}
980 	c = code;
981       }
982       break;
983     }
984     case ENC_UNICODE_BE:
985     case ENC_UNICODE_LE:
986     { int c1, c2;
987 
988       c1 = get_byte(s);
989       if ( c1 == EOF )
990       { c = -1;
991 	goto out;
992       }
993       c2 = get_byte(s);
994 
995       if ( c2 == EOF )
996       { Sseterr(s, SIO_WARN, "EOF in unicode character");
997 	c = UTF8_MALFORMED_REPLACEMENT;
998       } else
999       { if ( s->encoding == ENC_UNICODE_BE )
1000 	  c = (c1<<8)+c2;
1001 	else
1002 	  c = (c2<<8)+c1;
1003       }
1004 
1005       break;
1006     }
1007     case ENC_WCHAR:
1008     { pl_wchar_t chr;
1009       char *p = (char*)&chr;
1010       size_t n;
1011 
1012       for(n=0; n<sizeof(pl_wchar_t); n++)
1013       { int c1 = get_byte(s);
1014 
1015 	if ( c1 == EOF )
1016 	{ if ( n == 0 )
1017 	  { c = -1;
1018 	    goto out;
1019 	  } else
1020 	  { Sseterr(s, SIO_WARN, "EOF in UCS character");
1021 	    c = UTF8_MALFORMED_REPLACEMENT;
1022 	    goto out;
1023 	  }
1024 	}
1025 
1026 	*p++ = c1;
1027       }
1028 
1029       c = chr;
1030       break;
1031     }
1032     default:
1033       assert(0);
1034       c = -1;
1035   }
1036 
1037 out:
1038   if ( c == '\r' && (s->flags&SIO_TEXT) )
1039   { switch(s->newline)
1040     { case SIO_NL_DETECT:
1041 	s->newline = SIO_NL_DOS;
1042         /*FALLTHROUGH*/
1043       case SIO_NL_DOS:
1044 	goto retry;
1045     }
1046   }
1047 
1048   if ( s->tee && s->tee->magic == SIO_MAGIC && c != -1 )
1049     Sputcode(c, s->tee);
1050 
1051   return S__updatefilepos(s, c);
1052 }
1053 
1054 
1055 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1056 (*) For ENC_ANSI there is  a  problem   as  this  deals with multi-modal
1057 streams, streams that  may  hold  escape   sequences  to  move  from one
1058 character set to another: ascii  ...   <esc1>  japanese <esc2> ascii ...
1059 Suppose now we have two characters   [ascii, japanese]. When reading the
1060 japanese character the  first  time,  the   system  will  translate  the
1061 <esc><japanese> and the mode will be   japanese. When pushing back, only
1062 the japanese character will be put back,   not the escape sequence. What
1063 to do?
1064 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
1065 
1066 int
Sungetcode(int c,IOSTREAM * s)1067 Sungetcode(int c, IOSTREAM *s)
1068 { switch(s->encoding)
1069   { case ENC_OCTET:
1070     case ENC_ISO_LATIN_1:
1071       if ( c >= 256 )
1072 	return -1;			/* illegal */
1073     simple:
1074       if ( s->bufp > s->unbuffer )
1075       { unget_byte(c, s);
1076         return c;
1077       }
1078       return -1;			/* no room */
1079     case ENC_ASCII:
1080       if ( c >= 128 )
1081 	return -1;			/* illegal */
1082       goto simple;
1083     case ENC_ANSI:			/* (*) See above */
1084     { char b[MB_LEN_MAX];
1085       size_t n;
1086 
1087       if ( !s->mbstate )		/* do we need a seperate state? */
1088       { if ( !(s->mbstate = malloc(sizeof(*s->mbstate))) )
1089 	  return EOF;			/* out of memory */
1090 	memset(s->mbstate, 0, sizeof(*s->mbstate));
1091       }
1092 
1093       if ( (n = wcrtomb(b, (wchar_t)c, s->mbstate)) != (size_t)-1 &&
1094 	   s->bufp >= n + s->unbuffer )
1095       { size_t i;
1096 
1097 	for(i=n; i-- > 0; )
1098 	{ unget_byte(b[i], s);
1099 	}
1100 
1101         return c;
1102       }
1103 
1104       return -1;
1105     }
1106     case ENC_UTF8:
1107     { if ( (unsigned)c >= 0x8000000 )
1108 	return -1;
1109 
1110       if ( c < 0x80 )
1111       { goto simple;
1112       } else
1113       { char buf[6];
1114 	char *p, *end;
1115 
1116 	end = utf8_put_char(buf, c);
1117 	if ( s->bufp - s->unbuffer >= end-buf )
1118 	{ for(p=end-1; p>=buf; p--)
1119 	  { unget_byte(*p, s);
1120 	  }
1121 
1122           return c;
1123 	}
1124 
1125 	return -1;
1126       }
1127     }
1128     case ENC_UNICODE_BE:
1129     { if ( c >= 0x10000 )
1130 	return -1;
1131 
1132       if ( s->bufp-1 > s->unbuffer )
1133       { unget_byte(c&0xff, s);
1134 	unget_byte((c>>8)&0xff, s);
1135 
1136         return c;
1137       }
1138       return -1;
1139     }
1140     case ENC_UNICODE_LE:
1141     { if ( c >= 0x10000 )
1142 	return -1;
1143 
1144       if ( s->bufp-1 > s->unbuffer )
1145       { unget_byte((c>>8)&0xff, s);
1146 	unget_byte(c&0xff, s);
1147 
1148         return c;
1149       }
1150       return -1;
1151     }
1152     case ENC_WCHAR:
1153     { pl_wchar_t chr = c;
1154 
1155       if ( s->bufp-sizeof(chr) >= s->unbuffer )
1156       { char *p = (char*)&chr;
1157 	int n;
1158 
1159 	for(n=sizeof(chr); --n>=0; )
1160 	  unget_byte(p[n], s);
1161 
1162 	return c;
1163       }
1164       return -1;
1165     }
1166     case ENC_UNKNOWN:
1167       return -1;
1168   }
1169 
1170   assert(0);
1171   return -1;
1172 }
1173 
1174 		 /*******************************
1175 		 *	    PUTW/GETW		*
1176 		 *******************************/
1177 
1178 int
Sputw(int w,IOSTREAM * s)1179 Sputw(int w, IOSTREAM *s)
1180 { unsigned char *q = (unsigned char *)&w;
1181   unsigned int n;
1182 
1183   for(n=0; n<sizeof(w); n++)
1184   { if ( Sputc(*q++, s) < 0 )
1185       return -1;
1186   }
1187 
1188   return w;
1189 }
1190 
1191 
1192 int
Sgetw(IOSTREAM * s)1193 Sgetw(IOSTREAM *s)
1194 { int w;
1195   unsigned char *q = (unsigned char *)&w;
1196   unsigned int n;
1197 
1198   for(n=0; n<sizeof(w); n++)
1199   { int c;
1200 
1201     if ( (c = Sgetc(s)) < 0 )
1202       return -1;
1203     *q++ = c & 0xff;
1204   }
1205 
1206   return w;
1207 }
1208 
1209 		 /*******************************
1210 		 *	    FREAD/FWRITE	*
1211 		 *******************************/
1212 
1213 size_t
Sfread(void * data,size_t size,size_t elms,IOSTREAM * s)1214 Sfread(void *data, size_t size, size_t elms, IOSTREAM *s)
1215 { size_t chars = size * elms;
1216   char *buf = data;
1217 
1218   if ( s->position )
1219   { for( ; chars > 0; chars-- )
1220     { int c;
1221 
1222       if ( (c = Sgetc(s)) == EOF )
1223 	break;
1224 
1225       *buf++ = c & 0xff;
1226     }
1227   } else
1228   { while(chars > 0)
1229     { int c;
1230 
1231       if ( s->bufp < s->limitp )
1232       { size_t avail = s->limitp - s->bufp;
1233 
1234 	if ( chars <= avail )
1235 	{ memcpy(buf, s->bufp, chars);
1236 	  s->bufp += chars;
1237 	  return elms;
1238 	} else
1239 	{ memcpy(buf, s->bufp, avail);
1240 	  chars -= avail;
1241 	  buf += avail;
1242 	  s->bufp += avail;
1243 	}
1244       }
1245 
1246       if ( (c = S__fillbuf(s)) == EOF )
1247 	break;
1248 
1249       *buf++ = c & 0xff;
1250       chars--;
1251     }
1252   }
1253 
1254   return (size*elms - chars)/size;
1255 }
1256 
1257 
1258 size_t
Sfwrite(const void * data,size_t size,size_t elms,IOSTREAM * s)1259 Sfwrite(const void *data, size_t size, size_t elms, IOSTREAM *s)
1260 { size_t chars = size * elms;
1261   const char *buf = data;
1262 
1263   for( ; chars > 0; chars-- )
1264   { if ( Sputc(*buf++, s) < 0 )
1265       break;
1266   }
1267 
1268   return (size*elms - chars)/size;
1269 }
1270 
1271 
1272 		 /*******************************
1273 		 *	       PENDING		*
1274 		 *******************************/
1275 
1276 ssize_t
Sread_pending(IOSTREAM * s,char * buf,size_t limit,int flags)1277 Sread_pending(IOSTREAM *s, char *buf, size_t limit, int flags)
1278 { int done = 0;
1279   size_t n;
1280 
1281   if ( s->bufp >= s->limitp && (flags & SIO_RP_BLOCK) )
1282   { int c = S__fillbuf(s);
1283 
1284     if ( c < 0 )
1285     { if ( (s->flags & SIO_FEOF) )
1286 	return 0;
1287       return c;
1288     }
1289 
1290     buf[0] = c;
1291     limit--;
1292     done = 1;
1293   }
1294 
1295   n = s->limitp - s->bufp;
1296   if ( n > limit )
1297     n = limit;
1298   memcpy(&buf[done], s->bufp, n);
1299   s->bufp += n;
1300 
1301   return done+n;
1302 }
1303 
1304 
1305 		 /*******************************
1306 		 *               BOM		*
1307 		 *******************************/
1308 
1309 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1310 Check the stream for a BOM (Byte Order Marker).  If present (and known),
1311 update the stream encoding.  Return value is one of
1312 
1313 	-1:	error (check errno)
1314 	 0:	ok.  If BOM, SIO_BOM is added to flags
1315 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
1316 
1317 typedef struct
1318 { IOENC encoding;
1319   unsigned int bomlen;
1320   const char *bom;
1321 } bomdef;
1322 
1323 static const bomdef bomdefs[] =
1324 { { ENC_UTF8,       3, "\357\273\277" }, /* 0xef, 0xbb, 0xbb */
1325   { ENC_UNICODE_BE, 2, "\376\377" },	 /* 0xfe, oxff */
1326   { ENC_UNICODE_LE, 2, "\377\376" },	 /* 0xff, oxfe */
1327   { ENC_UNKNOWN,    0, NULL }
1328 };
1329 
1330 int
ScheckBOM(IOSTREAM * s)1331 ScheckBOM(IOSTREAM *s)
1332 { if ( (s->flags & SIO_NBUF) )
1333   { errno = EINVAL;
1334     return -1;
1335   }
1336 
1337   for(;;)
1338   { size_t avail = s->limitp - s->bufp;
1339     const bomdef *bd;
1340 
1341     for(bd=bomdefs; bd->bomlen; bd++)
1342     { if ( avail >= bd->bomlen && memcmp(s->bufp, bd->bom, bd->bomlen) == 0 )
1343       { s->encoding = bd->encoding;
1344 	s->bufp += bd->bomlen;
1345 	s->flags |= SIO_BOM;
1346 	return 0;
1347       }
1348     }
1349 
1350     if ( avail >= 4 )			/* longest BOM */
1351       return 0;
1352 
1353     if ( S__fillbuf(s) == -1 )
1354       return 0;				/* empty stream */
1355     s->bufp--;
1356   }
1357 }
1358 
1359 
1360 int
SwriteBOM(IOSTREAM * s)1361 SwriteBOM(IOSTREAM *s)
1362 { switch(s->encoding)
1363   { case ENC_UTF8:
1364     case ENC_UNICODE_LE:
1365     case ENC_UNICODE_BE:
1366     { if ( Sputcode(0xfeff, s) != -1 )
1367       { s->flags |= SIO_BOM;
1368 
1369 	return 0;
1370       }
1371       return -1;
1372     }
1373     default:
1374       return 0;
1375   }
1376 }
1377 
1378 
1379 		 /*******************************
1380 		 *               FLAGS		*
1381 		 *******************************/
1382 
1383 int
Sfeof(IOSTREAM * s)1384 Sfeof(IOSTREAM *s)
1385 { if ( s->flags & SIO_FEOF )
1386     return TRUE;
1387 
1388   if ( s->bufp < s->limitp )
1389     return FALSE;
1390 
1391   if ( s->flags & SIO_NBUF )
1392   { errno = EINVAL;
1393     return -1;
1394   }
1395 
1396   if ( S__fillbuf(s) == -1 )
1397     return TRUE;
1398 
1399   s->bufp--;
1400   return FALSE;
1401 }
1402 
1403 
1404 static int
S__seterror(IOSTREAM * s)1405 S__seterror(IOSTREAM *s)
1406 { s->io_errno = errno;
1407 
1408   if ( !(s->flags&SIO_CLOSING) &&	/* s->handle is already invalid */
1409        s->functions->control )
1410   { char *msg;
1411 
1412     if ( (*s->functions->control)(s->handle,
1413 				  SIO_LASTERROR,
1414 				  (void *)&msg) == 0 )
1415     { Sseterr(s, SIO_FERR, msg);
1416       return 0;
1417     }
1418   }
1419 
1420   s->flags |= SIO_FERR;
1421   return 0;
1422 }
1423 
1424 
1425 int
Sferror(IOSTREAM * s)1426 Sferror(IOSTREAM *s)
1427 { return (s->flags & SIO_FERR) != 0;
1428 }
1429 
1430 
1431 int
Sfpasteof(IOSTREAM * s)1432 Sfpasteof(IOSTREAM *s)
1433 { return (s->flags & (SIO_FEOF2ERR|SIO_FEOF2)) == (SIO_FEOF2ERR|SIO_FEOF2);
1434 }
1435 
1436 
1437 void
Sclearerr(IOSTREAM * s)1438 Sclearerr(IOSTREAM *s)
1439 { s->flags &= ~(SIO_FEOF|SIO_WARN|SIO_FERR|SIO_FEOF2|SIO_TIMEOUT|SIO_CLEARERR);
1440   s->io_errno = 0;
1441   Sseterr(s, 0, NULL);
1442 }
1443 
1444 
1445 void
Sseterr(IOSTREAM * s,int flag,const char * message)1446 Sseterr(IOSTREAM *s, int flag, const char *message)
1447 { if ( s->message )
1448   { free(s->message);
1449     s->message = NULL;
1450     s->flags &= ~SIO_CLEARERR;
1451   }
1452   if ( message )
1453   { s->flags |= flag;
1454     s->message = strdup(message);
1455   } else
1456   { s->flags &= ~flag;
1457   }
1458 }
1459 
1460 
1461 void
Sset_exception(IOSTREAM * s,term_t ex)1462 Sset_exception(IOSTREAM *s, term_t ex)
1463 { s->exception = PL_record(ex);
1464   s->flags |= SIO_FERR;
1465 }
1466 
1467 
1468 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1469 Set the encoding of a stream. The enc   argument is the new encoding. If
1470 old is not NULL, the old encoding is written to the given location.
1471 
1472 Please note that not all code changing  the encoding call Ssetenc at the
1473 moment.
1474 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
1475 
1476 int
Ssetenc(IOSTREAM * s,IOENC enc,IOENC * old)1477 Ssetenc(IOSTREAM *s, IOENC enc, IOENC *old)
1478 { if ( old )
1479     *old = s->encoding;
1480   if ( enc == s->encoding )
1481     return 0;
1482 
1483   if ( s->functions->control )
1484   { if ( (*s->functions->control)(s->handle,
1485 				  SIO_SETENCODING,
1486 				  (void *)&enc) != 0 )
1487       return -1;
1488   }
1489 
1490   s->encoding = enc;
1491   return 0;
1492 }
1493 
1494 		 /*******************************
1495 		 *	      FLUSH		*
1496 		 *******************************/
1497 
1498 int
Sflush(IOSTREAM * s)1499 Sflush(IOSTREAM *s)
1500 { if ( s->buffer && (s->flags & SIO_OUTPUT) )
1501   { if ( S__flushbuf(s) < 0 )
1502       return -1;
1503     if ( s->functions->control &&
1504 	 (*s->functions->control)(s->handle, SIO_FLUSHOUTPUT, NULL) < 0 )
1505       return -1;
1506   }
1507 
1508   return 0;
1509 }
1510 
1511 		 /*******************************
1512 		 *	      SEEK		*
1513 		 *******************************/
1514 
1515 int
Sunit_size(IOSTREAM * s)1516 Sunit_size(IOSTREAM *s)
1517 { switch(s->encoding)
1518   { case ENC_UNKNOWN:
1519     case ENC_OCTET:
1520     case ENC_ASCII:
1521     case ENC_ISO_LATIN_1:
1522     case ENC_ANSI:
1523     case ENC_UTF8:
1524       return 1;
1525     case ENC_UNICODE_BE:
1526     case ENC_UNICODE_LE:
1527       return 2;
1528     case ENC_WCHAR:
1529       return sizeof(wchar_t);
1530     default:
1531       assert(0);
1532       return 1;				/* not reached */
1533   }
1534 }
1535 
1536 
1537 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1538 Return the size of the underlying data object.  Should be optimized;
1539 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
1540 
1541 long
Ssize(IOSTREAM * s)1542 Ssize(IOSTREAM *s)
1543 { if ( s->functions->control )
1544   { long size;
1545 
1546     if ( (*s->functions->control)(s->handle, SIO_GETSIZE, (void *)&size) == 0 )
1547       return size;
1548   }
1549   if ( s->functions->seek )
1550   { long here = Stell(s);
1551     long end;
1552 
1553     if ( Sseek(s, 0, SIO_SEEK_END) == 0 )
1554       end = Stell(s);
1555     else
1556       end = -1;
1557     Sseek(s, here, SIO_SEEK_SET);
1558 
1559     return end;
1560   }
1561 
1562   errno = ESPIPE;
1563   S__seterror(s);
1564   return -1;
1565 }
1566 
1567 
1568 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1569 Sseek64(IOSTREAM *s, int64_t pos, int whence)
1570 
1571 Re-position the stream to byte-no 'pos'.
1572 
1573 Maybe we should optimise this to become block-aligned?  Or can we leave
1574 this to read/write?
1575 
1576 The first part checks whether  repositioning   the  read  pointer in the
1577 buffer suffices to achieve the seek.
1578 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
1579 
1580 int
Sseek64(IOSTREAM * s,int64_t pos,int whence)1581 Sseek64(IOSTREAM *s, int64_t pos, int whence)
1582 { if ( (s->flags & SIO_INPUT) && s->limitp > s->buffer ) /* something there */
1583   { int64_t now = Stell64(s);
1584 
1585     if ( now != -1 )
1586     { int64_t newpos;
1587       char *nbufp = (char *)-1;
1588 
1589       if ( whence == SIO_SEEK_CUR )
1590       { nbufp = s->bufp + pos;
1591 	newpos = now + pos;
1592       } else if ( whence == SIO_SEEK_SET )
1593       { nbufp = s->bufp + (pos - now);
1594 	newpos = pos;
1595       } else
1596 	newpos = -1;			/* should not happen */
1597 
1598       if ( nbufp >= s->buffer && nbufp < s->limitp )
1599       { s->bufp = nbufp;
1600 
1601 	pos = newpos;
1602 	goto update;
1603       }
1604     }
1605   }
1606 
1607   if ( !s->functions->seek && !s->functions->seek64 )
1608   { errno = ESPIPE;
1609     S__seterror(s);
1610     return -1;
1611   }
1612 
1613   Sflush(s);
1614 
1615   s->bufp   = s->buffer;
1616   if ( (s->flags & SIO_INPUT) )
1617     s->limitp = s->buffer;
1618 
1619   if ( whence == SIO_SEEK_CUR )
1620   { pos += Stell64(s);
1621     whence = SIO_SEEK_SET;
1622   }
1623 
1624   if ( s->functions->seek64 )
1625     pos = (*s->functions->seek64)(s->handle, pos, whence);
1626   else if ( pos <= LONG_MAX )
1627     pos = (*s->functions->seek)(s->handle, (long)pos, whence);
1628   else
1629   { errno = EINVAL;
1630     S__seterror(s);
1631     return -1;
1632   }
1633 
1634   if ( pos < 0 )
1635   { S__seterror(s);
1636     return -1;
1637   }
1638 
1639 update:
1640   s->flags &= ~(SIO_FEOF|SIO_FEOF2);	/* not on eof of file anymore */
1641 
1642   if ( s->position )
1643   { s->flags |= (SIO_NOLINENO|SIO_NOLINEPOS); /* no update this */
1644     s->position->byteno = pos;
1645     s->position->charno = pos/Sunit_size(s); /* compatibility */
1646   }
1647 
1648   return 0;
1649 }
1650 
1651 
1652 int
Sseek(IOSTREAM * s,long pos,int whence)1653 Sseek(IOSTREAM *s, long pos, int whence)
1654 { return Sseek64(s, (int64_t)pos, whence);
1655 }
1656 
1657 
1658 
1659 /* Stell64(IOSTREAM *s) returns the current position in the file in
1660    bytes
1661 */
1662 
1663 int64_t
Stell64(IOSTREAM * s)1664 Stell64(IOSTREAM *s)
1665 { if ( s->position )
1666   { return s->position->byteno;
1667   } else if ( s->functions->seek || s->functions->seek64 )
1668   { int64_t pos;
1669 
1670     if ( s->functions->seek64 )
1671       pos = (*s->functions->seek64)(s->handle, 0L, SIO_SEEK_CUR);
1672     else
1673       pos = (*s->functions->seek)(s->handle, 0L, SIO_SEEK_CUR);
1674 
1675     if ( s->buffer )			/* open */
1676     { int64_t off = s->bufp - s->buffer;
1677 
1678       if ( s->flags & SIO_INPUT )
1679 	off -= s->limitp - s->buffer;
1680 
1681       pos += off;
1682     }
1683 
1684     return pos;
1685   } else
1686   { errno = EINVAL;
1687     S__seterror(s);
1688     return -1;
1689   }
1690 }
1691 
1692 
1693 long
Stell(IOSTREAM * s)1694 Stell(IOSTREAM *s)
1695 { int64_t pos = Stell64(s);
1696 
1697   if ( pos == -1 )
1698     return -1;
1699   if ( pos <= LONG_MAX )
1700     return (long) pos;
1701 
1702   errno = EINVAL;
1703   S__seterror(s);
1704   return -1;
1705 }
1706 
1707 
1708 		 /*******************************
1709 		 *	      CLOSE		*
1710 		 *******************************/
1711 
1712 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1713 (*) Sclose() can be called recursively. For example if an XPCE object is
1714 only referenced from an open stream,  the close-function will delete the
1715 object, which in turn calls the  ->unlink   which  may wish to close the
1716 associated stream.
1717 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
1718 
1719 int
Sclose(IOSTREAM * s)1720 Sclose(IOSTREAM *s)
1721 { int rval = 0;
1722 
1723   if ( s->magic != SIO_MAGIC )		/* already closed!? */
1724   { s->io_errno = errno = EINVAL;
1725     return -1;
1726   }
1727 
1728   if ( (s->flags&SIO_CLOSING) )		/* recursive (*) */
1729     return rval;
1730 
1731   if ( s->upstream )
1732   { Sseterr(s, SIO_FERR, "Locked by upstream filter");
1733     reportStreamError(s);
1734     return -1;
1735   }
1736 
1737   SLOCK(s);
1738   s->flags |= SIO_CLOSING;
1739   rval = S__removebuf(s);
1740   if ( s->mbstate )
1741     free(s->mbstate);
1742 
1743 #ifdef __WINDOWS__
1744   if ( (s->flags & SIO_ADVLOCK) )
1745   { OVERLAPPED ov;
1746     HANDLE h = (HANDLE)_get_osfhandle((int)s->handle);
1747 
1748     memset(&ov, 0, sizeof(ov));
1749     UnlockFileEx(h, 0, 0, 0xffffffff, &ov);
1750     s->flags &= ~SIO_ADVLOCK;
1751   }
1752 #endif
1753   if ( s->functions->close && (*s->functions->close)(s->handle) < 0 )
1754   { S__seterror(s);
1755     rval = -1;
1756   }
1757 
1758   while(s->locks > 0)			/* remove buffer-locks */
1759   { int rc = Sunlock(s);
1760 
1761     if ( rval == 0 )
1762       rval = rc;
1763   }
1764   if ( rval < 0 )
1765     reportStreamError(s);
1766   run_close_hooks(s);			/* deletes Prolog registration */
1767   SUNLOCK(s);
1768 
1769 #ifdef O_PLMT
1770   if ( s->mutex )
1771   { recursiveMutexDelete(s->mutex);
1772     free(s->mutex);
1773     s->mutex = NULL;
1774   }
1775 #endif
1776 
1777   s->magic = SIO_CMAGIC;
1778   if ( s->message )
1779     free(s->message);
1780   if ( !(s->flags & SIO_STATIC) )
1781     free(s);
1782 
1783   return rval;
1784 }
1785 
1786 
1787 		 /*******************************
1788 		 *	     STRING I/O		*
1789 		 *******************************/
1790 
1791 char *
Sfgets(char * buf,int n,IOSTREAM * s)1792 Sfgets(char *buf, int n, IOSTREAM *s)
1793 { char *q = buf;
1794 
1795   while( n-- > 0 )
1796   { int c = Sgetc(s);
1797 
1798     if ( c == EOF )
1799     { *q = '\0';
1800       if ( q == buf )
1801 	buf = NULL;
1802       goto out;
1803     } else
1804     { *q++ = c;
1805       if ( c == '\n' )
1806       { if ( n > 0 )
1807 	  *q = '\0';
1808 	goto out;
1809       }
1810     }
1811   }
1812 
1813 out:
1814   return buf;
1815 }
1816 
1817 
1818 char *
Sgets(char * buf)1819 Sgets(char *buf)
1820 { char *s = Sfgets(buf, Slinesize, Sinput);
1821   char *q;
1822 
1823   if ( s )				/* delete trailing \n */
1824   { q = &s[strlen(s)];
1825     if ( q > s && q[-1] == '\n' )
1826       *--q = '\0';
1827   }
1828 
1829   return s;
1830 }
1831 
1832 
1833 int
Sfputs(const char * q,IOSTREAM * s)1834 Sfputs(const char *q, IOSTREAM *s)
1835 { for( ; *q; q++)
1836   { if ( Sputcode(*q&0xff, s) < 0 )
1837       return EOF;
1838   }
1839 
1840   return 0;
1841 }
1842 
1843 
1844 int
Sputs(const char * q)1845 Sputs(const char *q)
1846 { return Sfputs(q, Soutput);
1847 }
1848 
1849 
1850 		 /*******************************
1851 		 *	       PRINTF		*
1852 		 *******************************/
1853 
1854 int
Sfprintf(IOSTREAM * s,const char * fm,...)1855 Sfprintf(IOSTREAM *s, const char *fm, ...)
1856 { va_list args;
1857   int rval;
1858 
1859   va_start(args, fm);
1860   rval = Svfprintf(s, fm, args);
1861   va_end(args);
1862 
1863   return rval;
1864 }
1865 
1866 
1867 int
Sprintf(const char * fm,...)1868 Sprintf(const char *fm, ...)
1869 { va_list args;
1870   int rval;
1871 
1872   va_start(args, fm);
1873   rval = Svfprintf(Soutput, fm, args);
1874   va_end(args);
1875 
1876   return rval;
1877 }
1878 
1879 
1880 int
Svprintf(const char * fm,va_list args)1881 Svprintf(const char *fm, va_list args)
1882 { return Svfprintf(Soutput, fm, args);
1883 }
1884 
1885 
1886 #define NEXTCHR(s, c)	if ( utf8 ) \
1887 			{ (s) = utf8_get_char((s), &(c)); \
1888 			} else \
1889 			{ c = *(s)++; c &= 0xff; \
1890 			}
1891 
1892 #define OUTCHR(s, c)	do { printed++; \
1893 			     if ( Sputcode((c), (s)) < 0 ) goto error; \
1894 			   } while(0)
1895 #define valdigit(c)	((c) - '0')
1896 #define A_LEFT	0			/* left-aligned field */
1897 #define A_RIGHT 1			/* right-aligned field */
1898 
1899 int
Svfprintf(IOSTREAM * s,const char * fm,va_list args)1900 Svfprintf(IOSTREAM *s, const char *fm, va_list args)
1901 { intptr_t printed = 0;
1902   char buf[TMPBUFSIZE];
1903   int tmpbuf;
1904 
1905   SLOCK(s);
1906 
1907   if ( !s->buffer && (s->flags & SIO_NBUF) )
1908   { S__setbuf(s, buf, sizeof(buf));
1909     tmpbuf = TRUE;
1910   } else
1911     tmpbuf = FALSE;
1912 
1913   while(*fm)
1914   { if ( *fm == '%' )
1915     { fm++;
1916 
1917       if ( *fm == '%' )
1918       { OUTCHR(s, *fm);
1919 	fm++;
1920 	continue;
1921       } else
1922       { int align = A_RIGHT;
1923 	int modified = FALSE;
1924 	int has_arg1 = FALSE, has_arg2 = FALSE;
1925 	int arg1=0, arg2=0;
1926 	char fbuf[100], *fs = fbuf, *fe = fbuf;
1927 	int islong = 0;
1928 	int pad = ' ';
1929 	int utf8 = FALSE;
1930 
1931 	for(;;)
1932 	{ switch(*fm)
1933 	  { case '+':	align = A_RIGHT; fm++; continue;
1934 	    case '-':	align = A_LEFT;  fm++; continue;
1935 	    case '0':	pad = '0';	 fm++; continue;
1936 	    case ' ':	pad = ' ';       fm++; continue;
1937 	    case '#':   modified = TRUE; fm++; continue;
1938 	  }
1939 	  break;
1940 	}
1941 
1942 	if ( *fm == '*' )
1943 	{ has_arg1++;
1944 	  fm++;
1945 	  arg1 = va_arg(args, int);
1946 	} else if ( isdigit(char_to_int(*fm)) )
1947 	{ if ( *fm == '0' )
1948 	    pad = '0';
1949 	  arg1 = valdigit(*fm);
1950 	  has_arg1++;
1951 	  for( fm++; isdigit(char_to_int(*fm)); fm++)
1952 	    arg1 = arg1*10 + valdigit(*fm);
1953 	}
1954 	if ( *fm == '.' )
1955 	{ has_arg2++;
1956 	  fm++;
1957 	  if ( *fm == '*' )
1958 	  { arg2 = va_arg(args, int);
1959 	  } else
1960 	  { arg2 = 0;
1961 	    for( ; isdigit(char_to_int(*fm)); fm++)
1962 	      arg2 = arg2*10 + valdigit(*fm);
1963 	  }
1964 	}
1965 
1966 	if ( *fm == 'l' )
1967 	{ islong++;			/* 1: %ld */
1968 	  fm++;
1969 	}
1970 	if ( *fm == 'l' )
1971 	{ islong++;			/* 2: %lld */
1972 	  fm++;
1973 	}
1974 	if ( *fm == 'U' )		/* %Us: UTF-8 string */
1975 	{ utf8 = TRUE;
1976 	  fm++;
1977 	}
1978 
1979 	switch(*fm)
1980 	{ case 'c':
1981 	    *fe++ = va_arg(args, int);
1982 	    break;
1983 	  case 'p':
1984 	  { void *ptr = va_arg(args, void*);
1985 	    char fmbuf[8], *fp=fmbuf;
1986 	    *fp++ = '%';
1987 	    if ( modified )
1988 	      *fp++ = '#';
1989 	    *fp++ = 'p';
1990 	    *fp   = '\0';
1991 	    sprintf(fs, fmbuf, ptr);
1992 	    fe = &fs[strlen(fs)];
1993 
1994 	    break;
1995 	  }
1996 	  case 'd':
1997 	  case 'i':
1998 	  case 'o':
1999 	  case 'u':
2000 	  case 'x':
2001 	  case 'X':
2002 	  { intptr_t v = 0;			/* make compiler silent */
2003 	    int64_t vl = 0;
2004 	    char fmbuf[8], *fp=fmbuf;
2005 
2006 	    switch( islong )
2007 	    { case 0:
2008 		v = va_arg(args, int);
2009 	        break;
2010 	      case 1:
2011 		v = va_arg(args, long);
2012 	        break;
2013 	      case 2:
2014 	        vl = va_arg(args, int64_t);
2015 		break;
2016 	    }
2017 
2018 	    *fp++ = '%';
2019 	    if ( modified )
2020 	      *fp++ = '#';
2021 	    *fp++ = 'l';
2022 	    if ( islong < 2 )
2023 	    { *fp++ = *fm;
2024 	      *fp   = '\0';
2025 	      sprintf(fs, fmbuf, v);
2026 	    } else
2027 	    {
2028 #ifdef __WINDOWS__
2029 	      strcat(fp-1, "I64");	/* Synchronise with INT64_FORMAT! */
2030 	      fp += strlen(fp);
2031 #else
2032 	      *fp++ = 'l';
2033 #endif
2034 	      *fp++ = *fm;
2035 	      *fp   = '\0';
2036 	      sprintf(fs, fmbuf, vl);
2037 	    }
2038 	    fe = &fs[strlen(fs)];
2039 
2040 	    break;
2041 	  }
2042 	  case 'f':
2043 	  case 'e':
2044 	  case 'E':
2045 	  case 'g':
2046 	  case 'G':
2047 	  { double v = va_arg(args, double);
2048 	    char fmbuf[8], *fp=fmbuf;
2049 
2050 	    *fp++ = '%';
2051 	    if ( modified )
2052 	      *fp++ = '#';
2053 	    if ( has_arg2 )		/* specified percission */
2054 	    { *fp++ = '.';
2055 	      *fp++ = '*';
2056 	      *fp++ = *fm;
2057 	      *fp   = '\0';
2058 	      sprintf(fs, fmbuf, arg2, v);
2059 	    } else
2060 	    { *fp++ = *fm;
2061 	      *fp   = '\0';
2062 	      sprintf(fs, fmbuf, v);
2063 	    }
2064 	    fe = &fs[strlen(fs)];
2065 
2066 	    break;
2067 	  }
2068 	  case 's':
2069 	    fs = va_arg(args, char *);
2070 	    if ( !fs )
2071 	      fs = "(null)";
2072 	    break;
2073 	}
2074 
2075 	if ( has_arg1 )			/* aligned field */
2076 	{ if ( fs == fbuf )
2077 	    *fe = '\0';
2078 
2079 	  if ( align == A_LEFT )
2080 	  { int w = 0;
2081 	    while(*fs)
2082 	    { int c;
2083 	      NEXTCHR(fs, c);
2084 	      OUTCHR(s, c);
2085 	      w++;
2086 	    }
2087 	    while(w < arg1)
2088 	    { OUTCHR(s, pad);
2089 	      w++;
2090 	    }
2091 	  } else /*if ( align == A_RIGHT ) */
2092 	  { size_t w;
2093 
2094 	    if ( fs == fbuf )
2095 	      w = fe - fs;
2096 	    else
2097 	      w = strlen(fs);
2098 
2099 	    if ( utf8 )
2100 	      w = utf8_strlen(fs, w);
2101 
2102 	    if ( (ssize_t)w < arg1 )
2103 	    { w = arg1 - w;
2104 	      while(w > 0 )
2105 	      { OUTCHR(s, pad);
2106 		w--;
2107 	      }
2108 	    }
2109 	    while(*fs)
2110 	    { int c;
2111 	      NEXTCHR(fs, c);
2112 	      OUTCHR(s, c);
2113 	    }
2114 	  }
2115 	} else
2116 	{ if ( fs == fbuf )		/* unaligned field, just output */
2117 	  { while(fs < fe)
2118 	      OUTCHR(s, *fs++);
2119 	  } else
2120 	  { while(*fs)
2121 	    { int c;
2122 	      NEXTCHR(fs, c);
2123 	      OUTCHR(s, c);
2124 	    }
2125 	  }
2126 	}
2127 	fm++;
2128       }
2129     } else if ( *fm == '\\' && fm[1] )
2130     { OUTCHR(s, fm[1]);
2131       fm += 2;
2132     } else
2133     { OUTCHR(s, *fm);
2134       fm++;
2135     }
2136   }
2137 
2138   if ( tmpbuf )
2139   { if ( S__removebuf(s) < 0 )
2140       goto error;
2141   }
2142 
2143   SUNLOCK(s);
2144   return (int)printed;
2145 
2146 error:
2147   SUNLOCK(s);
2148   return -1;
2149 }
2150 
2151 
2152 int
Ssprintf(char * buf,const char * fm,...)2153 Ssprintf(char *buf, const char *fm, ...)
2154 { va_list args;
2155   int rval;
2156 
2157   va_start(args, fm);
2158   rval = Svsprintf(buf, fm, args);
2159   va_end(args);
2160 
2161   return rval;
2162 }
2163 
2164 
2165 int
Svsprintf(char * buf,const char * fm,va_list args)2166 Svsprintf(char *buf, const char *fm, va_list args)
2167 { IOSTREAM s;
2168   int rval;
2169 
2170   memset(&s, 0, sizeof(s));
2171   s.bufp      = buf;
2172   s.limitp    = (char *)(~0L);
2173   s.buffer    = buf;
2174   s.flags     = SIO_FBUF|SIO_OUTPUT;
2175   s.encoding  = ENC_ISO_LATIN_1;
2176 
2177   if ( (rval = Svfprintf(&s, fm, args)) >= 0 )
2178     *s.bufp = '\0';
2179 
2180   return rval;
2181 }
2182 
2183 
2184 int
Svdprintf(const char * fm,va_list args)2185 Svdprintf(const char *fm, va_list args)
2186 { int rval;
2187   IOSTREAM *s = Soutput;
2188 
2189   Slock(s);
2190   rval = Svfprintf(s, fm, args);
2191 #if defined(_DEBUG) && defined(__WINDOWS__)
2192   Sputc('\0', s);
2193   s->bufp--;				/* `Unput' */
2194   OutputDebugString(s->buffer);
2195 #endif
2196   if ( Sflush(s) != 0 )
2197     rval = -1;
2198   Sunlock(s);
2199 
2200   return rval;
2201 }
2202 
2203 
2204 int
Sdprintf(const char * fm,...)2205 Sdprintf(const char *fm, ...)
2206 { va_list args;
2207   int rval;
2208 
2209   va_start(args, fm);
2210   rval = Svdprintf(fm, args);
2211   va_end(args);
2212 
2213   return rval;
2214 }
2215 
2216 #if 0
2217 		 /*******************************
2218 		 *	      SCANF		*
2219 		 *******************************/
2220 
2221 int
2222 Svfscanf(IOSTREAM *s, const char *fm, va_list args)
2223 { int done = 0;				/* # items converted */
2224   int chread = 0;			/* # characters read */
2225   int c = GET(s);			/* current character */
2226   int supress;				/* if TRUE, don't assign (*) */
2227   int field_width;			/* max width of field */
2228   int tsize;				/* SZ_SHORT, SZ_NORMAL, SZ_LONG */
2229 
2230   while(*fm)
2231   { if ( *fm == ' ' )
2232     { while ( isblank(c) )
2233 	c = GET(s);
2234       fm++;
2235       continue;
2236     } else if ( *fm == '%' && fm[1] != '%' )
2237     { supress = FALSE;
2238       field_width = -1;
2239       int size = SZ_STANDARD;
2240 
2241       for(;;)				/* parse modifiers */
2242       { fm++;
2243 	if ( isdigit(*fm) )
2244 	{ field_width = valdigit(*fm);
2245 	  for(++fm; isdigit(*fm); fm++)
2246 	    field_width = 10*field_width + valdigit(*fm);
2247 	  fm--;
2248 	  continue;
2249 	}
2250 	if ( *fm == '*' )
2251 	{ supress++;
2252 	  continue;
2253 	}
2254 	if ( *fm == 'l' )
2255 	{ size = SZ_LONG;
2256 	  continue;
2257 	}
2258 	if ( *fm == 'h' )
2259 	{ size = SZ_SHORT;
2260 	  continue;
2261 	}
2262       }
2263 
2264       if ( *fm != '[' && *fm != c )
2265 	while(isblank(c))
2266 	  c = GET(s);
2267 
2268       switch(*fm)
2269       { { intptr_t v;			/* collect value here */
2270 	  int negative;			/* true if < 0 */
2271 	  int base;			/* base for conversion */
2272 	  int ok;			/* successful */
2273 	case 'd':
2274 	  base = 10;
2275 
2276 	do_int:
2277 	  negative = FALSE;
2278 	  if ( c == '+' )
2279 	    c = GET(s);
2280 	  else if ( c == '-' )
2281 	  { negative++;
2282 	    c = GET(s);
2283 	  }
2284 	do_unsigned:
2285 	  ok = FALSE;
2286 	  if ( base == 16 )		/* hexadecimal */
2287 	  { if ( isxdigit(c) )
2288 	    { v = valxdigit(c);
2289 	      for(c = GET(s); isxdigit(c); c = GET(s))
2290 		v = base*v + valxdigit(c);
2291 	      ok++;
2292 	    }
2293 	  } else
2294 	  { int cv;
2295 
2296 	    if ( isdigit(c) && (cv=valdigit(c)) < base )
2297 	    { v = cv;
2298 	      for(c = GET(s); isdigit(c) && (cv=valdigit(c)) < base; c = GET(s))
2299 		v = base*v + cv;
2300 	      ok++;
2301 	    }
2302 	  }
2303 
2304 	  if ( ok )
2305 	  { if ( !supress )
2306 	    { if ( negative )
2307 		v = -v;
2308 	      if ( tsize == SZ_SHORT )
2309 	      { short *vp = va_arg(args, short *);
2310 		*vp = v;
2311 	      } else if ( tsize == SZ_LONG )
2312 	      { intptr_t *vp = va_arg(args, intptr_t *);
2313 		*vp = v;
2314 	      } else
2315 	      { int *vp = va_arg(args, int *);
2316 		*vp = v;
2317 	      }
2318 	      done++;
2319 	    }
2320 	    continue;			/* with next */
2321 	  } else
2322 	    return done;
2323 	case 'u':
2324 	  base = 10;
2325 	  negative = FALSE;
2326 	  goto do_unsigned;
2327 	case 'o':
2328 	  base = 8;
2329 	  goto do_int;
2330 	case 'x':
2331 	  base = 16;
2332 	  goto do_int;
2333 	case 'i':
2334 	  if ( c == '0' )
2335 	  { int c2 = GET(s);
2336 
2337 	    if ( c2 == 'x' )
2338 	    { base = 16;
2339 	      c = GET(s);
2340 	    } else
2341 	    { UNGET(c2, s);
2342 	      base = 8;
2343 	    }
2344 	    negative = FALSE;
2345 	    goto do_unsigned;
2346 	  }
2347 	  base = 10;
2348 	  goto do_int;
2349 	}
2350 	case 'n':
2351 	  if ( !supress )
2352 	  { if ( tsize == SZ_SHORT )
2353 	    { short *vp = va_arg(args, short *);
2354 	      *vp = chread;
2355 	    } else if ( tsize == SZ_LONG )
2356 	    { intptr_t *vp = va_arg(args, intptr_t *);
2357 	      *vp = chread;
2358 	    } else
2359 	    { int *vp = va_arg(args, int *);
2360 	      *vp = chread;
2361 	    }
2362 	    done++;
2363 	  }
2364 	  fm++;
2365 	  continue;
2366 	case 'E':
2367 	case 'e':
2368 	case 'f':
2369 	case 'G':
2370 	case 'g':
2371 	{ char work[200];
2372 	  char *w = work;
2373 	  int ds = 0;
2374 	  double v;
2375 
2376 	  if ( c == '-' || c == '+' )	/* [+|-] */
2377 	  { *w++ = c;
2378 	    c = GET(s);
2379 	  }
2380 	  while(isdigit(c))		/* {digit} */
2381 	  { *w++ = c;
2382 	    c = GET(s);
2383 	    ds++;
2384 	  }
2385 	  if ( c == '.' )		/* [.] */
2386 	    *w++ = c;
2387 	  while(isdigit(c))		/* {digit} */
2388 	  { *w++ = c;
2389 	    c = GET(s);
2390 	    ds++;
2391 	  }
2392 	  if ( !ds )
2393 	    SCAN_ERROR(s)
2394 	  if ( c == 'e' || c == 'E' )	/* [e<digit>{<digit>}] */
2395 	  { *w++ = c;
2396 	    c = GET(s);
2397 	    if ( !isdigit(c) )
2398 	      SCAN_ERROR(s)
2399 	    while(isdigit(c))
2400 	    { *w++ = c;
2401 	    c = GET(s);
2402 	    }
2403 	  }
2404 
2405 	  if ( !supress )
2406 	  { *w = '\0';
2407 	    v = strtod(work, &w)
2408 	    if ( w == work )
2409 	      SCAN_ERROR(s);
2410 
2411 	    switch(tsize)
2412 	    { case SZ_NORMAL:
2413 	      { float *fp = va_arg(args, float *);
2414 		*fp = v;
2415 		break;
2416 	      }
2417 	      case SZ_LONG:
2418 	      { double *fp = va_arg(args, double *);
2419 		*fp = v;
2420 		break;
2421 	      }
2422 	    }
2423 	    done++;
2424 	  }
2425 
2426 	  fm++;
2427 	  continue;
2428 	}
2429 	case 's':
2430 	  if ( !supress )
2431 	  { char *sp = va_arg(args, char *);
2432 
2433 	    while(!isblank(c) && field_width-- != 0)
2434 	    { *sp++ = c;
2435 	      c = GET(s);
2436 	    }
2437 	  } else
2438 	    while(!isblank(c) && field_width-- != 0)
2439 	      c = GET(s);
2440 	  fm++;
2441 	  continue;
2442 	case 'c':
2443 	  if ( !supress )
2444 	  { char *cp = va_arg(args, char *);
2445 	    *cp = c;
2446 	  }
2447 	  c = GET(s);
2448 	  fm++;
2449 	  continue;
2450 	case '[':
2451 	{ char set[256];
2452 
2453 	  memset(set, 0, sizeof(set));
2454 	  fm++;
2455 	  if ( *fm == ']' )
2456 	    set[*fm++]++;
2457 	  else if ( *fm == '^' )
2458 	  { fm++;
2459 	    negate++;
2460 	  }
2461 	  while(*fm != ']')
2462 	  { if ( *fm == '-' )
2463 
2464 	  }
2465 	}
2466       }
2467     } else				/* normal character */
2468     { if ( c == *fm )
2469       { c = GET(s);
2470 	fm++;
2471 	continue;
2472       }
2473 
2474       break;
2475     }
2476   }
2477 
2478 out:
2479   UNGET(c, s);
2480 
2481   return done;
2482 }
2483 
2484 #endif /*0*/
2485 
2486 
2487 		 /*******************************
2488 		 *	   FILTER STREAMS	*
2489 		 *******************************/
2490 
2491 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2492 Link two streams in a pipeline,  where   filter  filters data for stream
2493 `parent'. If parent is an output steam we have
2494 
2495 	application --> filter --> parent -->
2496 
2497 If parent is an input stream we have
2498 
2499 	--> parent --> filter --> application
2500 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2501 
2502 int
Sset_filter(IOSTREAM * parent,IOSTREAM * filter)2503 Sset_filter(IOSTREAM *parent, IOSTREAM *filter)
2504 { if ( !parent || parent->magic != SIO_MAGIC )
2505   { errno = EINVAL;
2506     return -1;
2507   }
2508 
2509   if ( filter )
2510   { if ( filter->magic != SIO_MAGIC )
2511     { errno = EINVAL;
2512       return -1;
2513     }
2514   }
2515 
2516   parent->upstream = filter;
2517   if ( filter )
2518     filter->downstream = parent;
2519 
2520   return 0;
2521 }
2522 
2523 
2524 		 /*******************************
2525 		 *	    FILE STREAMS	*
2526 		 *******************************/
2527 
2528 static ssize_t
Sread_file(void * handle,char * buf,size_t size)2529 Sread_file(void *handle, char *buf, size_t size)
2530 { intptr_t h = (intptr_t) handle;
2531   ssize_t bytes;
2532 
2533   for(;;)
2534   {
2535 #ifdef __WINDOWS__
2536     bytes = read((int)h, buf, (int)size);
2537 #else
2538     bytes = read((int)h, buf, size);
2539 #endif
2540 
2541     if ( bytes == -1 && errno == EINTR )
2542     { if ( PL_handle_signals() < 0 )
2543       { errno = EPLEXCEPTION;
2544 	return -1;
2545       }
2546 
2547       continue;
2548     }
2549 
2550     return bytes;
2551   }
2552 }
2553 
2554 
2555 static ssize_t
Swrite_file(void * handle,char * buf,size_t size)2556 Swrite_file(void *handle, char *buf, size_t size)
2557 { intptr_t h = (intptr_t) handle;
2558   ssize_t bytes;
2559 
2560   for(;;)
2561   {
2562 #ifdef __WINDOWS__
2563     bytes = write((int)h, buf, (int)size);
2564 #else
2565     bytes = write((int)h, buf, size);
2566 #endif
2567 
2568     if ( bytes == -1 && errno == EINTR )
2569     { if ( PL_handle_signals() < 0 )
2570       { errno = EPLEXCEPTION;
2571 	return -1;
2572       }
2573 
2574       continue;
2575     }
2576 
2577     return bytes;
2578   }
2579 }
2580 
2581 
2582 static long
Sseek_file(void * handle,long pos,int whence)2583 Sseek_file(void *handle, long pos, int whence)
2584 { intptr_t h = (intptr_t) handle;
2585 
2586 					/* cannot do EINTR according to man */
2587   return lseek((int)h, pos, whence);
2588 }
2589 
2590 
2591 #ifdef O_LARGEFILES
2592 static int64_t
Sseek_file64(void * handle,int64_t pos,int whence)2593 Sseek_file64(void *handle, int64_t pos, int whence)
2594 { intptr_t h = (intptr_t) handle;
2595 
2596 					/* cannot do EINTR according to man */
2597   return lseek((int)h, pos, whence);
2598 }
2599 #endif
2600 
2601 
2602 static int
Sclose_file(void * handle)2603 Sclose_file(void *handle)
2604 { intptr_t h = (intptr_t) handle;
2605   int rc;
2606 
2607   do
2608   { rc = close((int) h);
2609   }  while ( rc == -1 && errno == EINTR );
2610 
2611   return rc;
2612 }
2613 
2614 
2615 static int
Scontrol_file(void * handle,int action,void * arg)2616 Scontrol_file(void *handle, int action, void *arg)
2617 { intptr_t h = (intptr_t) handle;
2618   int fd = (int)h;
2619 
2620   switch(action)
2621   { case SIO_GETSIZE:
2622     { intptr_t *rval = arg;
2623       struct stat buf;
2624 
2625       if ( fstat(fd, &buf) == 0 )
2626       {	*rval = buf.st_size;
2627         return 0;
2628       }
2629       return -1;
2630     }
2631     case SIO_SETENCODING:
2632     case SIO_FLUSHOUTPUT:
2633       return 0;
2634     default:
2635       return -1;
2636   }
2637 }
2638 
2639 
2640 IOFUNCTIONS Sfilefunctions =
2641 { Sread_file,
2642   Swrite_file,
2643   Sseek_file,
2644   Sclose_file,
2645   Scontrol_file,
2646 #ifdef O_LARGEFILES
2647   Sseek_file64
2648 #else
2649   NULL
2650 #endif
2651 };
2652 
2653 
2654 IOFUNCTIONS Sttyfunctions =
2655 { Sread_file,
2656   Swrite_file,
2657   NULL,
2658   Sclose_file,
2659   Scontrol_file,
2660 #ifdef O_LARGEFILES
2661   Sseek_file64
2662 #else
2663   NULL
2664 #endif
2665 };
2666 
2667 
2668 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2669 (*)  Windows  isatty()  is  totally  broken   since  VC9;  crashing  the
2670 application instead of returning EINVAL on  wrong   values  of fd. As we
2671 provide  the  socket-id  through   Sfileno,    this   code   crashes  on
2672 tcp_open_socket(). As ttys and its detection is   of no value on Windows
2673 anyway, we skip this. Second, Windows doesn't have fork(), so FD_CLOEXEC
2674 is of no value.
2675 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2676 
2677 IOSTREAM *
Snew(void * handle,int flags,IOFUNCTIONS * functions)2678 Snew(void *handle, int flags, IOFUNCTIONS *functions)
2679 { IOSTREAM *s;
2680   int fd;
2681 
2682   if ( !(s = malloc(sizeof(IOSTREAM))) )
2683   { errno = ENOMEM;
2684     return NULL;
2685   }
2686   memset((char *)s, 0, sizeof(IOSTREAM));
2687   s->magic         = SIO_MAGIC;
2688   s->lastc         = EOF;
2689   s->flags         = flags;
2690   s->handle        = handle;
2691   s->functions     = functions;
2692   s->timeout       = -1;		/* infinite */
2693   s->posbuf.lineno = 1;
2694   s->encoding      = ENC_ISO_LATIN_1;
2695 #if CRLF_MAPPING
2696   s->newline       = SIO_NL_DOS;
2697 #endif
2698   if ( flags & SIO_RECORDPOS )
2699     s->position = &s->posbuf;
2700 #ifdef O_PLMT
2701   if ( !(flags & SIO_NOMUTEX) )
2702   { if ( !(s->mutex = malloc(sizeof(recursiveMutex))) )
2703     { free(s);
2704       return NULL;
2705     }
2706     recursiveMutexInit(s->mutex);
2707   }
2708 #endif
2709 
2710 #ifndef __WINDOWS__			/* (*) */
2711   if ( (fd = Sfileno(s)) >= 0 )
2712   { if ( isatty(fd) )
2713       s->flags |= SIO_ISATTY;
2714 #if defined(F_SETFD) && defined(FD_CLOEXEC)
2715     fcntl(fd, F_SETFD, FD_CLOEXEC);
2716 #endif
2717   }
2718 #endif
2719 
2720   return s;
2721 }
2722 
2723 
2724 #ifndef O_BINARY
2725 #define O_BINARY 0
2726 #endif
2727 
2728 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2729 Open a file. In addition to the normal  arguments, "lr" means get a read
2730 (shared-) lock on the file and  "lw"   means  get  an write (exclusive-)
2731 lock.  How much do we need to test here?
2732 
2733 Note that the low-level open  is  always   binary  as  O_TEXT open files
2734 result in lost and corrupted data in   some  encodings (UTF-16 is one of
2735 them).  Sgetcode()  and  Sputcode()  do  the  LF  <->  CRLF  mapping  of
2736 CRLF_MAPPING is defined.
2737 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2738 
2739 IOSTREAM *
Sopen_file(const char * path,const char * how)2740 Sopen_file(const char *path, const char *how)
2741 { int fd;
2742   int oflags = O_BINARY;
2743   int flags = SIO_FILE|SIO_TEXT|SIO_RECORDPOS|SIO_FBUF;
2744   int op = *how++;
2745   intptr_t lfd;
2746   enum {lnone=0,lread,lwrite} lock = lnone;
2747   IOSTREAM *s;
2748   IOENC enc = ENC_UNKNOWN;
2749 
2750   for( ; *how; how++)
2751   { switch(*how)
2752     { case 'b':				/* binary */
2753 	flags &= ~SIO_TEXT;
2754 	enc = ENC_OCTET;
2755         break;
2756       case 'r':				/* no record */
2757 	flags &= ~SIO_RECORDPOS;
2758         break;
2759       case 'l':				/* lock r: read, w: write */
2760 	if ( *++how == 'r' )
2761 	  lock = lread;
2762         else if ( *how == 'w' )
2763 	  lock = lwrite;
2764         else
2765 	{ errno = EINVAL;
2766 	  return NULL;
2767 	}
2768         break;
2769       default:
2770 	errno = EINVAL;
2771         return NULL;
2772     }
2773   }
2774 
2775 #if O_LARGEFILES && defined(O_LARGEFILE)
2776   oflags |= O_LARGEFILE;
2777 #endif
2778 
2779   switch(op)
2780   { case 'w':
2781       fd = open(path, O_WRONLY|O_CREAT|O_TRUNC|oflags, 0666);
2782       flags |= SIO_OUTPUT;
2783       break;
2784     case 'a':
2785       fd = open(path, O_WRONLY|O_CREAT|O_APPEND|oflags, 0666);
2786       flags |= SIO_OUTPUT|SIO_APPEND;
2787       break;
2788     case 'u':
2789       fd = open(path, O_WRONLY|O_CREAT|oflags, 0666);
2790       flags |= SIO_OUTPUT|SIO_UPDATE;
2791       break;
2792     case 'r':
2793       fd = open(path, O_RDONLY|oflags);
2794       flags |= SIO_INPUT;
2795       break;
2796     default:
2797       errno = EINVAL;
2798       return NULL;
2799   }
2800 
2801 
2802 
2803   if ( fd < 0 )
2804     return NULL;
2805 
2806   if ( lock )
2807   {
2808 #ifdef FCNTL_LOCKS
2809     struct flock buf;
2810 
2811     memset(&buf, 0, sizeof(buf));
2812     buf.l_type = (lock == lread ? F_RDLCK : F_WRLCK);
2813 
2814     if ( fcntl(fd, F_SETLKW, &buf) < 0 )
2815     { int save = errno;
2816       close(fd);
2817       errno = save;
2818       return NULL;
2819     }
2820 #else					/* we don't have locking */
2821 #if __WINDOWS__
2822     HANDLE h = (HANDLE)_get_osfhandle(fd);
2823     OVERLAPPED ov;
2824 
2825     memset(&ov, 0, sizeof(ov));
2826     if ( !LockFileEx(h, (lock == lread ? 0 : LOCKFILE_EXCLUSIVE_LOCK),
2827 		     0,
2828 		     0, 0xfffffff,
2829 		     &ov) )
2830     { close(fd);
2831       errno = EACCES;			/* TBD: proper error */
2832       return NULL;
2833     }
2834 #else
2835     close(fd);
2836     errno = EINVAL;
2837     return NULL;
2838 #endif
2839 #endif
2840   }
2841 
2842   lfd = (intptr_t)fd;
2843   s = Snew((void *)lfd, flags, &Sfilefunctions);
2844   if ( enc != ENC_UNKNOWN )
2845     s->encoding = enc;
2846   if ( lock )
2847     s->flags |= SIO_ADVLOCK;
2848 
2849   return s;
2850 }
2851 
2852 
2853 IOSTREAM *
Sfdopen(int fd,const char * type)2854 Sfdopen(int fd, const char *type)
2855 { intptr_t lfd;
2856   int flags = SIO_FILE|SIO_RECORDPOS|SIO_FBUF;
2857 
2858   if ( fd < 0 )
2859   { errno = EINVAL;
2860     return NULL;
2861   }
2862 #if defined(HAVE_FCNTL) && defined(F_GETFL)
2863   if ( fcntl(fd, F_GETFL) == -1 )
2864     return NULL;
2865 #endif
2866 
2867   if ( *type == 'r' )
2868   { flags |= SIO_INPUT;
2869   } else if ( *type == 'w' )
2870   { flags |= SIO_OUTPUT;
2871   } else
2872   { errno = EINVAL;
2873     return NULL;
2874   }
2875   if ( type[1] != 'b' )
2876     flags |= SIO_TEXT;
2877 
2878   lfd = (intptr_t)fd;
2879 
2880   return Snew((void *)lfd, flags, &Sfilefunctions);
2881 }
2882 
2883 /* MT: as long as s is valid, this should be ok
2884 */
2885 
2886 int
Sfileno(IOSTREAM * s)2887 Sfileno(IOSTREAM *s)
2888 { int n;
2889 
2890   if ( s->flags & SIO_FILE )
2891   { intptr_t h = (intptr_t)s->handle;
2892     n = (int)h;
2893   } else if ( s->flags & SIO_PIPE )
2894   { n = fileno((FILE *)s->handle);
2895   } else if ( s->functions->control &&
2896 	      (*s->functions->control)(s->handle,
2897 				       SIO_GETFILENO,
2898 				       (void *)&n)  == 0 )
2899   { ;
2900   } else
2901   { errno = EINVAL;
2902     n = -1;				/* no file stream */
2903   }
2904 
2905   return n;
2906 }
2907 
2908 
2909 		 /*******************************
2910 		 *	       PIPES		*
2911 		 *******************************/
2912 
2913 #ifdef HAVE_POPEN
2914 #ifdef __WINDOWS__
2915 #include "popen.c"
2916 
2917 #define popen(cmd, how) pt_popen(cmd, how)
2918 #define pclose(fd)	pt_pclose(fd)
2919 #endif
2920 
2921 static ssize_t
Sread_pipe(void * handle,char * buf,size_t size)2922 Sread_pipe(void *handle, char *buf, size_t size)
2923 { FILE *fp = handle;
2924 
2925 #ifdef __WINDOWS__
2926   return read(fileno(fp), buf, (unsigned int)size);
2927 #else
2928   return read(fileno(fp), buf, size);
2929 #endif
2930 }
2931 
2932 
2933 static ssize_t
Swrite_pipe(void * handle,char * buf,size_t size)2934 Swrite_pipe(void *handle, char *buf, size_t size)
2935 { FILE *fp = handle;
2936 
2937 #ifdef __WINDOWS__
2938   return write(fileno(fp), buf, (unsigned int)size);
2939 #else
2940   return write(fileno(fp), buf, size);
2941 #endif
2942 }
2943 
2944 
2945 static int
Sclose_pipe(void * handle)2946 Sclose_pipe(void *handle)
2947 { FILE *fp = handle;
2948 
2949   pclose(fp);
2950   return 0;
2951 }
2952 
2953 
2954 IOFUNCTIONS Spipefunctions =
2955 { Sread_pipe,
2956   Swrite_pipe,
2957   (Sseek_function)0,
2958   Sclose_pipe
2959 };
2960 
2961 
2962 IOSTREAM *
Sopen_pipe(const char * command,const char * type)2963 Sopen_pipe(const char *command, const char *type)
2964 { char mode[2];
2965   FILE *fd;
2966 
2967 #if 0
2968   Sdprintf("Opening \"%s\", mode \"%s\" --> %p (%d)\n",
2969 	   command, type, fd, errno);
2970 #endif
2971 
2972   mode[0] = type[0];
2973   mode[1] = '\0';
2974 
2975   if ( (fd = popen(command, mode)) )
2976   { int flags;
2977 
2978     if ( *type == 'r' )
2979       flags = SIO_PIPE|SIO_INPUT|SIO_FBUF;
2980     else
2981       flags = SIO_PIPE|SIO_OUTPUT|SIO_FBUF;
2982 
2983     return Snew((void *)fd, flags, &Spipefunctions);
2984   }
2985 
2986   return NULL;
2987 }
2988 
2989 #endif /*HAVE_POPEN*/
2990 
2991 		 /*******************************
2992 		 *	  MEMORY STREAMS	*
2993 		 *******************************/
2994 
2995 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2996 Memory streams form a replacement for   sprintf(), sscanf() and friends.
2997 They allow regarding a piece of  (for output) malloc() maintained memory
2998 to serve as a temporary buffer.
2999 
3000 MT: we assume these handles are not passed between threads
3001 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
3002 
3003 typedef struct
3004 { size_t	here;			/* `here' location */
3005   size_t	size;			/* size of buffer */
3006   size_t       *sizep;			/* pointer to size */
3007   size_t	allocated;		/* allocated size */
3008   char	      **buffer;			/* allocated buffer */
3009   int		malloced;		/* malloc() maintained */
3010 } memfile;
3011 
3012 
3013 void
Sfree(void * ptr)3014 Sfree(void *ptr)			/* Windows: must free from same */
3015 { free(ptr);				/* DLL */
3016 }
3017 
3018 
3019 static size_t
S__memfile_nextsize(size_t needed)3020 S__memfile_nextsize(size_t needed)
3021 { size_t size = 512;
3022 
3023   while ( size < needed )
3024     size *= 2;
3025 
3026   return size;
3027 }
3028 
3029 
3030 static ssize_t
Swrite_memfile(void * handle,char * buf,size_t size)3031 Swrite_memfile(void *handle, char *buf, size_t size)
3032 { memfile *mf = handle;
3033 
3034   if ( mf->here + size + 1 >= mf->allocated )
3035   { size_t ns = S__memfile_nextsize(mf->here + size + 1);
3036     char *nb;
3037 
3038     if ( mf->allocated == 0 || !mf->malloced )
3039     { if ( !(nb = malloc(ns)) )
3040       { errno = ENOMEM;
3041 	return -1;
3042       }
3043       if ( !mf->malloced )
3044       { if ( *mf->buffer )
3045 	  memcpy(nb, *mf->buffer, mf->allocated);
3046 	mf->malloced = TRUE;
3047       }
3048     } else
3049     { if ( !(nb = realloc(*mf->buffer, ns)) )
3050       { errno = ENOMEM;
3051 	return -1;
3052       }
3053     }
3054 
3055     mf->allocated = ns;
3056     *mf->buffer = nb;
3057   }
3058 
3059   memcpy(&(*mf->buffer)[mf->here], buf, size);
3060   mf->here += size;
3061 
3062   if ( mf->here > mf->size )
3063   { mf->size = mf->here;
3064     if ( mf->sizep )			/* make externally known */
3065       *mf->sizep = mf->size;
3066     (*mf->buffer)[mf->size] = '\0';
3067   }
3068 
3069   return size;
3070 }
3071 
3072 
3073 static ssize_t
Sread_memfile(void * handle,char * buf,size_t size)3074 Sread_memfile(void *handle, char *buf, size_t size)
3075 { memfile *mf = handle;
3076 
3077   if ( size + mf->here > mf->size )
3078   { if ( mf->here > mf->size )
3079       size = 0;
3080     else
3081       size = mf->size - mf->here;
3082   }
3083 
3084   memcpy(buf, &(*mf->buffer)[mf->here], size);
3085   mf->here += size;
3086 
3087   return size;
3088 }
3089 
3090 
3091 static long
Sseek_memfile(void * handle,long offset,int whence)3092 Sseek_memfile(void *handle, long offset, int whence)
3093 { memfile *mf = handle;
3094 
3095   switch(whence)
3096   { case SIO_SEEK_SET:
3097       break;
3098     case SIO_SEEK_CUR:
3099       offset += (long)mf->here;		/* Win64: truncates */
3100       break;
3101     case SIO_SEEK_END:
3102       offset = (long)mf->size - offset;	/* Win64 */
3103       break;
3104     default:
3105       errno = EINVAL;
3106       return -1;
3107   }
3108   if ( offset < 0 || offset > (long)mf->size )
3109   { errno = EINVAL;
3110     return -1;
3111   }
3112   mf->here = offset;
3113 
3114   return offset;
3115 }
3116 
3117 
3118 static int
Sclose_memfile(void * handle)3119 Sclose_memfile(void *handle)
3120 { memfile *mf = handle;
3121 
3122   if ( mf )
3123   { free(mf);
3124     return 0;
3125   }
3126 
3127   errno = EINVAL;			/* not opened */
3128   return -1;
3129 }
3130 
3131 
3132 IOFUNCTIONS Smemfunctions =
3133 { Sread_memfile,
3134   Swrite_memfile,
3135   Sseek_memfile,
3136   Sclose_memfile
3137 };
3138 
3139 
3140 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
3141 Sopenmem(char **buffer, size_t *sizep, const char* mode)
3142     Open a memory area as a stream.  Output streams will automatically
3143     resized using realloc() if *size = 0 or the stream is opened with mode
3144     "wa".
3145 
3146     If the buffer is allocated or enlarged, this is achieved using malloc()
3147     or realloc().  In this case the returned buffer should be freed by the
3148     caller when done.  Example:
3149 
3150     { char buf[1024];			(don't allocate for small stuff)
3151       char *s = buf;
3152       IOSTREAM *fd;
3153       size_t size = sizeof(buf);
3154 
3155       fd = Sopenmem(&s, &size, "w");
3156       ...
3157       Sclose(fd);
3158       ...
3159       if ( s != buf )			(appearently moved)
3160 	Sfree(s);
3161     }
3162 
3163 Note: Its is NOT allows to access   streams  created with this call from
3164 multiple threads. This is ok for all   usage inside Prolog itself (often
3165 through tellString()/toldString(). This call is   intented  to use write
3166 and other output predicates to create strings.
3167 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
3168 
3169 IOSTREAM *
Sopenmem(char ** buffer,size_t * sizep,const char * mode)3170 Sopenmem(char **buffer, size_t *sizep, const char *mode)
3171 { memfile *mf = malloc(sizeof(memfile));
3172   int flags = SIO_FBUF|SIO_RECORDPOS|SIO_NOMUTEX;
3173   size_t size;
3174 
3175   if ( !mf )
3176   { errno = ENOMEM;
3177     return NULL;
3178   }
3179 
3180   mf->malloced = FALSE;
3181 
3182   switch(*mode)
3183   { case 'r':
3184       flags |= SIO_INPUT;
3185       if ( sizep == NULL || *sizep == (size_t)-1 )
3186 	size = (*buffer ? strlen(*buffer) : 0);
3187       else
3188 	size = *sizep;
3189       mf->size = size;
3190       mf->allocated = size+1;
3191       break;
3192     case 'w':
3193       flags |= SIO_OUTPUT;
3194       mf->size = 0;
3195       mf->allocated = (sizep ? *sizep : 0);
3196       if ( *buffer == NULL || mode[1] == 'a' )
3197 	mf->malloced = TRUE;
3198       if ( *buffer )
3199 	*buffer[0] = '\0';
3200       if ( sizep )
3201 	*sizep = mf->size;
3202       break;
3203     default:
3204       free(mf);
3205       errno = EINVAL;
3206       return NULL;
3207   }
3208 
3209   mf->sizep	= sizep;
3210   mf->here      = 0;
3211   mf->buffer    = buffer;
3212 
3213   return Snew(mf, flags, &Smemfunctions);
3214 }
3215 
3216 		 /*******************************
3217 		 *	      STRINGS		*
3218 		 *******************************/
3219 
3220 /* MT: we assume these handles are not passed between threads
3221 */
3222 
3223 static ssize_t
Sread_string(void * handle,char * buf,size_t size)3224 Sread_string(void *handle, char *buf, size_t size)
3225 { return 0;				/* signal EOF */
3226 }
3227 
3228 static ssize_t
Swrite_string(void * handle,char * buf,size_t size)3229 Swrite_string(void *handle, char *buf, size_t size)
3230 { errno = ENOSPC;			/* signal error */
3231   return -1;
3232 }
3233 
3234 static int
Sclose_string(void * handle)3235 Sclose_string(void *handle)
3236 { IOSTREAM *s = handle;
3237 
3238   if ( s->flags & SIO_OUTPUT )
3239   { if ( s->bufp < s->limitp )
3240     { *s->bufp++ = '\0';
3241       return 0;
3242     } else
3243     { errno = ENOSPC;			/* signal error */
3244       return -1;
3245     }
3246   } else
3247     return 0;				/* input string */
3248 }
3249 
3250 IOFUNCTIONS Sstringfunctions =
3251 { Sread_string,
3252   Swrite_string,
3253   (Sseek_function)0,
3254   Sclose_string
3255 };
3256 
3257 
3258 IOSTREAM *
Sopen_string(IOSTREAM * s,char * buf,size_t size,const char * mode)3259 Sopen_string(IOSTREAM *s, char *buf, size_t size, const char *mode)
3260 { int flags = SIO_FBUF|SIO_USERBUF;
3261 
3262   if ( !s )
3263   { if ( !(s = malloc(sizeof(IOSTREAM))) )
3264     { errno = ENOMEM;
3265       return NULL;
3266     }
3267   } else
3268     flags |= SIO_STATIC;
3269 
3270   memset((char *)s, 0, sizeof(IOSTREAM));
3271   s->timeout   = -1;
3272   s->buffer    = buf;
3273   s->bufp      = buf;
3274   s->unbuffer  = buf;
3275   s->handle    = s;			/* for Sclose_string() */
3276   s->functions = &Sstringfunctions;
3277   s->encoding  = ENC_ISO_LATIN_1;
3278 
3279   switch(*mode)
3280   { case 'r':
3281       if ( size == (size_t)-1 )
3282 	size = strlen(buf);
3283       flags |= SIO_INPUT;
3284       break;
3285     case 'w':
3286       flags |= SIO_OUTPUT;
3287       break;
3288     default:
3289       errno = EINVAL;
3290       return NULL;
3291   }
3292 
3293   s->flags  = flags;
3294   s->limitp = &buf[size];
3295   s->magic  = SIO_MAGIC;
3296 
3297   return s;
3298 }
3299 
3300 		 /*******************************
3301 		 *	 STANDARD HANDLES	*
3302 		 *******************************/
3303 
3304 #define STDIO(n, f) { NULL, NULL, NULL, NULL, \
3305 		      EOF, SIO_MAGIC, 0, f, {0, 0, 0}, NULL, \
3306 		      ((void *)(n)), &Sttyfunctions, \
3307 		      0, NULL, \
3308 		      (void (*)(void *))0, NULL, \
3309 		      -1, \
3310 		      0, \
3311 		      ENC_ISO_LATIN_1 \
3312 		    }
3313 
3314 #define SIO_STDIO (SIO_FILE|SIO_STATIC|SIO_NOCLOSE|SIO_ISATTY|SIO_TEXT)
3315 #define STDIO_STREAMS \
3316   STDIO(0, SIO_STDIO|SIO_LBUF|SIO_INPUT|SIO_NOFEOF),	/* Sinput */ \
3317   STDIO(1, SIO_STDIO|SIO_LBUF|SIO_OUTPUT|SIO_REPPL), 	/* Soutput */ \
3318   STDIO(2, SIO_STDIO|SIO_NBUF|SIO_OUTPUT|SIO_REPPL)	/* Serror */
3319 
3320 
3321 IOSTREAM S__iob[] =
3322 { STDIO_STREAMS
3323 };
3324 
3325 
3326 static const IOSTREAM S__iob0[] =
3327 { STDIO_STREAMS
3328 };
3329 
3330 
3331 void
SinitStreams()3332 SinitStreams()
3333 { static int done;
3334 
3335   if ( !done++ )
3336   { int i;
3337     IOENC enc = initEncoding();
3338 
3339     for(i=0; i<=2; i++)
3340     { if ( !isatty(i) )
3341       { S__iob[i].flags &= ~SIO_ISATTY;
3342 	S__iob[i].functions = &Sfilefunctions; /* Check for pipe? */
3343       }
3344       if ( S__iob[i].encoding == ENC_ISO_LATIN_1 )
3345 	S__iob[i].encoding = enc;
3346 #ifdef O_PLMT
3347       S__iob[i].mutex = malloc(sizeof(recursiveMutex));
3348       recursiveMutexInit(S__iob[i].mutex);
3349 #endif
3350 #if CRLF_MAPPING
3351       _setmode(i, O_BINARY);
3352       S__iob[i].newline = SIO_NL_DOS;
3353 #endif
3354     }
3355 
3356 #ifdef __WINDOWS__
3357     pt_init();				/* init popen() issues */
3358 #endif
3359   }
3360 }
3361 
3362 
3363 IOSTREAM *
S__getiob()3364 S__getiob()
3365 { return S__iob;
3366 }
3367 
3368 
3369 		 /*******************************
3370 		 *	       HOOKS		*
3371 		 *******************************/
3372 
3373 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
3374 This allows external packages (Prolog itself) to monitor the destruction
3375 of streams.
3376 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
3377 
3378 typedef struct _close_hook
3379 { struct _close_hook *next;
3380   void (*hook)(IOSTREAM *s);
3381 } close_hook;
3382 
3383 static close_hook *close_hooks;
3384 
3385 static void
run_close_hooks(IOSTREAM * s)3386 run_close_hooks(IOSTREAM *s)
3387 { close_hook *p;
3388 
3389   for(p=close_hooks; p; p = p->next)
3390     (*p->hook)(s);
3391 
3392   if ( s->close_hook )
3393     (*s->close_hook)(s->closure);
3394 }
3395 
3396 int
Sclosehook(void (* hook)(IOSTREAM * s))3397 Sclosehook(void (*hook)(IOSTREAM *s))
3398 { close_hook *h = malloc(sizeof(*h));
3399 
3400   if ( !h )
3401     return -1;
3402   h->next = close_hooks;
3403   h->hook = hook;
3404   close_hooks = h;
3405 
3406   return 0;
3407 }
3408 
3409 
3410 		 /*******************************
3411 		 *	       CLEANUP		*
3412 		 *******************************/
3413 
3414 void
Sreset(void)3415 Sreset(void)
3416 { IOSTREAM *s;
3417 
3418   if ( (s=Sinput) && s->magic == SIO_MAGIC )
3419   { s->bufp = s->limitp = s->buffer;
3420   }
3421   if ( (s=Soutput) && s->magic == SIO_MAGIC )
3422   { s->bufp = s->buffer;
3423   }
3424   if ( (s=Serror) && s->magic == SIO_MAGIC )
3425   { s->bufp = s->buffer;
3426   }
3427 }
3428 
3429 
3430 void
Scleanup(void)3431 Scleanup(void)
3432 { close_hook *p, *next;
3433   int i;
3434 
3435   for(p=close_hooks; p; p=next)
3436   { next = p->next;
3437     free(p);
3438   }
3439 
3440   close_hooks = NULL;
3441 
3442   for(i=0; i<=2; i++)
3443   { IOSTREAM *s = &S__iob[i];
3444 
3445     s->bufp = s->buffer;		/* avoid actual flush */
3446     S__removebuf(s);
3447 
3448 #ifdef O_PLMT
3449     if ( S__iob[i].mutex )
3450     { recursiveMutex *m = S__iob[i].mutex;
3451 
3452       S__iob[i].mutex = NULL;
3453       recursiveMutexDelete(m);
3454       free(m);
3455     }
3456 #endif
3457 
3458     *s = S__iob0[i];			/* re-initialise */
3459   }
3460 }
3461 
3462