2 * file - file I/O routines callable by users
4 * Copyright (C) 1999-2007 David I. Bell and Landon Curt Noll
6 * Primary author: David I. Bell
8 * Calc is open software; you can redistribute it and/or modify it under
9 * the terms of the version 2.1 of the GNU Lesser General Public License
10 * as published by the Free Software Foundation.
12 * Calc is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
15 * Public License for more details.
17 * A copy of version 2.1 of the GNU Lesser General Public License is
18 * distributed with calc under the filename COPYING-LGPL. You should have
19 * received a copy with calc; if not, write to Free Software Foundation, Inc.
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 * @(#) $Revision: 30.3 $
23 * @(#) $Id: file.c,v 30.3 2013/08/11 08:41:38 chongo Exp $
24 * @(#) $Source: /usr/local/src/bin/calc/RCS/file.c,v $
26 * Under source code control: 1991/07/20 00:21:56
27 * File existed as early as: 1991
29 * chongo <was here> /\oo/\ http://www.isthe.com/chongo/
30 * Share and enjoy! :-) http://www.isthe.com/chongo/tech/comp/calc/
35 #include <sys/types.h>
38 #include "have_unistd.h"
39 #if defined(HAVE_UNISTD_H)
41 #endif /* HAVE_UNISTD_H */
45 #include "have_fpos.h"
46 #include "have_fpos_pos.h"
55 #define READSIZE 1024 /* buffer size for reading */
58 * external STDIO functions
60 E_FUNC
void math_setfp(FILE *fp
);
61 E_FUNC
FILE *f_open(char *name
, char *mode
);
65 * Table of opened files.
66 * The first three entries always correspond to stdin, stdout, and stderr,
67 * and cannot be closed. Their file ids are always 0, 1, and 2.
69 STATIC FILEIO files
[MAXFILES
] = {
70 {FILEID_STDIN
, NULL
, (dev_t
)0, (ino_t
)0,
71 "(stdin)", TRUE
, FALSE
, FALSE
, FALSE
, 'r', "r"},
72 {FILEID_STDOUT
, NULL
, (dev_t
)0, (ino_t
)0,
73 "(stdout)", FALSE
, TRUE
, FALSE
, FALSE
, 'w', "w"},
74 {FILEID_STDERR
, NULL
, (dev_t
)0, (ino_t
)0,
75 "(stderr)", FALSE
, TRUE
, FALSE
, FALSE
, 'w', "w"}
79 STATIC
int ioindex
[MAXFILES
] = {0,1,2}; /* Indices for FILEIO table */
80 STATIC FILEID lastid
= FILEID_STDERR
; /* Last allocated file id */
81 STATIC
int idnum
= 3; /* Number of allocated file ids */
84 /* forward static declarations */
85 S_FUNC ZVALUE
filepos2z(FILEPOS pos
);
86 S_FUNC FILEPOS
z2filepos(ZVALUE pos
);
87 S_FUNC
int set_open_pos(FILE *fp
, ZVALUE zpos
);
88 S_FUNC
int get_open_pos(FILE *fp
, ZVALUE
*res
);
89 S_FUNC ZVALUE
off_t2z(off_t siz
);
90 S_FUNC ZVALUE
dev2z(dev_t dev
);
91 S_FUNC ZVALUE
inode2z(ino_t inode
);
92 S_FUNC
void getscanfield(FILE *fp
, BOOL skip
, unsigned int width
,
93 int scannum
, char *scanptr
, char **strptr
);
94 S_FUNC
void getscanwhite(FILE *fp
, BOOL skip
, unsigned int width
,
95 int scannum
, char **strptr
);
96 S_FUNC
int fscanfile(FILE *fp
, char *fmt
, int count
, VALUE
**vals
);
97 S_FUNC
void freadnum(FILE *fp
, VALUE
*valptr
);
98 S_FUNC
void freadsum(FILE *fp
, VALUE
*valptr
);
99 S_FUNC
void freadprod(FILE *fp
, VALUE
*valptr
);
100 S_FUNC
void fskipnum(FILE *fp
);
104 * file_init - perform needed initilization work
106 * On some systems, one cannot initialize a pointer to a FILE *.
107 * This routine, called once at startup is a work-a-round for
108 * systems with such bogons.
110 * We will also probe for any open files beyond stderr and set them up.
115 STATIC
int done
= 0; /* 1 => routine already called */
116 struct stat sbuf
; /* file status */
123 * setup the default set
126 files
[1].fp
= stdout
;
127 files
[2].fp
= stderr
;
128 for (i
= 0; i
< 3; ++i
) {
129 if (fstat(i
, &sbuf
) >= 0) {
130 files
[i
].dev
= sbuf
.st_dev
;
131 files
[i
].inode
= sbuf
.st_ino
;
136 * note any other files that we can find
139 for (i
= 3; i
< MAXFILES
; fiop
++, ++i
) {
143 files
[idnum
].reading
= TRUE
;
144 files
[idnum
].writing
= TRUE
;
145 files
[idnum
].action
= 0;
147 * stat the descriptor to see what we have
149 if (fstat(i
, &sbuf
) >= 0) {
150 fp
= (FILE *) fdopen(i
,"r+"); /*guess mode*/
152 strcpy(files
[idnum
].mode
, "r+");
154 fp
= (FILE *) fdopen(i
, "r");
156 strcpy(files
[idnum
].mode
, "r");
157 files
[idnum
].writing
= FALSE
;
159 fp
= (FILE *) fdopen(i
, "w");
161 strcpy(files
[idnum
].mode
, "w");
162 files
[idnum
].reading
= FALSE
;
168 tname
= (char *)malloc(sizeof("descriptor[19]"));
170 math_error("Out of memory for init_file");
173 sprintf(tname
, "descriptor[%d]", i
);
174 files
[idnum
].name
= tname
;
175 files
[idnum
].id
= idnum
;
176 files
[idnum
].fp
= fp
;
177 files
[idnum
].dev
= sbuf
.st_dev
;
178 files
[idnum
].inode
= sbuf
.st_ino
;
179 ioindex
[idnum
] = idnum
;
191 * init_fileio - initialize a FILEIO structure
193 * This function initializes a calc FILEIO structure. It will optionally
194 * malloc the filename string if given an non-NULL associated filename.
195 * It will canonicalize the file open mode string.
198 * fiop pointer to FILEIO structure to initialize
199 * name associated filename (NULL => caller will setup filename)
200 * mode open mode (one of {r,w,a}{,b}{,+})
201 * sbufp pointer to stat of open file
203 * fp open file stream
206 init_fileio(FILEIO
*fiop
, char *name
, char *mode
,
207 struct stat
*sbufp
, FILEID id
, FILE *fp
)
209 char modestr
[sizeof(fiop
->mode
)]; /* mode [rwa]b?\+? */
210 size_t namelen
; /* length of name */
212 /* allocate filename if requested */
214 namelen
= strlen(name
);
215 fiop
->name
= (char *)malloc(namelen
+ 1);
216 if (fiop
->name
== NULL
) {
217 math_error("No memory for filename");
222 /* initialize FILEIO structure */
224 strncpy(fiop
->name
, name
, namelen
+1);
228 fiop
->dev
= sbufp
->st_dev
;
229 fiop
->inode
= sbufp
->st_ino
;
230 fiop
->reading
= FALSE
;
231 fiop
->writing
= FALSE
;
232 fiop
->appending
= FALSE
;
233 fiop
->binary
= FALSE
;
235 fiop
->mode
[0] = '\0';
238 * determine file open mode
240 * While a leading 'r' is for reading and a leading 'w' is
241 * for writing, the presense of a '+' in the string means
242 * both reading and writing. A leading 'a' means append
245 /* canonicalize read modes */
246 if (mode
[0] == 'r') {
249 strcpy(modestr
, "r");
250 fiop
->reading
= TRUE
;
252 /* note binary mode even though mode is not used / ignored */
253 if (strchr(mode
, 'b') != NULL
) {
254 strcat(modestr
, "b");
257 /* note if reading and writing */
258 if (strchr(mode
, '+') != NULL
) {
259 fiop
->writing
= TRUE
;
260 strcat(modestr
, "+");
263 /* canonicalize write modes */
264 } else if (mode
[0] == 'w') {
266 /* note write mode */
267 strcpy(modestr
, "w");
268 fiop
->writing
= TRUE
;
270 /* note binary mode even though mode is not used / ignored */
271 if (strchr(mode
, 'b') != NULL
) {
272 strcat(modestr
, "b");
275 /* note if reading and writing */
276 if (strchr(mode
, '+') != NULL
) {
277 fiop
->reading
= TRUE
;
278 strcat(modestr
, "+");
281 /* canonicalize append modes */
282 } else if (mode
[0] == 'a') {
284 /* note append mode */
285 strcpy(modestr
, "a");
286 fiop
->writing
= TRUE
;
287 fiop
->appending
= TRUE
;
289 /* note binary mode even though mode is not used / ignored */
290 if (strchr(mode
, 'b') != NULL
) {
291 strcat(modestr
, "b");
294 /* note if reading and writing */
295 if (strchr(mode
, '+') != NULL
) {
296 fiop
->reading
= TRUE
;
297 strcat(modestr
, "+");
300 /* canonicalize no I/O modes */
304 modestr
[sizeof(modestr
)-1] = '\0'; /* firewall */
306 /* record canonical open mode string */
307 strncpy(fiop
->mode
, modestr
, sizeof(fiop
->mode
));
312 * openid - open the specified file name for reading or writing
316 * mode open mode (one of {r,w,a}{,b}{,+})
319 * >=3 FILEID which can be used to do I/O to the file
320 * <0 if the open failed
322 * NOTE: This function will not return 0, 1 or 2 since they are
323 * reserved for stdin, stdout, stderr. In fact, it must not
324 * return 0, 1, or 2 because it will confuse those who call
325 * the opensearchfiile() function
328 openid(char *name
, char *mode
)
330 FILEIO
*fiop
; /* file structure */
331 FILEID id
; /* new file id */
333 struct stat sbuf
; /* file status */
336 /* find the next open slot in the files array */
337 if (idnum
>= MAXFILES
)
340 for (i
= 3; i
< MAXFILES
; fiop
++,i
++) {
341 if (fiop
->name
== NULL
)
345 math_error("This should not happen in openid()!!!");
348 fp
= f_open(name
, mode
);
352 if (fstat(fileno(fp
), &sbuf
) < 0) {
353 math_error("bad fstat");
357 /* get a new FILEID */
359 ioindex
[idnum
++] = i
;
361 /* initialize FILEIO structure */
362 init_fileio(fiop
, name
, mode
, &sbuf
, id
, fp
);
364 /* return calc open file ID */
370 * openpathid - open the specified abse filename, or
371 * relative filename along a search path
375 * mode open mode (one of {r,w,a}{,b}{,+})
376 * pathlist list of colon separated paths (or NULL)
379 * >=3 FILEID which can be used to do I/O to the file
380 * <0 if the open failed
382 * NOTE: This function will not return 0, 1 or 2 since they are
383 * reserved for stdin, stdout, stderr. In fact, it must not
384 * return 0, 1, or 2 because it will confuse those who call
385 * the opensearchfiile() function
388 openpathid(char *name
, char *mode
, char *pathlist
)
390 FILEIO
*fiop
; /* file structure */
391 FILEID id
; /* new file id */
393 struct stat sbuf
; /* file status */
394 char *openpath
; /* malloc copy of path that was opened */
397 /* find the next open slot in the files array */
398 if (idnum
>= MAXFILES
)
401 for (i
= 3; i
< MAXFILES
; fiop
++,i
++) {
402 if (fiop
->name
== NULL
)
406 math_error("This should not happen in openpathid()!!!");
408 /* open a file - searching along a path */
410 fp
= f_pathopen(name
, mode
, pathlist
, &openpath
);
412 if (openpath
!= NULL
) {
413 /* should not happen, but just in case */
418 if (fstat(fileno(fp
), &sbuf
) < 0) {
419 if (openpath
!= NULL
) {
422 math_error("bad fstat");
425 if (openpath
== NULL
) {
427 math_error("bad openpath");
431 /* get a new FILEID */
433 ioindex
[idnum
++] = i
;
435 /* initialize FILEIO structure */
436 init_fileio(fiop
, NULL
, mode
, &sbuf
, id
, fp
);
437 fiop
->name
= openpath
; /* already malloced by f_pathopen */
439 /* return calc open file ID */
445 * reopenid - reopen a FILEID
448 * id FILEID to reopen
449 * mode new mode to open as
450 * mode new mode to open as (one of "r", "w", "a", "r+", "w+", "a+")
451 * name name of new file
454 * FILEID which can be used to do I/O to the file
455 * <0 if the open failed
458 reopenid(FILEID id
, char *mode
, char *name
)
460 FILEIO
*fiop
; /* file structure */
466 if ((id
== FILEID_STDIN
) || (id
== FILEID_STDOUT
) ||
467 (id
== FILEID_STDERR
)) {
468 math_error("Cannot freopen stdin, stdout, or stderr");
472 /* reopen the file */
474 for (i
= 3; i
< idnum
; i
++) {
475 fiop
= &files
[ioindex
[i
]];
481 fprintf(stderr
, "File not open, need file name\n");
484 if (idnum
>= MAXFILES
) {
485 fprintf(stderr
, "Too many open files\n");
488 for (fiop
= &files
[3], i
= 3; i
< MAXFILES
; fiop
++, i
++) {
489 if (fiop
->name
== NULL
)
493 math_error("This should not happen in reopenid");
496 fp
= f_open(name
, mode
);
498 fprintf(stderr
, "Cannot open file\n");
501 ioindex
[idnum
++] = i
;
505 fp
= freopen(fiop
->name
, mode
, fiop
->fp
);
507 fp
= freopen(name
, mode
, fiop
->fp
);
512 for (; i
< idnum
; i
++)
513 ioindex
[i
] = ioindex
[i
+ 1];
517 if (fstat(fileno(fp
), &sbuf
) < 0) {
518 math_error("bad fstat");
522 /* initialize FILEIO structure */
524 if (fiop
->name
== NULL
) {
525 math_error("old and new reopen filenames are NULL");
527 } else if (fiop
->name
!= NULL
) {
531 init_fileio(fiop
, name
, mode
, &sbuf
, id
, fp
);
533 /* return calc open file ID */
539 * Find the file I/O structure for the specified file id, and verifies that
540 * it is opened in the required manner (0 for reading or 1 for writing).
541 * If writable is -1, then no open checks are made at all and NULL is then
542 * returned if the id represents a closed file.
545 findid(FILEID id
, int writable
)
547 FILEIO
*fiop
; /* file structure */
552 if ((id
< 0) || (id
> lastid
))
555 for (i
= 0; i
< idnum
; i
++) {
556 fiop
= &files
[ioindex
[i
]];
565 if ((writable
&& !fiop
->writing
) ||
566 (!writable
&& !fiop
->reading
)) {
575 * Return whether or not a file id is valid. This is used for if tests.
580 return (findid(id
, -1) != NULL
);
585 * Return the file with id = index if this is the id of a file that has been
586 * opened (it may have since been closed). Otherwise returns FILEID_NONE.
595 if ((index
< 0) || (id
> lastid
))
602 * Close the specified file id. Returns TRUE if there was an error.
603 * Closing of stdin, stdout, or stderr is illegal, but closing of already
604 * closed files is allowed.
609 FILEIO
*fiop
; /* file structure */
616 if ((id
== FILEID_STDIN
) || (id
== FILEID_STDOUT
) ||
617 (id
== FILEID_STDERR
)) {
618 math_error("Cannot close stdin, stdout, or stderr");
622 /* get file structure */
623 for (i
= 3; i
< idnum
; i
++) {
624 fiop
= &files
[ioindex
[i
]];
629 return 1; /* File not open */
631 for (; i
< idnum
; i
++)
632 ioindex
[i
] = ioindex
[i
+ 1];
637 /* close file and note error state */
638 err
= ferror(fiop
->fp
);
639 err
|= fclose(fiop
->fp
);
642 /* return success or failure */
643 return (err
? EOF
: 0);
655 for (i
= 3; i
< idnum
; i
++) {
656 fiop
= &files
[ioindex
[i
]];
660 err
|= fclose(fiop
->fp
);
669 * Return whether or not an error occurred to a file.
674 FILEIO
*fiop
; /* file structure */
676 fiop
= findid(id
, -1);
679 return (ferror(fiop
->fp
) != 0);
684 * Return whether or not end of file occurred to a file.
689 FILEIO
*fiop
; /* file structure */
691 fiop
= findid(id
, -1);
694 return (feof(fiop
->fp
) != 0);
699 * Flush output to an opened file.
704 FILEIO
*fiop
; /* file structure */
706 fiop
= findid(id
, -1);
709 if (!fiop
->writing
|| fiop
->action
== 'r')
711 return fflush(fiop
->fp
);
724 for (i
= 3; i
< idnum
; i
++) {
725 fiop
= &files
[ioindex
[i
]];
726 if (fiop
->writing
&& fiop
->action
!= 'r')
727 err
|= fflush(fiop
->fp
);
731 #endif /* Windoz free systems */
735 * Read the next line, string or word from an opened file.
736 * Returns a pointer to an allocated string holding a null-terminated
737 * or newline terminated string. Where reading stops is controlled by
741 * bit 1: at null character
742 * bit 2: at white space (also skips leading white space)
744 * If neither '\n' nor '\0' is encountered reading continues until EOF.
745 * If bit 3 is set the stop character is removed.
748 * id file to read from
749 * flags read flags (see above)
750 * retstr returned pointer to string
753 readid(FILEID id
, int flags
, STRING
**retstr
)
755 FILEIO
*fiop
; /* file structure */
757 char *str
; /* current string */
758 unsigned long n
; /* current number characters read into buf */
759 unsigned long totlen
; /* total length of string copied from buf */
760 char buf
[READSIZE
]; /* temporary buffer */
763 BOOL nlstop
, nullstop
, wsstop
, rmstop
, done
;
770 fiop
= findid(id
, FALSE
);
773 nlstop
= (flags
& 1);
774 nullstop
= (flags
& 2);
775 wsstop
= (flags
& 4);
776 rmstop
= (flags
& 8);
780 if (fiop
->action
== 'w') {
783 if (f_seek_set(fp
, &fpos
) < 0)
789 while (isspace(c
= fgetc(fp
)));
801 if (nlstop
&& c
== '\n')
803 if (nullstop
&& c
== '\0')
805 if (wsstop
&& isspace(c
))
808 } while (n
< READSIZE
);
809 done
= ((nlstop
&& c
== '\n') || (nullstop
&& c
== '\0') ||
810 (wsstop
&& isspace(c
)) || c
== EOF
);
811 if (done
&& rmstop
&& c
!= EOF
)
814 str
= (char *)realloc(str
, totlen
+ n
+ 1);
816 str
= (char *)malloc(n
+ 1);
818 math_error("Out of memory for readid");
822 memcpy(&str
[totlen
], buf
, n
);
827 if (totlen
== 0 && c
== EOF
) {
831 if ((nlstop
&& c
== '\n') && !rmstop
)
832 str
[totlen
- 1] = '\n';
833 if ((nullstop
&& c
== '\0') && !rmstop
)
834 str
[totlen
- 1] = '\0';
837 newstr
->s_len
= totlen
;
845 * Return the next character from an opened file.
846 * Returns EOF if there was an error or end of file.
854 fiop
= findid(id
, FALSE
);
857 if (fiop
->action
== 'w') {
858 f_tell(fiop
->fp
, &fpos
);
860 if (f_seek_set(fiop
->fp
, &fpos
) < 0)
865 return fgetc(fiop
->fp
);
870 * Print out the name of an opened file.
871 * If the file has been closed, a null name is printed.
872 * If flags contain PRINT_UNAMBIG then extra information is printed
873 * identifying the output as a file and some data about it.
876 printid(FILEID id
, int flags
)
878 FILEIO
*fiop
; /* file structure */
880 ZVALUE pos
; /* file position */
883 * filewall - file is closed
885 fiop
= findid(id
, -1);
887 if (flags
& PRINT_UNAMBIG
)
888 math_fmt("FILE %ld closed", id
);
895 * print quoted filename and mode
897 if ((flags
& PRINT_UNAMBIG
) == 0) {
899 math_str(fiop
->name
);
903 math_fmt("FILE %ld \"%s\" (%s", id
, fiop
->name
, fiop
->mode
);
906 * print file position
911 if (get_open_pos(fp
, &pos
) < 0) {
913 math_str("Error while determining file position!");
919 zprintval(pos
, 0, 0);
923 * report special status
931 printf(" fileno: %d ", fileno(fp
));
937 * Print a formatted string similar to printf. Various formats of output
938 * are possible, depending on the format string AND the actual types of the
939 * values. Mismatches do not cause errors, instead something reasonable is
940 * printed instead. The output goes to the file with the specified id.
943 * id file id to print to
945 * fmt standard format string
946 * vals table of values to print
949 idprintf(FILEID id
, char *fmt
, int count
, VALUE
**vals
)
956 int oldmode
, newmode
;
957 long olddigits
, newdigits
;
958 long width
, precision
;
959 BOOL didneg
, didprecision
;
964 fiop
= findid(id
, TRUE
);
967 if (fiop
->action
== 'r') {
968 f_tell(fiop
->fp
, &fpos
);
969 if (f_seek_set(fiop
->fp
, &fpos
) < 0)
978 math_setfp(fiop
->fp
);
980 while ((ch
= *fmt
++) != '\0') {
987 * Here to handle formats.
990 didprecision
= FALSE
;
999 while ((ch
>= '0') && (ch
<= '9')) {
1000 width
= width
* 10 + (ch
- '0');
1004 didprecision
= TRUE
;
1006 while ((ch
>= '0') && (ch
<= '9')) {
1007 precision
= precision
* 10 + (ch
- '0');
1014 oldmode
= conf
->outmode
;
1016 olddigits
= conf
->outdigits
;
1017 newdigits
= olddigits
;
1019 newdigits
= precision
;
1029 newmode
= MODE_REAL
;
1035 newmode
= MODE_FRAC
;
1038 newmode
= MODE_OCTAL
;
1044 newmode
= MODE_BINARY
;
1061 math_setdigits(newdigits
);
1062 math_setmode(newmode
);
1065 * If there is no width specification, or if the type of
1066 * value requires multiple lines, then just output the
1070 (vp
->v_type
== V_MAT
) || (vp
->v_type
== V_LIST
)) {
1071 switch(vp
->v_type
) {
1074 math_str((char *)vp
->v_octet
);
1076 math_chr(*vp
->v_octet
);
1078 printvalue(vp
, PRINT_NORMAL
);
1085 math_chr(*vp
->v_block
->data
);
1087 printvalue(vp
, PRINT_NORMAL
);
1091 if (vp
->v_nblock
->blk
->data
!=
1096 } else if (printchar
) {
1097 if (vp
->v_nblock
->blk
->data
!=
1099 math_chr(*vp
->v_nblock
->
1102 printvalue(vp
, PRINT_NORMAL
);
1106 printvalue(vp
, PRINT_NORMAL
);
1109 math_setmode(oldmode
);
1110 math_setdigits(olddigits
);
1116 * There is a field width. Collect the output in a string,
1117 * print it padded appropriately with spaces, and free it.
1118 * However, if the output contains a newline, then ignore
1122 switch(vp
->v_type
) {
1125 math_str((char *)vp
->v_octet
);
1127 math_chr(*vp
->v_octet
);
1129 printvalue(vp
, PRINT_NORMAL
);
1133 math_str((char *)vp
->v_block
->data
);
1135 math_chr(*vp
->v_block
->data
);
1137 printvalue(vp
, PRINT_NORMAL
);
1141 if (vp
->v_nblock
->blk
->data
!= NULL
)
1143 vp
->v_nblock
->blk
->data
);
1145 else if (printchar
) {
1146 if (vp
->v_nblock
->blk
->data
!= NULL
)
1147 math_chr(*vp
->v_nblock
->blk
->data
);
1150 printvalue(vp
, PRINT_NORMAL
);
1153 printvalue(vp
, PRINT_NORMAL
);
1155 str
= math_getdivertedio();
1156 if (strchr(str
, '\n'))
1159 while (!didneg
&& ((size_t)width
> len
)) {
1165 while (didneg
&& ((size_t)width
> len
)) {
1169 math_setmode(oldmode
);
1170 math_setdigits(olddigits
);
1178 * Write a character to a file.
1181 * id file id to print to
1182 * ch character to write
1185 idfputc(FILEID id
, int ch
)
1190 /* get the file info pointer */
1191 fiop
= findid(id
, TRUE
);
1194 if (fiop
->action
== 'r') {
1195 f_tell(fiop
->fp
, &fpos
);
1196 if (f_seek_set(fiop
->fp
, &fpos
) < 0)
1202 /* set output to file */
1203 math_setfp(fiop
->fp
);
1208 /* restore output to stdout */
1215 * Unget a character read from a file.
1218 * id file id to print to
1219 * ch character to write
1222 idungetc(FILEID id
, int ch
)
1226 fiop
= findid(id
, FALSE
);
1229 if (fiop
->action
!= 'r')
1231 return ungetc(ch
, fiop
->fp
);
1236 * Write a string to a file.
1239 * id file id to print to
1240 * str string to write
1243 idfputs(FILEID id
, STRING
*str
)
1251 /* get the file info pointer */
1252 fiop
= findid(id
, TRUE
);
1256 if (fiop
->action
== 'r') {
1257 f_tell(fiop
->fp
, &fpos
);
1258 if (f_seek_set(fiop
->fp
, &fpos
) < 0)
1276 * Same as idfputs but writes a terminating null character
1279 * id file id to print to
1280 * str string to write
1283 idfputstr(FILEID id
, char *str
)
1288 /* get the file info pointer */
1289 fiop
= findid(id
, TRUE
);
1293 if (fiop
->action
== 'r') {
1294 f_tell(fiop
->fp
, &fpos
);
1295 if (f_seek_set(fiop
->fp
, &fpos
) < 0)
1301 /* set output to file */
1302 math_setfp(fiop
->fp
);
1304 /* write the string */
1309 /* restore output to stdout */
1319 fiop
= findid(id
, -1);
1334 for (i
= 3; i
< idnum
; i
++) {
1335 fiop
= &files
[ioindex
[i
]];
1337 (void) rewind(fiop
->fp
);
1345 * filepos2z - convert a positive file position into a ZVALUE
1351 * file position as a ZVALUE
1353 * NOTE: Does not support negative file positions.
1357 filepos2z(FILEPOS pos
)
1359 ZVALUE ret
; /* ZVALUE file position to return */
1362 * store FILEPOS in a ZVALUE as a positive value
1364 ret
.len
= FILEPOS_BITS
/BASEB
;
1365 ret
.v
= alloc(ret
.len
);
1367 SWAP_HALF_IN_FILEPOS(ret
.v
, &pos
);
1379 * z2filepos - convert a positive ZVALUE file position to a FILEPOS
1382 * zpos file position as a ZVALUE
1385 * file position as a FILEPOS
1387 * NOTE: Does not support negative file positions.
1390 z2filepos(ZVALUE zpos
)
1392 #if FILEPOS_BITS > FULL_BITS
1393 FILEPOS tmp
; /* temp file position as a FILEPOS */
1395 FILEPOS ret
; /* file position as a FILEPOS */
1396 #if FILEPOS_BITS < FULL_BITS
1397 long pos
; /* zpos as a long */
1399 FULL pos
; /* zpos as a FULL */
1405 zpos
.sign
= 0; /* deal only with the absolue value */
1408 * quick return if the position can fit into a long
1410 #if FILEPOS_BITS == FULL_BITS
1411 /* ztofull puts the value into native byte order */
1412 pos
= ztofull(zpos
);
1413 /* on some hosts, FILEPOS is not a scalar */
1414 memset(&ret
, 0, sizeof(FILEPOS
));
1415 memcpy((void *)&ret
, (void *)&pos
, sizeof(FILEPOS
));
1417 #elif FILEPOS_BITS < FULL_BITS
1418 /* ztofull puts the value into native byte order */
1419 pos
= ztolong(zpos
);
1420 /* on some hosts, FILEPOS is not a scalar */
1421 memset(&ret
, 0, sizeof(FILEPOS
));
1422 memcpy((void *)&ret
, (void *)&pos
, sizeof(pos
));
1424 #else /* FILEPOS_BITS > FULL_BITS */
1425 if (!zgtmaxfull(zpos
)) {
1426 /* ztofull puts the value into native byte order */
1427 pos
= ztofull(zpos
);
1428 memset(&ret
, 0, sizeof(FILEPOS
));
1429 memcpy((void *)&ret
, (void *)&pos
, sizeof(pos
));
1434 * copy (and swap if needed) lower part of the ZVALUE as needed
1436 if (zpos
.len
>= FILEPOS_BITS
/BASEB
) {
1437 /* copy the lower FILEPOS_BITS of the ZVALUE */
1438 memcpy(&tmp
, zpos
.v
, sizeof(FILEPOS
));
1440 /* copy what bits we can into the temp value */
1441 memset(&tmp
, 0, sizeof(FILEPOS
));
1442 memcpy(&tmp
, zpos
.v
, zpos
.len
*BASEB
/8);
1444 /* swap into native byte order */
1445 SWAP_HALF_IN_FILEPOS(&ret
, &tmp
);
1451 #endif /* FILEPOS_BITS <= FULL_BITS */
1456 * get_open_pos - get a an open file position
1459 * fp open file stream
1460 * res where to place the file position (ZVALUE)
1463 * 0 res points to the file position
1467 get_open_pos(FILE *fp
, ZVALUE
*res
)
1469 FILEPOS pos
; /* current file position */
1472 * get the file position
1474 if (f_tell(fp
, &pos
) < 0) {
1475 /* cannot get file position, return -1 */
1480 * update file position and return success
1482 *res
= filepos2z(pos
);
1488 * getloc - get the current position of the file
1491 * id file id of the file
1492 * loc pointer to result
1495 * 0 able to get file position
1496 * -1 unable to get file position
1499 getloc(FILEID id
, ZVALUE
*res
)
1501 FILEIO
*fiop
; /* file structure */
1505 * convert id to stream
1507 fiop
= findid(id
, -1);
1514 math_error("Bogus internal file pointer!");
1521 return get_open_pos(fp
, res
);
1526 ftellid(FILEID id
, ZVALUE
*res
)
1529 FILEPOS fpos
; /* current file position */
1532 fiop
= findid(id
, -1);
1536 /* get the file position */
1537 if (f_tell(fiop
->fp
, &fpos
) < 0)
1540 /* convert file position to ZVALUE */
1541 *res
= filepos2z(fpos
);
1547 fseekid(FILEID id
, ZVALUE offset
, int whence
)
1549 FILEIO
*fiop
; /* FILEIO of file */
1550 FILEPOS off
; /* offset as a FILEPOS */
1551 ZVALUE cur
, tmp
; /* current or end of file location */
1552 int ret
= 0; /* return code */
1555 fiop
= findid(id
, -1);
1559 /* seek depending on whence */
1562 /* construct seek position, off = offset */
1565 off
= z2filepos(offset
);
1568 ret
= f_seek_set(fiop
->fp
, &off
);
1572 /* construct seek position, off = cur+offset */
1573 f_tell(fiop
->fp
, &off
);
1574 cur
= filepos2z(off
);
1575 zadd(cur
, offset
, &tmp
);
1581 off
= z2filepos(tmp
);
1585 ret
= f_seek_set(fiop
->fp
, &off
);
1589 /* construct seek position, off = len+offset */
1590 if (get_open_siz(fiop
->fp
, &cur
) < 0)
1592 zadd(cur
, offset
, &tmp
);
1598 off
= z2filepos(tmp
);
1602 ret
= f_seek_set(fiop
->fp
, &off
);
1613 * set_open_pos - set a an open file position
1616 * fp open file stream
1617 * zpos file position (ZVALUE) to set
1620 * 0 res points to the file position
1623 * NOTE: Due to fsetpos limitation, position is set relative to only
1624 * the beginning of the file.
1627 set_open_pos(FILE *fp
, ZVALUE zpos
)
1629 FILEPOS pos
; /* current file position */
1632 * convert ZVALUE to file position
1634 pos
= z2filepos(zpos
);
1637 * set the file position
1639 if (f_seek_set(fp
, &pos
) < 0) {
1640 /* cannot set file position, return -1 */
1652 * setloc - set the current position of the file
1655 * id file id of the file
1656 * zpos file position (ZVALUE) to set
1659 * 0 able to set file position
1660 * -1 unable to set file position
1663 setloc(FILEID id
, ZVALUE zpos
)
1665 FILEIO
*fiop
; /* file structure */
1671 if ((id
== FILEID_STDIN
) || (id
== FILEID_STDOUT
) ||
1672 (id
== FILEID_STDERR
)) {
1673 math_error("Cannot fseek stdin, stdout, or stderr");
1678 * convert id to stream
1680 fiop
= findid(id
, -1);
1687 math_error("Bogus internal file pointer!");
1696 return set_open_pos(fp
, zpos
);
1701 * off_t2z - convert an off_t into a ZVALUE
1707 * file size as a ZVALUE
1713 ZVALUE ret
; /* ZVALUE file size to return */
1716 * store off_t in a ZVALUE as a positive value
1718 ret
.len
= OFF_T_BITS
/BASEB
;
1719 ret
.v
= alloc(ret
.len
);
1721 SWAP_HALF_IN_OFF_T(ret
.v
, &siz
);
1733 * dev2z - convert a stat.st_dev into a ZVALUE
1739 * file size as a ZVALUE
1744 ZVALUE ret
; /* ZVALUE file size to return */
1747 * store off_t in a ZVALUE as a positive value
1749 ret
.len
= DEV_BITS
/BASEB
;
1750 ret
.v
= alloc(ret
.len
);
1752 SWAP_HALF_IN_DEV(ret
.v
, &dev
);
1764 * inode2z - convert a stat.st_ino into a ZVALUE
1770 * file size as a ZVALUE
1774 inode2z(ino_t inode
)
1776 ZVALUE ret
; /* ZVALUE file size to return */
1779 * store off_t in a ZVALUE as a positive value
1781 ret
.len
= INODE_BITS
/BASEB
;
1782 ret
.v
= alloc(ret
.len
);
1784 SWAP_HALF_IN_INODE(ret
.v
, &inode
);
1796 * get_open_siz - get a an open file size
1799 * fp open file stream
1800 * res where to place the file size (ZVALUE)
1803 * 0 res points to the file size
1807 get_open_siz(FILE *fp
, ZVALUE
*res
)
1809 struct stat buf
; /* file status */
1814 if (fstat(fileno(fp
), &buf
) < 0) {
1820 * update file size and return success
1822 *res
= off_t2z(buf
.st_size
);
1828 * getsize - get the current size of the file
1831 * id file id of the file
1832 * res pointer to result
1835 * 0 able to get file size
1837 * other nonzero file not open or other problem
1840 getsize(FILEID id
, ZVALUE
*res
)
1842 FILEIO
*fiop
; /* file structure */
1846 * convert id to stream
1848 fiop
= findid(id
, -1);
1861 return get_open_siz(fp
, res
);
1866 * getdevice - get the device of the file
1869 * id file id of the file
1870 * dev pointer to the result
1873 * 0 able to get device
1874 * -1 unable to get device
1877 get_device(FILEID id
, ZVALUE
*dev
)
1879 FILEIO
*fiop
; /* file structure */
1882 * convert id to stream
1884 fiop
= findid(id
, -1);
1893 *dev
= dev2z(fiop
->dev
);
1899 * getinode - get the inode of the file
1902 * id file id of the file
1903 * inode pointer to the result
1906 * 0 able to get inode
1907 * -1 unable to get inode
1910 get_inode(FILEID id
, ZVALUE
*inode
)
1912 FILEIO
*fiop
; /* file structure */
1915 * convert id to stream
1917 fiop
= findid(id
, -1);
1926 *inode
= inode2z(fiop
->inode
);
1932 filesize(FILEIO
*fiop
)
1937 if (fstat(fileno(fiop
->fp
), &sbuf
) < 0) {
1938 math_error("bad fstat");
1941 return sbuf
.st_size
;
1946 zfilesize(FILEID id
)
1949 off_t len
; /* file length */
1950 ZVALUE ret
; /* file size as a ZVALUE return value */
1953 fiop
= findid(id
, -1);
1955 /* return neg value for non-file error */
1961 len
= filesize(fiop
);
1970 BOOL listed
[MAXFILES
];
1974 ino_t inodes
[MAXFILES
];
1975 off_t sizes
[MAXFILES
];
1978 for (i
= 0; i
< idnum
; i
++) {
1980 fiop
= &files
[ioindex
[i
]];
1982 if (fstat(fileno(fp
), &sbuf
) < 0) {
1983 printf("Bad fstat for file %d\n", (int) fiop
->id
);
1986 inodes
[i
] = sbuf
.st_ino
;
1987 sizes
[i
] = sbuf
.st_size
;
1990 for (i
= 0; i
< idnum
; i
++) {
1993 fiop
= &files
[ioindex
[i
]];
1995 printid(fiop
->id
, PRINT_UNAMBIG
);
1996 if (sizes
[i
] == -1) {
2000 printf(" size = %lld\n", (long long int)sizes
[i
]);
2001 for (j
= i
+ 1; j
< idnum
; j
++) {
2002 if (listed
[j
] || sizes
[j
] == -1)
2004 if (inodes
[j
] == inodes
[i
]) {
2006 fiop
= &files
[ioindex
[j
]];
2008 printid(fiop
->id
, PRINT_UNAMBIG
);
2013 printf("\tNumber open = %d\n", idnum
);
2014 printf("\tLastid = %d\n", (int) lastid
);
2019 * getscanfield - scan a field separated by some characters
2024 * width max field width
2025 * scannum Number of characters in scanset
2026 * scanptr string of characters considered separators
2027 * strptr pointer to where the new field pointer may be found
2030 getscanfield(FILE *fp
, BOOL skip
, unsigned int width
, int scannum
,
2031 char *scanptr
, char **strptr
)
2033 char *str
; /* current string */
2034 unsigned long len
; /* current length of string */
2035 unsigned long totlen
; /* total length of string */
2036 char buf
[READSIZE
]; /* temporary buffer */
2039 BOOL comp
; /* Use complement of scanset */
2045 comp
= (scannum
< 0);
2056 if (c
== EOF
|| c
== '\0')
2060 ((memchr(scanptr
,c
,scannum
)==NULL
) ^ comp
))
2065 if (len
>= READSIZE
)
2073 str
= (char *) realloc(str
, totlen
+ len
+ 1);
2075 str
= (char *) malloc(len
+ 1);
2077 math_error("Out of memory for scanning");
2081 memcpy(&str
[totlen
], buf
, len
);
2088 if (!(width
&& chnum
== width
) && c
!= '\0')
2099 * getscanwhite - scan a field separated by whitespace
2104 * width max field width
2105 * scannum Number of characters in scanset
2106 * strptr pointer to where the new field pointer may be found
2109 getscanwhite(FILE *fp
, BOOL skip
, unsigned int width
, int scannum
,
2112 char *str
; /* current string */
2113 unsigned long len
; /* current length of string */
2114 unsigned long totlen
; /* total length of string */
2115 char buf
[READSIZE
]; /* temporary buffer */
2118 BOOL comp
; /* Use complement of scanset */
2124 comp
= (scannum
< 0);
2135 if (c
== EOF
|| c
== '\0')
2138 if(scannum
&& (!isspace(c
) ^ comp
))
2143 if (len
>= READSIZE
)
2151 str
= (char *) realloc(str
, totlen
+ len
+ 1);
2153 str
= (char *) malloc(len
+ 1);
2155 math_error("Out of memory for scanning");
2159 memcpy(&str
[totlen
], buf
, len
);
2166 if (!(width
&& chnum
== width
) && c
!= '\0')
2177 fscanfile(FILE *fp
, char *fmt
, int count
, VALUE
**vals
)
2179 int assnum
; /* Number of assignments made */
2180 int c
; /* Character read from file */
2181 char f
; /* Character read from format string */
2182 int scannum
; /* Number of characters in scanlist */
2183 char *scanptr
; /* Start of scanlist */
2185 BOOL comp
; /* True scanset is complementary */
2186 BOOL skip
; /* True if string to be skipped rather than read */
2188 VALUE
*var
; /* lvalue to be assigned to */
2189 unsigned short subtype
; /* for var->v_subtype */
2190 FILEPOS cur
; /* current location */
2200 if (isspace((int)f
)) {
2201 getscanwhite(fp
,1,0,6,NULL
);
2204 } while (isspace((int)f
));
2211 if (f
!= '%' && f
!= '\0')
2214 if (f
!= c
|| f
== '\0') {
2221 if (!skip
&& count
== 0) {
2227 while (f
>= '0' && f
<= '9') {
2228 width
= 10 * width
+ f
- '0';
2235 getscanfield(fp
,skip
,width
,0,NULL
,&str
);
2238 getscanwhite(fp
,1,0,6,NULL
);
2241 getscanwhite(fp
,skip
,width
,-6,&str
);
2251 fmt
= strchr((f
== ']' ? fmt
+ 1 : fmt
), ']');
2254 scannum
= fmt
- scanptr
;
2258 getscanfield(fp
,skip
,
2259 width
,scannum
,scanptr
,&str
);
2265 getscanwhite(fp
,1,0,6, NULL
);
2274 if (var
->v_type
!= V_ADDR
)
2275 math_error("This should not happen!!");
2277 subtype
= var
->v_subtype
;
2281 var
->v_subtype
= subtype
;
2287 if (var
->v_type
!= V_ADDR
)
2288 math_error("This should not happen!!");
2290 subtype
= var
->v_subtype
;
2292 var
->v_type
= V_NUM
;
2293 var
->v_num
= qalloc();
2295 var
->v_num
->num
= filepos2z(cur
);
2296 var
->v_subtype
= subtype
;
2299 fprintf(stderr
, "Unsupported scan specifier");
2306 if (var
->v_type
!= V_ADDR
)
2307 math_error("Assigning to nonvariable");
2309 subtype
= var
->v_subtype
;
2311 var
->v_type
= V_STR
;
2312 var
->v_str
= makestring(str
);
2319 fscanfid(FILEID id
, char *fmt
, int count
, VALUE
**vals
)
2325 fiop
= findid(id
, FALSE
);
2331 if (fiop
->action
== 'w') {
2334 if (f_seek_set(fp
, &fpos
) < 0)
2339 return fscanfile(fp
, fmt
, count
, vals
);
2344 scanfstr(char *str
, char *fmt
, int count
, VALUE
**vals
)
2354 i
= fscanfile(fp
, fmt
, count
, vals
);
2361 * Read a number in floating-point format from a file. The first dot,
2362 * if any, is considered as the decimal point; later dots are ignored.
2363 * For example, -23.45..67. is interpreted as -23.4567
2364 * An optional 'e' or 'E' indicates multiplication by a power or 10,
2365 * e.g. -23.45e-6 has the effect of -23.45 * 10^-6. The reading
2366 * ceases when a character other than a digit, a leading sign,
2367 * a sign immediately following 'e' or 'E', or a dot is encountered.
2368 * Absence of digits is interpreted as zero.
2371 freadnum(FILE *fp
, VALUE
*valptr
)
2373 ZVALUE num
, zden
, newnum
, newden
, div
, tmp
;
2382 BOOL sign
, negexp
, havedp
, imag
, exptoobig
;
2393 if (ch
== '+' || ch
== '-') {
2403 if (ch
>= '0' && ch
<= '9') {
2404 f
= (FULL
) (ch
- '0');
2408 f
= 10 * (FULL
) *a
+ f
;
2413 a
= alloc(num
.len
+ 1);
2414 memcpy(a
, num
.v
, num
.len
* sizeof(HALF
));
2415 a
[num
.len
] = (HALF
) f
;
2429 if (ch
== 'e' || ch
== 'E') {
2431 if (ch
== '+' || ch
== '-') {
2436 while (ch
>= '0' && ch
<= '9') {
2438 exp
= (exp
* 10) + ch
- '0';
2445 if (ch
== 'i' || ch
== 'I') {
2454 val
.v_subtype
= V_NOSUBTYPE
;
2455 val
.v_num
= qlink(&_qzero_
);
2461 *valptr
= error_value(E_BIGEXP
);
2464 ztenpow(decimals
, &zden
);
2468 zmul(zden
, tmp
, &newden
);
2472 zmul(num
, tmp
, &newnum
);
2478 if (!zisunit(num
) && !zisunit(zden
)) {
2479 zgcd(num
, zden
, &div
);
2480 if (!zisunit(div
)) {
2481 zequo(num
, div
, &newnum
);
2483 zequo(zden
, div
, &newden
);
2503 val
.v_subtype
= V_NOSUBTYPE
;
2509 freadsum(FILE *fp
, VALUE
*valptr
)
2518 while (ch
== '+' || ch
== '-') {
2521 addvalue(&v1
, &v2
, &v3
);
2523 subvalue(&v1
, &v2
, &v3
);
2535 freadprod(FILE *fp
, VALUE
*valptr
)
2542 while (ch
== '*' || ch
== '/') {
2545 mulvalue(&v1
, &v2
, &v3
);
2547 divvalue(&v1
, &v2
, &v3
);
2565 if (ch
== '+' || ch
== '-')
2567 while ((ch
>= '0' && ch
<= '9') || ch
== '.')
2569 if (ch
== 'e' || ch
== 'E') {
2571 if (ch
== '+' || ch
== '-')
2573 while (ch
>= '0' && ch
<= '9')
2576 if (ch
== 'i' || ch
== 'I')
2578 } while (ch
== '/' || ch
== '*' || ch
== '+' || ch
== '-');
2589 fiop
= findid(id
, -1);
2592 return isatty(fileno(fiop
->fp
));
2597 * fsearch - search for a string in a file
2600 * id FILEID to search
2601 * str string to look for
2602 * pos file postion to start at (NULL => current position)
2605 * EOF if system error
2606 * other negative integer if file not open, etc.
2607 * positive integer if string not found
2608 * zero if string found, position stored at res
2610 * XXX - This search is a translation of the original search that did not
2611 * work with large files. The search algorithm used is slow and
2612 * should be spead up much more.
2615 fsearch(FILEID id
, char *str
, ZVALUE start
, ZVALUE end
, ZVALUE
*res
)
2617 FILEIO
*fiop
; /* FILEIO of file id */
2618 FILEPOS cur
; /* current file position */
2619 ZVALUE tmp
, tmp2
; /* temporary ZVALUEs */
2620 char c
; /* str comparison character */
2621 int r
; /* character read from file */
2622 char *s
; /* str comparison pointer */
2626 fiop
= findid(id
, FALSE
);
2633 if (fiop
->action
== 'w')
2636 zsub(end
, start
, &tmp2
);
2645 tmp
.v
= alloc(tmp
.len
);
2646 zcopyval(tmp2
, tmp
);
2649 cur
= z2filepos(start
);
2651 if (f_seek_set(fiop
->fp
, &cur
) < 0) {
2659 /* note the first str search character */
2667 while ((r
= fgetc(fiop
->fp
)) != EOF
) {
2669 (void) f_tell(fiop
->fp
, &cur
);
2672 r
= fgetc(fiop
->fp
);
2681 tmp
= filepos2z(cur
);
2682 zsub(tmp
, _one_
, res
);
2686 (void) f_seek_set(fiop
->fp
, &cur
);
2697 while (k
< tmp
.len
&& tmp
.v
[k
] == 0);
2699 math_error("This should not happen");
2703 if (tmp
.v
[tmp
.len
- 1] == 0)
2708 if (ferror(fiop
->fp
))
2715 * frsearch - reverse search for a string in a file
2718 * id FILEID to search
2719 * str string to look for
2720 * search starts at pos = first and continues for decreasing
2724 * EOF if system error
2725 * other negative integer if file not open, etc.
2726 * positive integer if string not found
2727 * zero if string found, position stored at res
2729 * XXX - This search is a translation of the original search that did not
2730 * work with large files. The search algorithm used is so slow
2731 * as to be painful to the user and needs to be sped up much more.
2734 frsearch(FILEID id
, char *str
, ZVALUE first
, ZVALUE last
, ZVALUE
*res
)
2736 FILEIO
*fiop
; /* FILEIO of file id */
2737 FILEPOS cur
; /* current file position */
2738 ZVALUE pos
; /* current file position as ZVALUE */
2739 ZVALUE tmp
; /* temporary ZVALUEs */
2740 char c
; /* str comparison character */
2741 int r
; /* character read from file */
2742 char *s
; /* str comparison pointer */
2745 fiop
= findid(id
, FALSE
);
2752 if (fiop
->action
== 'w')
2760 /* note the first str search character */
2764 cur
= z2filepos(pos
);
2765 if (f_seek_set(fiop
->fp
, &cur
) < 0) {
2775 while(zrel(pos
, last
) >= 0) {
2776 cur
= z2filepos(pos
);
2777 if (f_seek_set(fiop
->fp
, &cur
) < 0) {
2781 r
= fgetc(fiop
->fp
);
2786 if ((char) r
== c
) {
2789 r
= fgetc(fiop
->fp
);
2800 ungetc(r
, fiop
->fp
);
2804 zsub(pos
, _one_
, &tmp
);
2808 cur
= z2filepos(last
);
2809 f_seek_set(fiop
->fp
, &cur
);
2811 if (ferror(fiop
->fp
))
2818 findfname(FILEID id
)
2822 fiop
= findid(id
, -1);