1 /*
2  * An implementation of printf that
3  * - allows printing of 16-bit unicode characters and strings
4  * - translates output to a specified character set
5  *
6  * "char8" is 8 bits and contains ISO-Latin-1 (or ASCII) values
7  * "char16" is 16 bits and contains UTF-16 values
8  * "Char" is char8 or char16 depending on whether CHAR_SIZE is 8 or 16
9  *
10  * Author: Richard Tobin
11  */
12 
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <stdarg.h>
17 #include <err.h>
18 
19 #ifdef FOR_LT
20 
21 #include "lt-memory.h"
22 #include "nsl-err.h"
23 
24 #define ERR(m) LT_ERROR(NECHAR,m)
25 #define ERR1(m,x) LT_ERROR1(NECHAR,m,x)
26 #define ERR2(m,x,y) LT_ERROR2(NECHAR,m,x,y)
27 
28 #define Malloc salloc
29 #define Realloc srealloc
30 #define Free sfree
31 
32 #else
33 
34 #include "system.h"
35 
36 #define ERR(m) fprintf(stderr,m)
37 #define ERR1(m,x) fprintf(stderr,m,x)
38 #define ERR2(m,x,y) fprintf(stderr,m,x,y)
39 #endif
40 
41 #if defined(WIN32) && !defined(__CYGWIN__)
42 #undef boolean
43 #include <winsock.h>
44 #include <fcntl.h>
45 #include <io.h>
46 #else
47 #include <unistd.h>
48 #endif
49 
50 #include "charset.h"
51 #include "string16.h"
52 #include "stdio16.h"
53 
54 /* When we return -1 for non-io errors, we set errno to 0 to avoid confusion */
55 #include <errno.h>
56 
57 #define BufferSize 4096
58 
59 typedef int ReadProc(FILE16 *file, unsigned char *buf, int max_count);
60 typedef int WriteProc(FILE16 *file, const unsigned char *buf, int count);
61 typedef int SeekProc(FILE16 *file, long offset, int ptrname);
62 typedef int FlushProc(FILE16 *file);
63 typedef int CloseProc(FILE16 *file);
64 
65 struct _FILE16 {
66     union {
67 	void *handle;
68 	const void *chandle;
69     };
70     int handle2, handle3;
71     ReadProc *read;
72     WriteProc *write;
73     SeekProc *seek;
74     FlushProc *flush;
75     CloseProc *close;
76     int flags;
77     CharacterEncoding enc;
78     char16 save;
79     /* There are incount unread bytes starting at inbuf[inoffset] */
80     unsigned char inbuf[4096];
81     int incount, inoffset;
82 };
83 
84 #define FILE16_read             0x01
85 #define FILE16_write            0x02
86 #define FILE16_close_underlying 0x04
87 #define FILE16_crlf		0x08
88 #define FILE16_needs_binary     0x8000
89 #define FILE16_error		0x4000
90 #define FILE16_eof		0x2000
91 
92 static void filbuf(FILE16 *file);
93 
94 static int FileRead(FILE16 *file, unsigned char *buf, int max_count);
95 static int FileWrite(FILE16 *file, const unsigned char *buf, int count);
96 static int FileSeek(FILE16 *file, long offset, int ptrname);
97 static int FileClose(FILE16 *file);
98 static int FileFlush(FILE16 *file);
99 
100 static int FDRead(FILE16 *file, unsigned char *buf, int max_count);
101 static int FDWrite(FILE16 *file, const unsigned char *buf, int count);
102 static int FDSeek(FILE16 *file, long offset, int ptrname);
103 static int FDClose(FILE16 *file);
104 static int FDFlush(FILE16 *file);
105 
106 static int StringRead(FILE16 *file, unsigned char *buf, int max_count);
107 static int StringWrite(FILE16 *file, const unsigned char *buf, int count);
108 static int StringWriteTrunc(FILE16 *file, const unsigned char *buf, int count);
109 static int StringSeek(FILE16 *file, long offset, int ptrname);
110 static int StringClose(FILE16 *file);
111 static int StringFlush(FILE16 *file);
112 
113 #if defined(WIN32) && ! defined(__CYGWIN__)
114 #ifdef SOCKETS_IMPLEMENTED
115 static int WinsockRead(FILE16 *file, unsigned char *buf, int max_count);
116 static int WinsockWrite(FILE16 *file, const unsigned char *buf, int count);
117 static int WinsockSeek(FILE16 *file, long offset, int ptrname);
118 static int WinsockClose(FILE16 *file);
119 static int WinsockFlush(FILE16 *file);
120 #endif
121 #endif
122 
123 #ifdef HAVE_LIBZ
124 static int GzipRead(FILE16 *file, unsigned char *buf, int max_count);
125 static int GzipWrite(FILE16 *file, const unsigned char *buf, int count);
126 static int GzipSeek(FILE16 *file, long offset, int ptrname);
127 static int GzipClose(FILE16 *file);
128 static int GzipFlush(FILE16 *file);
129 #endif
130 
131 FILE16 *Stdin,  *Stdout, *Stderr;
132 static int Stdin_open = 0, Stdout_open = 0, Stderr_open = 0;
133 
init_stdio16(void)134 int init_stdio16(void) {
135     if(!Stdin_open)
136     {
137 	if(!(Stdin = MakeFILE16FromFILE(stdin, "r")))
138 	    return -1;
139 	SetFileEncoding(Stdin, CE_ISO_8859_1);
140 	Stdin_open = 1;
141     }
142     if(!Stdout_open)
143     {
144 	if(!(Stdout = MakeFILE16FromFILE(stdout, "w")))
145 	    return -1;
146 	SetFileEncoding(Stdout, CE_ISO_8859_1);
147 	Stdout_open = 1;
148     }
149     if(!Stderr_open)
150     {
151 	if(!(Stderr = MakeFILE16FromFILE(stderr, "w")))
152 	    return -1;
153 	SetFileEncoding(Stderr, CE_ISO_8859_1);
154 	Stderr_open = 1;
155     }
156 
157     return 0;
158 }
159 
deinit_stdio16(void)160 void deinit_stdio16(void) {
161     if(Stdin_open) Fclose(Stdin);
162     if(Stdout_open) Fclose(Stdout);
163     if(Stderr_open) Fclose(Stderr);
164 }
165 
166 /* Return the size (in bytes) of nul in the given encoding */
167 
NullSize(CharacterEncoding enc)168 static int NullSize(CharacterEncoding enc)
169 {
170     switch(enc)
171     {
172     default:
173 	return 1;
174 
175     case CE_UTF_16B:
176     case CE_ISO_10646_UCS_2B:
177     case CE_UTF_16L:
178     case CE_ISO_10646_UCS_2L:
179 	return 2;
180     }
181 }
182 
183 /* Output an ASCII buffer in the specified encoding */
184 
185 /* In fact, we don't translate the buffer at all if we are outputting
186    an 8-bit encoding, and we treat it as Latin-1 is we are outputting
187    a 16-bit encoding.  This means that all the various ASCII supersets
188    will be passed through unaltered in the usual case, since we don't
189    translate them on input either. */
190 
ConvertASCII(const char8 * buf,int count,FILE16 * file)191 static int ConvertASCII(const char8 *buf, int count, FILE16 *file)
192 {
193     /* It *could* be that every character is linefeed... */
194     unsigned char outbuf[BufferSize*4];
195     unsigned char c;
196     int i, j;
197 
198     switch(file->enc)
199     {
200     case CE_ISO_646:
201     case CE_ISO_8859_1:
202     case CE_ISO_8859_2:
203     case CE_ISO_8859_3:
204     case CE_ISO_8859_4:
205     case CE_ISO_8859_5:
206     case CE_ISO_8859_6:
207     case CE_ISO_8859_7:
208     case CE_ISO_8859_8:
209     case CE_ISO_8859_9:
210     case CE_ISO_8859_10:
211     case CE_ISO_8859_11:
212     case CE_ISO_8859_13:
213     case CE_ISO_8859_14:
214     case CE_ISO_8859_15:
215     case CE_unspecified_ascii_superset:
216 	if(file->flags & FILE16_crlf)
217 	{
218 	    for(i=j=0; i<count; i++)
219 	    {
220 		c = buf[i];
221 		if(c == '\n')
222 		    outbuf[j++] = '\r';
223 		outbuf[j++] = c;
224 	    }
225 	    return Writeu(file, outbuf, j);
226 	}
227 	else
228 	    return Writeu(file, (const unsigned char *)buf, count);
229 
230     case CE_UTF_8:
231 	for(i=j=0; i<count; i++)
232 	{
233 	    c = buf[i];
234 	    if(c == '\n' && (file->flags & FILE16_crlf))
235 		outbuf[j++] = '\r';
236 	    if(c < 128)
237 		outbuf[j++] = c;
238 	    else
239 	    {
240 		outbuf[j++] = 0xc0 + (c >> 6);
241 		outbuf[j++] = 0x80 + (c & 0x3f);
242 	    }
243 	}
244 	return Writeu(file, outbuf, j);
245 
246     case CE_UTF_16B:
247     case CE_ISO_10646_UCS_2B:
248 	for(i=j=0; i<count; i++)
249 	{
250 	    c = buf[i];
251 	    if(c == '\n' && (file->flags & FILE16_crlf))
252 	    {
253 		outbuf[j++] = 0;
254 		outbuf[j++] = '\r';
255 	    }
256 	    outbuf[j++] = 0;
257 	    outbuf[j++] = c;
258 	}
259 	return Writeu(file, outbuf, j);
260 
261     case CE_UTF_16L:
262     case CE_ISO_10646_UCS_2L:
263 	for(i=j=0; i<count; i++)
264 	{
265 	    c = buf[i];
266 	    if(c == '\n' && (file->flags & FILE16_crlf))
267 	    {
268 		outbuf[j++] = '\r';
269 		outbuf[j++] = 0;
270 	    }
271 	    outbuf[j++] = c;
272 	    outbuf[j++] = 0;
273 	}
274 	return Writeu(file, outbuf, j);
275 
276     default:
277       ERR2("Bad output character encoding %d (%s)\n",
278 		file->enc,
279 		file->enc < CE_enum_count ? CharacterEncodingName[file->enc] :
280 		                            "unknown");
281 	errno = 0;
282 	return -1;
283     }
284 }
285 
286 /* Output a UTF-16 buffer in the specified encoding */
287 
ConvertUTF16(const char16 * buf,int count,FILE16 * file)288 static int ConvertUTF16(const char16 *buf, int count, FILE16 *file)
289 {
290     /* It *could* be that every character is linefeed... */
291     unsigned char outbuf[BufferSize*4];
292     char16 c;
293     int i, j, tablenum, max;
294     char8 *from_unicode;
295     char32 big;
296 
297     switch(file->enc)
298     {
299     case CE_ISO_646:		/* should really check for >127 */
300     case CE_ISO_8859_1:
301     case CE_unspecified_ascii_superset:
302 	for(i=j=0; i<count; i++)
303 	{
304 	    c = buf[i];
305 	    if(c == '\n' && (file->flags & FILE16_crlf))
306 		outbuf[j++] = '\r';
307 	    if(c < 256)
308 		outbuf[j++] = (unsigned char)c;
309 	    else
310 		outbuf[j++] = '?';
311 	}
312 	return Writeu(file, outbuf, j);
313 
314     case CE_ISO_8859_2:
315     case CE_ISO_8859_3:
316     case CE_ISO_8859_4:
317     case CE_ISO_8859_5:
318     case CE_ISO_8859_6:
319     case CE_ISO_8859_7:
320     case CE_ISO_8859_8:
321     case CE_ISO_8859_9:
322     case CE_ISO_8859_10:
323     case CE_ISO_8859_11:
324     case CE_ISO_8859_13:
325     case CE_ISO_8859_14:
326     case CE_ISO_8859_15:
327 	tablenum = (file->enc - CE_ISO_8859_2);
328 	max = iso_max_val[tablenum];
329 	from_unicode = unicode_to_iso[tablenum];
330 	for(i=j=0; i<count; i++)
331 	{
332 	    c = buf[i];
333 	    if(c == '\n' && (file->flags & FILE16_crlf))
334 		outbuf[j++] = '\r';
335 	    if(c <= max)
336 		outbuf[j++] = (unsigned char)from_unicode[c];
337 	    else
338 		outbuf[j++] = '?';
339 	}
340 	return Writeu(file, outbuf, j);
341 
342     case CE_UTF_8:
343 	for(i=j=0; i<count; i++)
344 	{
345 	    c = buf[i];
346 	    if(c == '\n' && (file->flags & FILE16_crlf))
347 		outbuf[j++] = '\r';
348 	    if(c < 0x80)
349 		outbuf[j++] = (unsigned char)c;
350 	    else if(c < 0x800)
351 	    {
352 		outbuf[j++] = 0xc0 + (c >> 6);
353 		outbuf[j++] = 0x80 + (c & 0x3f);
354 	    }
355 	    else if(c >= 0xd800 && c <= 0xdbff)
356 		file->save = c;
357 	    else if(c >= 0xdc00 && c <= 0xdfff)
358 	    {
359 		big = 0x10000 +
360 		    ((file->save - 0xd800) << 10) + (c - 0xdc00);
361 		outbuf[j++] = 0xf0 + (big >> 18);
362 		outbuf[j++] = 0x80 + ((big >> 12) & 0x3f);
363 		outbuf[j++] = 0x80 + ((big >> 6) & 0x3f);
364 		outbuf[j++] = 0x80 + (big & 0x3f);
365 	    }
366 	    else
367 	    {
368 		outbuf[j++] = 0xe0 + (c >> 12);
369 		outbuf[j++] = 0x80 + ((c >> 6) & 0x3f);
370 		outbuf[j++] = 0x80 + (c & 0x3f);
371 	    }
372 	}
373 	return Writeu(file, outbuf, j);
374 
375     case CE_UTF_16B:
376     case CE_ISO_10646_UCS_2B:
377  	for(i=j=0; i<count; i++)
378 	{
379 	    c = buf[i];
380 	    if(c == '\n' && (file->flags & FILE16_crlf))
381 	    {
382 		outbuf[j++] = 0;
383 		outbuf[j++] = '\r';
384 	    }
385 	    outbuf[j++] = (c >> 8);
386 	    outbuf[j++] = (c & 0xff);
387 
388 	}
389 	return Writeu(file, outbuf, j);
390 
391     case CE_UTF_16L:
392     case CE_ISO_10646_UCS_2L:
393  	for(i=j=0; i<count; i++)
394 	{
395 	    c = buf[i];
396 	    if(c == '\n' && (file->flags & FILE16_crlf))
397 	    {
398 		outbuf[j++] = '\r';
399 		outbuf[j++] = 0;
400 	    }
401 	    outbuf[j++] = (c & 0xff);
402 	    outbuf[j++] = (c >> 8);
403 
404 	}
405 	return Writeu(file, outbuf, j);
406 
407     default:
408       ERR2("Bad output character encoding %d (%s)\n",
409 		file->enc,
410 		file->enc < CE_enum_count ? CharacterEncodingName[file->enc] :
411 		                            "unknown");
412 	errno = 0;
413 	return -1;
414     }
415 }
416 
417 #if 0
418 int Readu(FILE16 *file, unsigned char *buf, int max_count)
419 {
420     return file->read(file, buf, max_count);
421 }
422 #endif
423 
Writeu(FILE16 * file,const unsigned char * buf,int count)424 int Writeu(FILE16 *file, const unsigned char *buf, int count)
425 {
426     int ret;
427 
428     ret = file->write(file, buf, count);
429     if(ret < 0)
430 	file->flags |= FILE16_error;
431 
432     return ret;
433 }
434 
Fclose(FILE16 * file)435 int Fclose(FILE16 *file)
436 {
437     int ret = 0;
438 
439     ret = file->close(file);
440     Free(file);
441 
442     if(file == Stdin)
443 	Stdin_open = 0;
444     else if(file == Stdout)
445 	Stdout_open = 0;
446     else if(file == Stderr)
447 	Stderr_open = 0;
448 
449     return ret;
450 }
451 
Fseek(FILE16 * file,long offset,int ptrname)452 int Fseek(FILE16 *file, long offset, int ptrname)
453 {
454     file->incount = file->inoffset = 0;
455     file->flags &= ~(FILE16_error | FILE16_eof);
456     return file->seek(file, offset, ptrname);
457 }
458 
Fflush(FILE16 * file)459 int Fflush(FILE16 *file)
460 {
461     return file->flush(file);
462 }
463 
Ferror(FILE16 * file)464 int Ferror(FILE16 *file)
465 {
466     return file->flags & FILE16_error;
467 }
468 
Feof(FILE16 * file)469 int Feof(FILE16 *file)
470 {
471     return file->flags & FILE16_eof;
472 }
473 
Getu(FILE16 * file)474 int Getu(FILE16 *file)
475 {
476     filbuf(file);
477     if(file->flags & (FILE16_error | FILE16_eof))
478 	return EOF;
479     file->incount--;
480     return file->inbuf[file->inoffset++];
481 }
482 
Readu(FILE16 * file,unsigned char * buf,int max_count)483 int Readu(FILE16 *file, unsigned char *buf, int max_count)
484 {
485     int count=0, lump;
486 
487     while(count < max_count)
488     {
489 	filbuf(file);
490 	if(file->flags & FILE16_error)
491 	    return 0;
492 	if(file->flags & FILE16_eof)
493 	    break;
494 	lump = max_count - count;
495 	if(lump > file->incount)
496 	    lump = file->incount;
497 	memcpy(buf+count, file->inbuf+file->inoffset, lump);
498 	count += lump;
499 	file->inoffset += lump;
500 	file->incount -= lump;
501     }
502 
503     return count;
504 }
505 
506 /* After calling filbuf, either file->incount > 0 or file->flags has
507    FILE16_eof or FILE16_error set. */
508 
filbuf(FILE16 * file)509 static void filbuf(FILE16 *file)
510 {
511     int ret;
512 
513     if(file->incount > 0)
514 	return;
515 
516     ret = file->read(file, file->inbuf, sizeof(file->inbuf));
517     if(ret < 0)
518 	file->flags |= FILE16_error;
519     else if(ret == 0)
520 	file->flags |= FILE16_eof;
521     else
522     {
523 	file->inoffset = 0;
524 	file->incount = ret;
525     }
526 }
527 
GetFILE(FILE16 * file)528 FILE *GetFILE(FILE16 *file)
529 {
530     if(file->read == FileRead)
531 	return (FILE *)file->handle;
532     else
533 	return 0;
534 }
535 
SetCloseUnderlying(FILE16 * file,int cu)536 void SetCloseUnderlying(FILE16 *file, int cu)
537 {
538     if(cu)
539 	file->flags |= FILE16_close_underlying;
540     else
541 	file->flags &= ~FILE16_close_underlying;
542 }
543 
SetFileEncoding(FILE16 * file,CharacterEncoding encoding)544 void SetFileEncoding(FILE16 *file, CharacterEncoding encoding)
545 {
546     file->enc = encoding;
547 }
548 
GetFileEncoding(FILE16 * file)549 CharacterEncoding GetFileEncoding(FILE16 *file)
550 {
551     return file->enc;
552 }
553 
554 #if defined(WIN32)
SetNormalizeLineEnd(FILE16 * file,int nle)555 void SetNormalizeLineEnd(FILE16 *file, int nle)
556 {
557     if(nle)
558 	file->flags |= FILE16_crlf;
559     else
560 	file->flags &= ~FILE16_crlf;
561 }
562 #endif
563 
Fprintf(FILE16 * file,const char * format,...)564 int Fprintf(FILE16 *file, const char *format, ...)
565 {
566     va_list args;
567     va_start(args, format);
568     return Vfprintf(file, format, args);
569 }
570 
Printf(const char * format,...)571 int Printf(const char *format, ...)
572 {
573     va_list args;
574     va_start(args, format);
575     return Vfprintf(Stdout, format, args);
576 }
577 
Sprintf(void * buf,CharacterEncoding enc,const char * format,...)578 int Sprintf(void *buf, CharacterEncoding enc, const char *format, ...)
579 {
580     va_list args;
581     va_start(args, format);
582     return Vsprintf(buf, enc, format, args);
583 }
584 
Snprintf(void * buf,size_t size,CharacterEncoding enc,const char * format,...)585 int Snprintf(void *buf, size_t size, CharacterEncoding enc,
586 	     const char *format, ...)
587 {
588     va_list args;
589     va_start(args, format);
590     return Vsnprintf(buf, size, enc, format, args);
591 }
592 
Vprintf(const char * format,va_list args)593 int Vprintf(const char *format, va_list args)
594 {
595     return Vfprintf(Stdout, format, args);
596 }
597 
Vsprintf(void * buf,CharacterEncoding enc,const char * format,va_list args)598 int Vsprintf(void *buf, CharacterEncoding enc, const char *format,
599 	     va_list args)
600 {
601     int nchars;
602     FILE16 file = {.handle3 = -1, .write = StringWrite, .flush = StringFlush,
603         .close = StringClose, .flags = FILE16_write};
604 
605     file.handle = buf;
606     file.enc = enc;
607 
608     nchars = Vfprintf(&file, format, args);
609     file.close(&file);		/* Fclose would try to free file */
610 
611     return nchars;
612 }
613 
Vsnprintf(void * buf,size_t size,CharacterEncoding enc,const char * format,va_list args)614 int Vsnprintf(void *buf, size_t size, CharacterEncoding enc,
615 	      const char *format, va_list args)
616 {
617     int nchars;
618     FILE16 file = {.handle3 = -1, .write = StringWriteTrunc,
619         .flush = StringFlush, .close = StringClose, .flags = FILE16_write};
620 
621     file.handle = buf;
622     file.enc = enc;
623     file.handle3 = size - NullSize(enc); /* make sure we can null-terminate */
624 
625     nchars = Vfprintf(&file, format, args);
626     file.handle3 = size;	/* ready for null-termination */
627     file.close(&file);		/* Fclose would try to free file */
628 
629     return nchars;
630 }
631 
632 #define put(x) {nchars++; if(count == sizeof(buf)) {if(ConvertASCII(buf, count, file) == -1) return -1; count = 0;} buf[count++] = x;}
633 
Vfprintf(FILE16 * file,const char * format,va_list args)634 int Vfprintf(FILE16 *file, const char *format, va_list args)
635 {
636     char8 buf[BufferSize];
637     int count = 0;
638     int c, i, n, width, prec;
639     char fmt[200];
640     char8 val[200];
641     const char8 *start;
642     const char8 *p;
643     const char16 *q;
644 #if CHAR_SIZE == 16
645     char16 cbuf[1];
646 #endif
647     int mflag;
648     int l, h, L, ll;
649     int nchars = 0;
650 
651     while((c = *format++))
652     {
653 	if(c != '%')
654 	{
655 	    put(c);
656 	    continue;
657 	}
658 
659 	start = format-1;
660 	width = 0;
661 	prec = -1;
662 	mflag=0;
663 	l=0, h=0, L=0, ll=0;
664 
665 	while(1)
666 	{
667 	    switch(c = *format++)
668 	    {
669 	    case '-':
670 		mflag = 1;
671 		break;
672 	    default:
673 		goto flags_done;
674 	    }
675 	}
676     flags_done:
677 
678 	if(c == '*')
679 	{
680 	    width = va_arg(args, int);
681 	    c = *format++;
682 	}
683 	else if(c >= '0' && c <= '9')
684 	{
685 	    width = c - '0';
686 	    while((c = *format++) >= '0' && c <= '9')
687 		width = width * 10 + c - '0';
688 	}
689 
690 	if(c == '.')
691 	{
692 	    c = *format++;
693 	    if(c == '*')
694 	    {
695 		prec = va_arg(args, int);
696 		c = *format++;
697 	    }
698 	    else if(c >= '0' && c <= '9')
699 	    {
700 		prec = c - '0';
701 		while((c = *format++) >= '0' && c <= '9')
702 		    prec = prec * 10 + c - '0';
703 	    }
704 	    else
705 		prec = 0;
706 	}
707 
708 	switch(c)
709 	{
710 	case 'l':
711 	    l = 1;
712 	    c = *format++;
713 #ifdef HAVE_LONG_LONG
714 	    if(c == 'l')
715 	    {
716 		l = 0;
717 		ll = 1;
718 		c = *format++;
719 	    }
720 #endif
721 	    break;
722 	case 'h':
723 	    h = 1;
724 	    c = *format++;
725 	    break;
726 #ifdef HAVE_LONG_DOUBLE
727 	case 'L':
728 	    L = 1;
729 	    c = *format++;
730 	    break;
731 #endif
732 	}
733 
734 	if (format - start + 1 > (int)sizeof(fmt))
735 	{
736 	  ERR("Printf: format specifier too long");
737 	    errno = 0;
738 	    return -1;
739 	}
740 
741 	strncpy(fmt, start, format - start);
742 	fmt[format - start] = '\0';
743 
744 	/* XXX should check it fits in val */
745 
746 	switch(c)
747 	{
748 	case 'n':
749 	    *va_arg(args, int *) = nchars;
750 	    break;
751 	case 'd':
752 	case 'i':
753 	case 'o':
754 	case 'u':
755 	case 'x':
756 	case 'X':
757 	    if(h)
758 		sprintf(val, fmt, va_arg(args, int)); /* promoted to int */
759 	    else if(l)
760 		sprintf(val, fmt, va_arg(args, long));
761 #ifdef HAVE_LONG_LONG
762 	    else if(ll)
763 		sprintf(val, fmt, va_arg(args, long long));
764 #endif
765 	    else
766 		sprintf(val, fmt, va_arg(args, int));
767 	    for(p=val; *p; p++)
768 		put(*p);
769 	    break;
770 	case 'f':
771 	case 'e':
772 	case 'E':
773 	case 'g':
774 	case 'G':
775 #ifdef HAVE_LONG_DOUBLE
776 	    if(L)
777 		sprintf(val, fmt, va_arg(args, long double));
778 	    else
779 #endif
780 		sprintf(val, fmt, va_arg(args, double));
781 	    for(p=val; *p; p++)
782 		put(*p);
783 	    break;
784 	case 'c':
785 #if CHAR_SIZE == 16
786 	    if(ConvertASCII(buf, count, file) == -1)
787 		return -1;
788 	    count = 0;
789 	    cbuf[0] = va_arg(args, int);
790 	    if(ConvertUTF16(cbuf, 1, file) == -1)
791 		return -1;
792 #else
793             put(va_arg(args, int));
794 #endif
795 	    break;
796 	case 'p':
797 	    sprintf(val, fmt, va_arg(args, void *));
798 	    for(p=val; *p; p++)
799 		put(*p);
800 	    break;
801 	case '%':
802 	    put('%');
803 	    break;
804 	case 's':
805 	    if(l)
806 	    {
807 		static char16 sNULL[] = {'(','N','U','L','L',')',0};
808 #if CHAR_SIZE == 16
809 	    string:
810 #endif
811 		q = va_arg(args, char16 *);
812 		if(!q)
813 		    q = sNULL;
814 		for(n=0; n != prec && q[n]; n++)
815 		    ;
816 		if(n < width && !mflag)
817 		    for(i=width-n; i>0; i--)
818 			put(' ');
819 		if(ConvertASCII(buf, count, file) == -1)
820 		    return -1;
821 		count = 0;
822 		nchars += n;
823 		for(i = n; i > 0; i -= BufferSize)
824 		{
825 		    /* ConvertUTF16 can only handle <= BufferSize chars */
826 		    if(ConvertUTF16(q, i > BufferSize ? BufferSize : i, file) == -1)
827 			return -1;
828 		    q += BufferSize;
829 		}
830 	    }
831 	    else
832 	    {
833 #if CHAR_SIZE == 8
834 	    string:
835 #endif
836 		p = va_arg(args, char8 *);
837 		if(!p)
838 		    p = "(null)";
839 		for(n=0; n != prec && p[n]; n++)
840 		    ;
841 		if(n < width && !mflag)
842 		    for(i=width-n; i>0; i--)
843 			put(' ');
844 		for(i=0; i<n; i++)
845 		    put(p[i]);
846 	    }
847 	    if(n < width && mflag)
848 		for(i=width-n; i>0; i--)
849 		    put(' ');
850 	    break;
851 	case 'S':
852 	    goto string;
853 	default:
854 	  ERR1("unknown format character %c\n", c);
855 	    errno = 0;
856 	    return -1;
857 	}
858     }
859 
860     if(count > 0)
861 	if(ConvertASCII(buf, count, file) == -1)
862 	    return -1;
863 
864     return nchars;
865 }
866 
MakeFILE16(const char * type)867 static FILE16 *MakeFILE16(const char *type)
868 {
869     FILE16 *file;
870 
871     if(!(file = Malloc(sizeof(*file))))
872 	return 0;
873 
874     file->flags = 0;
875     if(*type == 'r')
876     {
877 	file->flags |= FILE16_read;
878 	type++;
879     }
880     if(*type == 'w')
881 	file->flags |= FILE16_write;
882 
883     file->enc = InternalCharacterEncoding;
884 
885     file->incount = file->inoffset = 0;
886 
887     return file;
888 }
889 
MakeFILE16FromFD(int fd,const char * type)890 FILE16 *MakeFILE16FromFD(int fd, const char *type)
891 {
892     FILE16 *file;
893 
894     if(!(file = MakeFILE16(type)))
895 	return 0;
896 
897 #if defined(WIN32)
898     file->flags |= FILE16_crlf;
899 #endif
900 
901     file->read = FDRead;
902     file->write = FDWrite;
903     file->seek = FDSeek;
904     file->close = FDClose;
905     file->flush = FDFlush;
906     file->handle2 = fd;
907 
908     return file;
909 }
910 
FDRead(FILE16 * file,unsigned char * buf,int max_count)911 static int FDRead(FILE16 *file, unsigned char *buf, int max_count)
912 {
913     int fd = file->handle2;
914     int count = 0;
915 
916     count = read(fd, buf, max_count);
917 
918     return count;
919 }
920 
FDWrite(FILE16 * file,const unsigned char * buf,int count)921 static int FDWrite(FILE16 *file, const unsigned char *buf, int count)
922 {
923     int fd = file->handle2;
924     int ret;
925 
926     while(count > 0)
927     {
928 	ret = write(fd, buf, count);
929 	if(ret < 0)
930 	    return ret;
931 	count -= ret;
932 	buf += ret;
933     }
934 
935     return 0;
936 }
937 
FDSeek(FILE16 * file,long offset,int ptrname)938 static int FDSeek(FILE16 *file, long offset, int ptrname)
939 {
940     int fd = file->handle2;
941 
942     return lseek(fd, offset, ptrname);
943 }
944 
FDClose(FILE16 * file)945 static int FDClose(FILE16 *file)
946 {
947     int fd = file->handle2;
948 
949     return (file->flags & FILE16_close_underlying) ? close(fd) : 0;
950 }
951 
FDFlush(FILE16 * file __unused)952 static int FDFlush(FILE16 *file __unused)
953 {
954     return 0;
955 }
956 
MakeFILE16FromFILE(FILE * f,const char * type)957 FILE16 *MakeFILE16FromFILE(FILE *f, const char *type)
958 {
959     FILE16 *file;
960 
961     if(!(file = MakeFILE16(type)))
962 	return 0;
963 
964 #if defined(WIN32)
965     /* We need to do Windows-style lf <-> crlf conversion ourselves,
966        because the standard i/o library doesn't know about 16-bit
967        characters. We don't want it to insert cr before output bytes
968        that just happen to look like linefeed, or change input byte
969        sequences that happen to look like crlf. */
970 
971     file->flags |= FILE16_crlf;
972 
973     /* So we have to switch the underlying file into binary mode, but
974        we probably don't want to automatically do that for Stdin/out/err
975        which are opened though the user may not want them (and may
976        want to keep stdin/out/err in text mode).  So we just flag
977        it as needing to be switched when the first i/o is done. */
978 
979     file->flags |= FILE16_needs_binary;
980 #endif
981 
982     file->read = FileRead;
983     file->write = FileWrite;
984     file->seek = FileSeek;
985     file->close = FileClose;
986     file->flush = FileFlush;
987     file->handle = f;
988 
989     return file;
990 }
991 
FileRead(FILE16 * file,unsigned char * buf,int max_count)992 static int FileRead(FILE16 *file, unsigned char *buf, int max_count)
993 {
994     FILE *f = file->handle;
995     int count = 0;
996 
997 #if defined(WIN32)
998     if(file->flags & FILE16_needs_binary)
999     {
1000 #ifdef __CYGWIN__
1001 	setmode(fileno(f), _O_BINARY);
1002 #else
1003 	_setmode(fileno(f), _O_BINARY);
1004 #endif
1005 	file->flags &= ~FILE16_needs_binary;
1006     }
1007 #endif
1008 
1009     /* Terminal EOF is sticky for fread() on Linux, even though feof() is true.
1010        How can they have got this wrong and never noticed it?
1011        So check feof() here.
1012     */
1013     if(feof(f))
1014 	return 0;
1015 
1016     count = fread(buf, 1, max_count, f);
1017 
1018     return ferror(f) ? -1 : count;
1019 }
1020 
FileWrite(FILE16 * file,const unsigned char * buf,int count)1021 static int FileWrite(FILE16 *file, const unsigned char *buf, int count)
1022 {
1023     FILE *f = file->handle;
1024 
1025 #if defined(WIN32)
1026     if(file->flags & FILE16_needs_binary)
1027     {
1028 #ifdef __CYGWIN__
1029 	setmode(fileno(f), _O_BINARY);
1030 #else
1031 	_setmode(fileno(f), _O_BINARY);
1032 #endif
1033 	file->flags &= ~FILE16_needs_binary;
1034     }
1035 #endif
1036 
1037     if(count == 0)
1038 	return 0;
1039     return fwrite(buf, 1, count, f) == 0 ? -1 : 0;
1040 }
1041 
FileSeek(FILE16 * file,long offset,int ptrname)1042 static int FileSeek(FILE16 *file, long offset, int ptrname)
1043 {
1044     FILE *f = file->handle;
1045 
1046     return fseek(f, offset, ptrname);
1047 }
1048 
FileClose(FILE16 * file)1049 static int FileClose(FILE16 *file)
1050 {
1051     FILE *f = file->handle;
1052 
1053     return (file->flags & FILE16_close_underlying) ? fclose(f) : 0;
1054 }
1055 
FileFlush(FILE16 * file)1056 static int FileFlush(FILE16 *file)
1057 {
1058     FILE *f = file->handle;
1059 
1060     return fflush(f);
1061 }
1062 
MakeFILE16FromString(const void * buf,long size,const char * type)1063 FILE16 *MakeFILE16FromString(const void *buf, long size, const char *type)
1064 {
1065     FILE16 *file;
1066 
1067     if(!(file = MakeFILE16(type)))
1068 	return 0;
1069 
1070     file->read = StringRead;
1071     file->write = StringWrite;
1072     file->seek = StringSeek;
1073     file->close = StringClose;
1074     file->flush = StringFlush;
1075 
1076     file->chandle = buf;
1077     file->handle2 = 0;
1078     file->handle3 = size;
1079 
1080     return file;
1081 }
1082 
StringRead(FILE16 * file,unsigned char * buf,int max_count)1083 static int StringRead(FILE16 *file, unsigned char *buf, int max_count)
1084 {
1085     char *p = (char *)file->handle + file->handle2;
1086 
1087     if(file->handle3 >= 0 && file->handle2 + max_count > file->handle3)
1088 	max_count = file->handle3 - file->handle2;
1089 
1090     if(max_count <= 0)
1091 	return 0;
1092 
1093     memcpy(buf, p, max_count);
1094     file->handle2 += max_count;
1095 
1096     return max_count;
1097 }
1098 
StringWrite(FILE16 * file,const unsigned char * buf,int count)1099 static int StringWrite(FILE16 *file, const unsigned char *buf, int count)
1100 {
1101     char *p = (char *)file->handle + file->handle2;
1102 
1103     if(file->handle3 >= 0 && file->handle2 + count > file->handle3)
1104 	return -1;
1105 
1106     memcpy(p, buf, count);
1107     file->handle2 += count;
1108 
1109     return 0;
1110 }
1111 
1112 /* Like StringWrite, but ignores overflow rather than returning an error.
1113    Used for Vsnprintf. */
1114 
StringWriteTrunc(FILE16 * file,const unsigned char * buf,int count)1115 static int StringWriteTrunc(FILE16 *file, const unsigned char *buf, int count)
1116 {
1117     char *p = (char *)file->handle + file->handle2;
1118 
1119     if(file->handle3 >= 0 && file->handle2 + count > file->handle3)
1120 	/* XXX This doesn't really work; a character might be truncated.
1121 	   Safe for fixed-size encodings if buffer size is a multiple
1122 	   of the character size. */
1123 	count = file->handle3 - file->handle2;
1124 
1125     memcpy(p, buf, count);
1126     file->handle2 += count;
1127 
1128     return 0;
1129 }
1130 
StringSeek(FILE16 * file,long offset,int ptrname)1131 static int StringSeek(FILE16 *file, long offset, int ptrname)
1132 {
1133     switch(ptrname)
1134     {
1135     case SEEK_CUR:
1136 	offset = file->handle2 + offset;
1137 	break;
1138     case SEEK_END:
1139 	if(file->handle3 < 0)
1140 	    return -1;
1141 	offset = file->handle3 + offset;
1142 	break;
1143     }
1144 
1145     if(file->handle3 >= 0 && offset > file->handle3)
1146 	return -1;
1147 
1148     file->handle2 = offset;
1149 
1150     return 0;
1151 }
1152 
StringClose(FILE16 * file)1153 static int StringClose(FILE16 *file)
1154 {
1155     static char8 null = 0;
1156 
1157     if(file->flags & FILE16_write)
1158 	ConvertASCII(&null, 1, file); /* null terminate */
1159 
1160     if(file->flags & FILE16_close_underlying)
1161 	Free((char *)file->handle);
1162 
1163     return 0;
1164 }
1165 
StringFlush(FILE16 * file __unused)1166 static int StringFlush(FILE16 *file __unused)
1167 {
1168     return 0;
1169 }
1170 
1171 
1172 #if defined(WIN32) && ! defined(__CYGWIN__)
1173 #ifdef SOCKETS_IMPLEMENTED
1174 
MakeFILE16FromWinsock(int sock,const char * type)1175 FILE16 *MakeFILE16FromWinsock(int sock, const char *type)
1176 {
1177     FILE16 *file;
1178 
1179     if(!(file = MakeFILE16(type)))
1180 	return 0;
1181 
1182     file->flags |= FILE16_crlf;
1183 
1184     file->read = WinsockRead;
1185     file->write = WinsockWrite;
1186     file->seek = WinsockSeek;
1187     file->close = WinsockClose;
1188     file->flush = WinsockFlush;
1189     file->handle2 = sock;
1190 
1191     return file;
1192 }
1193 
WinsockRead(FILE16 * file,unsigned char * buf,int max_count)1194 static int WinsockRead(FILE16 *file, unsigned char *buf, int max_count)
1195 {
1196     int f = file->handle2;
1197     int count;
1198 
1199     /* "Relax" said the nightman, we are programmed to recv() */
1200     count = recv(f, buf, max_count, 0);
1201 
1202     return count;
1203 }
1204 
WinsockWrite(FILE16 * file,const unsigned char * buf,int count)1205 static int WinsockWrite(FILE16 *file, const unsigned char *buf, int count)
1206 {
1207     int f = file->handle2;
1208 
1209     return send(f, buf, count ,0);
1210 }
1211 
WinsockSeek(FILE16 * file,long offset,int ptrname)1212 static int WinsockSeek(FILE16 *file, long offset, int ptrname)
1213 {
1214     return -1;
1215 }
1216 
WinsockClose(FILE16 * file)1217 static int WinsockClose(FILE16 *file)
1218 {
1219     int f = file->handle2;
1220 
1221     if(file->flags & FILE16_close_underlying)
1222 	closesocket(f);
1223 
1224     return 0;
1225 }
1226 
WinsockFlush(FILE16 * file)1227 static int WinsockFlush(FILE16 *file)
1228 {
1229     return 0;
1230 }
1231 
1232 #endif
1233 #endif
1234 
1235 #ifdef HAVE_LIBZ
1236 
MakeFILE16FromGzip(gzFile f,const char * type)1237 FILE16 *MakeFILE16FromGzip(gzFile f, const char *type)
1238 {
1239     FILE16 *file;
1240 
1241     if(!(file = MakeFILE16(type)))
1242 	return 0;
1243 
1244     file->read = GzipRead;
1245     file->write = GzipWrite;
1246     file->seek = GzipSeek;
1247     file->close = GzipClose;
1248     file->flush = GzipFlush;
1249     file->handle = (void *)f;
1250 
1251     return file;
1252 }
1253 
GzipRead(FILE16 * file,unsigned char * buf,int max_count)1254 static int GzipRead(FILE16 *file, unsigned char *buf, int max_count)
1255 {
1256     gzFile f = (gzFile)file->handle;
1257     int count = 0;
1258     int gzerr;
1259     const char *errorString;
1260 
1261     count = gzread(f, buf, max_count);
1262 
1263     errorString = gzerror(f, &gzerr);
1264     if (gzerr != 0 && gzerr != Z_STREAM_END) {
1265 	warnx("%s: %s", __func__, errorString);
1266 	return -1;
1267     }
1268 
1269     return count;
1270 }
1271 
GzipWrite(FILE16 * file,const unsigned char * buf,int count)1272 static int GzipWrite(FILE16 *file, const unsigned char *buf, int count)
1273 {
1274     gzFile f = (gzFile)file->handle;
1275     int gzerr;
1276     const char *errorString;
1277 
1278     count = gzwrite(f, buf, count);
1279 
1280     errorString = gzerror(f, &gzerr);
1281     if (gzerr != 0 && gzerr != Z_STREAM_END) {
1282 	warnx("%s: %s", __func__, errorString);
1283 	return -1;
1284     }
1285 
1286     return count;
1287 }
1288 
GzipSeek(FILE16 * file __unused,long offset __unused,int ptrname __unused)1289 static int GzipSeek(FILE16 *file __unused, long offset __unused, int ptrname __unused)
1290 {
1291     return -1;
1292 }
1293 
GzipClose(FILE16 * file)1294 static int GzipClose(FILE16 *file)
1295 {
1296     gzFile f = (gzFile)file->handle;
1297 
1298     return (file->flags & FILE16_close_underlying) ? gzclose(f) : 0;
1299 }
1300 
GzipFlush(FILE16 * file __unused)1301 static int GzipFlush(FILE16 *file __unused)
1302 {
1303     return 0;
1304 }
1305 
1306 #endif
1307 
1308 #ifdef test
1309 
main(int argc,char ** argv)1310 int main(int argc, char **argv)
1311 {
1312     short s=3;
1313     int n, c;
1314     char16 S[] = {'w', 'o', 'r', 'l', 'd', ' ', '�' & 0xff, 0xd841, 0xdc42, 0};
1315 
1316     n=Printf(argv[1], s, 98765432, &c, 5.3, 3.2L, "�hello", S);
1317     printf("\nreturned %d, c=%d\n", n, c);
1318     n=Printf(argv[1], s, 98765432, &c, 5.3, 3.2L, "�hello", S);
1319     printf("\nreturned %d, c=%d\n", n, c);
1320     n=Printf(argv[1], s, 98765432, &c, 5.3, 3.2L, "�hello", S);
1321     printf("\nreturned %d, c=%d\n", n, c);
1322     n=Printf(argv[1], s, 98765432, &c, 5.3, 3.2L, "�hello", S);
1323     printf("\nreturned %d, c=%d\n", n, c);
1324 
1325     return 0;
1326 }
1327 
1328 #endif
1329 
1330