home *** CD-ROM | disk | FTP | other *** search
- /* streams.c -- Lisp stream handling
- Copyright (C) 1993, 1994 John Harper <jsh@ukc.ac.uk>
-
- This file is part of Jade.
-
- Jade is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
- Jade is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with Jade; see the file COPYING. If not, write to
- the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
-
- /*
- * These are the Lisp objects which are classed as streams:
- *
- * file: (rw)
- * mark: (rw), advance pos attribute of mark afterwards
- * buffer: (rw), from cursor pos
- * (number . string): (r), from the number'th char of string
- * (string . ??): (w), to end of string
- * (buffer . pos): (rw), from buffer, pos is advanced
- * (buffer . t): (w), end of buffer
- * function-name: (rw), call function, when reading function is expected to
- * return the next character, when writing it is called with
- * one arg, either character or string.
- * process: (w), write to the stdin of the process if it's running
- * t: (w), display in status line
- */
-
- #include "jade.h"
- #include "jade_protos.h"
- #include "regexp/regexp.h"
-
- #include <string.h>
- #include <fcntl.h>
- #include <ctype.h>
- #include <stdlib.h>
-
- _PR int streamgetc(VALUE);
- _PR int streamungetc(VALUE, int);
- _PR int streamputc(VALUE, int);
- _PR int streamputs(VALUE, u_char *, int);
- _PR u_char escstreamchar(VALUE, int *);
- _PR void streamputcntl(VALUE, int);
-
- _PR void file_sweep(void);
- _PR int file_cmp(VALUE, VALUE);
- _PR void file_prin(VALUE, VALUE);
-
- _PR void streams_init(void);
- _PR void streams_kill(void);
-
- static int
- posgetc(TX *tx, POS *pos)
- {
- int c = EOF;
- if(pos->pos_Line < tx->tx_NumLines)
- {
- LINE *ln = tx->tx_Lines + pos->pos_Line;
- if(pos->pos_Col >= (ln->ln_Strlen - 1))
- {
- if(++pos->pos_Line == tx->tx_NumLines)
- {
- --pos->pos_Line;
- return(EOF);
- }
- pos->pos_Col = 0;
- return('\n');
- }
- c = ln->ln_Line[pos->pos_Col++];
- }
- return(c);
- }
- static int
- posputc(TX *tx, POS *pos, int c)
- {
- int rc = EOF;
- if(!readonly(tx) && padpos(tx, pos))
- {
- u_char tmps[2];
- tmps[0] = (u_char)c;
- tmps[1] = 0;
- if(iscntrl(c))
- {
- if(insertstring(tx, tmps, tx->tx_TabSize, pos))
- rc = 1;
- }
- else
- {
- POS start = *pos;
- if(insertstrn(tx, tmps, 1, pos))
- {
- flaginsertion(tx, &start, pos);
- rc = 1;
- }
- }
- }
- return(rc);
- }
- static int
- posputs(TX *tx, POS *pos, u_char *buf)
- {
- int rc = EOF;
- if(!readonly(tx) && padpos(tx, pos))
- {
- if(insertstring(tx, buf, tx->tx_TabSize, pos))
- rc = strlen(buf);
- }
- return(rc);
- }
-
- int
- streamgetc(VALUE stream)
- {
- int c = EOF;
- if(NILP(stream) && !(stream = cmd_symbol_value(sym_standard_input)))
- return(c);
- switch(VTYPE(stream))
- {
- VALUE res;
- int oldgci;
- case V_File:
- if(VFILE(stream)->lf_Name)
- c = getc(VFILE(stream)->lf_File);
- break;
- case V_Mark:
- if(!VMARK(stream)->mk_Resident)
- cmd_signal(sym_invalid_stream, list_2(stream, MKSTR("Marks used as streams must be resident")));
- else
- c = posgetc(VMARK(stream)->mk_File.tx, &VPOS(VMARK(stream)->mk_Pos));
- break;
- case V_TX:
- c = posgetc(VTX(stream), gettxcurspos(VTX(stream)));
- break;
- case V_Cons:
- res = VCAR(stream);
- if(NUMBERP(res) && STRINGP(VCDR(stream)))
- {
- c = (int)VSTR(VCDR(stream))[VNUM(res)];
- if(c)
- VCAR(stream) = newnumber(VNUM(res) + 1);
- else
- c = EOF;
- break;
- }
- else if(BUFFERP(res) && POSP(VCDR(stream)))
- {
- c = posgetc(VTX(res), &VPOS(VCDR(stream)));
- break;
- }
- else if(res != sym_lambda)
- {
- cmd_signal(sym_invalid_stream, LIST_1(stream));
- break;
- }
- /* FALL THROUGH */
- case V_Symbol:
- oldgci = GCinhibit;
- GCinhibit = TRUE;
- if((res = calllisp0(stream)) && NUMBERP(res))
- c = VNUM(res);
- GCinhibit = oldgci;
- break;
- #ifdef HAVE_UNIX
- case V_Process:
- cmd_signal(sym_invalid_stream, list_2(stream, MKSTR("Processes are not input streams")));
- break;
- #endif
- default:
- cmd_signal(sym_invalid_stream, LIST_1(stream));
- }
- return(c);
- }
-
- /*
- * Puts back one character, it will be read next call to streamgetc on
- * this stream.
- * Note that some types of stream don't actually use c, they just rewind
- * pointers.
- * Never call this unless you *have* *successfully* read from the stream
- * previously. (few checks are performed here, I assume they were made in
- * streamgetc()).
- */
- #define POSUNGETC(p,tx) \
- if(--((p)->pos_Col) < 0) \
- { \
- (p)->pos_Line--; \
- (p)->pos_Col = (tx)->tx_Lines[(p)->pos_Line].ln_Strlen - 1; \
- }
- int
- streamungetc(VALUE stream, int c)
- {
- int rc = FALSE;
- if(NILP(stream) && !(stream = cmd_symbol_value(sym_standard_input)))
- return(rc);
- switch(VTYPE(stream))
- {
- POS *pos;
- VALUE tmp;
- int oldgci;
- case V_File:
- if(ungetc(c, VFILE(stream)->lf_File) != EOF)
- rc = TRUE;
- break;
- case V_Mark:
- pos = &VPOS(VMARK(stream)->mk_Pos);
- POSUNGETC(pos, VMARK(stream)->mk_File.tx)
- rc = TRUE;
- break;
- case V_TX:
- pos = gettxcurspos(VTX(stream));
- POSUNGETC(pos, VTX(stream))
- rc = TRUE;
- break;
- case V_Cons:
- tmp = VCAR(stream);
- if(NUMBERP(tmp) && STRINGP(VCDR(stream)))
- {
- VCAR(stream) = newnumber(VNUM(tmp) - 1);
- rc = TRUE;
- break;
- }
- else if(BUFFERP(tmp) && POSP(VCDR(stream)))
- {
- POSUNGETC(&VPOS(VCDR(stream)), VTX(tmp));
- rc = TRUE;
- break;
- }
- /* FALL THROUGH */
- case V_Symbol:
- tmp = newnumber(c);
- oldgci = GCinhibit;
- GCinhibit = TRUE;
- if((tmp = calllisp1(stream, tmp)) && !NILP(tmp))
- rc = TRUE;
- GCinhibit = oldgci;
- break;
- }
- return(rc);
- }
-
- int
- streamputc(VALUE stream, int c)
- {
- int rc = 0;
- if(NILP(stream) && !(stream = cmd_symbol_value(sym_standard_output)))
- return(rc);
- switch(VTYPE(stream))
- {
- VALUE args, res, new;
- int len;
- u_char tmps[2];
- POS pos;
- int oldgci;
- case V_File:
- if(VFILE(stream)->lf_Name)
- {
- if(putc(c, VFILE(stream)->lf_File) != EOF)
- rc = 1;
- }
- break;
- case V_Mark:
- if(!VMARK(stream)->mk_Resident)
- cmd_signal(sym_invalid_stream, list_2(stream, MKSTR("Marks used as streams must be resident")));
- else
- {
- pos = VPOS(VMARK(stream)->mk_Pos);
- rc = posputc(VMARK(stream)->mk_File.tx, &pos, c);
- }
- break;
- case V_TX:
- pos = *(gettxcurspos(VTX(stream)));
- rc = posputc(VTX(stream), &pos, c);
- break;
- case V_Cons:
- args = VCAR(stream);
- if(STRINGP(args))
- {
- len = strlen(VSTR(args));
- new = valstralloc(len + 2);
- if(new)
- {
- memcpy(VSTR(new), VSTR(args), len);
- VSTR(new)[len] = (u_char)c;
- VSTR(new)[len+1] = 0;
- VCAR(stream) = new;
- rc = 1;
- }
- break;
- }
- else if(BUFFERP(args))
- {
- if(POSP(VCDR(stream)))
- rc = posputc(VTX(args), &VPOS(VCDR(stream)), c);
- else
- {
- pos.pos_Line = VTX(args)->tx_NumLines - 1;
- pos.pos_Col = VTX(args)->tx_Lines[pos.pos_Line].ln_Strlen - 1;
- rc = posputc(VTX(args), &pos, c);
- }
- break;
- }
- else if(args != sym_lambda)
- {
- cmd_signal(sym_invalid_stream, LIST_1(stream));
- break;
- }
- /* FALL THROUGH */
- case V_Symbol:
- if(stream == sym_t)
- {
- if(CurrVW->vw_NonStdTitle)
- {
- VW *vw = CurrVW;
- u_char *s;
- len = strlen(vw->vw_LastTitle);
- s = mystrdupn(vw->vw_LastTitle, len + 1);
- if(s)
- {
- s[len] = c;
- s[len + 1] = 0;
- mystrfree(vw->vw_LastTitle);
- vw->vw_LastTitle = s;
- vw->vw_Flags |= VWFF_REFRESH_STATUS;
- }
- }
- else
- {
- tmps[0] = (u_char)c;
- tmps[1] = 0;
- settitle(tmps);
- }
- rc = 1;
- }
- else
- {
- oldgci = GCinhibit;
- GCinhibit = TRUE;
- if((res = calllisp1(stream, newnumber(c))) && !NILP(res))
- rc = 1;
- GCinhibit = oldgci;
- }
- break;
- #ifdef HAVE_UNIX
- case V_Process:
- tmps[0] = (u_char)c;
- tmps[1] = 0;
- rc = writetoproc(stream, tmps);
- break;
- #endif
- default:
- cmd_signal(sym_invalid_stream, LIST_1(stream));
- }
- return(rc);
- }
-
- int
- streamputs(VALUE stream, u_char *buf, int isValString)
- {
- int rc = 0;
- if(NILP(stream) && !(stream = cmd_symbol_value(sym_standard_output)))
- return(rc);
- switch(VTYPE(stream))
- {
- VALUE args, res, new;
- int len, newlen;
- POS pos;
- int oldgci;
- case V_File:
- if(VFILE(stream)->lf_Name)
- {
- if((rc = fputs(buf, VFILE(stream)->lf_File)) != EOF)
- rc = strlen(buf);
- }
- break;
- case V_Mark:
- if(!VMARK(stream)->mk_Resident)
- cmd_signal(sym_invalid_stream, list_2(stream, MKSTR("Marks used as streams must be resident")));
- else
- {
- pos = VPOS(VMARK(stream)->mk_Pos);
- rc = posputs(VMARK(stream)->mk_File.tx, &pos, buf);
- }
- break;
- case V_TX:
- pos = *(gettxcurspos(VTX(stream)));
- rc = posputs(VTX(stream), &pos, buf);
- break;
- case V_Cons:
- args = VCAR(stream);
- if(STRINGP(args))
- {
- len = strlen(VSTR(args));
- newlen = len + strlen(buf);
- new = valstralloc(newlen + 1);
- if(new)
- {
- memcpy(VSTR(new), VSTR(args), len);
- strcpy(VSTR(new) + len, buf);
- VCAR(stream) = new;
- rc = newlen - len;
- }
- break;
- }
- else if(BUFFERP(args))
- {
- if(POSP(VCDR(stream)))
- rc = posputs(VTX(args), &VPOS(VCDR(stream)), buf);
- else
- {
- pos.pos_Line = VTX(args)->tx_NumLines - 1;
- pos.pos_Col = VTX(args)->tx_Lines[pos.pos_Line].ln_Strlen - 1;
- rc = posputs(VTX(args), &pos, buf);
- }
- break;
- }
- else if(args != sym_lambda)
- {
- cmd_signal(sym_invalid_stream, LIST_1(stream));
- break;
- }
- /* FALL THROUGH */
- case V_Symbol:
- if(stream == sym_t)
- {
- len = strlen(buf);
- if(CurrVW->vw_NonStdTitle)
- {
- VW *vw = CurrVW;
- u_char *s;
- newlen = strlen(vw->vw_LastTitle) + len;
- s = mystrdupn(vw->vw_LastTitle, newlen);
- if(s)
- {
- strcpy(s + (newlen - len), buf);
- mystrfree(vw->vw_LastTitle);
- vw->vw_LastTitle = s;
- vw->vw_Flags |= VWFF_REFRESH_STATUS;
- }
- }
- else
- settitle(buf);
- rc = len;
- }
- else
- {
- if(isValString)
- args = buf - 1;
- else
- args = valstrdup(buf);
- oldgci = GCinhibit;
- GCinhibit = TRUE;
- if((res = calllisp1(stream, args)) && !NILP(res))
- {
- if(NUMBERP(res))
- rc = VNUM(res);
- else
- rc = strlen(buf);
- }
- GCinhibit = oldgci;
- }
- break;
- #ifdef HAVE_UNIX
- case V_Process:
- rc = writetoproc(stream, buf);
- break;
- #endif
- default:
- cmd_signal(sym_invalid_stream, LIST_1(stream));
- }
- return(rc);
- }
-
- u_char
- escstreamchar(VALUE stream, int *c_p)
- {
- u_char c;
- switch(*c_p)
- {
- case 'n':
- c = '\n';
- break;
- case 'r':
- c = '\r';
- break;
- case 'f':
- c = '\f';
- break;
- case 't':
- c = '\t';
- break;
- case 'a':
- c = '\a';
- break;
- case '^':
- c = toupper(streamgetc(stream)) ^ 0x40;
- break;
- case '0':
- case '1':
- case '2':
- case '3':
- c = (*c_p - '0') * 64;
- c += ((streamgetc(stream) - '0') * 8);
- c += (streamgetc(stream) - '0');
- break;
- default:
- c = *c_p;
- }
- *c_p = streamgetc(stream);
- return(c);
- }
- void
- streamputcntl(VALUE stream, int c)
- {
- u_char buff[40];
- u_char *buf = buff + 1;
- buff[0] = V_String;
- switch(c)
- {
- case '\n':
- strcpy(buf, "\\n");
- break;
- case '\t':
- strcpy(buf, "\\t");
- break;
- case '\r':
- strcpy(buf, "\\r");
- break;
- case '\f':
- strcpy(buf, "\\f");
- break;
- case '\a':
- strcpy(buf, "\\a");
- break;
- default:
- if(c <= 0x3f)
- sprintf(buf, "\\^%c", c + 0x40);
- else
- sprintf(buf, "\\%03o", (int)c);
- break;
- }
- streamputs(stream, buf, TRUE);
- }
-
- _PR VALUE cmd_write(VALUE stream, VALUE data);
- DEFUN("write", cmd_write, subr_write, (VALUE stream, VALUE data), V_Subr2, DOC_write) /*
- ::doc:write::
- (write STREAM DATA)
- Writes DATA, which can either be a string or a character, to the stream
- STREAM, returning the number of characters actually written.
- ::end:: */
- {
- int actual;
- switch(VTYPE(data))
- {
- case V_Number:
- actual = streamputc(stream, VNUM(data));
- break;
- case V_StaticString:
- case V_String:
- actual = streamputs(stream, VSTR(data), TRUE);
- break;
- default:
- cmd_signal(sym_bad_arg, list_2(data, newnumber(2)));
- return(NULL);
- }
- return(newnumber(actual));
- }
-
- _PR VALUE cmd_read_char(VALUE stream);
- DEFUN("read-char", cmd_read_char, subr_read_char, (VALUE stream), V_Subr1, DOC_read_char) /*
- ::doc:read_char::
- (read-char STREAM)
- Reads the next character from the input-stream STREAM, if no more characters
- are available returns nil.
- ::end:: */
- {
- int rc;
- if((rc = streamgetc(stream)) != EOF)
- return(newnumber(rc));
- return(sym_nil);
- }
-
- _PR VALUE cmd_read_line(VALUE stream);
- DEFUN("read-line", cmd_read_line, subr_read_line, (VALUE stream), V_Subr1, DOC_read_line) /*
- ::doc:read_line::
- (read-line STREAM)
- Read one line of text from STREAM.
- ::end:: */
- {
- u_char buf[400];
- if(FILEP(stream))
- {
- /* Special case for file streams. We can read a line in one go. */
- if(VFILE(stream)->lf_Name && fgets(buf, 400, VFILE(stream)->lf_File))
- return(valstrdup(buf));
- return(sym_nil);
- }
- else
- {
- u_char *bufp = buf;
- int len = 0, c;
- while((c = streamgetc(stream)) != EOF)
- {
- *bufp++ = (u_char)c;
- if((++len >= 399) || (c == '\n'))
- break;
- }
- if(len == 0)
- return(sym_nil);
- return(valstrdupn(buf, len));
- }
- }
-
- _PR VALUE cmd_copy_stream(VALUE source, VALUE dest);
- DEFUN("copy-stream", cmd_copy_stream, subr_copy_stream, (VALUE source, VALUE dest), V_Subr2, DOC_copy_stream) /*
- ::doc:copy_stream::
- (copy-stream SOURCE-STREAM DEST-STREAM)
- Copy all characters from SOURCE-STREAM to DEST-STREAM until an EOF is read.
- ::end:: */
- {
- int len = 0, i = 0, c;
- u_char buff[402];
- u_char *buf = buff + 1;
- buff[0] = V_StaticString;
- while((c = streamgetc(source)) != EOF)
- {
- if(i == 400)
- {
- buf[i] = 0;
- if(streamputs(dest, buf, TRUE) == EOF)
- break;
- i = 0;
- }
- else
- buf[i++] = c;
- len++;
- }
- if(i > 0)
- {
- buff[i] = 0;
- streamputs(dest, buf, TRUE);
- }
- if(len)
- return(newnumber(len));
- return(sym_nil);
- }
-
- _PR VALUE cmd_read(VALUE);
- DEFUN("read", cmd_read, subr_read, (VALUE stream), V_Subr1, DOC_read) /*
- ::doc:read::
- (read [STREAM])
- Reads one lisp-object from the input-stream STREAM (or the value of the
- variable `standard-input' if STREAM is unspecified) and return it.
- ::end:: */
- {
- VALUE res;
- int c;
- if(NILP(stream) && !(stream = cmd_symbol_value(sym_standard_input)))
- {
- signalargerror(stream, 1);
- return(NULL);
- }
- c = streamgetc(stream);
- if(c == EOF)
- res = cmd_signal(sym_end_of_stream, LIST_1(stream));
- else
- res = readlispexp(stream, &c);
- /* If an error occurred leave stream where it is. */
- if(res && c != EOF)
- streamungetc(stream, c);
- return(res);
- }
-
- _PR VALUE cmd_print(VALUE, VALUE);
- DEFUN("print", cmd_print, subr_print, (VALUE obj, VALUE stream), V_Subr2, DOC_print) /*
- ::doc:print::
- (print OBJECT [STREAM])
- First outputs a newline, then prints a text representation of OBJECT to
- STREAM (or the contents of the variable `standard-output') in a form suitable
- for `read'.
- ::end:: */
- {
- if(NILP(stream) && !(stream = cmd_symbol_value(sym_standard_output)))
- {
- signalargerror(stream, 1);
- return(NULL);
- }
- streamputc(stream, '\n');
- printval(stream, obj);
- return(stream);
- }
-
- _PR VALUE cmd_prin1(VALUE, VALUE);
- DEFUN("prin1", cmd_prin1, subr_prin1, (VALUE obj, VALUE stream), V_Subr2, DOC_prin1) /*
- ::doc:prin1::
- (prin1 OBJECT [STREAM])
- Prints a text representation of OBJECT to STREAM (or the contents of the
- variable `standard-output') in a form suitable for `read'.
- ::end:: */
- {
- if(NILP(stream) && !(stream = cmd_symbol_value(sym_standard_output)))
- {
- signalargerror(stream, 1);
- return(NULL);
- }
- printval(stream, obj);
- return(stream);
- }
-
- _PR VALUE cmd_princ(VALUE, VALUE);
- DEFUN("princ", cmd_princ, subr_princ, (VALUE obj, VALUE stream), V_Subr2, DOC_princ) /*
- ::doc:princ::
- (princ OBJECT [STREAM])
- Prints a text representation of OBJECT to STREAM (or the contents of the
- variable standard-output), no strange characters are quoted and no quotes
- are printed around strings.
- ::end:: */
- {
- if(NILP(stream) && !(stream = cmd_symbol_value(sym_standard_output)))
- {
- signalargerror(stream, 1);
- return(NULL);
- }
- princval(stream, obj);
- return(stream);
- }
-
- _PR VALUE cmd_format(VALUE);
- DEFUN("format", cmd_format, subr_format, (VALUE args), V_SubrN, DOC_format) /*
- ::doc:format::
- (format STREAM FORMAT-STRING ARGS... )
- Writes a string created from the format specification FORMAT-STRING and
- the argument-values ARGS to the stream, STREAM.
- FORMAT-STRING is a template for the result, any `%' characters introduce
- a substitution, using the next unused ARG. These format specifiers are
- implemented:
- d print next ARG as decimal integer
- x print next ARG as hexadecimal integer
- c print next ARG as ASCII character
- s unquoted representation (as from `princ') of next ARG
- S normal print'ed representation of next ARG
- % literal percentage character
-
- Returns STREAM.
- ::end:: */
- {
- u_char *fmt;
- VALUE stream = ARG2;
- u_char c;
- DECLARE1(stream, STRINGP);
- fmt = VSTR(stream);
- stream = ARG1;
- if(NILP(stream) && !(stream = cmd_symbol_value(sym_standard_output)))
- {
- signalargerror(stream, 1);
- return(NULL);
- }
- args = movedownlist(args, 2);
- while((c = *fmt++))
- {
- if(c == '%')
- {
- u_char tbuf[40], nfmt[4];
- VALUE val = ARG1;
- switch(c = *fmt++)
- {
- case 'd':
- case 'x':
- case 'c':
- nfmt[0] = '%';
- nfmt[1] = 'l';
- nfmt[2] = c;
- nfmt[3] = 0;
- sprintf(tbuf, nfmt, NUMBERP(val) ? VNUM(val) : (long)val);
- streamputs(stream, tbuf, FALSE);
- break;
- case 's':
- princval(stream, val);
- break;
- case 'S':
- printval(stream, val);
- break;
- case '%':
- streamputc(stream, '%');
- break;
- }
- args = movedownlist(args, 1);
- }
- else
- streamputc(stream, c);
- }
- return(stream);
- }
-
- LFile *LFileChain;
-
- void
- file_sweep(void)
- {
- LFile *lf = LFileChain;
- LFileChain = NULL;
- while(lf)
- {
- LFile *nxt = lf->lf_Next;
- if(!GC_MARKEDP(lf))
- {
- if(lf->lf_Name && !(lf->lf_Flags & LFF_DONT_CLOSE))
- fclose(lf->lf_File);
- mystrfree(lf);
- }
- else
- {
- GC_CLR(lf);
- lf->lf_Next = LFileChain;
- LFileChain = lf;
- }
- lf = nxt;
- }
- }
- int
- file_cmp(VALUE v1, VALUE v2)
- {
- if(VTYPE(v1) == VTYPE(v2))
- {
- if(VFILE(v1)->lf_Name && VFILE(v2)->lf_Name)
- return(!samefiles(VSTR(VFILE(v1)->lf_Name), VSTR(VFILE(v2)->lf_Name)));
- }
- return(1);
- }
- void
- file_prin(VALUE strm, VALUE obj)
- {
- streamputs(strm, "#<file ", FALSE);
- if(VFILE(obj)->lf_Name)
- {
- streamputs(strm, VSTR(VFILE(obj)->lf_Name), FALSE);
- streamputc(strm, '>');
- }
- else
- streamputs(strm, "*unbound*>", FALSE);
- }
-
- _PR VALUE cmd_open(VALUE name, VALUE modes, VALUE file);
- DEFUN("open", cmd_open, subr_open, (VALUE name, VALUE modes, VALUE file), V_Subr3, DOC_open) /*
- ::doc:open::
- (open [FILE-NAME MODE-STRING] [FILE])
- Opens a file called FILE-NAME with modes MODE-STRING (standard c-library
- modes, ie `r' == read, `w' == write, etc). If FILE is given it is an
- existing file object which is to be closed before opening the new file on it.
- ::end:: */
- {
- LFile *lf;
- if(!FILEP(file))
- {
- lf = mystralloc(sizeof(LFile));
- if(lf)
- {
- lf->lf_Next = LFileChain;
- LFileChain = lf;
- lf->lf_Type = V_File;
- }
- }
- else
- {
- lf = VFILE(file);
- if(lf->lf_Name && !(lf->lf_Flags & LFF_DONT_CLOSE))
- fclose(lf->lf_File);
- }
- if(lf)
- {
- lf->lf_File = lf->lf_Name = NULL;
- lf->lf_Flags = 0;
- if(STRINGP(name) && STRINGP(modes))
- {
- lf->lf_File = fopen(VSTR(name), VSTR(modes));
- if(lf->lf_File)
- {
- lf->lf_Name = name;
- #ifdef HAVE_UNIX
- /*
- * set close-on-exec for easy process fork()ing
- */
- fcntl(fileno(lf->lf_File), F_SETFD, 1);
- #endif
- }
- else
- return(cmd_signal(sym_file_error, list_2(geterrstring(), name)));
- }
- return(lf);
- }
- return(NULL);
- }
-
- _PR VALUE cmd_close(VALUE file);
- DEFUN("close", cmd_close, subr_close, (VALUE file), V_Subr1, DOC_close) /*
- ::doc:close::
- (close FILE)
- Kills any association between object FILE and the file in the filesystem that
- it has open.
- ::end:: */
- {
- DECLARE1(file, FILEP);
- if(VFILE(file)->lf_Name && !(VFILE(file)->lf_Flags & LFF_DONT_CLOSE))
- fclose(VFILE(file)->lf_File);
- VFILE(file)->lf_File = VFILE(file)->lf_Name = NULL;
- return(file);
- }
-
- _PR VALUE cmd_flush_file(VALUE file);
- DEFUN("flush-file", cmd_flush_file, subr_flush_file, (VALUE file), V_Subr1, DOC_flush_file) /*
- ::doc:flush_file::
- (flush-file FILE)
- Flushes any buffered output on FILE.
- ::end:: */
- {
- DECLARE1(file, FILEP);
- if(VFILE(file)->lf_Name)
- fflush(VFILE(file)->lf_File);
- return(file);
- }
-
- _PR VALUE cmd_file_p(VALUE arg);
- DEFUN("file-p", cmd_file_p, subr_file_p, (VALUE arg), V_Subr1, DOC_file_p) /*
- ::doc:file_p::
- (file-p ARG)
- Returns t if ARG is a file object.
- ::end:: */
- {
- if(FILEP(arg))
- return(sym_t);
- return(sym_nil);
- }
-
- _PR VALUE cmd_file_bound_p(VALUE file);
- DEFUN("file-bound-p", cmd_file_bound_p, subr_file_bound_p, (VALUE file), V_Subr1, DOC_file_bound_p) /*
- ::doc:file_bound_p::
- (file-bound-p FILE)
- Returns t if FILE is currently bound to a physical file.
- ::end:: */
- {
- DECLARE1(file, FILEP);
- if(VFILE(file)->lf_Name)
- return(sym_t);
- return(sym_nil);
- }
-
- _PR VALUE cmd_file_binding(VALUE file);
- DEFUN("file-binding", cmd_file_binding, subr_file_binding, (VALUE file), V_Subr1, DOC_file_binding) /*
- ::doc:file_binding::
- (file-binding FILE)
- Returns the name of the physical file FILE is bound to, or nil.
- ::end:: */
- {
- DECLARE1(file, FILEP);
- if(VFILE(file)->lf_Name)
- return(VFILE(file)->lf_Name);
- return(sym_nil);
- }
-
- _PR VALUE cmd_file_eof_p(VALUE file);
- DEFUN("file-eof-p", cmd_file_eof_p, subr_file_eof_p, (VALUE file), V_Subr1, DOC_file_eof_p) /*
- ::doc:file_eof_p::
- (file-eof-p FILE
- Returns t when end of FILE is reached.
- ::end:: */
- {
- DECLARE1(file, FILEP);
- if(VFILE(file)->lf_Name && feof(VFILE(file)->lf_File))
- return(sym_t);
- return(sym_nil);
- }
-
- _PR VALUE cmd_read_file_until(VALUE file, VALUE re, VALUE nocase_p);
- DEFUN("read-file-until", cmd_read_file_until, subr_read_file_until, (VALUE file, VALUE re, VALUE nocase_p), V_Subr3, DOC_read_file_until) /*
- ::doc:read_file_until::
- (read-file-until FILE REGEXP [IGNORE-CASE-P])
- Read lines from the Lisp file object FILE until one matching the regular
- expression REGEXP is found. The matching line is returned, or nil if no
- lines match.
- If IGNORE-CASE-P is non-nil the regexp matching is not case-sensitive.
- ::end:: */
- {
- regexp *prog;
- u_char buf[400]; /* Fix this later. */
- DECLARE1(file, FILEP);
- DECLARE2(re, STRINGP);
- if(!VFILE(file)->lf_Name)
- return(cmd_signal(sym_bad_arg, list_2(MKSTR("File object is unbound"), file)));
- prog = regcomp(VSTR(re));
- if(prog)
- {
- int eflags = NILP(nocase_p) ? 0 : REG_NOCASE;
- FILE *fh = VFILE(file)->lf_File;
- VALUE res = sym_nil;
- while(fgets(buf, 400, fh))
- {
- if(regexec2(prog, buf, eflags))
- {
- res = valstrdup(buf);
- break;
- }
- }
- free(prog);
- return(res);
- }
- return(NULL);
- }
-
- _PR VALUE cmd_stdin_file(void);
- DEFUN("stdin-file", cmd_stdin_file, subr_stdin_file, (void), V_Subr0, DOC_stdin_file) /*
- ::doc:stdin_file::
- (stdin-file)
- Returns the file object representing the editor's standard input.
- ::end:: */
- {
- static VALUE stdin_file;
- if(stdin_file)
- return(stdin_file);
- stdin_file = cmd_open(sym_nil, sym_nil, sym_nil);
- VFILE(stdin_file)->lf_Name = MKSTR("<stdin>");
- VFILE(stdin_file)->lf_File = stdin;
- VFILE(stdin_file)->lf_Flags |= LFF_DONT_CLOSE;
- markstatic(&stdin_file);
- return(stdin_file);
- }
-
- _PR VALUE cmd_stdout_file(void);
- DEFUN("stdout-file", cmd_stdout_file, subr_stdout_file, (void), V_Subr0, DOC_stdout_file) /*
- ::doc:stdout_file::
- (stdout-file)
- Returns the file object representing the editor's standard output.
- ::end:: */
- {
- static VALUE stdout_file;
- if(stdout_file)
- return(stdout_file);
- stdout_file = cmd_open(sym_nil, sym_nil, sym_nil);
- VFILE(stdout_file)->lf_Name = MKSTR("<stdout>");
- VFILE(stdout_file)->lf_File = stdout;
- VFILE(stdout_file)->lf_Flags |= LFF_DONT_CLOSE;
- markstatic(&stdout_file);
- return(stdout_file);
- }
-
- void
- streams_init(void)
- {
- ADD_SUBR(subr_write);
- ADD_SUBR(subr_read_char);
- ADD_SUBR(subr_read_line);
- ADD_SUBR(subr_copy_stream);
- ADD_SUBR(subr_read);
- ADD_SUBR(subr_print);
- ADD_SUBR(subr_prin1);
- ADD_SUBR(subr_princ);
- ADD_SUBR(subr_format);
- ADD_SUBR(subr_open);
- ADD_SUBR(subr_close);
- ADD_SUBR(subr_flush_file);
- ADD_SUBR(subr_file_p);
- ADD_SUBR(subr_file_bound_p);
- ADD_SUBR(subr_file_binding);
- ADD_SUBR(subr_file_eof_p);
- ADD_SUBR(subr_read_file_until);
- ADD_SUBR(subr_stdin_file);
- ADD_SUBR(subr_stdout_file);
- }
- void
- streams_kill(void)
- {
- LFile *lf = LFileChain;
- while(lf)
- {
- LFile *nxt = lf->lf_Next;
- if(lf->lf_Name && !(lf->lf_Flags & LFF_DONT_CLOSE))
- fclose(lf->lf_File);
- mystrfree(lf);
- lf = nxt;
- }
- }
-