Initial commit, 3-52-19 alpha
[cls.git] / src / c / xlfio.c
blob2154b3996d0e552c515c308d00c323e699956872
1 /* xlfio.c - xlisp file i/o */
2 /* Copyright (c) 1989, by David Michael Betz. */
3 /* You may give out copies of this software; for conditions see the file */
4 /* COPYING included with this distribution. */
6 #include "xlisp.h"
8 #ifdef FILETABLE
9 #include <errno.h>
10 #ifdef MACINTOSH
11 #include <stat.h>
12 #else
13 #include <sys/stat.h>
14 #endif
15 #endif
17 /* forward declarations */
18 LOCAL LVAL printit P2H(int, int);
19 LOCAL FIXTYPE flatsize P2H(LVAL, int);
20 LOCAL FILEP opencmd P3H(char *, char *, int);
21 LOCAL VOID toomanyopt P1H(LVAL);
22 LOCAL char * skip_pp P3H(char *, int *, int *);
23 LOCAL char * decode_pp P7H(char *, FIXTYPE *, int, int *, int *, int *, LVAL);
24 LOCAL VOID opt_print P6H(LVAL, LVAL, int, FIXTYPE *, int, int);
25 LOCAL int trimzeros P2H(char *, int);
26 LOCAL int allzeros P1H(char *);
27 LOCAL VOID write_double_ffmt P4H(char *, double, int, int);
28 LOCAL VOID integer_print P5H(LVAL, LVAL, int, FIXTYPE *,int);
29 LOCAL VOID flonum_fprint P4H(LVAL, LVAL, FIXTYPE *,int);
30 LOCAL VOID flonum_eprint P4H(LVAL, LVAL, FIXTYPE *,int);
31 LOCAL VOID flonum_gprint P4H(LVAL, LVAL, FIXTYPE *,int);
32 LOCAL VOID tab_print P3H(LVAL, FIXTYPE *, int);
33 LOCAL VOID indirect_print P2H(LVAL, int);
34 LOCAL VOID case_convert_print P4H(char *, LVAL, int, int);
35 LOCAL VOID conditional_print P5H(char *, LVAL, FIXTYPE, int, int);
36 LOCAL VOID iterative_print P5H(char *, LVAL, FIXTYPE, int, int);
37 LOCAL char *skip_past_directive P3H(char *, int, int);
39 /* xread - read an expression */
40 /* eof-error-p added - L. Tierney */
41 LVAL xread(V)
43 LVAL fptr,eof,val;
44 int eof_error_p, recursive_p = FALSE;
46 /* get file pointer and eof value */
47 fptr = (moreargs() ? xlgetfile(FALSE) : getvalue(s_stdin));
48 eof_error_p = moreargs() ? ((xlgetarg() != NIL) ? TRUE : FALSE) : TRUE;
49 eof = (moreargs() ? xlgetarg() : NIL);
50 if (moreargs() && !null(xlgetarg())) recursive_p = TRUE;
51 xllastarg();
53 /* read an expression */
54 if (!xlread(fptr, &val, recursive_p, FALSE)) {
55 if (eof_error_p) xlfail("end of file on read");
56 else val = eof;
59 /* return the expression */
60 return (val);
63 /* TAA MOD 9/97 -- added read-preserving-whitespace */
64 LVAL xreadpw(V)
66 LVAL fptr,eof,val;
67 int eof_error_p, recursive_p = FALSE;
69 /* get file pointer and eof value */
70 fptr = (moreargs() ? xlgetfile(FALSE) : getvalue(s_stdin));
71 eof_error_p = moreargs() ? ((xlgetarg() != NIL) ? TRUE : FALSE) : TRUE;
72 eof = (moreargs() ? xlgetarg() : NIL);
73 if (moreargs() && !null(xlgetarg())) recursive_p = TRUE;
74 xllastarg();
76 /* read an expression */
77 if (!xlread(fptr, &val, recursive_p, TRUE)) {
78 if (eof_error_p) xlfail("end of file on read");
79 else val = eof;
82 /* return the expression */
83 return (val);
86 /* xprint - built-in function 'print' */
87 LVAL xprint(V)
89 return (printit(TRUE,TRUE));
92 /* xprin1 - built-in function 'prin1' */
93 LVAL xprin1(V)
95 return (printit(TRUE,FALSE));
98 /* xprinc - built-in function princ */
99 LVAL xprinc(V)
101 return (printit(FALSE,FALSE));
104 /* xfreshline - start a new line if not at begining of line */
105 LVAL xfreshline(V)
107 LVAL fptr;
109 /* get file pointer */
110 fptr = (moreargs() ? xlgetfile(TRUE) : getvalue(s_stdout));
111 xllastarg();
113 /* optionally terminate the print line and return action */
114 return (xlfreshline(fptr)? s_true : NIL);
118 /* xterpri - terminate the current print line */
119 LVAL xterpri(V)
121 LVAL fptr;
123 /* get file pointer */
124 fptr = (moreargs() ? xlgetfile(TRUE) : getvalue(s_stdout));
125 xllastarg();
127 /* terminate the print line and return nil */
128 xlterpri(fptr);
129 return (NIL);
132 /* printit - common print function */
133 LOCAL LVAL printit P2C(int, pflag, int, tflag)
135 LVAL fptr,val;
137 /* get expression to print and file pointer */
138 val = xlgetarg();
139 fptr = (moreargs() ? xlgetfile(TRUE) : getvalue(s_stdout));
140 xllastarg();
142 #ifndef OLDPRINT /* fixed to make PRINT correspond the CL specs - L. Tierney */
143 /* terminate the previous line if necessary */
144 if (tflag) xlterpri(fptr);
145 #endif /* OLDPRINT */
147 /* print the value */
148 xlprint(fptr,val,pflag);
150 #ifndef OLDPRINT
151 /* print space if needed */
152 if (tflag) xlputc(fptr, ' ');
153 #endif /* OLDPRINT */
155 #ifdef OLDPRINT
156 /* terminate the print line if necessary */
157 if (tflag)
158 xlterpri(fptr);
159 #endif /* OLDPRINT */
160 /* return the result */
161 return (val);
164 /* xflatsize - compute the size of a printed representation using prin1 */
165 LVAL xflatsize(V)
167 /* TAA MOD -- rewritten to use a USTREAM 1/21/97 */
168 LVAL val;
170 /* get the expression */
171 val = xlgetarg();
172 xllastarg();
174 return (cvfixnum(flatsize(val, TRUE)));
177 /* xflatc - compute the size of a printed representation using princ */
178 LVAL xflatc(V)
180 /* TAA MOD -- rewritten to use a USTREAM 1/21/97 */
181 LVAL val;
183 /* get the expression */
184 val = xlgetarg();
185 xllastarg();
187 return (cvfixnum(flatsize(val, FALSE)));
190 /* flatsize - compute the size of a printed expression */
191 LOCAL FIXTYPE flatsize P2C(LVAL, val, int, pflag)
193 /* TAA MOD -- rewritten to use a USTREAM 1/21/97 */
194 LVAL ustream;
195 FIXTYPE size;
197 /* create and protect the stream */
198 ustream = newustream();
199 xlprot1(ustream);
201 /* print the value to compute its size */
202 xlprint(ustream,val,pflag);
204 /* calculate size */
205 for (size = 0, ustream = gethead(ustream);
206 !null(ustream);
207 size++, ustream = cdr(ustream)) ;
209 /* unprotect */
210 xlpop();
212 /* return the length of the expression */
213 return (size);
216 enum ACTIONS {A_NIL, A_ERR, A_REN, A_OVER, A_APP, A_SUPER, A_CREATE};
218 LOCAL FILEP opencmd P3C(char *, name, char *, mode, int, binary)
220 if (binary) return OSBOPEN(name, mode);
221 else return OSAOPEN(name, mode);
224 /* xopen - open a file */
225 LVAL xopen(V)
227 #ifdef BIGNUMS
228 FIXTYPE nbits = 0;
229 #endif
230 char *name; /* file name strings */
231 FILEP fp; /* opened file pointer */
232 LVAL fname; /* file name string LVAL */
233 LVAL temp; /* key arguments */
234 int iomode; /* file mode, as stored in node */
235 enum ACTIONS exist; /* exist action */
236 enum ACTIONS nexist;/* non-exist action */
237 int binary;
239 /* get file name */
240 name = getstring(fname = xlgetfname());
242 /* get direction */
243 if (xlgetkeyarg(k_direction,&temp) && temp != k_input) {
244 if (temp == k_output) iomode = S_FORWRITING;
245 else if (temp == k_io) iomode = S_FORREADING|S_FORWRITING;
246 else if (temp == k_probe) iomode = 0;
247 else goto argerror;
249 else iomode = S_FORREADING;
251 /* get type */
253 if (xlgetkeyarg(k_elementtype,&temp) && temp != a_char ) {
254 #ifdef BIGNUMS
255 int notsigned = TRUE;
256 nbits = 8; /* default size */
257 if (temp == a_sbyte) notsigned = FALSE;
258 else if (consp(temp) && car(temp) == a_sbyte &&
259 consp(cdr(temp)) && fixp(car(cdr(temp))) &&
260 null(cdr(cdr(temp)))) {
261 nbits = getfixnum(car(cdr(temp)));
262 notsigned = FALSE;
264 else if (consp(temp) && car(temp) == a_unbyte &&
265 consp(cdr(temp)) && fixp(car(cdr(temp))) &&
266 null(cdr(cdr(temp)))) {
267 nbits = getfixnum(car(cdr(temp)));
269 else if (temp != a_unbyte && temp != a_fixnum)
270 goto argerror;
271 if (nbits < 0 || (nbits & 7) != 0 || nbits > /*32L* */MAXVECLEN)
272 goto argerror; /* invalid value for number of bits */
273 if (iomode) iomode |= (notsigned ? S_BINARY|S_UNSIGNED : S_BINARY);
274 binary = TRUE;
275 #else
276 if (temp == a_fixnum ) {
277 if (iomode) iomode |= S_BINARY; /* mark as binary file type */
278 binary = TRUE;
280 else goto argerror;
281 #endif
283 else
284 binary = FALSE;
286 /* get exists action */
287 if (xlgetkeyarg(k_exist, &temp) &&
288 (iomode & S_FORWRITING) && /* ignore value if :input or :probe */
289 temp != k_rename && temp != k_newversion) {
290 if (null(temp)) exist = A_NIL;
291 else if (temp == k_error) exist = A_ERR;
292 else if (temp == k_overwrite) exist = A_OVER;
293 else if (temp == k_append) exist = A_APP;
294 else if (temp == k_supersede || temp == k_rendel)
295 exist = A_SUPER;
296 else goto argerror;
298 else exist = A_REN;
300 /* get non-exist action */
302 if (xlgetkeyarg(k_nexist, &temp)) {
303 if (null(temp)) nexist = A_NIL;
304 else if (temp == k_error) nexist = A_ERR;
305 else if (temp == k_create) nexist = A_CREATE;
306 else goto argerror;
308 else { /* handle confusing mess of defaults */
309 if (iomode == S_FORREADING || exist == A_OVER || exist == A_APP)
310 nexist = A_ERR;
311 else if (iomode & S_FORWRITING) nexist = A_CREATE;
312 else nexist = A_NIL;
315 xllastkey();
317 /* attempt to open the file */
319 if ((fp = opencmd(name,
320 (iomode & S_FORWRITING) ? OPEN_UPDATE : OPEN_RO,
321 binary))!=CLOSED) {
322 /* success! */
323 if (iomode & S_FORWRITING) switch (exist) { /* do exist action */
324 case A_ERR: /* give error */
325 OSCLOSE(fp);
326 xlerror("file exists", fname);
327 break;
328 case A_REN: /* create new version */
329 OSCLOSE(fp);
330 fp = CLOSED;
331 if (!renamebackup(name))
332 xlerror("couldn't create backup file", fname);
333 break;
334 case A_APP: /* position to end of file */
335 OSSEEKEND(fp);
336 break;
337 case A_SUPER: /* supersede file */
338 OSCLOSE(fp);
339 fp = CLOSED;
340 break;
341 case A_NIL: /* return NIL */
342 OSCLOSE(fp);
343 return NIL;
344 /*case A_OVER:*/ /* overwrite -- does nothing special */
345 default: ;
348 else { /* file does not exist */
349 switch (nexist) {
350 case A_ERR: /* give error */
351 xlerror("file does not exist", fname);
352 break;
353 case A_NIL: /* return NIL */
354 return NIL;
355 /*case A_CREATE:*/ /* create a new file */
356 default: ;
360 /* we now create the file if it is not already open */
361 if (fp == CLOSED)
362 if ((fp = opencmd(name,
363 (iomode&S_FORREADING)? CREATE_UPDATE: CREATE_WR,
364 binary)) == CLOSED)
365 xlerror("couldn't create file", fname);
367 /* take concluding actions */
368 if (iomode == 0) { /* probe */
369 OSCLOSE(fp);
370 fp = CLOSED;
373 #ifdef BIGNUMS
374 temp = cvfile(fp, iomode);
375 temp->n_bsiz = (short)(unsigned short)(nbits/8);
376 return temp;
377 #else
378 return cvfile(fp,iomode);
379 #endif
380 argerror: xlerror("invalid argument", temp);
381 return NIL;
385 /* xfileposition - get position of file stream */
386 LVAL xfileposition(V)
388 long j,fsize;
389 double i;
390 int t = 0;
391 LVAL pos, fptr;
392 FILEP fp;
393 /* get file pointer */
394 fp = getfile(fptr = xlgastream());
396 /* make sure the file exists */
397 if (fp == CLOSED)
398 xlfail("file not open");
400 /* get current position, adjusting for posible "unget" */
401 j = OSTELL(fp) + (getsavech(fptr) ? -1L : 0L);
403 if (moreargs()) { /* must be set position */
404 pos = xlgetarg();
405 xllastarg();
406 if (pos == k_end) t=OSSEEKEND(fp);
407 else if (pos == k_start) t=OSSEEK(fp,0L);
408 else if (fixp(pos)) { /* check for in range, then position */
409 /* STDIO allows positioning beyond end of file, so we must check
410 the file size (boo his!) */
411 i = getfixnum(pos);
412 #ifdef BIGNUMS
413 if (fptr->n_sflags & S_BINARY) i *= fptr->n_bsiz;
414 #endif
415 t = OSSEEKEND(fp);
416 fsize = OSTELL(fp);
417 if (t == 0 && fp != CONSOLE && (i < 0 || i > fsize)) {
418 OSSEEK(fp,j);
419 xlerror("position outside of file", pos);
421 t = OSSEEK(fp, (long)i);
423 else xlbadtype(pos);
425 setsavech(fptr,'\0'); /* toss unget character, if any */
426 fptr->n_sflags &= ~(S_READING|S_WRITING);
427 /* neither reading or writing currently */
428 /* t is non-zero if couldn't do seek */
429 return (t != 0 || fp == CONSOLE ? NIL : s_true);
432 #ifdef BIGNUMS
433 return ((j == -1L || fp == CONSOLE) ? NIL :
434 cvfixnum(fptr->n_sflags & S_BINARY ? j/fptr->n_bsiz : j));
435 #else
436 return ((j == -1L || fp == CONSOLE) ? NIL : cvfixnum(j));
437 #endif
440 /* xfilelength - returns length of file */
441 LVAL xfilelength(V)
443 #ifdef BIGNUMS
444 LVAL stream;
445 #endif
446 FILEP fp;
447 long i,j;
449 /* get file pointer */
450 #ifdef BIGNUMS
451 fp = getfile(stream = xlgastream());
452 #else
453 fp = getfile(xlgastream());
454 #endif
455 xllastarg();
457 /* make sure the file exists */
458 if (fp == CLOSED)
459 xlfail("file not open");
461 /* not all stdio packages will catch the following gaffe */
462 if (fp == CONSOLE) return NIL;
464 if ((i=OSTELL(fp)) == -1L ||
465 OSSEEKEND(fp) ||
466 (j = OSTELL(fp)) == -1L ||
467 OSSEEK(fp,i)) {
468 return NIL;
471 #ifdef BIGNUMS
472 return cvfixnum(stream->n_sflags & S_BINARY ? j/stream->n_bsiz : j);
473 #else
474 return cvfixnum(j);
475 #endif
478 #ifdef FILETABLE
479 /* xfilemtime - returns modification time of file */
480 LVAL xfilemtime(V)
482 LVAL fname;
483 char *str;
484 time_t mtime;
486 str = getstring(fname = xlgetfname());
487 xllastarg();
489 if (osmtime(str, &mtime))
490 xlerror("can't get modification time", fname);
492 if ((double) mtime > (double) MAXFIX)
493 return cvflonum((FLOTYPE) mtime);
494 else
495 return cvfixnum((FIXTYPE) mtime);
498 /* xrenamefile - renames file */
499 LVAL xrenamefile(V)
501 LVAL oldname, newname;
502 char *oldstr, *newstr;
504 oldstr = getstring(oldname = xlgetfname());
505 newstr = getstring(newname = xlgastring());
506 xllastarg();
508 /* in applec and THINK_C rename fails if the new file exists, */
509 /* so remove it first */
510 if ((remove(newstr) != 0 && errno == EACCES) ||
511 rename(oldstr, newstr) != 0)
512 xlerror("can't rename file", oldname);
513 /**** need to fix the truename of open file streams */
514 /**** need to add multiple return values */
516 return newname;
518 #endif /* FILETABLE */
520 LVAL xforceoutput(V)
522 LVAL fptr;
524 fptr = (moreargs() ? xlgetfile(TRUE) : getvalue(s_stdout));
525 xllastarg();
527 if (streamp(fptr)) osforce(getfile(fptr));
529 return(NIL);
533 #ifdef FILETABLE
534 LVAL xtruename(V)
536 LVAL f = xlgetfname();
537 char namebuf[FNAMEMAX+1];
539 xllastarg();
542 STRCPY(buf, getstring(f));
544 if (!truename(buf, namebuf)) xlerror("strange file name", f);
546 return cvstring(namebuf);
549 LVAL xdeletefile(V)
551 LVAL arg;
552 FILEP fp;
554 /* get the argument */
556 arg = xlgetarg();
557 xllastarg();
559 if (streamp(arg) && getfile(arg) > CONSOLE) {
560 /* close file first */
561 fp = getfile(arg);
562 STRCPY(buf, filetab[fp].tname);
563 OSCLOSE(fp);
564 setsavech(arg, '\0');
565 setfile(arg,CLOSED);
567 else {
568 if (symbolp(arg)) arg = getpname(arg);
569 else if (!stringp(arg)) xlbadtype(arg);
571 if (getslength(arg) >= FNAMEMAX)
572 xlerror("file name too long", arg);
574 STRCPY(buf,getstring(arg));
576 if (remove(buf) != 0 && errno == EACCES)
577 xlerror("cannot delete file", arg);
579 return s_true;
582 LVAL xbasedir()
584 LVAL name;
586 name = xlgastring();
587 xllastarg();
589 return dirlist(getstring(name));
592 LVAL xfiletype()
594 struct stat s;
595 char *name = getstring(xlgastring());
596 xllastarg();
597 if (stat(name, &s) != 0)
598 return NIL;
599 else if (S_ISDIR(s.st_mode))
600 return xlenter(":DIRECTORY");
601 else if (S_ISCHR(s.st_mode))
602 return xlenter(":CHARACTER-SPECIAL");
603 else if (S_ISBLK(s.st_mode))
604 return xlenter(":BLOCK-SPECIAL");
605 else if (S_ISREG(s.st_mode))
606 return xlenter(":REGULAR");
607 else if (S_ISFIFO(s.st_mode))
608 return xlenter(":FIFO");
609 else
610 return xlenter(":UNKNOWN");
612 #endif
614 /* xclose - close a file */
615 LVAL xclose(V)
617 LVAL fptr;
618 FILEP fp; /* TAA MOD to allow closing closed files,
619 prohibit closing the console, return the correct
620 values (true on success), and close string streams */
623 /* get file pointer */
624 fptr = xlgetarg();
625 xllastarg();
627 /* handle string stream case by converting to a closed file! */
628 if (ustreamp(fptr)) {
629 fptr->n_type = STREAM;
630 setfile(fptr, CLOSED);
631 setsavech(fptr, '\0');
632 return (s_true);
635 /* give error of not file stream */
636 if (!streamp(fptr)) xlbadtype(fptr);
639 /* make sure the file exists */
641 if ((fp = getfile(fptr)) == CLOSED || fp == CONSOLE)
642 return (NIL);
644 /* close the file */
645 OSCLOSE(fp);
646 setsavech(fptr, '\0');
647 setfile(fptr,CLOSED);
649 /* return true */
650 return (s_true);
653 /* xrdchar - read a character from a file */
654 /* eof, eof-error-p added - L. Tierney */
655 LVAL xrdchar(V)
657 LVAL fptr, eof;
658 int ch, eof_error_p;
660 /* get file pointer */
661 fptr = (moreargs() ? xlgetfile(FALSE) : getvalue(s_stdin));
662 eof_error_p = moreargs() ? ((xlgetarg() != NIL) ? TRUE : FALSE) : TRUE;
663 eof = (moreargs() ? xlgetarg() : NIL);
664 if (moreargs()) xlgetarg(); /* remove and ignore recursive-p argument */
665 xllastarg();
667 /* get character and check for eof */
668 ch = xlgetc(fptr);
669 if (ch == EOF && eof_error_p) xlfail("end of file on read");
670 return (ch == EOF ? eof : cvchar(ch));
673 #ifdef BIGENDIAN
674 #define bs(n) (n) /* byte select in short */
675 #else
676 #define bs(n) ((n)^1)
677 #endif
679 /* xrdbyte - read a byte from a file */
680 /* eof, eof-error-p added - L. Tierney */
681 #ifdef BIGNUMS
682 LVAL xrdbyte(V)
684 LVAL fptr, eof, val;
685 BIGNUMDATA *vx;
686 unsigned char *v;
687 int ch, eof_error_p, i, size, ofs;
688 FIXTYPE temp;
690 /* get file pointer */
691 fptr = xlgastream();
692 if ((fptr->n_sflags & S_BINARY) == 0)
693 xlfail("not a binary file");
694 eof_error_p = moreargs() ? ((xlgetarg() != NIL) ? TRUE : FALSE) : TRUE;
695 eof = (moreargs() ? xlgetarg() : NIL);
696 xllastarg();
698 if (fptr->n_bsiz == 1) { /* file of bytes */
699 ch = xlgetc(fptr);
700 if (ch == EOF && eof_error_p) xlfail("end of file on read");
701 return(ch == EOF ? eof :
702 ((fptr->n_sflags&S_UNSIGNED) ? cvfixnum((FIXTYPE)ch)
703 : cvfixnum((FIXTYPE)(/*signed*/ char)ch)));
705 else { /* file of more than that */
706 size = (fptr->n_bsiz+sizeof(BIGNUMDATA)-1)/sizeof(BIGNUMDATA);
707 /* size of bignum needed */
708 if (size < 2) size = 2;
709 ofs = size*sizeof(BIGNUMDATA) - fptr->n_bsiz; /* unused bytes */
710 val = newbignum(size);
711 vx = getbignumarray(val)+1; /* point to data array start */
712 v = (unsigned char *)vx;
713 #ifdef BIGENDIANFILE
714 for (i = ofs; i < size*sizeof(BIGNUMDATA); i++)
715 #else
716 for (i = size*sizeof(BIGNUMDATA)-1; i >= ofs; i--)
717 #endif
719 ch = xlgetc(fptr);
720 if (ch == EOF) {
721 if (eof_error_p) xlfail("end of file on read");
722 else return eof;
724 v[bs(i)] = (unsigned char)ch;
726 if ((/*signed*/ char)(v[bs(ofs)]) < 0 && (fptr->n_sflags&S_UNSIGNED)==0)
728 /* we need to handle negative number */
729 unsigned long sum;
730 int carry = 1;
731 vx[-1] = 1;
732 for (i = ofs-1; i >=0; i--) v[bs(i)] = 0xff;
733 for (i = size-1; i >= 0; i--) {
734 sum = (unsigned long)(BIGNUMDATA)(~vx[i]) + carry;
735 carry = (int)(sum >> 16);
736 vx[i] = (BIGNUMDATA)sum;
739 val = normalBignum(val); /* normalize in case of leading zeroes */
740 return (cvtbigfixnum(val, &temp) ? cvfixnum(temp) : val);
743 #else
744 LVAL xrdbyte(V)
746 LVAL fptr, eof;
747 int ch, eof_error_p;
749 /* get file pointer */
750 fptr = (moreargs() ? xlgetfile(FALSE) : getvalue(s_stdin));
751 eof_error_p = moreargs() ? ((xlgetarg() != NIL) ? TRUE : FALSE) : TRUE;
752 eof = (moreargs() ? xlgetarg() : NIL);
753 xllastarg();
755 /* get character and check for eof */
756 ch = xlgetc(fptr);
757 if (ch == EOF && eof_error_p) xlfail("end of file on read");
758 return(ch == EOF ? eof : cvfixnum((FIXTYPE)ch));
760 #endif
762 /* xpkchar - peek at a character from a file */
763 /* eof, eof-error-p added */
764 LVAL xpkchar(V)
766 LVAL flag,fptr,eof;
767 int ch,eof_error_p;
769 /* peek flag and get file pointer */
770 flag = (moreargs() ? xlgetarg() : NIL);
771 fptr = (moreargs() ? xlgetfile(FALSE) : getvalue(s_stdin));
772 eof_error_p = moreargs() ? ((xlgetarg() != NIL) ? TRUE : FALSE) : TRUE;
773 eof = (moreargs() ? xlgetarg() : NIL);
774 if (moreargs()) xlgetarg(); /* remove and ignore recursive-p argument */
775 xllastarg();
777 /* skip leading white space and get a character */
778 if (!null(flag))
779 while ((ch = xlpeek(fptr)) != EOF && isspace(ch))
780 xlgetc(fptr);
781 else
782 ch = xlpeek(fptr);
784 /* return the character */
785 if (ch == EOF && eof_error_p) xlfail("end of file on read");
786 return (ch == EOF ? eof : cvchar(ch));
789 /* xwrchar - write a character to a file */
790 LVAL xwrchar(V)
792 LVAL fptr,chr;
794 /* get the character and file pointer */
795 chr = xlgachar();
796 fptr = (moreargs() ? xlgetfile(TRUE) : getvalue(s_stdout));
797 xllastarg();
799 /* put character to the file */
800 xlputc(fptr,getchcode(chr));
802 /* return the character */
803 return (chr);
806 /* xwrbyte - write a byte to a file */
807 #ifdef BIGNUMS
808 /* we will continue XLISP's tradition of not checking for value
809 to write being out of range. At any rate, this will save time. */
810 LVAL xwrbyte()
812 LVAL fptr,chr,chr2;
813 BIGNUMDATA *vx;
814 unsigned char *v;
815 int size, i, ofs;
817 /* get the byte and file pointer */
818 chr = xlgetarg();
819 if (!(fixp(chr) || bignump(chr))) xlbadtype(chr);
820 fptr = xlgastream();
821 if ((fptr->n_sflags & S_BINARY) == 0)
822 xlfail("not a binary file");
823 xllastarg();
825 /* can't really do an unsigned write of a negative number */
826 if ((fptr->n_sflags&S_UNSIGNED) &&
827 ((fixp(chr)&&getfixnum(chr)<0) || (bignump(chr)&&getbignumsign(chr))))
828 xlerror("Can't do unsigned write-byte of", chr);
831 if (fptr->n_bsiz == 1 && fixp(chr)) { /* handle easy case */
832 /* put byte to the file */
833 xlputc(fptr,(int)getfixnum(chr));
834 return (chr);
836 /* work only with bignums from now on */
837 if (fixp(chr)) chr2 = cvtfixbignum(getfixnum(chr));
838 else chr2 = chr;
839 vx = getbignumarray(chr2);
840 size = getbignumsize(chr2) * sizeof(BIGNUMDATA); /* number size in bytes */
841 ofs = fptr->n_bsiz - size; /* number of excess bytes to write */
842 if (*vx++) { /* negative value */
843 #ifdef BIGENDIANFILE
844 int j;
845 v = (unsigned char *)vx;
846 for (i = ofs; i > 0; i--) xlputc(fptr, 0xff); /* filler */
847 for (i = size-1; i >= -ofs && i >= 0; i--) { /* find end of carries */
848 if (v[bs(i)] != 0) { /* only zeroes will generate carries */
849 for (j = (ofs >= 0 ? 0 : -ofs); j < i; j++) {
850 xlputc(fptr, (unsigned char) (~v[bs(j)]));
852 break;
855 for (; i < size; i++) xlputc(fptr, 1 + (unsigned char)(~v[bs(i)]));
856 #else
857 unsigned sum;
858 int carry=1;
859 v = (unsigned char *)vx;
860 for (i = size-1; i >= -ofs && i >= 0; i--) {
861 sum = (unsigned)(unsigned char)~v[bs(i)] + carry;
862 carry = sum >> 8;
863 xlputc(fptr, (unsigned char) sum);
865 for (i = ofs; i > 0; i--) xlputc(fptr, 0xff); /* filler */
866 #endif
868 else { /* postive value */
869 v = (unsigned char *)vx;
870 #ifdef BIGENDIANFILE
871 for (i = ofs; i > 0; i--) xlputc(fptr, 0); /* filler */
872 for (i = (ofs >= 0 ? 0 : -ofs); i < size; i++) xlputc(fptr, v[bs(i)]);
873 #else
874 for (i = size-1; i >= -ofs && i >= 0; i--) xlputc(fptr, v[bs(i)]);
875 for (i = ofs; i > 0; i--) xlputc(fptr, 0); /* filler */
876 #endif
879 /* return the byte */
880 return (chr);
882 #else
883 LVAL xwrbyte(V)
885 LVAL fptr,chr;
887 /* get the byte and file pointer */
888 chr = xlgafixnum();
889 fptr = (moreargs() ? xlgetfile(TRUE) : getvalue(s_stdout));
890 xllastarg();
892 /* put byte to the file */
893 xlputc(fptr,(int)getfixnum(chr));
895 /* return the character */
896 return (chr);
898 #endif
900 /* xreadline - read a line from a file */
901 /* eof, eof-error-p added - L. Tierney */
902 LVAL xreadline(V)
904 char *p, *sptr;
905 LVAL fptr,str,newstr,eof;
906 int len,blen,ch,eof_error_p;
908 /* protect some pointers */
909 xlsave1(str);
911 /* get file pointer */
912 fptr = (moreargs() ? xlgetfile(FALSE) : getvalue(s_stdin));
913 eof_error_p = moreargs() ? ((xlgetarg() != NIL) ? TRUE : FALSE) : TRUE;
914 eof = (moreargs() ? xlgetarg() : NIL);
915 if (moreargs()) xlgetarg(); /* remove and ignore recursive-p argument */
916 xllastarg();
918 /* get character and check for eof */
919 len = blen = 0; p = buf;
920 while ((ch = xlgetc(fptr)) != EOF && ch != '\n') {
922 /* check for buffer overflow TAA MOD to use MEMCPY instead of strcat*/
923 if (blen >= STRMAX) {
924 newstr = newstring(len + STRMAX);
925 sptr = getstring(newstr);
926 if (str != NIL) MEMCPY(sptr, getstring(str), len);
927 MEMCPY(sptr+len, buf, blen);
928 p = buf; blen = 0;
929 len += STRMAX;
930 str = newstr;
933 /* store the character */
934 *p++ = ch; ++blen;
937 /* check for end of file */
938 if (len == 0 && p == buf && ch == EOF) {
939 xlpop();
940 if (eof_error_p) xlfail("end of file on read");
941 return (eof);
944 /* append the last substring */
945 /* conditional removed because code always executes! */
946 newstr = newstring(len + blen);
947 sptr = getstring(newstr);
948 if (str != NIL) MEMCPY(sptr, getstring(str), len);
949 MEMCPY(sptr+len, buf, blen);
950 sptr[len+blen] = '\0';
951 str = newstr;
953 /* restore the stack */
954 xlpop();
956 /* return the string */
957 return (str);
960 /* xunrdchar - unread a character from a file */
961 LVAL xunrdchar(V)
963 LVAL fptr,chr;
965 /* get the character and file pointer */
966 chr = xlgachar();
967 fptr = (moreargs() ? xlgetfile(TRUE) : getvalue(s_stdout));
968 xllastarg();
970 /* unread character from the file */
971 xlungetc(fptr,getchcode(chr));
973 /* return the character */
974 return (NIL);
977 /* xmkstrinput - make a string input stream */
978 /* TAA MOD - reworked for unsigned lengths */
979 LVAL xmkstrinput(V)
981 unsigned start,end=0,len,i;
982 FIXTYPE temp;
983 char *str;
984 LVAL string,val;
986 /* protect the return value */
987 xlsave1(val);
989 /* get the string and length */
990 string = xlgastring();
991 str = getstring(string);
992 len = getslength(string);
994 /* get the starting offset */
995 if (moreargs()) {
996 val = xlgafixnum();
997 temp = getfixnum(val);
998 if (temp < 0 || temp > len)
999 xlerror("string index out of bounds",val);
1000 start = (unsigned) temp;
1002 else start = 0;
1004 /* get the ending offset */
1005 if (moreargs()) { /* TAA mod to allow NIL for end offset */
1006 val = nextarg();
1007 if (null(val)) end = len;
1008 else if (fixp(val)) {
1009 temp = getfixnum(val);
1010 if (temp < start || temp > len)
1011 xlerror("string index out of bounds",val);
1012 end = (unsigned) temp;
1014 else xlbadtype(val);
1016 xllastarg();
1018 else end = len;
1020 /* make the stream */
1021 val = newustream();
1023 /* copy the substring into the stream */
1024 for (i = start; i < end; ++i)
1025 xlputc(val,str[i]);
1027 /* restore the stack */
1028 xlpop();
1030 /* return the new stream */
1031 return (val);
1034 /* xmkstroutput - make a string output stream */
1035 LVAL xmkstroutput(V)
1037 return (newustream());
1040 /* xgetstroutput - get output stream string */
1041 LVAL xgetstroutput(V)
1043 LVAL stream;
1044 stream = xlgaustream();
1045 xllastarg();
1046 return (getstroutput(stream));
1049 /* xgetlstoutput - get output stream list */
1050 LVAL xgetlstoutput(V)
1052 LVAL stream,val;
1054 /* get the stream */
1055 stream = xlgaustream();
1056 xllastarg();
1058 /* get the output character list */
1059 val = gethead(stream);
1061 /* empty the character list */
1062 sethead(stream,NIL);
1063 settail(stream,NIL);
1065 /* return the list */
1066 return (val);
1069 #define FMTMAX 256
1070 LOCAL VOID toomanyopt P1C(LVAL, fmt)
1072 xlerror("too many prefix parameters in format",fmt);
1075 /* decode prefix parameters and modifiers for a format directive */
1076 /* TAA MOD Entirely rewritten -- return value -1 for unassigned since
1077 negative numbers are inappropriate for all arguments we are concerned
1078 with. Also clips args to reasonable values, allows both : and @ modifiers
1079 at once. */
1080 LOCAL char *decode_pp P7C(char *, fmt,
1081 FIXTYPE *, pp, /* prefix parameters */
1082 int, maxnpp, /* maximum number of them */
1083 int *, npp, /* actual number of them */
1084 int *, colon, /* colon modifier given? */
1085 int *, atsign, /* atsign modifier given? */
1086 LVAL, lfmt) /* format string for failure */
1088 int i;
1089 int gotone = 0;
1090 FIXTYPE accum;
1092 for (i = 0; i < maxnpp; i++) pp[i] = -1; /* initially all undefined */
1093 *npp = 0;
1094 *colon = 0;
1095 *atsign = 0;
1096 do {
1097 if (*fmt == '\'') { /* character code */
1098 pp[*npp] = *(++fmt);
1099 gotone = 1;
1100 fmt++;
1102 else if (*fmt == '#') { /* xlargc is value */
1103 accum = xlargc;
1104 if (accum>FMTMAX) accum = FMTMAX;
1105 pp[*npp] = accum;
1106 gotone = 1;
1107 fmt++;
1109 else if (*fmt == 'v' || *fmt == 'V') { /* lisp arg is value */
1110 LVAL arg = xlgetarg();
1111 if (fixp(arg)) {
1112 accum = getfixnum(arg);
1113 if (accum < 0) accum = 0; /* clip at reasonable values */
1114 else if (accum>FMTMAX) accum = FMTMAX;
1116 else if (charp(arg))
1117 accum = getchcode(arg);
1118 else {
1119 if (! null(arg))
1120 xlbadtype(arg);
1121 accum = -1;
1123 pp[*npp] = accum;
1124 gotone = 1;
1125 fmt++;
1127 else if (isdigit(*fmt)) { /* integer literal */
1128 accum = 0;
1129 do {
1130 accum = accum*10 + (int)(*fmt++ - '0');
1131 if (accum > FMTMAX)
1132 accum = FMTMAX; /* Clip at reasonable value */
1133 } while (isdigit(*fmt));
1134 gotone = 1;
1135 pp[*npp] = accum;
1137 else if (*fmt == '#') { /* use number of remaining arguments */
1138 pp[*npp] = xlargc;
1139 gotone = 1;
1140 fmt++;
1142 else if (*fmt == ',') { /* empty field */
1143 gotone = 1;
1145 else break; /* nothing to process */
1147 if (*fmt != ',') break; /* no comma -- done */
1148 *npp += 1; /* got an argument */
1149 fmt++; /* toss comma */
1150 if( *npp >= maxnpp ) toomanyopt(lfmt);
1151 } while (TRUE);
1152 *npp += gotone;
1154 do { /* pick up any colon or atsign modifier */
1155 if (*fmt == ':') *colon = 1;
1156 else if (*fmt == '@') *atsign = 1;
1157 else break;
1158 fmt++;
1159 } while (TRUE);
1160 return fmt;
1163 #define mincol pp[0]
1164 #define colinc pp[1]
1165 #define minpad pp[2]
1166 #define padchar pp[3]
1168 /* opt_print - print a value using prefix parameter options */
1169 LOCAL VOID opt_print P6C(LVAL, stream,
1170 LVAL, val,
1171 int, pflag, /* quoting or not */
1172 FIXTYPE *, pp, /* prefix parameters */
1173 int, colon, /* colon modifier given? */
1174 int, atsign) /* at-sign modifier given? */
1176 int flatsiz = 0;
1177 int i;
1179 if (mincol < 0) mincol = 0; /* handle default values */
1180 if (colinc < 1) colinc = 1; /* also arg of 0 for colinc */
1181 if (minpad < 0) minpad = 0;
1182 if (padchar < 0) padchar = ' ';
1184 if( mincol < minpad )
1185 mincol = minpad;
1187 if (mincol > 0) {
1188 /* we will need to pad, so must calculate flat size */
1189 if (colon && null(val)) /* flat size is 2 */
1190 flatsiz = 2;
1191 else
1192 flatsiz = (int)flatsize(val, pflag);
1193 if (atsign) { /* padding may be required on left */
1194 for( i = 0; i < minpad; flatsiz++, i++ )
1195 xlputc(stream,(int)padchar);
1196 while( flatsiz < mincol ) {
1197 for( i = 0; i < colinc; i++ )
1198 xlputc(stream,(int)padchar);
1199 flatsiz += (int)colinc;
1204 /* print the value */
1205 if( colon && null(val))
1206 xlputstr(stream,"()");
1207 else
1208 xlprint(stream,val,pflag);
1210 if( mincol > 0 && !atsign ) { /* padding required on right */
1211 for( i = 0; i < minpad; flatsiz++, i++ )
1212 xlputc(stream,(int)padchar);
1213 while( flatsiz < mincol ) {
1214 for( i = 0; i < colinc; i++ )
1215 xlputc(stream,(int)padchar);
1216 flatsiz += (int)colinc;
1220 #undef round
1221 #define round pp[1]
1222 LOCAL VOID integer_print P5C(LVAL, stream,
1223 LVAL, val,
1224 int, pflag, /* Style */
1225 FIXTYPE *, pp, /* prefix parameters */
1226 int, atsign) /* at-sign modifier given? */
1228 #ifdef BIGNUMS
1229 char *bufr;
1230 FIXTYPE radix = 10;
1231 #endif
1232 int fillchar, i;
1234 #ifdef BIGNUMS
1235 if (pflag == 'R') {
1236 radix = pp[0];
1237 if (radix < 2 || radix > 36)
1238 xlerror("bad radix specified", cvfixnum(radix));
1239 pp++;
1241 #endif
1243 fillchar = (int)pp[1];
1245 if (fillchar < 0) fillchar = ' ';
1247 #ifdef BIGNUMS
1248 /* can't print in binary or arbitrary radix with printf */
1249 if (fixp(val) && (pflag == 'B' || pflag == 'R')) {
1250 if (getfixnum(val) == 0) pflag = 'D'; /* print zero in "decimal" */
1251 else val = cvtfixbignum(getfixnum(val));
1253 #endif
1255 if (fixp(val)) { /* D O or X and fixnum */
1256 FIXTYPE v = getfixnum(val); /* TAA mod 3/95 to handle @X and @O
1257 and negative values with X and O */
1258 switch (pflag) {
1259 case 'D':
1260 sprintf(buf, (atsign?"%+ld":"%ld"), v);
1261 break;
1262 case 'O':
1263 if (v < 0) sprintf(buf, "-%lo", -v);
1264 else sprintf(buf, (atsign ? "+%lo" : "%lo"), v);
1265 break;
1266 case 'X':
1267 if (v < 0) sprintf(buf, "-%lx", -v);
1268 else sprintf(buf, (atsign ? "+%lx" : "%lx"), v);
1269 break;
1271 if (mincol > 0) { /* need to fill */
1272 for (i = (int)mincol-strlen(buf); i-- > 0;)
1273 xlputc(stream,fillchar);
1275 xlputstr(stream,buf);
1276 return;
1278 #ifdef BIGNUMS
1279 else if (bignump(val)) { /* D O or X and bignum */
1280 switch (pflag) {
1281 case 'D': radix = 10; break;
1282 case 'X': radix = 16; break;
1283 case 'B': radix = 2; break;
1284 case 'O': radix = 8; break;
1286 bufr = cvtbignumstr(val, (int)radix);
1287 if (atsign && getbignumsign(val)) atsign = 0; /* add leading "+"? */
1288 if (mincol > 0) { /* need to fill */
1289 for (i = (int)mincol - atsign - STRLEN(bufr); i-- > 0;)
1290 xlputc(stream,fillchar);
1292 if (atsign) xlputc(stream, '+');
1293 xlputstr(stream,bufr);
1294 MFREE(bufr);
1295 return;
1297 #endif
1298 else { /* not a number */
1299 padchar = colinc = minpad = -1; /* zap arg if provided */
1300 opt_print(stream,val,FALSE,pp,0,0);
1301 return;
1306 Floating point formatting has been modified (L. Tierney) to correspond
1307 more closely to CL. All prefix parameters for ~F, ~E and ~G except the
1308 scale parameter k are supported. (I don't have much use for k, and it
1309 would be hard to add since it can be negative.) The routines operate
1310 by letting sprintf do the formatting work and parsing the results it
1311 produces. IEEE NaN's and Infinities are recognized by checking for an
1312 alpha character as the first character in the printed representation
1313 of fabs(num). Also, I have tried to write all inequalities in such a
1314 way as to insure that Infinities and NaN's should only be handled in
1315 the ~E directive. Most reasonable cases seem to be handled correctly,
1316 but I may have gotten some of the extreme cases wrong (w = 1, d = 0 and
1317 that sort of stuff). There seems to be some disagreement among CL
1318 implementations on these.
1321 /* trim trailing zeros in fractional part; return length of fraction left */
1322 LOCAL int trimzeros P2C(char *, s, int, skip_one)
1324 int d, dd, i;
1326 /* locate the decimal point */
1327 for (i = 0, d = -1; s[i] != 0; i++) {
1328 if (s[i] == '.') {
1329 d = i;
1330 break;
1334 /* drop trailing zeros if decimal point is found */
1335 if (d != -1) {
1336 dd = (skip_one) ? d + 1 : d;
1337 for (i = d + 1; s[i] != 0 && isdigit(s[i]); i++);
1338 s[i] = 0;
1339 for (i--; i > dd && s[i] == '0'; i--)
1340 s[i] = 0;
1341 return(strlen(s+d+1));
1343 else return(0);
1346 LOCAL int allzeros P1C(char *, s)
1348 for (; *s != 0; s++)
1349 if (*s != '.' && *s != '0')
1350 return(FALSE);
1351 return(TRUE);
1354 LOCAL VOID write_double_ffmt P4C(char *, s, double, y, int, e, int, d)
1356 char cmd[50];
1357 int f, i, m;
1359 f = e + d;
1360 if (f > 16) f = 16;
1361 sprintf(cmd, "%%.%de", f > 0 ? f : 0);
1362 sprintf(s, cmd, y);
1364 /* re-read exponent in case changed by rounding */
1365 if (read_exponent(strchr(s, 'e') + 1) > e) {
1366 s[f > 0 ? f + 2 : 2] = '0'; /* extend fractional part with a zero */
1367 f++; /* increment length of fractional part */
1368 e++; /* increment exponent */
1371 s[1] = '.'; /* make sure decimal point is there */
1372 s[f > 0 ? f + 2 : 2] = 0; /* terminate the string */
1374 if (e >= 0) {
1375 m = e > f ? f : e; /* shift the decimal point */
1376 MEMMOVE(s + 1, s + 2, m);
1377 f -= m;
1378 s[e + 1] = '.';
1379 for (i = m + 1; i < e + 1; i++) /* insert zeros */
1380 s[i] = '0';
1381 if (f < d) { /* add trailing zeros if needed */
1382 for (i = e + f + 2; i < e + d + 2; i++)
1383 s[i] = '0';
1384 s[e + d + 2] = 0;
1387 else if (f >= 0) {
1388 MEMMOVE(s + 2 - e, s + 2, f); /* shift fractional part */
1389 s[1 - e] = s[0]; /* move leading digit */
1390 s[0] = '0'; /* set leading digit to zero */
1391 for (i = 2; i < 1 - e; i++) /* insert zeros if needed */
1392 s[i] = '0';
1393 s[2 + f - e] = 0; /* terminate string */
1395 else {
1396 int ld = s[0]; /* round up leading digit if needed */
1397 s[0] = (d == 0 && e == -1 && ld >= '5') ? '1' : '0';
1398 s[1] = '.';
1399 for (i = 2; i < d + 1; i++) /* insert zeros if needed */
1400 s[i] = '0';
1401 if (d > 0) /* round up final digit if needed */
1402 s[d + 1] = (f == -1 && ld >= '5') ? '1' : '0';
1403 s[d + 2] = 0; /* terminate string */
1407 VOID write_double_efmt P3C(char *, s, double, y, int, d)
1409 char cmd[50];
1410 int Finite;
1412 /* tries to use d - 1 digits if the result reads in as equal to y */
1413 sprintf(cmd, "%%.%de", d > 0 ? d - 1 : 0);
1414 sprintf(s, cmd, y);
1415 #ifdef IEEEFP
1416 if (! is_finite(y))
1417 sprintf(s, is_nan(y) ? "NaN" : "Inf");
1418 #endif /* IEEEFP */
1419 Finite = isdigit(s[0]);
1420 if (d > 0 && Finite) {
1421 double n;
1422 sscanf(s, "%lf", &n);
1423 if (n != y) {
1424 sprintf(cmd, "%%.%de", d);
1425 sprintf(s, cmd, y);
1430 LOCAL VOID flonum_fprint P4C(LVAL, stream,
1431 LVAL, val,
1432 FIXTYPE *, pp, /* prefix parameters */
1433 int, atsign) /* at-sign modifier given? */
1435 if (! realp(val)) { /* not a real number */
1436 padchar = colinc = minpad = -1; /* zap arg if provided */
1437 opt_print(stream,val,FALSE,pp,0,0);
1438 return;
1440 else {
1441 FLOTYPE num = makefloat(val);
1442 FLOTYPE y = fabs(num);
1443 int needsign = (atsign || num < 0.0) ? TRUE : FALSE;
1444 int w = pp[0], d = pp[1];
1445 int overflowchar = pp[3], fillchar = pp[4];
1446 int i, rw, intsize, fracsize, exponent, len, haved;
1447 char *p;
1449 #ifdef __SASC__
1450 /* IBM 370 floating pt format; the largest number isn't */
1451 /* quite so large... - Dave Rivers (rivers@ponds.uucp) */
1452 if (y == 0.0 || (y > 1e-100 && y < 1e75))
1453 #else
1454 if (y == 0.0 || (y > 1e-100 && y < 1e100))
1455 #endif
1457 /* don't generate extra big number */
1458 /* test should be false for Infinity, NaN */
1460 /* control width and decimals */
1461 if (w > 100) w = 100;
1462 if (d > 100) d = 100;
1464 /* compute the sizes of the integer and fractional parts */
1465 if (y == 0.0) {
1466 exponent = 0;
1467 intsize = 0;
1468 fracsize = 1;
1470 else {
1471 write_double_efmt(buf, y, 16);
1472 p = strchr(buf, 'e');
1473 if (p != NULL) {
1474 exponent = read_exponent(p + 1);
1475 intsize = (exponent >= 0) ? 1 + exponent : 0;
1476 i = trimzeros(buf, FALSE);
1477 fracsize = (exponent < i) ? i - exponent : 1;
1479 else { /* should not happen */
1480 exponent = intsize = fracsize = 0;
1483 if (d == 0 && intsize == 0) intsize = 1;
1485 /* if d is given, check for overflow; otherwise, compute d */
1486 if (d >= 0) {
1487 haved = TRUE;
1488 if (w >= 0) {
1489 rw = (needsign) ? d + 2 : d + 1;
1490 rw += intsize;
1491 if (rw > w) {
1492 if (overflowchar >= 0) goto overflow;
1493 else w = rw;
1497 else {
1498 haved = FALSE;
1499 if (w >= 0) {
1500 d = w - 1 - intsize;
1501 if (needsign) d--;
1502 if ((intsize == 0 && d < 1) || d < 0) {
1503 if (overflowchar >= 0) goto overflow;
1504 else {
1505 d = (intsize == 0) ? 1 : 0;
1506 w = (needsign) ? intsize + 2 + d : intsize + 1 + d;
1510 else {
1511 d = fracsize;
1513 write_double_ffmt(buf, y, exponent, d);
1514 if (y == 0.0 || ! allzeros(buf))
1515 d = trimzeros(buf, TRUE);
1518 /* write number using computed d */
1519 write_double_ffmt(buf, y, exponent, d);
1521 /* fiddle it if no leading zero or rounded to be too long */
1522 p = buf;
1523 len = strlen(buf);
1524 if (w >= 0 && len > ((needsign) ? w - 1 : w)) {
1525 if (p[0] == '0')
1526 p++;
1527 else if (p[len - 1] == '0' && ! haved)
1528 p[len - 1] = 0;
1529 else if (overflowchar >= 0)
1530 goto overflow;
1533 /* if w is supplied, output pad characters */
1534 if (w >= 0) {
1535 if (fillchar < 0) fillchar = ' ';
1536 i = w - strlen(p);
1537 if (needsign) i--;
1538 while (i-- > 0)
1539 xlputc(stream,fillchar);
1542 /* print the sign if needed */
1543 if (num < 0) xlputc(stream, '-');
1544 else if (atsign) xlputc(stream, '+');
1546 /* output number */
1547 xlputstr(stream,p);
1548 return;
1550 else if (w >= 0 && overflowchar >= 0) goto overflow;
1551 else {
1552 /* do E format */
1553 for (i = 0; i < 7; i++) pp[i] = -1; /* zap any arguments */
1554 flonum_eprint(stream, val, pp, atsign);
1555 return;
1558 /* handle overflows */
1559 overflow:
1560 for (i = 0; i < w; i++) xlputc(stream, overflowchar);
1564 LOCAL VOID flonum_eprint P4C(LVAL, stream,
1565 LVAL, val,
1566 FIXTYPE *, pp, /* prefix parameters */
1567 int, atsign) /* at-sign modifier given? */
1569 if (! realp(val)) { /* not a real number */
1570 padchar = colinc = minpad = -1; /* zap arg if provided */
1571 opt_print(stream,val,FALSE,pp,0,0);
1573 else {
1574 FLOTYPE num = makefloat(val);
1575 FLOTYPE y = fabs(num);
1576 int needsign = (atsign || num < 0.0) ? TRUE : FALSE;
1577 int w = pp[0], d = pp[1], dd = pp[1], e = pp[2], ee = pp[2];
1578 int overflowchar = pp[4], fillchar = pp[5], expchar = pp[6];
1579 int i, rw, fracsize, expsize, exponent, finite;
1580 char cmd[50], *p;
1582 /* control width and decimals */
1583 if (w > 100) w = 100;
1584 if (d > 100) d = 100;
1586 /* compute the sizes of the parts */
1587 if (y == 0.0) {
1588 finite = TRUE;
1589 fracsize = 1;
1590 expsize = 1;
1591 exponent = 0;
1593 else {
1594 write_double_efmt(buf, y, d >= 0 ? d : 16);
1595 finite = isdigit(buf[0]);
1596 for (p = buf; *p == '.' || isdigit(*p); p++);
1597 if (finite && isalpha(*p)) {
1598 exponent = read_exponent(p + 1);
1599 fracsize = trimzeros(buf, TRUE);
1600 sprintf(buf, "%+d", exponent);
1601 expsize = strlen(buf) - 1;
1603 else {
1604 fracsize = strlen(buf);
1605 e = exponent = expsize = 0;
1609 /* set the fill character */
1610 if (fillchar < 0) fillchar = ' ';
1612 /* handle non-finite numbers */
1613 if (! finite) {
1614 if (w >= 0) {
1615 i = strlen(buf);
1616 if (needsign) i++;
1617 if (w < i && overflowchar >= 0) goto overflow;
1618 else
1619 for (i = w - i; i-- > 0;)
1620 xlputc(stream, fillchar);
1622 if (num < 0.0) xlputc(stream, '-');
1623 else if (atsign) xlputc(stream, (num > 0.0) ? '+' : fillchar);
1624 xlputstr(stream,buf);
1625 return;
1628 /* check for exponent overflow and adjust e */
1629 if (e >= 0) {
1630 if (expsize > e) {
1631 if (w >= 0 && overflowchar >= 0) goto overflow;
1632 else e = expsize;
1635 else e = expsize;
1637 /* if d is given, check for overflow; otherwise, compute d */
1638 if (d >= 0) {
1639 if (w >= 0) {
1640 rw = (needsign) ? d + e + 5 : d + e + 4;
1641 if (rw > w) {
1642 if (overflowchar >= 0) goto overflow;
1643 else w = rw;
1647 else {
1648 if (w >= 0) {
1649 d = w - e - 4;
1650 if (needsign) d--;
1651 if (d < 0) {
1652 if (overflowchar >= 0) goto overflow;
1653 else {
1654 d = 0;
1655 w = (needsign) ? e + 5 : e + 4;
1659 else {
1660 d = fracsize;
1662 sprintf(cmd, "%%.%de", d);
1663 sprintf(buf, cmd, y);
1665 /* adjust and recheck the exponent */
1666 for (p = buf; *p == '.' || isdigit(*p); p++);
1667 if (isalpha(*p)) {
1668 exponent = read_exponent(p + 1);
1669 if (dd < 0) d = trimzeros(buf, TRUE);
1670 sprintf(buf, "%+d", exponent);
1671 expsize = strlen(buf) - 1;
1672 if (expsize > e) {
1673 if (ee >= 0 && expsize > ee && w >= 0 && overflowchar >= 0)
1674 goto overflow;
1675 else e = expsize;
1680 /* write number using computed and modified d */
1681 sprintf(cmd, "%%.%de", d);
1682 sprintf(buf, cmd, y);
1684 /* remove the exponent and fiddle it if nothing after the decimal */
1685 if (d == 0) {
1686 buf[1] = '.';
1687 buf[2] = 0;
1689 else buf[2 + d] = 0;
1691 /* if w is supplied, output pad characters */
1692 if (w >= 0) {
1693 i = w - e - 2 - strlen(buf);
1694 if (needsign) i--;
1695 while (i-- > 0)
1696 xlputc(stream,fillchar);
1699 /* print the sign if needed */
1700 if (num < 0) xlputc(stream, '-');
1701 else if (atsign) xlputc(stream, '+');
1703 /* output number */
1704 xlputstr(stream,buf);
1706 /* print the exponent */
1707 xlputc(stream, (expchar >= 0) ? expchar : 'E');
1708 xlputc(stream, (exponent >= 0) ? '+' : '-');
1709 for (i = e - expsize; i > 0; i--) xlputc(stream, '0');
1710 sprintf(buf, "%d", (exponent >= 0) ? exponent : -exponent);
1711 xlputstr(stream, buf);
1712 return;
1714 /* handle overflows */
1715 overflow:
1716 for (i = 0; i < w; i++) xlputc(stream, overflowchar);
1720 LOCAL VOID flonum_gprint P4C(LVAL, stream,
1721 LVAL, val,
1722 FIXTYPE *, pp, /* prefix parameters */
1723 int, atsign) /* at-sign modifier given? */
1725 int fillchar;
1727 fillchar = (int)pp[2];
1729 if (fillchar < 0) fillchar = ' ';
1731 if (! realp(val)) { /* not a real number */
1732 padchar = colinc = minpad = -1; /* zap arg if provided */
1733 opt_print(stream,val,FALSE,pp,0,0);
1735 else {
1736 FLOTYPE num = makefloat(val);
1737 FLOTYPE y = fabs(num);
1738 int w = pp[0], d = pp[1], e = pp[2];
1739 int overflowchar = pp[4], fillchar = pp[5];
1740 int i, fracsize, expsize, exponent, finite;
1741 int ww, dd, ee, q, n;
1742 char *p;
1744 /* control width and decimals */
1745 if (w > 100) w = 100;
1746 if (d > 100) d = 100;
1748 /* compute the sizes of the parts */
1749 if (y == 0.0) {
1750 finite = TRUE;
1751 fracsize = 0;
1752 expsize = 1;
1753 exponent = 0;
1755 else {
1756 write_double_efmt(buf, y, 16);
1757 finite = isdigit(buf[0]);
1758 for (p = buf; *p == '.' || isdigit(*p); p++);
1759 if (finite && isalpha(*p)) {
1760 exponent = read_exponent(p + 1);
1761 fracsize = trimzeros(buf, FALSE);
1762 sprintf(buf, "%+d", exponent);
1763 expsize = strlen(buf) - 1;
1765 else {
1766 fracsize = strlen(buf);
1767 exponent = expsize = 0;
1771 /* compute n such that 10^(n-1) <= y < 10^n, with n = 0 for y = 0 */
1772 if (y == 0.0) n = 0;
1773 else n = exponent + 1;
1775 /* compute ee */
1776 ee = (e >= 0) ? e + 2 : 4;
1778 /* compute ww */
1779 ww = (w >= 0) ? w - ee : -1;
1781 /* compute d, if not supplied, and dd */
1782 if (d < 0) {
1783 q = 1 + fracsize;
1784 i = (n > 7) ? 7 : n;
1785 d = (q > i) ? q : i;
1787 dd = d - n;
1789 /* print the number using F or E format */
1790 if (finite && 0 <= dd && dd <= d) {
1791 /* use F format */
1792 pp[0] = ww;
1793 pp[1] = (dd == 0 || pp[1] >= 0) ? dd : -1; /* to get zeros trimmed */
1794 /*pp[1] = dd;*/
1795 pp[2] = -1;
1796 pp[3] = overflowchar;
1797 pp[4] = fillchar;
1798 flonum_fprint(stream, val, pp, atsign);
1799 if (w >= 0)
1800 for (i = 0; i < ee; i++)
1801 xlputc(stream, ' ');
1803 else {
1804 /* use E format */
1805 pp[1] = (pp[1] >= 0) ? d : -1; /* to get zeros trimmed */
1806 /*pp[1] = d;*/
1807 flonum_eprint(stream, val, pp, atsign);
1812 #undef colinc
1813 /* tabulate */
1814 LOCAL VOID tab_print P3C(LVAL, stream, FIXTYPE *, pp, int, atsign)
1816 int pos = xlgetcolumn(stream); /* where are we now??? */
1817 int count; /* number of spaces to insert */
1818 int column = (int)pp[0]; /* desired column */
1819 int colinc = (int)pp[1]; /* desired column increment */
1821 if (column < 0) column = 1; /* handle defaults */
1822 if (colinc < 0) colinc = 1;
1824 if (atsign) { /* relative */
1825 if (colinc == 0) colinc = 1;
1826 count = column + (colinc - (pos + column) % colinc) % colinc;
1828 else { /* absolute */
1829 if (pos >= column) {
1830 if (colinc > 0) {
1831 int k = (pos+ (colinc-1) - column)/colinc;
1832 count = column-pos + k*colinc;
1833 if (count==0) count = colinc;
1835 else count = 0;
1837 else count = column - pos;
1839 while (count-- > 0)
1840 xlputc(stream, ' ');
1843 LOCAL VOID indirect_print P2C(LVAL, stream, int, atsign)
1845 LVAL *oldargv, lfmt, args;
1846 int oldargc;
1848 lfmt = xlgastring();
1850 if (atsign) xlformat(lfmt, stream);
1851 else {
1852 args = xlgalist();
1853 oldargv = xlargv;
1854 oldargc = xlargc;
1855 xlargv = xlsp;
1856 for (xlargc = 0; consp(args); args = cdr(args), xlargc++)
1857 pusharg(car(args));
1858 xlformat(lfmt, stream);
1859 xlargv = oldargv;
1860 xlargc = oldargc;
1864 /* adapted from changecase in xlstr.c */
1865 LOCAL VOID case_convert_print P4C(char *, fmt, LVAL, stream, int, colon, int, atsign)
1867 LVAL tmp;
1868 LVAL lfmt;
1869 int ch, fcn;
1870 int lastspace = TRUE;
1872 xlstkcheck(2);
1873 xlsave(lfmt);
1874 xlsave(tmp);
1876 lfmt = cvstring(fmt);
1877 tmp = newustream();
1879 xlformat(lfmt, tmp);
1881 if (colon && atsign) fcn = 'U';
1882 else if (colon) fcn = 'C';
1883 else if (atsign) fcn = 'S';
1884 else fcn = 'D';
1886 while ((ch = xlgetc(tmp)) != EOF) {
1887 switch (fcn) {
1888 case 'U': if (ISLOWER(ch)) ch = TOUPPER(ch); break;
1889 case 'D': if (ISUPPER(ch)) ch = TOLOWER(ch); break;
1890 case 'C': if (lastspace && ISLOWER(ch)) ch = TOUPPER(ch);
1891 if (!lastspace && ISUPPER(ch)) ch = TOLOWER(ch);
1892 lastspace = !ISLOWERA(ch) && !ISUPPER(ch);
1893 break;
1894 case 'S': if (lastspace && ISLOWER(ch)) ch = TOUPPER(ch);
1895 if (!lastspace && ISUPPER(ch)) ch = TOLOWER(ch);
1896 if (ISUPPER(ch)) lastspace = FALSE;
1897 break;
1899 xlputc(stream, ch);
1902 xlpopn(2);
1905 LOCAL VOID conditional_print P5C(char *, fmt, LVAL, stream,
1906 FIXTYPE, count, int, colon, int, atsign)
1908 LVAL lfmt;
1909 char *oldfmt;
1911 xlsave1(lfmt);
1913 lfmt = cvstring(fmt);
1915 if (atsign) {
1916 if (! null(xlgetarg())) {
1917 xlargv--;
1918 xlargc++;
1919 xlformat(lfmt, stream);
1922 else if (colon) {
1923 if (! null(xlgetarg())) {
1924 fmt = skip_past_directive(fmt, ';', FALSE);
1925 if (fmt == NULL) xlerror("missing 'true' clause", lfmt);
1926 lfmt = cvstring(fmt);
1928 xlformat(lfmt, stream);
1930 else {
1931 if (count < 0) count = getfixnum(xlgafixnum());
1932 oldfmt = fmt;
1933 while (count-- > 0) {
1934 fmt = skip_past_directive(fmt, ';', FALSE);
1935 if (fmt == NULL) break;
1937 if (fmt == NULL)
1938 fmt = skip_past_directive(oldfmt, ';', TRUE);
1939 if (fmt != NULL) {
1940 lfmt = cvstring(fmt);
1941 xlformat(lfmt, stream);
1945 xlpop();
1948 #define MAXNPP 7
1950 /* this does not support the termination directive ~^ */
1951 LOCAL VOID iterative_print P5C(char *, fmt,
1952 LVAL, stream,
1953 FIXTYPE, count,
1954 int, colon,
1955 int, atsign)
1957 LVAL lfmt, args = NIL, alist;
1958 LVAL *oldargv = NULL, *oldsp = NULL;
1959 int oldargc = 0, once;
1960 int npp; /* number of prefix parameters */
1961 FIXTYPE pp[MAXNPP]; /* list of prefix parameters */
1962 int tcolon, tatsign;
1964 xlsave1(lfmt);
1966 lfmt = cvstring(fmt);
1967 once = (skip_past_directive(fmt, '}', TRUE) == NULL) ? FALSE : TRUE;
1968 if (*fmt == '~' &&
1969 *decode_pp(fmt + 1, pp, MAXNPP, &npp, &tcolon, &tatsign, lfmt) == '}')
1970 lfmt = xlgastring();
1971 if (! atsign) args = xlgetarg();
1973 if (! atsign || colon) {
1974 oldargv = xlargv;
1975 oldargc = xlargc;
1976 oldsp = xlsp;
1977 xlargv = xlsp;
1978 xlargc = 0;
1981 if (colon) {
1982 if (atsign) {
1983 for (; (oldargc > 0 || once) && count != 0; oldargc--, count--) {
1984 once = FALSE;
1985 alist = *oldargv++;
1986 xlargc = 0;
1987 xlargv = oldsp;
1988 xlsp = oldsp;
1989 for (; consp(alist); alist = cdr(alist)) {
1990 pusharg(car(alist));
1991 xlargc++;
1993 xlformat(lfmt, stream);
1996 else {
1997 for (; (consp(args) || once) && count != 0; args = cdr(args), count--) {
1998 once = FALSE;
1999 alist = car(args);
2000 xlargc = 0;
2001 xlargv = oldsp;
2002 xlsp = oldsp;
2003 for (; consp(alist); alist = cdr(alist)) {
2004 pusharg(car(alist));
2005 xlargc++;
2007 xlformat(lfmt, stream);
2011 else {
2012 if (! atsign) {
2013 for (; consp(args); args = cdr(args)) {
2014 pusharg(car(args));
2015 xlargc++;
2018 while ((xlargc > 0 || once) && count-- != 0) {
2019 once = FALSE;
2020 if (--xlsample <= 0) {
2021 xlsample = SAMPLE;
2022 oscheck();
2024 xlformat(lfmt, stream);
2028 if (! atsign || colon) {
2029 xlargv = oldargv;
2030 xlargc = oldargc;
2031 xlsp = oldsp;
2033 xlpop();
2036 /* skip prefix parameters and modifiers for a format directive */
2037 LOCAL char *skip_pp P3C(char *, fmt,
2038 int *, colon, /* colon modifier given? */
2039 int *, atsign) /* atsign modifier given? */
2041 *colon = 0;
2042 *atsign= 0;
2043 do {
2044 if (*fmt == '\'') fmt += 2; /* character code */
2045 else if (*fmt == '#') fmt++; /* xlargc is value */
2046 else if (*fmt == 'v' || *fmt == 'V') fmt++; /* lisp arg is value */
2047 else if (isdigit(*fmt)) /* integer literal */
2048 do { fmt++; } while (isdigit(*fmt));
2049 else if (*fmt == ',') { /* empty field */
2051 else break; /* nothing to process */
2053 if (*fmt != ',') break; /* no comma -- done */
2054 fmt++; /* toss comma */
2055 } while (TRUE);
2057 do { /* pick up any colon or atsign modifier */
2058 if (*fmt == ':') *colon = 1;
2059 else if (*fmt == '@') *atsign = 1;
2060 else break;
2061 fmt++;
2062 } while (TRUE);
2063 return fmt;
2066 LOCAL char *skip_past_directive P3C(char *, fmt,
2067 int, tch,
2068 int, want_colon)
2070 int ch;
2071 int colon, atsign; /* : and @ modifiers given? */
2072 int nesting = 0;
2074 /* process the format string */
2075 while ((ch = *fmt++) != 0)
2076 if (ch == '~') {
2077 fmt = skip_pp(fmt, &colon, &atsign);
2078 ch = *fmt++;
2079 if (! nesting && (! want_colon || colon) && ch == tch)
2080 return(fmt);
2081 switch (ch) {
2082 case '[':
2083 case '(':
2084 case '{':
2085 nesting++;
2086 break;
2087 case ']':
2088 case ')':
2089 case '}':
2090 nesting--;
2091 break;
2093 if (nesting < 0) break;
2095 return (NULL);
2098 /* xlformat - formatted output function */
2099 /* TAA MOD 6/22/93 -- split out from xformat so routine can
2100 be called internally by xerror() and xcerror() */
2101 VOID xlformat P2C(LVAL, lfmt, LVAL, stream)
2103 int ch;
2104 int npp; /* number of prefix parameters */
2105 FIXTYPE pp[MAXNPP]; /* list of prefix parameters */
2106 int colon, atsign; /* : and @ modifiers given? */
2107 char *fmt = getstring(lfmt);
2108 LVAL *oldargv;
2109 int oldargc;
2111 oldargv = xlargv;
2112 oldargc = xlargc;
2114 /* process the format string */
2115 while ((ch = *fmt++) != 0)
2116 if (ch == '~') {
2117 fmt = decode_pp( fmt, pp, MAXNPP, &npp, &colon, &atsign, lfmt);
2118 ch = *fmt++;
2119 if (ISLOWER7(ch)) ch = toupper(ch);
2120 switch (ch) {
2121 case '\0':
2122 xlerror("expecting a format directive",cvstring(fmt-1));
2123 case 'A':
2124 opt_print(stream,xlgetarg(),FALSE,pp,colon,atsign);
2125 break;
2126 case 'S':
2127 opt_print(stream,xlgetarg(),TRUE,pp,colon,atsign);
2128 break;
2129 #ifdef BIGNUMS
2130 case 'R':
2131 if (npp > 3) toomanyopt(lfmt);
2132 integer_print(stream,xlgetarg(),ch,pp,atsign);
2133 break;
2134 case 'B':
2135 #endif
2136 case 'D':
2137 case 'O':
2138 case 'X':
2139 if (npp > 4) toomanyopt(lfmt);
2140 integer_print(stream,xlgetarg(),ch,pp,atsign);
2141 break;
2142 case 'E':
2143 if (npp > 7) toomanyopt(lfmt);
2144 flonum_eprint(stream,xlgetarg(),pp,atsign);
2145 break;
2146 case 'F':
2147 if (npp > 5) toomanyopt(lfmt);
2148 flonum_fprint(stream,xlgetarg(),pp,atsign);
2149 break;
2150 case 'G':
2151 if (npp > 7) toomanyopt(lfmt);
2152 flonum_gprint(stream,xlgetarg(),pp,atsign);
2153 break;
2154 case '&':
2155 if ( pp[0] < 0 ) pp[0] = 1;
2156 if ((pp[0])-- > 0)
2157 xlfreshline(stream);
2158 while( (pp[0])-- > 0 )
2159 xlterpri(stream);
2160 break;
2161 case '*':
2162 if (npp > 1) toomanyopt(lfmt);
2163 if (atsign) {
2164 if (pp[0] < 0) pp[0] = 0;
2165 if (pp[0] > oldargc) xltoofew();
2166 xlargc = oldargc - (int)pp[0];
2167 xlargv = oldargv + (int)pp[0];
2169 else if (colon) {
2170 if (pp[0] < 0) pp[0] = 1;
2171 if (pp[0] > oldargc - xlargc) xltoofew();
2172 xlargc += (int)pp[0];
2173 xlargv -= (int)pp[0];
2175 else {
2176 if (pp[0] < 0) pp[0] = 1;
2177 if (pp[0] > xlargc) xltoofew();
2178 xlargc -= (int)pp[0];
2179 xlargv += (int)pp[0];
2181 break;
2182 case 'T':
2183 tab_print(stream,pp,atsign);
2184 break;
2185 case '%':
2186 if( pp[0] < 0 ) pp[0] = 1;
2187 while( (pp[0])-- > 0 )
2188 xlterpri(stream);
2189 break;
2190 case '~':
2191 if( pp[0] <= 0 ) pp[0] = 1;
2192 while( (pp[0])-- > 0 )
2193 xlputc(stream,'~');
2194 break;
2195 case '\n':
2196 if( colon )
2197 break;
2198 if( atsign )
2199 xlterpri(stream);
2200 while (*fmt && *fmt != '\n' && isspace(*fmt))
2201 ++fmt;
2202 break;
2203 case '?':
2204 indirect_print(stream, atsign);
2205 break;
2206 case '|':
2207 if (pp[0] < 0) pp[0] = 1;
2208 while ((pp[0])-- > 0)
2209 xlputc(stream, '\f');
2210 break;
2211 case '(':
2212 case_convert_print(fmt, stream, colon, atsign);
2213 fmt = skip_past_directive(fmt, ')', FALSE);
2214 if (fmt == NULL) xlerror("incomplete ( directive", lfmt);
2215 break;
2216 case '[':
2217 conditional_print(fmt, stream, pp[0], colon, atsign);
2218 fmt = skip_past_directive(fmt, ']', FALSE);
2219 if (fmt == NULL) xlerror("incomplete [ directive", lfmt);
2220 break;
2221 case '{':
2222 iterative_print(fmt, stream, pp[0], colon, atsign);
2223 fmt = skip_past_directive(fmt, '}', FALSE);
2224 if (fmt == NULL) xlerror("incomplete { directive", lfmt);
2225 break;
2226 case ';':
2227 case ')':
2228 case ']':
2229 case '}':
2230 return;
2231 default:
2232 xlerror("unknown format directive",cvstring(fmt-1));
2235 else
2236 xlputc(stream,ch);
2239 /* xformat - formatted output function */
2240 LVAL xformat(V)
2242 LVAL stream,val;
2243 LVAL lfmt;
2245 xlsave1(val); /* TAA fix */
2247 /* get the stream and format string */
2248 stream = xlgetarg();
2249 if (null(stream)) {
2250 val = stream = newustream();
2252 else {
2253 if (stream == s_true)
2254 stream = getvalue(s_stdout);
2255 /* fix from xlispbug.417 */
2256 else if (streamp(stream)) { /* copied from xlgetfile() */
2257 if (getfile(stream) == CLOSED)
2258 xlfail("file not open");
2260 else if (!ustreamp(stream))
2261 xlbadtype(stream);
2262 val = NIL;
2265 lfmt=xlgastring();
2267 /* go do it! */
2268 xlformat(lfmt, stream);
2270 /* get string if output to string */
2271 if (!null(val)) val = getstroutput(val);
2273 /* unprotect */
2274 xlpop();
2276 /* return the value */
2277 return val;
2281 /* getstroutput - get the output stream string (internal) */
2282 LVAL getstroutput P1C(LVAL, stream)
2284 char *str;
2285 LVAL next,val;
2286 unsigned len; /* TAA MOD */
2287 int ch;
2289 /* compute the length of the stream */
2290 for (len = 0, next = gethead(stream); consp(next); next = cdr(next)) {
2291 ++len;
2292 /****if (len > MAXSLEN)
2293 xltoolong();*/ /* TAA MOD addition for overflow detect */
2296 /* create a new string */
2297 xlprot1(stream);
2298 val = newstring(len);
2299 xlpop();
2301 /* copy the characters into the new string */
2302 str = getstring(val);
2303 while ((ch = xlgetc(stream)) != EOF)
2304 *str++ = ch;
2305 *str = '\0';
2307 /* return the string */
2308 return (val);