1 #include "ptoc.h"
2 #include <errno.h>
3 #include <ctype.h>
4 
5 #define IO_OK           0
6 #define IO_ERROR        errno
7 #define IO_EOF          -1
8 #define IO_FORMAT_ERROR -2
9 
10 #define input_error(f) \
11      (ferror(f) ? IO_ERROR : feof(f) ? IO_EOF : IO_FORMAT_ERROR)
12 
13 #define Ok             0
14 
15 #define TRUNCATE_FILE_ON_REWRITE 1
16 
17 integer ioresult; /* Turbo Pascal IO operation completion status */
18 
19 enum file_open_mode {
20     seek_mode   = 0x001,
21     append_mode = 0x002,
22     rd_mode     = 0x004,
23     wr_mode     = 0x008,
24     rdwr_mode   = 0x00C,
25     trunc_mode  = 0x010,
26     nocr_mode   = 0x020,
27     bin_mode    = 0x040,
28     shr_mode    = 0x080,
29     wal_mode    = 0x100,
30     dlk_mode    = 0x200,
31     tmp_mode    = 0x400,
32     buf_mode    = 0x800
33 };
34 
35 struct {
36     char* option;
37     int   flag;
38 } open_mode_decode[] = {
39     { "seek", seek_mode },
40     { "apd",  append_mode },
41     { "ro",   rd_mode },
42     { "rw",   rdwr_mode },
43     { "nocr", nocr_mode },
44     { "shr",  shr_mode },
45     { "bin",  bin_mode },
46     { "dlk",  dlk_mode },
47     { "wal",  wal_mode },
48     { "temp", tmp_mode },
49     { "buff", buf_mode },
50     { NULL,   0}
51 };
52 
53 enum file_state {fs_record_defined = 1, fs_next_pos = 2, fs_ignore_error = 4};
54 
55 
56 text_descriptor input;
57 text_descriptor output;
58 
59 int paramcount;
60 char const* const* param_array;
61 boolean directvideo;
62 void (*exitproc)(void);
63 
pio_exit_handler(void)64 static void pio_exit_handler(void)
65 {
66     if (exitproc) {
67 	(*exitproc)();
68     }
69 }
70 
pio_initialize(int argc,char const * argv[])71 void pio_initialize(int argc, char const* argv[])
72 {
73     input.desc.f = stdin;
74     output.desc.f = stdout;
75     paramcount = argc-1;
76     param_array = argv;
77     atexit(pio_exit_handler);
78 }
79 
80 new_line_marker NL;
81 
82 boolean pio_ignore_error;
83 
84 
handle_error(file_descriptor * fd)85 static void handle_error(file_descriptor* fd)
86 {
87     if (fd->error != IO_OK
88 	&& !(fd->state & fs_ignore_error)
89 	&& !pio_ignore_error)
90     {
91         if (fd->error == IO_FORMAT_ERROR) {
92 	    fprintf(stderr, "IO: input format error in file '%s'\n",
93 		    fd->name ? fd->name : "input");
94 	} else if (fd->error == IO_EOF) {
95 	    fprintf(stderr, "IO: access beyond end of file '%s'\n",
96 		    fd->name ? fd->name : "input");
97 	} else {
98 	    if (fd->name) {
99 		fprintf(stderr, "IO: file '%s' ", fd->name);
100 		perror("");
101 	    } else {
102 		perror("IO");
103 	    }
104 	}
105 	exit(2);
106     }
107     if (fd->error != IO_OK && fd->error != IO_FORMAT_ERROR
108 	&& fd->error != IO_EOF && fd->f != NULL)
109     {
110 	clearerr(fd->f);
111     }
112 }
113 
114 
parse_options(char const * options,char * new_file_name,char * extension)115 static int parse_options(char const* options,
116 			 char* new_file_name,
117 			 char* extension)
118 {
119     int i;
120     int mode = 0;
121     enum { st_start, st_file_name, st_extension } state = st_start;
122 
123     char* np; /* pointer to the end of file name */
124     char* ep; /* pointer to the end of file extension */
125 
126     if (options == NULL) return 0;
127 
128     np = new_file_name + strlen(new_file_name);
129     ep = extension + strlen(extension);
130 
131   parse_options:
132     while (*options != '\0') {
133 	switch(*options) {
134 	  case '/':
135 	    state = st_start;
136 	    options += 1;
137 	    for (i = 0; open_mode_decode[i].option != NULL; i++) {
138 		char *s = open_mode_decode[i].option;
139 		const char* p = options;
140 		while( *s != 0 && *s == tolower(*(unsigned char*)p)) {
141 		    s += 1, p += 1;
142 		}
143 		if (*s == 0) {
144 		    int flag = open_mode_decode[i].flag;
145 		    if (flag == tmp_mode) {
146 			mode |= flag;
147 			tmpnam(new_file_name);
148 			np += strlen (new_file_name);
149 		    } else if(flag == buf_mode) {
150 			if (*p == ':') {
151 			    while(isdigit(*(unsigned char*)++p));
152 			}
153 		    } else {
154 			mode |= flag;
155 		    }
156 		    options = p;
157 		    goto parse_options;
158 		}
159 	    }
160 	    continue;
161 	  case ' ':
162 	    options += 1;
163 	    continue;
164 	  case '.':
165 	    if (state == st_start) {
166 		state = st_extension;
167 	    }
168 	  default:
169 	    if (state == st_extension) {
170 		*ep++ = *options++;
171 	    } else {
172 		state = st_file_name;
173 		*np++ = *options++;
174 	    }
175 	}
176     }
177     *ep = '\0';
178     *np = '\0';
179     return mode;
180 }
181 
182 #define MAX_OPTIONS   1024
183 #define MAX_FILE_NAME 1024
184 #define MAX_EXTENSION 8
185 
186 
187 /*  Ouput:
188  *    -1    if error (file not opened)
189  *    size of file (in 512 byte blocks), else
190  */
pio_open_status(file_descriptor * fd)191 static integer pio_open_status(file_descriptor* fd)
192 {
193     if (fd->f != NULL) {
194 	fpos_t pos;
195 	long   length;
196 	fgetpos(fd->f, &pos);
197 	fseek(fd->f, 0, SEEK_END);
198 	length = ftell(fd->f);
199 	fsetpos(fd->f, &pos);
200 	return (length + 511) / 512;
201     }
202     return -1;
203 }
204 
205 
open_file(file_descriptor * fd,const char * file_name,const char * options,integer * error_code,int mode,size_t record_size)206 static void open_file(file_descriptor* fd, const char* file_name,
207 		      const char* options, integer* error_code,
208 		      int mode, size_t record_size)
209 
210 {
211     char  new_file_name[MAX_FILE_NAME];
212     char  suffix[MAX_EXTENSION];
213     char* mode_str;
214 
215     pio_close_file(fd);
216 
217     new_file_name[0] = '\0';
218     suffix[0] = '\0';
219 
220     mode |= parse_options(options, new_file_name, suffix)
221 	  | parse_options(file_name, new_file_name, suffix);
222 
223     strcat(new_file_name, suffix);
224 
225     if (mode & seek_mode) mode |= rd_mode|wr_mode;
226 
227     if (record_size > 1) mode |= bin_mode;
228 
229     if (mode & bin_mode) {
230 	if ((mode & (append_mode|rd_mode)) == (append_mode|rd_mode))
231 	    mode_str = "ab+";
232 	else if (mode & append_mode) mode_str = "ab";
233 	else if ((mode & rdwr_mode) == rd_mode) mode_str = "rb";
234 	else if ((mode & rdwr_mode) == wr_mode) mode_str = "wb";
235 	else if (mode & trunc_mode) mode_str = "wb+";
236 	else mode_str = "rb+";
237     } else {
238 	if ((mode & (append_mode|rd_mode)) == (append_mode|rd_mode))
239 	    mode_str = "a+";
240 	else if (mode & append_mode) mode_str = "a";
241 	else if ((mode & rdwr_mode) == rd_mode) mode_str = "r";
242 	else if ((mode & rdwr_mode) == wr_mode) mode_str = "w";
243 	else if (mode & trunc_mode) mode_str = "w+";
244 	else mode_str = "r+";
245     }
246 
247     fd->mode = mode;
248     fd->state &= ~(fs_record_defined|fs_next_pos);
249     fd->name = new_file_name;
250 
251     fd->f = fopen(new_file_name, mode_str);
252     if (mode & tmp_mode) {
253        remove(new_file_name);
254     }
255 
256     if (fd->f == NULL) {
257 	ioresult = fd->error = IO_ERROR;
258 	if (error_code == NULL) {
259 	    handle_error(fd);
260 	}
261 	fd->name = NULL;
262     } else {
263 	ioresult = fd->error = IO_OK;
264 	fd->name = strdup(new_file_name);
265     }
266     if (error_code != NULL) {
267 	*error_code = pio_open_status(fd);
268     }
269 }
270 
pio_rewrite_file(file_descriptor * fd,size_t record_size,const char * file_name,const char * options,integer * error_code)271 void pio_rewrite_file(file_descriptor* fd,  size_t record_size,
272 		      const char* file_name, const char* options,
273 		      integer* error_code)
274 {
275     if (file_name == NULL) {
276 	if (fd == &output.desc) {
277 	    /* Oregon Pascal-2 extension */
278 	    if (error_code) {
279 		boolean ignore_error = pio_ignore_error;
280 		pio_ignore_error = true;
281 		cwrite("\n");
282 		pio_ignore_error = ignore_error;
283 		*error_code = pio_open_status(fd);
284 	    } else {
285 		cwrite("\n");
286 	    }
287 	} else {
288 	    if (fd->f == NULL) {
289 		char new_file_name[MAX_FILE_NAME];
290 		int mode = wr_mode|trunc_mode;
291 		if (fd->name != NULL) {
292 		    strcpy(new_file_name, fd->name);
293 		} else {
294 		    mode |= tmp_mode|rd_mode;
295 		    tmpnam(new_file_name);
296 		}
297 		open_file(fd, new_file_name, options, error_code, mode,
298 			  record_size);
299 	    } else {
300 #if TRUNCATE_FILE_ON_REWRITE
301 		if ((fd->f = freopen(fd->name, (fd->mode & bin_mode)
302 				     ? "wb+" : "w+", fd->f)) == NULL)
303 #else
304 		if (fseek(fd->f, 0, 0) != Ok)
305 #endif
306 		{
307 	            ioresult = fd->error = IO_ERROR;
308 		    if (error_code == NULL) {
309 			handle_error(fd);
310 		    } else {
311 			*error_code = IO_ERROR;
312 		    }
313 		} else {
314 		    ioresult = fd->error = IO_OK;
315 		}
316 	    }
317 	}
318     } else {
319 	open_file(fd, file_name, options, error_code, wr_mode|trunc_mode,
320 		  record_size);
321     }
322 }
323 
324 #ifndef _WIN32
stricmp(char const * a,char const * b)325 static int stricmp(char const* a, char const* b)
326 {
327     while (*b != 0) {
328 	int diff = tolower(*(unsigned char*)a) - tolower(*(unsigned char*)b);
329 	if (diff != 0) {
330 	    return diff;
331 	}
332 	a += 1;
333 	b += 1;
334     }
335     return *(unsigned char*)a;
336 }
337 #endif
338 
pio_open_file(file_descriptor * fd,size_t record_size,const char * file_name,const char * history,integer * error_code)339 void pio_open_file(file_descriptor* fd, size_t record_size,
340 		   const char* file_name, const char* history,
341 		   integer* error_code)
342 {
343     char const* name = file_name;
344     int   mode;
345     int   error;
346     char  buf[MAX_FILE_NAME];
347 
348     if (stricmp(name, "-stdin") == 0) {
349 	fd->f = stdin;
350 	name = "stdin";
351 	mode = rd_mode;
352 	goto complete_open;
353     }
354 
355     if (stricmp(name, "-stdout") == 0) {
356 	fd->f = stdout;
357 	name = "stdout";
358 	mode = wr_mode;
359 	goto complete_open;
360     }
361 
362     if (*name == '^') {
363 	int n;
364 	if (sscanf(name+1, "%d", &n) != 1 || n > paramcount) {
365 	    error = file_not_specified;
366 	    goto handle_error;
367 	}
368 	name = param_array[n];
369     } else if (*name == '*') {
370 	printf("%s", name+1);
371 	if (scanf("%s", buf) != 1) {
372 	    error = file_not_specified;
373 	    goto handle_error;
374 	}
375 	name = buf;
376     }
377 
378     if (stricmp(history, "new") == 0) {
379 	FILE* f = fopen(name, "r");
380 	if (f != NULL) {
381 	    fclose(f);
382 	    error = file_already_exists;
383 	    goto handle_error;
384 	}
385 	fd->f = fopen(name, "w+");
386 	mode = rdwr_mode|trunc_mode;
387     } else if (stricmp(history, "old") == 0) {
388 	fd->f = fopen(name, "r+");
389 	mode = rdwr_mode;
390 	if (fd->f == NULL) {
391 	    fd->f = fopen(name, "r");
392 	    mode = rd_mode;
393 	}
394     } else if (stricmp(history, "unknown") == 0) {
395 	fd->f = fopen(name, "r+");
396 	mode = rdwr_mode;
397 	if (fd->f == NULL) {
398 	    fd->f = fopen(name, "w+");
399 	}
400     } else {
401 	error = invalid_history;
402 	goto handle_error;
403     }
404     if (fd->f == NULL) {
405 	error = unable_to_open_file;
406 	goto handle_error;
407     }
408   complete_open:
409     fd->mode = mode;
410     fd->state &= ~(fs_record_defined|fs_next_pos);
411     fd->name = strdup(name);
412     if (error_code != NULL) {
413 	*error_code = file_successfully_opened;
414     }
415     return;
416 
417    handle_error:
418     if (error_code == NULL) {
419 	if (!(fd->state & fs_ignore_error)) {
420 	    if (error == file_already_exists) {
421 		fprintf(stderr, "open: 'new' history is specified and file "
422 			"'%s' already exists\n", name);
423 	    } else {
424 		perror("open");
425 	    }
426 	    exit(2);
427 	}
428     } else {
429 	*error_code = (error == file_already_exists)
430 	            ? unable_to_open_file : error;
431     }
432 }
433 
434 
435 
pio_reset_file(file_descriptor * fd,size_t record_size,const char * file_name,const char * options,integer * error_code)436 void pio_reset_file(file_descriptor* fd, size_t record_size,
437 		    const char* file_name, const char* options,
438 		    integer* error_code)
439 {
440     if (file_name == NULL) {
441 	ioresult = fd->error = IO_OK;
442 	if (fd == &input.desc) {
443 	    /* Oregon Pascal-2 extension */
444 	    if (error_code) {
445 		boolean ignore_error = pio_ignore_error;
446 		pio_ignore_error = true;
447 		cread("\n");
448 		pio_ignore_error = ignore_error;
449 		*error_code = pio_open_status(fd);
450 	    } else {
451 		cread("\n");
452 	    }
453 	} else {
454 	    char new_file_name[MAX_FILE_NAME];
455 	    if (fd->f == NULL) {
456 		assert(fd->name != NULL);
457 		strcpy(new_file_name, fd->name);
458 		open_file(fd, new_file_name, options, error_code, rd_mode,
459 			  record_size);
460 	    } else {
461 		if (!(fd->mode & rd_mode)) {
462 		    strcpy(new_file_name, fd->name);
463 		    open_file(fd, new_file_name, options, error_code, rd_mode,
464 			      record_size);
465 		} else {
466 		    if (fseek(fd->f, 0, SEEK_SET) == Ok) {
467 			fd->mode |= rd_mode;
468 			fd->state &= ~(fs_record_defined|fs_next_pos);
469 		    } else {
470 			ioresult = fd->error = IO_ERROR;
471 			if (error_code == NULL) {
472 			    handle_error(fd);
473 			}
474 		    }
475 		}
476 		if (error_code) {
477 		    *error_code = pio_open_status(fd);
478 		}
479 	    }
480 	}
481     } else {
482 	open_file(fd, file_name, options, error_code, rd_mode, record_size);
483     }
484 }
485 
486 
pio_get_record(file_descriptor * fd,void * record,size_t record_size)487 void pio_get_record(file_descriptor* fd, void* record, size_t record_size)
488 {
489     ioresult = fd->error = IO_OK;
490     if (!(fd->state & fs_next_pos)) {
491 	if (fread(record, record_size, 1, fd->f) != 1) {
492 	    ioresult = fd->error = input_error(fd->f);
493 	    handle_error(fd);
494 	}
495     }
496     fd->state &= ~(fs_record_defined|fs_next_pos);
497 }
498 
pio_put_record(file_descriptor * fd,void * record,size_t record_size)499 void pio_put_record(file_descriptor* fd, void* record, size_t record_size)
500 {
501     ioresult = fd->error = IO_OK;
502     if (fd->state & fs_record_defined) {
503 	if (fd->state & fs_next_pos) {
504 	    if (fseek(fd->f, -(long)record_size, SEEK_CUR) != Ok) {
505 		ioresult = fd->error = IO_ERROR;
506 		handle_error(fd);
507 		return;
508 	    }
509 	}
510 	if (fwrite(record, record_size, 1, fd->f) != 1) {
511 	    ioresult = fd->error = IO_ERROR;
512 	}
513     } else {
514 	if (fseek(fd->f, record_size, SEEK_CUR) != Ok) {
515 	    ioresult = fd->error = IO_ERROR;
516 	}
517     }
518     handle_error(fd);
519     fd->state &= ~(fs_record_defined|fs_next_pos);
520 }
521 
522 
pio_access_record(file_descriptor * fd,void * record,size_t record_size)523 void pio_access_record(file_descriptor* fd, void* record, size_t record_size)
524 {
525     ioresult = fd->error = IO_OK;
526     if (!(fd->state & fs_record_defined)) {
527 	if (fread(record, record_size, 1, fd->f) != 1) {
528 	    ioresult = fd->error = input_error(fd->f);
529 	    handle_error(fd);
530 	} else {
531 	    fd->state |= (fs_record_defined|fs_next_pos);
532 	}
533     }
534 }
535 
pio_store_record(file_descriptor * fd,void * record,size_t record_size,void * src)536 void pio_store_record(file_descriptor* fd, void* record, size_t record_size,
537 		  void* src)
538 {
539     memcpy(record, src, record_size);
540     fd->state |= fs_record_defined;
541 }
542 
pio_copy_record(file_descriptor * src_fd,void * src_record,file_descriptor * dst_fd,void * dst_record,size_t record_size)543 void pio_copy_record(file_descriptor* src_fd, void* src_record,
544 		     file_descriptor* dst_fd, void* dst_record,
545 		     size_t record_size)
546 {
547     pio_access_record(src_fd, src_record, record_size);
548     if (src_fd->error == IO_OK) {
549 	if (src_fd->error == IO_OK) {
550 	    pio_store_record(dst_fd, dst_record, record_size, src_record);
551 	    pio_get_record(src_fd, src_record, record_size);
552 	}
553     }
554 }
555 
pio_check_end_of_file(file_descriptor * fd)556 boolean pio_check_end_of_file(file_descriptor *fd)
557 {
558     if (!(fd->state & fs_next_pos)) {
559 	int ch = getc(fd->f);
560 	if (ch == EOF) return true;
561 	ungetc(ch, fd->f);
562     }
563     return false;
564 }
565 
pio_check_end_of_line(text_descriptor * td)566 boolean pio_check_end_of_line(text_descriptor *td)
567 {
568     if (!(td->desc.state & fs_record_defined)) {
569 	int ch = getc(td->desc.f);
570 	if (ch == EOF) {
571 	   return true;
572 	}
573 	td->record = ch;
574 	td->desc.state |= (fs_record_defined|fs_next_pos);
575     }
576     return td->record == '\n';
577 }
578 
pio_close_file(file_descriptor * fd)579 void pio_close_file(file_descriptor* fd)
580 {
581     if (fd->f != NULL) {
582 	if (fclose(fd->f) < 0) {
583 	    ioresult = fd->error = IO_ERROR;
584 	    handle_error(fd);
585 	}
586 	fd->f = NULL;
587     }
588     if (fd->name != NULL) {
589 	free(fd->name);
590 	fd->name = NULL;
591     }
592 }
593 
pio_seek_file(file_descriptor * fd,void * record,size_t record_size,integer position)594 void pio_seek_file(file_descriptor* fd, void *record, size_t record_size,
595 		   integer position)
596 {
597     ioresult = fd->error = IO_OK;
598     if (fseek(fd->f, (position-1)*record_size, SEEK_SET) != Ok) {
599 	ioresult = fd->error = IO_ERROR;
600         handle_error(fd);
601     } else {
602 	fd->state &= ~(fs_record_defined|fs_next_pos);
603     }
604 }
605 
pio_get_file_size(file_descriptor * fd,size_t record_size)606 long pio_get_file_size(file_descriptor* fd, size_t record_size)
607 {
608     fpos_t pos;
609     long size;
610     fgetpos(fd->f, &pos);
611     fseek(fd->f, 0, SEEK_END);
612     size = ftell(fd->f);
613     fsetpos(fd->f, &pos);
614     return size / record_size;
615 }
616 
617 
618 #undef rename
619 
pio_rename_file(file_descriptor * fd,const char * new_name)620 void pio_rename_file(file_descriptor* fd, const char* new_name)
621 {
622     if (fd->f != NULL) {
623 	fclose(fd->f);
624 	fd->f = NULL;
625     }
626     assert(fd->name != NULL);
627     ioresult = fd->error = IO_OK;
628     if (*new_name == '.') {
629 	char *s = strrchr(fd->name, '.');
630 	char *name_with_ext;
631 	if (s == NULL) {
632 	    s = fd->name + strlen(fd->name);
633 	}
634 	name_with_ext = (char*)malloc(s+1-fd->name + strlen(new_name));
635 	sprintf(name_with_ext, "%.*s%s", (int)(s - fd->name),
636 		                         fd->name, new_name);
637 	if (rename(fd->name, name_with_ext) != Ok) {
638 	    ioresult = fd->error = IO_ERROR;
639 	    handle_error(fd);
640 	    return;
641 	}
642 	free(fd->name);
643 	fd->name = name_with_ext;
644     } else {
645 	if (rename(fd->name, new_name) != Ok) {
646 	    ioresult = fd->error = IO_ERROR;
647 	    handle_error(fd);
648 	    return;
649 	}
650 	free(fd->name);
651 	fd->name = strdup(new_name);
652     }
653 }
654 
pio_flush_file(file_descriptor * fd)655 void pio_flush_file(file_descriptor* fd)
656 {
657     fflush(fd->f);
658 }
659 
pio_delete_file(file_descriptor * fd)660 void pio_delete_file(file_descriptor* fd)
661 {
662     ioresult = fd->error = IO_OK;
663     if (fd->f != NULL) {
664 	fclose(fd->f);
665 	fd->f = NULL;
666     }
667     if (fd->name != NULL) {
668 	if (remove(fd->name) != Ok) {
669 	    ioresult = fd->error = IO_ERROR;
670 	    handle_error(fd);
671 	} else {
672 	    free(fd->name);
673 	    fd->name = NULL;
674 	}
675     }
676 }
677 
pio_read_record(file_descriptor * fd,void * record,size_t record_size,void * dst)678 void pio_read_record(file_descriptor* fd, void* record, size_t record_size,
679 		 void* dst)
680 {
681     pio_access_record(fd, record, record_size);
682     if (fd->error == IO_OK) {
683 	memcpy(dst, record, record_size);
684 	pio_get_record(fd, record, record_size);
685     }
686 }
687 
pio_write_record(file_descriptor * fd,void * record,size_t record_size,void * src)688 void pio_write_record(file_descriptor* fd, void* record, size_t record_size,
689 		  void* src)
690 {
691     memcpy(record, src, record_size);
692     fd->state |= fs_record_defined;
693     pio_put_record(fd, record, record_size);
694 }
695 
696 
697 /*
698  * Text input functions
699  */
700 
701 #define preinput(td) \
702     if (td->desc.state & fs_next_pos) { \
703 	ungetc(td->record, td->desc.f); \
704         td->desc.state &= ~(fs_record_defined|fs_next_pos); \
705     }
706 
pio_input_real(text_descriptor * td,double * val)707 void pio_input_real(text_descriptor* td, double* val)
708 {
709     preinput(td);
710     td->desc.error = ioresult = IO_OK;
711     if (fscanf(td->desc.f, "%lf", val) != 1) {
712 	td->desc.error = ioresult = input_error(td->desc.f);
713 	handle_error(&td->desc);
714     }
715 }
716 
pio_input_integer(text_descriptor * td,int * val)717 void pio_input_integer(text_descriptor* td, int* val)
718 {
719     preinput(td);
720     td->desc.error = ioresult = IO_OK;
721     if (fscanf(td->desc.f, "%d", val) != 1) {
722 	td->desc.error = ioresult = input_error(td->desc.f);
723 	handle_error(&td->desc);
724     }
725 }
726 
pio_input_unsigned(text_descriptor * td,unsigned * val)727 void pio_input_unsigned(text_descriptor* td, unsigned* val)
728 {
729     preinput(td);
730     td->desc.error = ioresult = IO_OK;
731     if (fscanf(td->desc.f, "%u", val) != 1) {
732 	td->desc.error = ioresult = input_error(td->desc.f);
733 	handle_error(&td->desc);
734     }
735 }
736 
pio_input_long(text_descriptor * td,long * val)737 void pio_input_long(text_descriptor* td, long* val)
738 {
739     preinput(td);
740     td->desc.error = ioresult = IO_OK;
741     if (fscanf(td->desc.f, "%ld", val) != 1) {
742 	td->desc.error = ioresult = input_error(td->desc.f);
743 	handle_error(&td->desc);
744     }
745 }
746 
pio_input_ulong(text_descriptor * td,unsigned long * val)747 void pio_input_ulong(text_descriptor* td, unsigned long* val)
748 {
749     preinput(td);
750     td->desc.error = ioresult = IO_OK;
751     if (fscanf(td->desc.f, "%lu", val) != 1) {
752 	td->desc.error = ioresult = input_error(td->desc.f);
753 	handle_error(&td->desc);
754     }
755 }
756 
pio_input_char(text_descriptor * td,char * val)757 void pio_input_char(text_descriptor* td, char* val)
758 {
759     if (!(td->desc.state & fs_next_pos)) {
760 	int ch = getc(td->desc.f);
761 	if (ch == EOF) {
762 	    td->desc.error = ioresult = IO_EOF;
763 	    handle_error(&td->desc);
764 	    return;
765 	}
766 	td->record = ch;
767     }
768     if (td->record == '\n') td->record = ' ';
769     *val = td->record;
770     td->desc.state &= ~(fs_record_defined|fs_next_pos);
771 }
772 
pio_input_string(text_descriptor * td,char * dst,size_t len,int padding)773 int pio_input_string(text_descriptor* td, char* dst, size_t len, int padding)
774 {
775     int ch = (td->desc.state & fs_next_pos) ? td->record : getc(td->desc.f);
776     size_t n = 0;
777 
778     while (n < len && ch != EOF && ch != '\n') {
779 	*dst++ = ch;
780 	ch = getc(td->desc.f);
781 	n += 1;
782     }
783     if (padding) {
784 	while (n < len) {
785 	    *dst++ = ' ';
786 	    n += 1;
787 	}
788     }
789     td->record = ch;
790     if (ch != EOF) {
791 	td->desc.state |= (fs_record_defined|fs_next_pos);
792         td->desc.error = ioresult = IO_OK;
793     } else {
794         td->desc.error = ioresult = IO_EOF;
795 	handle_error(&td->desc);
796     }
797     return n;
798 }
799 
pio_input_newline(text_descriptor * td)800 void pio_input_newline(text_descriptor* td)
801 {
802    int ch = (td->desc.state & fs_next_pos) ? td->record : getc(td->desc.f);
803    while(ch != EOF && ch != '\n') {
804        ch = getc(td->desc.f);
805    }
806    td->desc.state &= ~(fs_record_defined|fs_next_pos);
807    td->desc.error = ioresult = (ch == EOF) ? IO_EOF : IO_OK;
808    handle_error(&td->desc);
809 }
810 
811 /*
812  * Text output functions
813  */
814 
815 #define postoutput(td) (td->desc.state &= ~(fs_record_defined|fs_next_pos),  \
816 			td->desc.error=ferror(td->desc.f) ? IO_ERROR : IO_OK,\
817 			handle_error(&td->desc))
818 
pio_output_end_of_page(text_descriptor * td)819 void pio_output_end_of_page(text_descriptor* td)
820 {
821     putc('\f', td->desc.f);
822     postoutput(td);
823 }
824 
pio_output_real(text_descriptor * td,double val,const int * width,const int * prec)825 void pio_output_real(text_descriptor* td, double val, const int* width,
826 		 const int* prec)
827 {
828     if (prec == NULL && width == NULL) {
829 	fprintf(td->desc.f, "% G", val);
830     } else if (prec != NULL && width != NULL) {
831 	if (*prec < 0) {
832 	    fprintf(td->desc.f, "%*.*E", *width, -*prec, val);
833 	} else {
834 	    fprintf(td->desc.f, "%*.*f", *width, *prec, val);
835 	}
836     } else {
837 	fprintf(td->desc.f, "%*E", *width, val);
838     }
839     postoutput(td);
840 }
841 
pio_output_integer(text_descriptor * td,int val,const int * width)842 void pio_output_integer(text_descriptor* td, int val, const int* width)
843 {
844     if (width == NULL) {
845 	fprintf(td->desc.f, "%7d", val);
846     } else if(*width < 0) {
847 	fprintf(td->desc.f, "%*o", -*width, val);
848     } else {
849 	fprintf(td->desc.f, "%*d", *width, val);
850     }
851     postoutput(td);
852 }
853 
pio_output_long(text_descriptor * td,long val,const int * width)854 void pio_output_long(text_descriptor* td, long val, const int* width)
855 {
856     if (width == NULL) {
857 	fprintf(td->desc.f, "%7ld", val);
858     } else if(*width < 0) {
859 	fprintf(td->desc.f, "%*lo", -*width, val);
860     } else {
861 	fprintf(td->desc.f, "%*ld", *width, val);
862     }
863     postoutput(td);
864 }
865 
pio_output_unsigned(text_descriptor * td,unsigned val,const int * width)866 void pio_output_unsigned(text_descriptor* td, unsigned val, const int* width)
867 {
868     if (width == NULL) {
869 	fprintf(td->desc.f, "%7u", val);
870     } else if(*width < 0) {
871 	fprintf(td->desc.f, "%*o", -*width, val);
872     } else {
873 	fprintf(td->desc.f, "%*u", *width, val);
874     }
875     postoutput(td);
876 }
877 
pio_output_ulong(text_descriptor * td,unsigned long val,const int * width)878 void pio_output_ulong(text_descriptor* td, unsigned long val, const int* width)
879 {
880     if (width == NULL) {
881 	fprintf(td->desc.f, "%7lu", val);
882     } else if(*width < 0) {
883 	fprintf(td->desc.f, "%*lo", -*width, val);
884     } else {
885 	fprintf(td->desc.f, "%*lu", *width, val);
886     }
887     postoutput(td);
888 }
889 
pio_output_char(text_descriptor * td,char val,const int * width)890 void pio_output_char(text_descriptor* td, char val, const int* width)
891 {
892     if (width == NULL) {
893 	fprintf(td->desc.f, "%c", val);
894     } else {
895 	fprintf(td->desc.f, "%*c", *width, val);
896     }
897     postoutput(td);
898 }
899 
pio_output_boolean(text_descriptor * td,boolean val,const int * width)900 void pio_output_boolean(text_descriptor* td, boolean val, const int* width)
901 {
902     if (width == NULL) {
903 	fprintf(td->desc.f, val ? " TRUE" : "FALSE");
904     } else {
905 	fprintf(td->desc.f, "%*s", *width, val ? "TRUE" : "FALSE");
906     }
907     postoutput(td);
908 }
909 
pio_output_string(text_descriptor * td,const char * buf,size_t len,const int * width)910 void pio_output_string(text_descriptor* td, const char* buf, size_t len,
911 		   const int* width)
912 {
913     if (width == NULL) {
914 	fprintf(td->desc.f, "%.*s", (int)len, buf);
915     } else if ((size_t)*width >= len) {
916         fprintf(td->desc.f, "%*.*s", *width, (int)len, buf);
917     } else {
918 	fprintf(td->desc.f, "%.*s", *width, buf);
919     }
920     postoutput(td);
921 }
922 
pio_output_newline(text_descriptor * td)923 void pio_output_newline(text_descriptor* td)
924 {
925     putc('\n', td->desc.f);
926     postoutput(td);
927 }
928 
929 
format_read(text_descriptor * td,char * fmt,va_list ap)930 static void format_read(text_descriptor* td, char* fmt, va_list ap)
931 {
932     td->desc.error = ioresult = IO_OK;
933 
934     while(*fmt != '\0' && td->desc.error == IO_OK) {
935 	if (*fmt++ == '%') {
936 	    switch (*fmt++) {
937 	      case 'i':
938 		{ integer* ptr = va_arg(ap, integer*);
939 		  int tmp;
940 		  pio_input_integer(td, &tmp);
941 		  *ptr = tmp;
942                 }
943 		continue;
944 	      case 'f':
945 		{ real* ptr = va_arg(ap, real*);
946 		  double tmp;
947 		  pio_input_real(td, &tmp);
948 		  *ptr = (real)tmp;
949                 }
950 		continue;
951 	      case 'c':
952 		{ char* ptr = va_arg(ap, char*);
953 		  pio_input_char(td, ptr);
954 		}
955 		continue;
956 	      case 'B':
957 		{ int tmp;
958 		  pio_input_integer(td, &tmp);
959 		  *va_arg(ap, char*) = tmp;
960 		}
961 		continue;
962 	      case 'W':
963 		{ int tmp;
964 		  pio_input_integer(td, &tmp);
965 		  *va_arg(ap, short*) = tmp;
966 		}
967 		continue;
968 	      case 's':
969 		{ int  low  = va_arg(ap, int);
970 		  int  high = va_arg(ap, int);
971 		  char *ptr = va_arg(ap, char*);
972 		  pio_input_string(td, ptr, high-low+1, true);
973 		}
974 		continue;
975 	    }
976 	}
977 	if (*(fmt-1) == '\n') {
978 	    pio_input_newline(td);
979 	} else {
980 	    /*extension: pascal converter never generate such format strings*/
981 	    int ch = (td->desc.state & fs_next_pos) ? td->record
982 		                                    : getc(td->desc.f);
983 	    td->desc.state &= ~(fs_record_defined|fs_next_pos);
984 	    if (ch != *(unsigned char*)(fmt-1)) {
985 		td->desc.error = ioresult = IO_FORMAT_ERROR;
986 		handle_error(&td->desc);
987 	    }
988 	}
989     }
990 }
991 
992 
993 enum { const_prec=1, var_prec=2, const_width=4, var_width=8 };
994 
format_write(text_descriptor * td,char * fmt,va_list ap)995 static void format_write(text_descriptor* td, char* fmt, va_list ap)
996 {
997     td->desc.error = ioresult = IO_OK;
998     while(*fmt != '\0' && td->desc.error == IO_OK) {
999 	if (*fmt++ == '%') {
1000 	    int prec, width;
1001 	    int format = 0;
1002 
1003 	    if (fmt[0] == '*') {
1004 		format |= var_width;
1005 		fmt += 1;
1006 		if (fmt[0] == '.' &&  fmt[1] == '*') {
1007 		    format |= var_prec;
1008 		    fmt += 2;
1009 		}
1010 	    } else {
1011 		int pos;
1012 		if (sscanf(fmt, "%d.%d%n", &width, &prec, &pos) == 2) {
1013 		    format = const_width|const_prec;
1014 		    fmt += pos;
1015 		} else if (sscanf(fmt, "%d%n", &width, &pos) == 1) {
1016 		    format = const_width;
1017 		    fmt += pos;
1018 		}
1019 	    }
1020 	    switch (*fmt++) {
1021 	      case 'i':
1022 		{ int val = va_arg(ap, int);
1023 		  if (format & var_width) width = va_arg(ap, int);
1024 		  pio_output_integer(td, val,
1025 				     (format & (const_width|var_width))
1026 				     ? &width : NULL);
1027 		}
1028 		continue;
1029 	      case 'f':
1030 		{ real val = (real)va_arg(ap, double);
1031 		  if (format & var_width) {
1032 		      width = va_arg(ap, int);
1033 		      if (format & var_prec) {
1034 			  prec = va_arg(ap, int);
1035 		      }
1036 		  }
1037 		  pio_output_real(td, val,
1038 				  (format & (const_width|var_width))
1039 				      ? &width : NULL,
1040 				  (format & (const_prec|var_prec))
1041 		           	      ? &prec : NULL);
1042 		}
1043 		continue;
1044 	      case 's':
1045 		{ int  low  = va_arg(ap, int);
1046 		  int  high = va_arg(ap, int);
1047 		  char *val = va_arg(ap, char*);
1048 		  if (format & var_width) width = va_arg(ap, int);
1049 		  pio_output_string(td, val, high-low+1,
1050 				    (format & (const_width|var_width))
1051 				        ? &width : NULL);
1052 		}
1053 		continue;
1054 	      case 'z':
1055 		{ char *val = va_arg(ap, char*);
1056 		  if (format & var_width) width = va_arg(ap, int);
1057 		  pio_output_string(td, val, strlen(val),
1058 				    (format & (const_width|var_width))
1059 				        ? &width : NULL);
1060 		}
1061 		continue;
1062 	      case 'c':
1063 		{ char val = (char)va_arg(ap, int);
1064 		  if (format & var_width) width = va_arg(ap, int);
1065 		  pio_output_char(td, val, (format & (const_width|var_width))
1066 				           ? &width : NULL);
1067 		}
1068 		continue;
1069 	      case 'b':
1070 		{ boolean val = (boolean)va_arg(ap, int);
1071 		  if (format & var_width) width = va_arg(ap, int);
1072 		  pio_output_boolean(td, val, (format&(const_width|var_width))
1073 				              ? &width : NULL);
1074 		}
1075 		continue;
1076 	    }
1077 	}
1078 	putc(*(fmt-1), td->desc.f);
1079 	postoutput(td);
1080     }
1081 }
1082 
1083 
cread(char * fmt,...)1084 void cread(char* fmt, ...)
1085 {
1086     va_list ap;
1087     va_start(ap, fmt);
1088 
1089     format_read(&input, fmt, ap);
1090 
1091     va_end(ap);
1092 }
1093 
tread(text_descriptor * td,char * fmt,...)1094 void tread(text_descriptor* td, char* fmt, ...)
1095 {
1096     va_list ap;
1097     va_start(ap, fmt);
1098 
1099     format_read(td, fmt, ap);
1100 
1101     va_end(ap);
1102 }
1103 
cwrite(char * fmt,...)1104 void cwrite(char* fmt, ...)
1105 {
1106     va_list ap;
1107     va_start(ap, fmt);
1108 
1109     format_write(&output, fmt, ap);
1110 
1111     va_end(ap);
1112 }
1113 
twrite(text_descriptor * td,char * fmt,...)1114 void twrite(text_descriptor* td, char* fmt, ...)
1115 {
1116     va_list ap;
1117     va_start(ap, fmt);
1118 
1119     format_write(td, fmt, ap);
1120 
1121     va_end(ap);
1122 }
1123 
pio_file_ignore_error(file_descriptor * fd)1124 void pio_file_ignore_error(file_descriptor* fd)
1125 {
1126     fd->state |= fs_ignore_error;
1127 }
1128 
pio_ioerror(file_descriptor * fd)1129 boolean pio_ioerror(file_descriptor* fd)
1130 {
1131     return fd->error != IO_OK;
1132 }
1133 
pio_iostatus(file_descriptor * fd)1134 integer pio_iostatus(file_descriptor* fd)
1135 {
1136     return fd->error;
1137 }
1138 
1139 
1140