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