new method to handle current files, with global variables

_INPUT and _OUTPUT.
This commit is contained in:
Roberto Ierusalimschy 1997-06-26 17:39:10 -03:00
parent e81f184164
commit da585783e3
2 changed files with 105 additions and 75 deletions

89
iolib.c
View File

@ -10,8 +10,6 @@
#include "lualib.h" #include "lualib.h"
FILE *lua_infile, *lua_outfile;
int lua_tagio; int lua_tagio;
@ -39,59 +37,80 @@ static void pushresult (int i)
} }
static void closefile (FILE *f)
static FILE *getfile (char *name)
{ {
if (f == stdin || f == stdout) lua_Object f = lua_getglobal(name);
return; if (lua_tag(f) != lua_tagio)
if (f == lua_infile) luaL_verror("global variable %s is not a file handle", name);
lua_infile = stdin; return lua_getuserdata(f);
if (f == lua_outfile) }
lua_outfile = stdout;
static void closefile (char *name)
{
FILE *f = getfile(name);
if (f == stdin || f == stdout) return;
if (pclose(f) == -1) if (pclose(f) == -1)
fclose(f); fclose(f);
} }
static void setfile (FILE *f, char *name)
{
lua_pushusertag(f, lua_tagio);
lua_setglobal(name);
}
static void setreturn (FILE *f, char *name)
{
setfile(f, name);
lua_pushusertag(f, lua_tagio);
}
static void io_readfrom (void) static void io_readfrom (void)
{ {
FILE *current;
lua_Object f = lua_getparam(1); lua_Object f = lua_getparam(1);
if (f == LUA_NOOBJECT) if (f == LUA_NOOBJECT) {
closefile(lua_infile); /* restore standart input */ closefile("_INPUT");
current = stdin;
}
else if (lua_tag(f) == lua_tagio) else if (lua_tag(f) == lua_tagio)
lua_infile = lua_getuserdata(f); current = lua_getuserdata(f);
else { else {
char *s = luaL_check_string(1); char *s = luaL_check_string(1);
FILE *fp = (*s == '|') ? popen(s+1, "r") : fopen(s, "r"); current = (*s == '|') ? popen(s+1, "r") : fopen(s, "r");
if (fp) if (current == NULL) {
lua_infile = fp;
else {
pushresult(0); pushresult(0);
return; return;
} }
} }
lua_pushusertag(lua_infile, lua_tagio); setreturn(current, "_INPUT");
} }
static void io_writeto (void) static void io_writeto (void)
{ {
FILE *current;
lua_Object f = lua_getparam(1); lua_Object f = lua_getparam(1);
if (f == LUA_NOOBJECT) if (f == LUA_NOOBJECT) {
closefile(lua_outfile); /* restore standart output */ closefile("_OUTPUT");
current = stdout;
}
else if (lua_tag(f) == lua_tagio) else if (lua_tag(f) == lua_tagio)
lua_outfile = lua_getuserdata(f); current = lua_getuserdata(f);
else { else {
char *s = luaL_check_string(1); char *s = luaL_check_string(1);
FILE *fp = (*s == '|') ? popen(s+1,"w") : fopen(s,"w"); current = (*s == '|') ? popen(s+1,"w") : fopen(s,"w");
if (fp) if (current == NULL) {
lua_outfile = fp;
else {
pushresult(0); pushresult(0);
return; return;
} }
} }
lua_pushusertag(lua_outfile, lua_tagio); setreturn(current, "_OUTPUT");
} }
@ -99,10 +118,8 @@ static void io_appendto (void)
{ {
char *s = luaL_check_string(1); char *s = luaL_check_string(1);
FILE *fp = fopen (s, "a"); FILE *fp = fopen (s, "a");
if (fp != NULL) { if (fp != NULL)
lua_outfile = fp; setreturn(fp, "_OUTPUT");
lua_pushusertag(lua_outfile, lua_tagio);
}
else else
pushresult(0); pushresult(0);
} }
@ -112,6 +129,7 @@ static void io_appendto (void)
static void io_read (void) static void io_read (void)
{ {
FILE *f = getfile("_INPUT");
char *buff; char *buff;
char *p = luaL_opt_string(1, "[^\n]*{\n}"); char *p = luaL_opt_string(1, "[^\n]*{\n}");
int inskip = 0; /* to control {skips} */ int inskip = 0; /* to control {skips} */
@ -131,7 +149,7 @@ static void io_read (void)
else { else {
char *ep = luaL_item_end(p); /* get what is next */ char *ep = luaL_item_end(p); /* get what is next */
int m; /* match result */ int m; /* match result */
if (c == NEED_OTHER) c = getc(lua_infile); if (c == NEED_OTHER) c = getc(f);
m = (c == EOF) ? 0 : luaL_singlematch((char)c, p); m = (c == EOF) ? 0 : luaL_singlematch((char)c, p);
if (m) { if (m) {
if (inskip == 0) luaI_addchar(c); if (inskip == 0) luaI_addchar(c);
@ -152,7 +170,7 @@ static void io_read (void)
} }
} break_while: } break_while:
if (c >= 0) /* not EOF nor NEED_OTHER? */ if (c >= 0) /* not EOF nor NEED_OTHER? */
ungetc(c, lua_infile); ungetc(c, f);
buff = luaI_addchar(0); buff = luaI_addchar(0);
if (*buff != 0 || *p == 0) /* read something or did not fail? */ if (*buff != 0 || *p == 0) /* read something or did not fail? */
lua_pushstring(buff); lua_pushstring(buff);
@ -161,11 +179,12 @@ static void io_read (void)
static void io_write (void) static void io_write (void)
{ {
FILE *f = getfile("_OUTPUT");
int arg = 1; int arg = 1;
int status = 1; int status = 1;
char *s; char *s;
while ((s = luaL_opt_string(arg++, NULL)) != NULL) while ((s = luaL_opt_string(arg++, NULL)) != NULL)
status = status && (fputs(s, lua_outfile) != EOF); status = status && (fputs(s, f) != EOF);
pushresult(status); pushresult(status);
} }
@ -300,7 +319,11 @@ static struct luaL_reg iolib[] = {
void iolib_open (void) void iolib_open (void)
{ {
lua_tagio = lua_newtag(); lua_tagio = lua_newtag();
lua_infile=stdin; lua_outfile=stdout; setfile(stdin, "_INPUT");
setfile(stdout, "_OUTPUT");
setfile(stdin, "_STDIN");
setfile(stdout, "_STDOUT");
setfile(stderr, "_STDERR");
luaL_openlib(iolib, (sizeof(iolib)/sizeof(iolib[0]))); luaL_openlib(iolib, (sizeof(iolib)/sizeof(iolib[0])));
lua_pushcfunction(errorfb); lua_pushcfunction(errorfb);
lua_seterrormethod(); lua_seterrormethod();

View File

@ -1,4 +1,4 @@
% $Id: manual.tex,v 2.4 1997/06/19 18:49:40 roberto Exp roberto $ % $Id: manual.tex,v 2.5 1997/06/20 19:28:16 roberto Exp roberto $
\documentstyle[fullpage,11pt,bnf]{article} \documentstyle[fullpage,11pt,bnf]{article}
@ -38,7 +38,7 @@ Waldemar Celes
\tecgraf\ --- Computer Science Department --- PUC-Rio \tecgraf\ --- Computer Science Department --- PUC-Rio
} }
\date{\small \verb$Date: 1997/06/19 18:49:40 $} \date{\small \verb$Date: 1997/06/20 19:28:16 $}
\maketitle \maketitle
@ -1034,7 +1034,7 @@ This method cannot be set for tables with default tag.
function settable_event (table, index, value) function settable_event (table, index, value)
local tm = gettagmethod(tag(table), "settable") local tm = gettagmethod(tag(table), "settable")
if tm then if tm then
return tm(table, index, value) tm(table, index, value)
elseif type(table) ~= "table" then elseif type(table) ~= "table" then
error("indexed expression not a table") error("indexed expression not a table")
else else
@ -1598,11 +1598,10 @@ If \verb|retmode| is absent,
all results from \verb|func| are just returned by the call. all results from \verb|func| are just returned by the call.
If \verb|retmode| is equal to \verb|"pack"|, If \verb|retmode| is equal to \verb|"pack"|,
the results are {\em packed\/} in a single table.\index{packed results} the results are {\em packed\/} in a single table.\index{packed results}
That is, \verb|call| returns just one table. That is, \verb|call| returns just one table;
At index \verb|n|, the table has the total number of results at index \verb|n|, the table has the total number of results
from the call; from the call;
the first result is at index 1, etc. the first result is at index 1, etc.
For instance, the following calls produce the following results: For instance, the following calls produce the following results:
\begin{verbatim} \begin{verbatim}
a = call(sin, {5}) --> a = 0.0871557 = sin(5) a = call(sin, {5}) --> a = 0.0871557 = sin(5)
@ -1666,7 +1665,9 @@ semantically, there is no difference between a
field not present in a table or a field with value \nil. field not present in a table or a field with value \nil.
Therefore, the function only considers fields with non \nil\ values. Therefore, the function only considers fields with non \nil\ values.
The order in which the indices are enumerated is not specified, The order in which the indices are enumerated is not specified,
{\em not even for numeric indices}. {\em not even for numeric indices}
(to traverse a table in numeric order,
use a counter).
If the table is modified in any way during a traversal, If the table is modified in any way during a traversal,
the semantics of \verb|next| is undefined. the semantics of \verb|next| is undefined.
@ -1904,7 +1905,7 @@ stands for the value of the n-th captured substring.
If \verb|repl| is a function, then this function is called every time a If \verb|repl| is a function, then this function is called every time a
match occurs, with the following arguments: match occurs, with the following arguments:
If \verb|table| is present, the the first argument is this table If \verb|table| is present, then the first argument is this table
and the second one is a match counter (1 for the first call). and the second one is a match counter (1 for the first call).
Independently of these two optional arguments, Independently of these two optional arguments,
all captured substrings are passed as arguments, all captured substrings are passed as arguments,
@ -2072,10 +2073,21 @@ range \Math{[0,1)}.
\subsection{I/O Facilities} \label{libio} \subsection{I/O Facilities} \label{libio}
All input and output operations in Lua are done over two {\em current\/} files: All input and output operations in Lua are done over two
one for reading and one for writing. \Def{file handles}, one for reading and one for writing.
Initially, the current input file is \verb|stdin|, These handles are stored in two Lua global variables,
and the current output file is \verb|stdout|. called \verb|_INPUT| and \verb|_OUTPUT|.
The global variables
\verb|_STDIN|, \verb|_STDOUT| and \verb|_STDERR|
are initialized with file descriptors for
\verb|stdin|, \verb|stdout| and \verb|stderr|.
Initially, \verb|_INPUT=_STDIN| and \verb|_OUTPUT=_STDOUT|.
\Deffunc{_INPUT}\Deffunc{_OUTPUT}
\Deffunc{_STDIN}\Deffunc{_STDOUT}\Deffunc{_STDERR}
A file handle is a userdata containing the file stream \verb|FILE*|,
and with a distinctive tag created by the I/O library.
Unless otherwise stated, Unless otherwise stated,
all I/O functions return \nil\ on failure and all I/O functions return \nil\ on failure and
@ -2083,18 +2095,16 @@ some value different from \nil\ on success.
\subsubsection*{\ff {\tt readfrom (filename)}}\Deffunc{readfrom} \subsubsection*{\ff {\tt readfrom (filename)}}\Deffunc{readfrom}
This function may be called in three ways. This function may be called in two ways.
When called with a file name, When called with a file name, it opens the named file,
it opens the named file, sets its handle as the value of \verb|_INPUT|,
sets it as the {\em current\/} input file, and returns this value.
and returns a {\em handle\/} to the file
(this handle is a userdata containing the file stream \verb|FILE*|).
It does not close the current input file. It does not close the current input file.
When called with a file handle returned by a previous call, %When called with a file handle returned by a previous call,
it restores the file as the current input. %it simply assigns it to \verb|_INPUT|.
When called without parameters, When called without parameters,
it closes the current input file, it closes the \verb|_INPUT| file,
and restores \verb|stdin| as the current input file. and restores \verb|stdin| as the value of \verb|_INPUT|.
If this function fails, it returns \nil, If this function fails, it returns \nil,
plus a string describing the error. plus a string describing the error.
@ -2105,28 +2115,26 @@ plus a string describing the error.
then a \Index{piped input} is open, via function \IndexVerb{popen}. then a \Index{piped input} is open, via function \IndexVerb{popen}.
Not all systems implement pipes. Not all systems implement pipes.
Moreover, Moreover,
the number of files that can be open at the same time is usually limited and the number of files that can be open at the same time is
depends on the system. usually limited and depends on the system.
\end{quotation} \end{quotation}
\subsubsection*{\ff {\tt writeto (filename)}}\Deffunc{writeto} \subsubsection*{\ff {\tt writeto (filename)}}\Deffunc{writeto}
This function may be called in three ways. This function may be called in two ways.
When called with a file name, When called with a file name,
it opens the named file, it opens the named file,
sets it as the {\em current\/} output file, sets its handle as the value of \verb|_OUTPUT|,
and returns a {\em handle\/} to the file and returns this value.
(this handle is a user data containing the file stream \verb|FILE*|).
It does not close the current output file. It does not close the current output file.
Notice that, if the file already exists, Notice that, if the file already exists,
then it will be {\em completely erased\/} with this operation. then it will be {\em completely erased\/} with this operation.
When called with a file handle returned by a previous call, %When called with a file handle returned by a previous call,
it restores the file as the current output. %it restores the file as the current output.
When called without parameters, When called without parameters,
this function closes the current output file, this function closes the \verb|_OUTPUT| file,
and restores \verb|stdout| as the current output file. and restores \verb|stdout| as the value of \verb|_OUTPUT|.
\index{closing a file} \index{closing a file}
%%LHF: nao tem como escrever em stderr, tem?
If this function fails, it returns \nil, If this function fails, it returns \nil,
plus a string describing the error. plus a string describing the error.
@ -2137,16 +2145,14 @@ plus a string describing the error.
then a \Index{piped output} is open, via function \IndexVerb{popen}. then a \Index{piped output} is open, via function \IndexVerb{popen}.
Not all systems implement pipes. Not all systems implement pipes.
Moreover, Moreover,
the number of files that can be open at the same time is usually limited and the number of files that can be open at the same time is
depends on the system. usually limited and depends on the system.
\end{quotation} \end{quotation}
\subsubsection*{\ff {\tt appendto (filename)}}\Deffunc{appendto} \subsubsection*{\ff {\tt appendto (filename)}}\Deffunc{appendto}
This function opens a file named \verb|filename| and sets it as the This function opens a file named \verb|filename| and sets it as the
{\em current\/} output file. value of \verb|_OUTPUT|.
It returns the file handle,
or \nil\ in case of error.
Unlike the \verb|writeto| operation, Unlike the \verb|writeto| operation,
this function does not erase any previous content of the file. this function does not erase any previous content of the file.
If this function fails, it returns \nil, If this function fails, it returns \nil,
@ -2174,7 +2180,7 @@ The file must be explicitly removed when no longer needed.
\subsubsection*{\ff {\tt read ([readpattern])}}\Deffunc{read} \subsubsection*{\ff {\tt read ([readpattern])}}\Deffunc{read}
This function reads the current input This function reads the file \verb|_INPUT|
according to a read pattern, that specifies how much to read; according to a read pattern, that specifies how much to read;
characters are read from the current input file until characters are read from the current input file until
the read pattern fails or ends. the read pattern fails or ends.
@ -2196,7 +2202,7 @@ from the input if it belongs to the class;
it never fails. it never fails.
A character class followed by \verb|*| reads until a character that A character class followed by \verb|*| reads until a character that
does not belong to the class, or end of file; does not belong to the class, or end of file;
since it can match a sequence of zero characteres, it never fails.% since it can match a sequence of zero characters, it never fails.%
\footnote{ \footnote{
Notice that the behavior of read patterns is different from Notice that the behavior of read patterns is different from
the regular pattern matching behavior, the regular pattern matching behavior,
@ -2220,6 +2226,7 @@ Following are some examples of read patterns and their meanings:
This is the default pattern. This is the default pattern.
\item \verb|"{%s*}%S%S*"| returns the next word \item \verb|"{%s*}%S%S*"| returns the next word
(maximal sequence of non white-space characters), (maximal sequence of non white-space characters),
skipping spaces if necessary,
or \nil\ on end of file. or \nil\ on end of file.
\item \verb|"{%s*}[+-]?%d%d*"| returns the next integer \item \verb|"{%s*}[+-]?%d%d*"| returns the next integer
or \nil\ if the next characters do not conform to an integer format. or \nil\ if the next characters do not conform to an integer format.
@ -2228,10 +2235,10 @@ or \nil\ if the next characters do not conform to an integer format.
\subsubsection*{\ff {\tt write (value1, ...)}}\Deffunc{write} \subsubsection*{\ff {\tt write (value1, ...)}}\Deffunc{write}
This function writes the value of each of its arguments to the This function writes the value of each of its arguments to the
current output file. file \verb|_OUTPUT|.
The arguments must be strings or numbers. The arguments must be strings or numbers.
To write other values, To write other values,
use \verb|tostring| before \verb|write|. use \verb|tostring| or \verb|format| before \verb|write|.
If this function fails, it returns \nil, If this function fails, it returns \nil,
plus a string describing the error. plus a string describing the error.