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