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