Line |
Branch |
Decision |
Exec |
Source |
1 |
|
|
|
/* |
2 |
|
|
|
** $Id: liolib.c $ |
3 |
|
|
|
** Standard I/O (and system) library |
4 |
|
|
|
** See Copyright Notice in lua.h |
5 |
|
|
|
*/ |
6 |
|
|
|
|
7 |
|
|
|
#define liolib_c |
8 |
|
|
|
#define LUA_LIB |
9 |
|
|
|
|
10 |
|
|
|
#include "lprefix.h" |
11 |
|
|
|
|
12 |
|
|
|
|
13 |
|
|
|
#include <ctype.h> |
14 |
|
|
|
#include <errno.h> |
15 |
|
|
|
#include <locale.h> |
16 |
|
|
|
#include <stdio.h> |
17 |
|
|
|
#include <stdlib.h> |
18 |
|
|
|
#include <string.h> |
19 |
|
|
|
|
20 |
|
|
|
#include "lua.h" |
21 |
|
|
|
|
22 |
|
|
|
#include "lauxlib.h" |
23 |
|
|
|
#include "lualib.h" |
24 |
|
|
|
|
25 |
|
|
|
|
26 |
|
|
|
|
27 |
|
|
|
|
28 |
|
|
|
/* |
29 |
|
|
|
** Change this macro to accept other modes for 'fopen' besides |
30 |
|
|
|
** the standard ones. |
31 |
|
|
|
*/ |
32 |
|
|
|
#if !defined(l_checkmode) |
33 |
|
|
|
|
34 |
|
|
|
/* accepted extensions to 'mode' in 'fopen' */ |
35 |
|
|
|
#if !defined(L_MODEEXT) |
36 |
|
|
|
#define L_MODEEXT "b" |
37 |
|
|
|
#endif |
38 |
|
|
|
|
39 |
|
|
|
/* Check whether 'mode' matches '[rwa]%+?[L_MODEEXT]*' */ |
40 |
|
|
✗ |
static int l_checkmode (const char *mode) { |
41 |
|
|
✗ |
return (*mode != '\0' && strchr("rwa", *(mode++)) != NULL && |
42 |
|
|
✗ |
(*mode != '+' || ((void)(++mode), 1)) && /* skip if char is '+' */ |
43 |
|
|
✗ |
(strspn(mode, L_MODEEXT) == strlen(mode))); /* check extensions */ |
44 |
|
|
|
} |
45 |
|
|
|
|
46 |
|
|
|
#endif |
47 |
|
|
|
|
48 |
|
|
|
/* |
49 |
|
|
|
** {====================================================== |
50 |
|
|
|
** l_popen spawns a new process connected to the current |
51 |
|
|
|
** one through the file streams. |
52 |
|
|
|
** ======================================================= |
53 |
|
|
|
*/ |
54 |
|
|
|
|
55 |
|
|
|
#if !defined(l_popen) /* { */ |
56 |
|
|
|
|
57 |
|
|
|
#if defined(LUA_USE_POSIX) /* { */ |
58 |
|
|
|
|
59 |
|
|
|
#define l_popen(L,c,m) (fflush(NULL), popen(c,m)) |
60 |
|
|
|
#define l_pclose(L,file) (pclose(file)) |
61 |
|
|
|
|
62 |
|
|
|
#elif defined(LUA_USE_WINDOWS) /* }{ */ |
63 |
|
|
|
|
64 |
|
|
|
#define l_popen(L,c,m) (_popen(c,m)) |
65 |
|
|
|
#define l_pclose(L,file) (_pclose(file)) |
66 |
|
|
|
|
67 |
|
|
|
#if !defined(l_checkmodep) |
68 |
|
|
|
/* Windows accepts "[rw][bt]?" as valid modes */ |
69 |
|
|
|
#define l_checkmodep(m) ((m[0] == 'r' || m[0] == 'w') && \ |
70 |
|
|
|
(m[1] == '\0' || ((m[1] == 'b' || m[1] == 't') && m[2] == '\0'))) |
71 |
|
|
|
#endif |
72 |
|
|
|
|
73 |
|
|
|
#else /* }{ */ |
74 |
|
|
|
|
75 |
|
|
|
/* ISO C definitions */ |
76 |
|
|
|
#define l_popen(L,c,m) \ |
77 |
|
|
|
((void)c, (void)m, \ |
78 |
|
|
|
luaL_error(L, "'popen' not supported"), \ |
79 |
|
|
|
(FILE*)0) |
80 |
|
|
|
#define l_pclose(L,file) ((void)L, (void)file, -1) |
81 |
|
|
|
|
82 |
|
|
|
#endif /* } */ |
83 |
|
|
|
|
84 |
|
|
|
#endif /* } */ |
85 |
|
|
|
|
86 |
|
|
|
|
87 |
|
|
|
#if !defined(l_checkmodep) |
88 |
|
|
|
/* By default, Lua accepts only "r" or "w" as valid modes */ |
89 |
|
|
|
#define l_checkmodep(m) ((m[0] == 'r' || m[0] == 'w') && m[1] == '\0') |
90 |
|
|
|
#endif |
91 |
|
|
|
|
92 |
|
|
|
/* }====================================================== */ |
93 |
|
|
|
|
94 |
|
|
|
|
95 |
|
|
|
#if !defined(l_getc) /* { */ |
96 |
|
|
|
|
97 |
|
|
|
#if defined(LUA_USE_POSIX) |
98 |
|
|
|
#define l_getc(f) getc_unlocked(f) |
99 |
|
|
|
#define l_lockfile(f) flockfile(f) |
100 |
|
|
|
#define l_unlockfile(f) funlockfile(f) |
101 |
|
|
|
#else |
102 |
|
|
|
#define l_getc(f) getc(f) |
103 |
|
|
|
#define l_lockfile(f) ((void)0) |
104 |
|
|
|
#define l_unlockfile(f) ((void)0) |
105 |
|
|
|
#endif |
106 |
|
|
|
|
107 |
|
|
|
#endif /* } */ |
108 |
|
|
|
|
109 |
|
|
|
|
110 |
|
|
|
/* |
111 |
|
|
|
** {====================================================== |
112 |
|
|
|
** l_fseek: configuration for longer offsets |
113 |
|
|
|
** ======================================================= |
114 |
|
|
|
*/ |
115 |
|
|
|
|
116 |
|
|
|
#if !defined(l_fseek) /* { */ |
117 |
|
|
|
|
118 |
|
|
|
#if defined(LUA_USE_POSIX) /* { */ |
119 |
|
|
|
|
120 |
|
|
|
#include <sys/types.h> |
121 |
|
|
|
|
122 |
|
|
|
#define l_fseek(f,o,w) fseeko(f,o,w) |
123 |
|
|
|
#define l_ftell(f) ftello(f) |
124 |
|
|
|
#define l_seeknum off_t |
125 |
|
|
|
|
126 |
|
|
|
#elif defined(LUA_USE_WINDOWS) && !defined(_CRTIMP_TYPEINFO) \ |
127 |
|
|
|
&& defined(_MSC_VER) && (_MSC_VER >= 1400) /* }{ */ |
128 |
|
|
|
|
129 |
|
|
|
/* Windows (but not DDK) and Visual C++ 2005 or higher */ |
130 |
|
|
|
#define l_fseek(f,o,w) _fseeki64(f,o,w) |
131 |
|
|
|
#define l_ftell(f) _ftelli64(f) |
132 |
|
|
|
#define l_seeknum __int64 |
133 |
|
|
|
|
134 |
|
|
|
#else /* }{ */ |
135 |
|
|
|
|
136 |
|
|
|
/* ISO C definitions */ |
137 |
|
|
|
#define l_fseek(f,o,w) fseek(f,o,w) |
138 |
|
|
|
#define l_ftell(f) ftell(f) |
139 |
|
|
|
#define l_seeknum long |
140 |
|
|
|
|
141 |
|
|
|
#endif /* } */ |
142 |
|
|
|
|
143 |
|
|
|
#endif /* } */ |
144 |
|
|
|
|
145 |
|
|
|
/* }====================================================== */ |
146 |
|
|
|
|
147 |
|
|
|
|
148 |
|
|
|
|
149 |
|
|
|
#define IO_PREFIX "_IO_" |
150 |
|
|
|
#define IOPREF_LEN (sizeof(IO_PREFIX)/sizeof(char) - 1) |
151 |
|
|
|
#define IO_INPUT (IO_PREFIX "input") |
152 |
|
|
|
#define IO_OUTPUT (IO_PREFIX "output") |
153 |
|
|
|
|
154 |
|
|
|
|
155 |
|
|
|
typedef luaL_Stream LStream; |
156 |
|
|
|
|
157 |
|
|
|
|
158 |
|
|
|
#define tolstream(L) ((LStream *)luaL_checkudata(L, 1, LUA_FILEHANDLE)) |
159 |
|
|
|
|
160 |
|
|
|
#define isclosed(p) ((p)->closef == NULL) |
161 |
|
|
|
|
162 |
|
|
|
|
163 |
|
|
✗ |
static int io_type (lua_State *L) { |
164 |
|
|
|
LStream *p; |
165 |
|
|
✗ |
luaL_checkany(L, 1); |
166 |
|
|
✗ |
p = (LStream *)luaL_testudata(L, 1, LUA_FILEHANDLE); |
167 |
|
|
✗ |
if (p == NULL) |
168 |
|
|
✗ |
luaL_pushfail(L); /* not a file */ |
169 |
|
|
✗ |
else if (isclosed(p)) |
170 |
|
|
✗ |
lua_pushliteral(L, "closed file"); |
171 |
|
|
|
else |
172 |
|
|
✗ |
lua_pushliteral(L, "file"); |
173 |
|
|
✗ |
return 1; |
174 |
|
|
|
} |
175 |
|
|
|
|
176 |
|
|
|
|
177 |
|
|
✗ |
static int f_tostring (lua_State *L) { |
178 |
|
|
✗ |
LStream *p = tolstream(L); |
179 |
|
|
✗ |
if (isclosed(p)) |
180 |
|
|
✗ |
lua_pushliteral(L, "file (closed)"); |
181 |
|
|
|
else |
182 |
|
|
✗ |
lua_pushfstring(L, "file (%p)", p->f); |
183 |
|
|
✗ |
return 1; |
184 |
|
|
|
} |
185 |
|
|
|
|
186 |
|
|
|
|
187 |
|
|
✗ |
static FILE *tofile (lua_State *L) { |
188 |
|
|
✗ |
LStream *p = tolstream(L); |
189 |
|
|
✗ |
if (l_unlikely(isclosed(p))) |
190 |
|
|
✗ |
luaL_error(L, "attempt to use a closed file"); |
191 |
|
|
|
lua_assert(p->f); |
192 |
|
|
✗ |
return p->f; |
193 |
|
|
|
} |
194 |
|
|
|
|
195 |
|
|
|
|
196 |
|
|
|
/* |
197 |
|
|
|
** When creating file handles, always creates a 'closed' file handle |
198 |
|
|
|
** before opening the actual file; so, if there is a memory error, the |
199 |
|
|
|
** handle is in a consistent state. |
200 |
|
|
|
*/ |
201 |
|
|
✗ |
static LStream *newprefile (lua_State *L) { |
202 |
|
|
✗ |
LStream *p = (LStream *)lua_newuserdatauv(L, sizeof(LStream), 0); |
203 |
|
|
✗ |
p->closef = NULL; /* mark file handle as 'closed' */ |
204 |
|
|
✗ |
luaL_setmetatable(L, LUA_FILEHANDLE); |
205 |
|
|
✗ |
return p; |
206 |
|
|
|
} |
207 |
|
|
|
|
208 |
|
|
|
|
209 |
|
|
|
/* |
210 |
|
|
|
** Calls the 'close' function from a file handle. The 'volatile' avoids |
211 |
|
|
|
** a bug in some versions of the Clang compiler (e.g., clang 3.0 for |
212 |
|
|
|
** 32 bits). |
213 |
|
|
|
*/ |
214 |
|
|
✗ |
static int aux_close (lua_State *L) { |
215 |
|
|
✗ |
LStream *p = tolstream(L); |
216 |
|
|
✗ |
volatile lua_CFunction cf = p->closef; |
217 |
|
|
✗ |
p->closef = NULL; /* mark stream as closed */ |
218 |
|
|
✗ |
return (*cf)(L); /* close it */ |
219 |
|
|
|
} |
220 |
|
|
|
|
221 |
|
|
|
|
222 |
|
|
✗ |
static int f_close (lua_State *L) { |
223 |
|
|
✗ |
tofile(L); /* make sure argument is an open stream */ |
224 |
|
|
✗ |
return aux_close(L); |
225 |
|
|
|
} |
226 |
|
|
|
|
227 |
|
|
|
|
228 |
|
|
✗ |
static int io_close (lua_State *L) { |
229 |
|
|
✗ |
if (lua_isnone(L, 1)) /* no argument? */ |
230 |
|
|
✗ |
lua_getfield(L, LUA_REGISTRYINDEX, IO_OUTPUT); /* use default output */ |
231 |
|
|
✗ |
return f_close(L); |
232 |
|
|
|
} |
233 |
|
|
|
|
234 |
|
|
|
|
235 |
|
|
✗ |
static int f_gc (lua_State *L) { |
236 |
|
|
✗ |
LStream *p = tolstream(L); |
237 |
|
|
✗ |
if (!isclosed(p) && p->f != NULL) |
238 |
|
|
✗ |
aux_close(L); /* ignore closed and incompletely open files */ |
239 |
|
|
✗ |
return 0; |
240 |
|
|
|
} |
241 |
|
|
|
|
242 |
|
|
|
|
243 |
|
|
|
/* |
244 |
|
|
|
** function to close regular files |
245 |
|
|
|
*/ |
246 |
|
|
✗ |
static int io_fclose (lua_State *L) { |
247 |
|
|
✗ |
LStream *p = tolstream(L); |
248 |
|
|
✗ |
int res = fclose(p->f); |
249 |
|
|
✗ |
return luaL_fileresult(L, (res == 0), NULL); |
250 |
|
|
|
} |
251 |
|
|
|
|
252 |
|
|
|
|
253 |
|
|
✗ |
static LStream *newfile (lua_State *L) { |
254 |
|
|
✗ |
LStream *p = newprefile(L); |
255 |
|
|
✗ |
p->f = NULL; |
256 |
|
|
✗ |
p->closef = &io_fclose; |
257 |
|
|
✗ |
return p; |
258 |
|
|
|
} |
259 |
|
|
|
|
260 |
|
|
|
|
261 |
|
|
✗ |
static void opencheck (lua_State *L, const char *fname, const char *mode) { |
262 |
|
|
✗ |
LStream *p = newfile(L); |
263 |
|
|
✗ |
p->f = fopen(fname, mode); |
264 |
|
|
✗ |
if (l_unlikely(p->f == NULL)) |
265 |
|
|
✗ |
luaL_error(L, "cannot open file '%s' (%s)", fname, strerror(errno)); |
266 |
|
|
✗ |
} |
267 |
|
|
|
|
268 |
|
|
|
|
269 |
|
|
✗ |
static int io_open (lua_State *L) { |
270 |
|
|
✗ |
const char *filename = luaL_checkstring(L, 1); |
271 |
|
|
✗ |
const char *mode = luaL_optstring(L, 2, "r"); |
272 |
|
|
✗ |
LStream *p = newfile(L); |
273 |
|
|
✗ |
const char *md = mode; /* to traverse/check mode */ |
274 |
|
|
✗ |
luaL_argcheck(L, l_checkmode(md), 2, "invalid mode"); |
275 |
|
|
✗ |
p->f = fopen(filename, mode); |
276 |
|
|
✗ |
return (p->f == NULL) ? luaL_fileresult(L, 0, filename) : 1; |
277 |
|
|
|
} |
278 |
|
|
|
|
279 |
|
|
|
|
280 |
|
|
|
/* |
281 |
|
|
|
** function to close 'popen' files |
282 |
|
|
|
*/ |
283 |
|
|
✗ |
static int io_pclose (lua_State *L) { |
284 |
|
|
✗ |
LStream *p = tolstream(L); |
285 |
|
|
✗ |
errno = 0; |
286 |
|
|
✗ |
return luaL_execresult(L, l_pclose(L, p->f)); |
287 |
|
|
|
} |
288 |
|
|
|
|
289 |
|
|
|
|
290 |
|
|
✗ |
static int io_popen (lua_State *L) { |
291 |
|
|
✗ |
const char *filename = luaL_checkstring(L, 1); |
292 |
|
|
✗ |
const char *mode = luaL_optstring(L, 2, "r"); |
293 |
|
|
✗ |
LStream *p = newprefile(L); |
294 |
|
|
✗ |
luaL_argcheck(L, l_checkmodep(mode), 2, "invalid mode"); |
295 |
|
|
✗ |
p->f = l_popen(L, filename, mode); |
296 |
|
|
✗ |
p->closef = &io_pclose; |
297 |
|
|
✗ |
return (p->f == NULL) ? luaL_fileresult(L, 0, filename) : 1; |
298 |
|
|
|
} |
299 |
|
|
|
|
300 |
|
|
|
|
301 |
|
|
✗ |
static int io_tmpfile (lua_State *L) { |
302 |
|
|
✗ |
LStream *p = newfile(L); |
303 |
|
|
✗ |
p->f = tmpfile(); |
304 |
|
|
✗ |
return (p->f == NULL) ? luaL_fileresult(L, 0, NULL) : 1; |
305 |
|
|
|
} |
306 |
|
|
|
|
307 |
|
|
|
|
308 |
|
|
✗ |
static FILE *getiofile (lua_State *L, const char *findex) { |
309 |
|
|
|
LStream *p; |
310 |
|
|
✗ |
lua_getfield(L, LUA_REGISTRYINDEX, findex); |
311 |
|
|
✗ |
p = (LStream *)lua_touserdata(L, -1); |
312 |
|
|
✗ |
if (l_unlikely(isclosed(p))) |
313 |
|
|
✗ |
luaL_error(L, "default %s file is closed", findex + IOPREF_LEN); |
314 |
|
|
✗ |
return p->f; |
315 |
|
|
|
} |
316 |
|
|
|
|
317 |
|
|
|
|
318 |
|
|
✗ |
static int g_iofile (lua_State *L, const char *f, const char *mode) { |
319 |
|
|
✗ |
if (!lua_isnoneornil(L, 1)) { |
320 |
|
|
✗ |
const char *filename = lua_tostring(L, 1); |
321 |
|
|
✗ |
if (filename) |
322 |
|
|
✗ |
opencheck(L, filename, mode); |
323 |
|
|
|
else { |
324 |
|
|
✗ |
tofile(L); /* check that it's a valid file handle */ |
325 |
|
|
✗ |
lua_pushvalue(L, 1); |
326 |
|
|
|
} |
327 |
|
|
✗ |
lua_setfield(L, LUA_REGISTRYINDEX, f); |
328 |
|
|
|
} |
329 |
|
|
|
/* return current value */ |
330 |
|
|
✗ |
lua_getfield(L, LUA_REGISTRYINDEX, f); |
331 |
|
|
✗ |
return 1; |
332 |
|
|
|
} |
333 |
|
|
|
|
334 |
|
|
|
|
335 |
|
|
✗ |
static int io_input (lua_State *L) { |
336 |
|
|
✗ |
return g_iofile(L, IO_INPUT, "r"); |
337 |
|
|
|
} |
338 |
|
|
|
|
339 |
|
|
|
|
340 |
|
|
✗ |
static int io_output (lua_State *L) { |
341 |
|
|
✗ |
return g_iofile(L, IO_OUTPUT, "w"); |
342 |
|
|
|
} |
343 |
|
|
|
|
344 |
|
|
|
|
345 |
|
|
|
static int io_readline (lua_State *L); |
346 |
|
|
|
|
347 |
|
|
|
|
348 |
|
|
|
/* |
349 |
|
|
|
** maximum number of arguments to 'f:lines'/'io.lines' (it + 3 must fit |
350 |
|
|
|
** in the limit for upvalues of a closure) |
351 |
|
|
|
*/ |
352 |
|
|
|
#define MAXARGLINE 250 |
353 |
|
|
|
|
354 |
|
|
|
/* |
355 |
|
|
|
** Auxiliary function to create the iteration function for 'lines'. |
356 |
|
|
|
** The iteration function is a closure over 'io_readline', with |
357 |
|
|
|
** the following upvalues: |
358 |
|
|
|
** 1) The file being read (first value in the stack) |
359 |
|
|
|
** 2) the number of arguments to read |
360 |
|
|
|
** 3) a boolean, true iff file has to be closed when finished ('toclose') |
361 |
|
|
|
** *) a variable number of format arguments (rest of the stack) |
362 |
|
|
|
*/ |
363 |
|
|
✗ |
static void aux_lines (lua_State *L, int toclose) { |
364 |
|
|
✗ |
int n = lua_gettop(L) - 1; /* number of arguments to read */ |
365 |
|
|
✗ |
luaL_argcheck(L, n <= MAXARGLINE, MAXARGLINE + 2, "too many arguments"); |
366 |
|
|
✗ |
lua_pushvalue(L, 1); /* file */ |
367 |
|
|
✗ |
lua_pushinteger(L, n); /* number of arguments to read */ |
368 |
|
|
✗ |
lua_pushboolean(L, toclose); /* close/not close file when finished */ |
369 |
|
|
✗ |
lua_rotate(L, 2, 3); /* move the three values to their positions */ |
370 |
|
|
✗ |
lua_pushcclosure(L, io_readline, 3 + n); |
371 |
|
|
✗ |
} |
372 |
|
|
|
|
373 |
|
|
|
|
374 |
|
|
✗ |
static int f_lines (lua_State *L) { |
375 |
|
|
✗ |
tofile(L); /* check that it's a valid file handle */ |
376 |
|
|
✗ |
aux_lines(L, 0); |
377 |
|
|
✗ |
return 1; |
378 |
|
|
|
} |
379 |
|
|
|
|
380 |
|
|
|
|
381 |
|
|
|
/* |
382 |
|
|
|
** Return an iteration function for 'io.lines'. If file has to be |
383 |
|
|
|
** closed, also returns the file itself as a second result (to be |
384 |
|
|
|
** closed as the state at the exit of a generic for). |
385 |
|
|
|
*/ |
386 |
|
|
✗ |
static int io_lines (lua_State *L) { |
387 |
|
|
|
int toclose; |
388 |
|
|
✗ |
if (lua_isnone(L, 1)) lua_pushnil(L); /* at least one argument */ |
389 |
|
|
✗ |
if (lua_isnil(L, 1)) { /* no file name? */ |
390 |
|
|
✗ |
lua_getfield(L, LUA_REGISTRYINDEX, IO_INPUT); /* get default input */ |
391 |
|
|
✗ |
lua_replace(L, 1); /* put it at index 1 */ |
392 |
|
|
✗ |
tofile(L); /* check that it's a valid file handle */ |
393 |
|
|
✗ |
toclose = 0; /* do not close it after iteration */ |
394 |
|
|
|
} |
395 |
|
|
|
else { /* open a new file */ |
396 |
|
|
✗ |
const char *filename = luaL_checkstring(L, 1); |
397 |
|
|
✗ |
opencheck(L, filename, "r"); |
398 |
|
|
✗ |
lua_replace(L, 1); /* put file at index 1 */ |
399 |
|
|
✗ |
toclose = 1; /* close it after iteration */ |
400 |
|
|
|
} |
401 |
|
|
✗ |
aux_lines(L, toclose); /* push iteration function */ |
402 |
|
|
✗ |
if (toclose) { |
403 |
|
|
✗ |
lua_pushnil(L); /* state */ |
404 |
|
|
✗ |
lua_pushnil(L); /* control */ |
405 |
|
|
✗ |
lua_pushvalue(L, 1); /* file is the to-be-closed variable (4th result) */ |
406 |
|
|
✗ |
return 4; |
407 |
|
|
|
} |
408 |
|
|
|
else |
409 |
|
|
✗ |
return 1; |
410 |
|
|
|
} |
411 |
|
|
|
|
412 |
|
|
|
|
413 |
|
|
|
/* |
414 |
|
|
|
** {====================================================== |
415 |
|
|
|
** READ |
416 |
|
|
|
** ======================================================= |
417 |
|
|
|
*/ |
418 |
|
|
|
|
419 |
|
|
|
|
420 |
|
|
|
/* maximum length of a numeral */ |
421 |
|
|
|
#if !defined (L_MAXLENNUM) |
422 |
|
|
|
#define L_MAXLENNUM 200 |
423 |
|
|
|
#endif |
424 |
|
|
|
|
425 |
|
|
|
|
426 |
|
|
|
/* auxiliary structure used by 'read_number' */ |
427 |
|
|
|
typedef struct { |
428 |
|
|
|
FILE *f; /* file being read */ |
429 |
|
|
|
int c; /* current character (look ahead) */ |
430 |
|
|
|
int n; /* number of elements in buffer 'buff' */ |
431 |
|
|
|
char buff[L_MAXLENNUM + 1]; /* +1 for ending '\0' */ |
432 |
|
|
|
} RN; |
433 |
|
|
|
|
434 |
|
|
|
|
435 |
|
|
|
/* |
436 |
|
|
|
** Add current char to buffer (if not out of space) and read next one |
437 |
|
|
|
*/ |
438 |
|
|
✗ |
static int nextc (RN *rn) { |
439 |
|
|
✗ |
if (l_unlikely(rn->n >= L_MAXLENNUM)) { /* buffer overflow? */ |
440 |
|
|
✗ |
rn->buff[0] = '\0'; /* invalidate result */ |
441 |
|
|
✗ |
return 0; /* fail */ |
442 |
|
|
|
} |
443 |
|
|
|
else { |
444 |
|
|
✗ |
rn->buff[rn->n++] = rn->c; /* save current char */ |
445 |
|
|
✗ |
rn->c = l_getc(rn->f); /* read next one */ |
446 |
|
|
✗ |
return 1; |
447 |
|
|
|
} |
448 |
|
|
|
} |
449 |
|
|
|
|
450 |
|
|
|
|
451 |
|
|
|
/* |
452 |
|
|
|
** Accept current char if it is in 'set' (of size 2) |
453 |
|
|
|
*/ |
454 |
|
|
✗ |
static int test2 (RN *rn, const char *set) { |
455 |
|
|
✗ |
if (rn->c == set[0] || rn->c == set[1]) |
456 |
|
|
✗ |
return nextc(rn); |
457 |
|
|
✗ |
else return 0; |
458 |
|
|
|
} |
459 |
|
|
|
|
460 |
|
|
|
|
461 |
|
|
|
/* |
462 |
|
|
|
** Read a sequence of (hex)digits |
463 |
|
|
|
*/ |
464 |
|
|
✗ |
static int readdigits (RN *rn, int hex) { |
465 |
|
|
✗ |
int count = 0; |
466 |
|
|
✗ |
while ((hex ? isxdigit(rn->c) : isdigit(rn->c)) && nextc(rn)) |
467 |
|
|
✗ |
count++; |
468 |
|
|
✗ |
return count; |
469 |
|
|
|
} |
470 |
|
|
|
|
471 |
|
|
|
|
472 |
|
|
|
/* |
473 |
|
|
|
** Read a number: first reads a valid prefix of a numeral into a buffer. |
474 |
|
|
|
** Then it calls 'lua_stringtonumber' to check whether the format is |
475 |
|
|
|
** correct and to convert it to a Lua number. |
476 |
|
|
|
*/ |
477 |
|
|
✗ |
static int read_number (lua_State *L, FILE *f) { |
478 |
|
|
|
RN rn; |
479 |
|
|
✗ |
int count = 0; |
480 |
|
|
✗ |
int hex = 0; |
481 |
|
|
|
char decp[2]; |
482 |
|
|
✗ |
rn.f = f; rn.n = 0; |
483 |
|
|
✗ |
decp[0] = lua_getlocaledecpoint(); /* get decimal point from locale */ |
484 |
|
|
✗ |
decp[1] = '.'; /* always accept a dot */ |
485 |
|
|
|
l_lockfile(rn.f); |
486 |
|
|
✗ |
do { rn.c = l_getc(rn.f); } while (isspace(rn.c)); /* skip spaces */ |
487 |
|
|
✗ |
test2(&rn, "-+"); /* optional sign */ |
488 |
|
|
✗ |
if (test2(&rn, "00")) { |
489 |
|
|
✗ |
if (test2(&rn, "xX")) hex = 1; /* numeral is hexadecimal */ |
490 |
|
|
✗ |
else count = 1; /* count initial '0' as a valid digit */ |
491 |
|
|
|
} |
492 |
|
|
✗ |
count += readdigits(&rn, hex); /* integral part */ |
493 |
|
|
✗ |
if (test2(&rn, decp)) /* decimal point? */ |
494 |
|
|
✗ |
count += readdigits(&rn, hex); /* fractional part */ |
495 |
|
|
✗ |
if (count > 0 && test2(&rn, (hex ? "pP" : "eE"))) { /* exponent mark? */ |
496 |
|
|
✗ |
test2(&rn, "-+"); /* exponent sign */ |
497 |
|
|
✗ |
readdigits(&rn, 0); /* exponent digits */ |
498 |
|
|
|
} |
499 |
|
|
✗ |
ungetc(rn.c, rn.f); /* unread look-ahead char */ |
500 |
|
|
|
l_unlockfile(rn.f); |
501 |
|
|
✗ |
rn.buff[rn.n] = '\0'; /* finish string */ |
502 |
|
|
✗ |
if (l_likely(lua_stringtonumber(L, rn.buff))) |
503 |
|
|
✗ |
return 1; /* ok, it is a valid number */ |
504 |
|
|
|
else { /* invalid format */ |
505 |
|
|
✗ |
lua_pushnil(L); /* "result" to be removed */ |
506 |
|
|
✗ |
return 0; /* read fails */ |
507 |
|
|
|
} |
508 |
|
|
|
} |
509 |
|
|
|
|
510 |
|
|
|
|
511 |
|
|
✗ |
static int test_eof (lua_State *L, FILE *f) { |
512 |
|
|
✗ |
int c = getc(f); |
513 |
|
|
✗ |
ungetc(c, f); /* no-op when c == EOF */ |
514 |
|
|
✗ |
lua_pushliteral(L, ""); |
515 |
|
|
✗ |
return (c != EOF); |
516 |
|
|
|
} |
517 |
|
|
|
|
518 |
|
|
|
|
519 |
|
|
✗ |
static int read_line (lua_State *L, FILE *f, int chop) { |
520 |
|
|
|
luaL_Buffer b; |
521 |
|
|
|
int c; |
522 |
|
|
✗ |
luaL_buffinit(L, &b); |
523 |
|
|
|
do { /* may need to read several chunks to get whole line */ |
524 |
|
|
✗ |
char *buff = luaL_prepbuffer(&b); /* preallocate buffer space */ |
525 |
|
|
✗ |
int i = 0; |
526 |
|
|
|
l_lockfile(f); /* no memory errors can happen inside the lock */ |
527 |
|
|
✗ |
while (i < LUAL_BUFFERSIZE && (c = l_getc(f)) != EOF && c != '\n') |
528 |
|
|
✗ |
buff[i++] = c; /* read up to end of line or buffer limit */ |
529 |
|
|
|
l_unlockfile(f); |
530 |
|
|
✗ |
luaL_addsize(&b, i); |
531 |
|
|
✗ |
} while (c != EOF && c != '\n'); /* repeat until end of line */ |
532 |
|
|
✗ |
if (!chop && c == '\n') /* want a newline and have one? */ |
533 |
|
|
✗ |
luaL_addchar(&b, c); /* add ending newline to result */ |
534 |
|
|
✗ |
luaL_pushresult(&b); /* close buffer */ |
535 |
|
|
|
/* return ok if read something (either a newline or something else) */ |
536 |
|
|
✗ |
return (c == '\n' || lua_rawlen(L, -1) > 0); |
537 |
|
|
|
} |
538 |
|
|
|
|
539 |
|
|
|
|
540 |
|
|
✗ |
static void read_all (lua_State *L, FILE *f) { |
541 |
|
|
|
size_t nr; |
542 |
|
|
|
luaL_Buffer b; |
543 |
|
|
✗ |
luaL_buffinit(L, &b); |
544 |
|
|
|
do { /* read file in chunks of LUAL_BUFFERSIZE bytes */ |
545 |
|
|
✗ |
char *p = luaL_prepbuffer(&b); |
546 |
|
|
✗ |
nr = fread(p, sizeof(char), LUAL_BUFFERSIZE, f); |
547 |
|
|
✗ |
luaL_addsize(&b, nr); |
548 |
|
|
✗ |
} while (nr == LUAL_BUFFERSIZE); |
549 |
|
|
✗ |
luaL_pushresult(&b); /* close buffer */ |
550 |
|
|
✗ |
} |
551 |
|
|
|
|
552 |
|
|
|
|
553 |
|
|
✗ |
static int read_chars (lua_State *L, FILE *f, size_t n) { |
554 |
|
|
|
size_t nr; /* number of chars actually read */ |
555 |
|
|
|
char *p; |
556 |
|
|
|
luaL_Buffer b; |
557 |
|
|
✗ |
luaL_buffinit(L, &b); |
558 |
|
|
✗ |
p = luaL_prepbuffsize(&b, n); /* prepare buffer to read whole block */ |
559 |
|
|
✗ |
nr = fread(p, sizeof(char), n, f); /* try to read 'n' chars */ |
560 |
|
|
✗ |
luaL_addsize(&b, nr); |
561 |
|
|
✗ |
luaL_pushresult(&b); /* close buffer */ |
562 |
|
|
✗ |
return (nr > 0); /* true iff read something */ |
563 |
|
|
|
} |
564 |
|
|
|
|
565 |
|
|
|
|
566 |
|
|
✗ |
static int g_read (lua_State *L, FILE *f, int first) { |
567 |
|
|
✗ |
int nargs = lua_gettop(L) - 1; |
568 |
|
|
|
int n, success; |
569 |
|
|
✗ |
clearerr(f); |
570 |
|
|
✗ |
if (nargs == 0) { /* no arguments? */ |
571 |
|
|
✗ |
success = read_line(L, f, 1); |
572 |
|
|
✗ |
n = first + 1; /* to return 1 result */ |
573 |
|
|
|
} |
574 |
|
|
|
else { |
575 |
|
|
|
/* ensure stack space for all results and for auxlib's buffer */ |
576 |
|
|
✗ |
luaL_checkstack(L, nargs+LUA_MINSTACK, "too many arguments"); |
577 |
|
|
✗ |
success = 1; |
578 |
|
|
✗ |
for (n = first; nargs-- && success; n++) { |
579 |
|
|
✗ |
if (lua_type(L, n) == LUA_TNUMBER) { |
580 |
|
|
✗ |
size_t l = (size_t)luaL_checkinteger(L, n); |
581 |
|
|
✗ |
success = (l == 0) ? test_eof(L, f) : read_chars(L, f, l); |
582 |
|
|
|
} |
583 |
|
|
|
else { |
584 |
|
|
✗ |
const char *p = luaL_checkstring(L, n); |
585 |
|
|
✗ |
if (*p == '*') p++; /* skip optional '*' (for compatibility) */ |
586 |
|
|
✗ |
switch (*p) { |
587 |
|
|
✗ |
case 'n': /* number */ |
588 |
|
|
✗ |
success = read_number(L, f); |
589 |
|
|
✗ |
break; |
590 |
|
|
✗ |
case 'l': /* line */ |
591 |
|
|
✗ |
success = read_line(L, f, 1); |
592 |
|
|
✗ |
break; |
593 |
|
|
✗ |
case 'L': /* line with end-of-line */ |
594 |
|
|
✗ |
success = read_line(L, f, 0); |
595 |
|
|
✗ |
break; |
596 |
|
|
✗ |
case 'a': /* file */ |
597 |
|
|
✗ |
read_all(L, f); /* read entire file */ |
598 |
|
|
✗ |
success = 1; /* always success */ |
599 |
|
|
✗ |
break; |
600 |
|
|
✗ |
default: |
601 |
|
|
✗ |
return luaL_argerror(L, n, "invalid format"); |
602 |
|
|
|
} |
603 |
|
|
|
} |
604 |
|
|
|
} |
605 |
|
|
|
} |
606 |
|
|
✗ |
if (ferror(f)) |
607 |
|
|
✗ |
return luaL_fileresult(L, 0, NULL); |
608 |
|
|
✗ |
if (!success) { |
609 |
|
|
✗ |
lua_pop(L, 1); /* remove last result */ |
610 |
|
|
✗ |
luaL_pushfail(L); /* push nil instead */ |
611 |
|
|
|
} |
612 |
|
|
✗ |
return n - first; |
613 |
|
|
|
} |
614 |
|
|
|
|
615 |
|
|
|
|
616 |
|
|
✗ |
static int io_read (lua_State *L) { |
617 |
|
|
✗ |
return g_read(L, getiofile(L, IO_INPUT), 1); |
618 |
|
|
|
} |
619 |
|
|
|
|
620 |
|
|
|
|
621 |
|
|
✗ |
static int f_read (lua_State *L) { |
622 |
|
|
✗ |
return g_read(L, tofile(L), 2); |
623 |
|
|
|
} |
624 |
|
|
|
|
625 |
|
|
|
|
626 |
|
|
|
/* |
627 |
|
|
|
** Iteration function for 'lines'. |
628 |
|
|
|
*/ |
629 |
|
|
✗ |
static int io_readline (lua_State *L) { |
630 |
|
|
✗ |
LStream *p = (LStream *)lua_touserdata(L, lua_upvalueindex(1)); |
631 |
|
|
|
int i; |
632 |
|
|
✗ |
int n = (int)lua_tointeger(L, lua_upvalueindex(2)); |
633 |
|
|
✗ |
if (isclosed(p)) /* file is already closed? */ |
634 |
|
|
✗ |
return luaL_error(L, "file is already closed"); |
635 |
|
|
✗ |
lua_settop(L , 1); |
636 |
|
|
✗ |
luaL_checkstack(L, n, "too many arguments"); |
637 |
|
|
✗ |
for (i = 1; i <= n; i++) /* push arguments to 'g_read' */ |
638 |
|
|
✗ |
lua_pushvalue(L, lua_upvalueindex(3 + i)); |
639 |
|
|
✗ |
n = g_read(L, p->f, 2); /* 'n' is number of results */ |
640 |
|
|
|
lua_assert(n > 0); /* should return at least a nil */ |
641 |
|
|
✗ |
if (lua_toboolean(L, -n)) /* read at least one value? */ |
642 |
|
|
✗ |
return n; /* return them */ |
643 |
|
|
|
else { /* first result is false: EOF or error */ |
644 |
|
|
✗ |
if (n > 1) { /* is there error information? */ |
645 |
|
|
|
/* 2nd result is error message */ |
646 |
|
|
✗ |
return luaL_error(L, "%s", lua_tostring(L, -n + 1)); |
647 |
|
|
|
} |
648 |
|
|
✗ |
if (lua_toboolean(L, lua_upvalueindex(3))) { /* generator created file? */ |
649 |
|
|
✗ |
lua_settop(L, 0); /* clear stack */ |
650 |
|
|
✗ |
lua_pushvalue(L, lua_upvalueindex(1)); /* push file at index 1 */ |
651 |
|
|
✗ |
aux_close(L); /* close it */ |
652 |
|
|
|
} |
653 |
|
|
✗ |
return 0; |
654 |
|
|
|
} |
655 |
|
|
|
} |
656 |
|
|
|
|
657 |
|
|
|
/* }====================================================== */ |
658 |
|
|
|
|
659 |
|
|
|
|
660 |
|
|
✗ |
static int g_write (lua_State *L, FILE *f, int arg) { |
661 |
|
|
✗ |
int nargs = lua_gettop(L) - arg; |
662 |
|
|
✗ |
int status = 1; |
663 |
|
|
✗ |
for (; nargs--; arg++) { |
664 |
|
|
✗ |
if (lua_type(L, arg) == LUA_TNUMBER) { |
665 |
|
|
|
/* optimization: could be done exactly as for strings */ |
666 |
|
|
✗ |
int len = lua_isinteger(L, arg) |
667 |
|
|
✗ |
? fprintf(f, LUA_INTEGER_FMT, |
668 |
|
|
✗ |
(LUAI_UACINT)lua_tointeger(L, arg)) |
669 |
|
|
✗ |
: fprintf(f, LUA_NUMBER_FMT, |
670 |
|
|
✗ |
(LUAI_UACNUMBER)lua_tonumber(L, arg)); |
671 |
|
|
✗ |
status = status && (len > 0); |
672 |
|
|
|
} |
673 |
|
|
|
else { |
674 |
|
|
|
size_t l; |
675 |
|
|
✗ |
const char *s = luaL_checklstring(L, arg, &l); |
676 |
|
|
✗ |
status = status && (fwrite(s, sizeof(char), l, f) == l); |
677 |
|
|
|
} |
678 |
|
|
|
} |
679 |
|
|
✗ |
if (l_likely(status)) |
680 |
|
|
✗ |
return 1; /* file handle already on stack top */ |
681 |
|
|
✗ |
else return luaL_fileresult(L, status, NULL); |
682 |
|
|
|
} |
683 |
|
|
|
|
684 |
|
|
|
|
685 |
|
|
✗ |
static int io_write (lua_State *L) { |
686 |
|
|
✗ |
return g_write(L, getiofile(L, IO_OUTPUT), 1); |
687 |
|
|
|
} |
688 |
|
|
|
|
689 |
|
|
|
|
690 |
|
|
✗ |
static int f_write (lua_State *L) { |
691 |
|
|
✗ |
FILE *f = tofile(L); |
692 |
|
|
✗ |
lua_pushvalue(L, 1); /* push file at the stack top (to be returned) */ |
693 |
|
|
✗ |
return g_write(L, f, 2); |
694 |
|
|
|
} |
695 |
|
|
|
|
696 |
|
|
|
|
697 |
|
|
✗ |
static int f_seek (lua_State *L) { |
698 |
|
|
|
static const int mode[] = {SEEK_SET, SEEK_CUR, SEEK_END}; |
699 |
|
|
|
static const char *const modenames[] = {"set", "cur", "end", NULL}; |
700 |
|
|
✗ |
FILE *f = tofile(L); |
701 |
|
|
✗ |
int op = luaL_checkoption(L, 2, "cur", modenames); |
702 |
|
|
✗ |
lua_Integer p3 = luaL_optinteger(L, 3, 0); |
703 |
|
|
✗ |
l_seeknum offset = (l_seeknum)p3; |
704 |
|
|
✗ |
luaL_argcheck(L, (lua_Integer)offset == p3, 3, |
705 |
|
|
|
"not an integer in proper range"); |
706 |
|
|
✗ |
op = l_fseek(f, offset, mode[op]); |
707 |
|
|
✗ |
if (l_unlikely(op)) |
708 |
|
|
✗ |
return luaL_fileresult(L, 0, NULL); /* error */ |
709 |
|
|
|
else { |
710 |
|
|
✗ |
lua_pushinteger(L, (lua_Integer)l_ftell(f)); |
711 |
|
|
✗ |
return 1; |
712 |
|
|
|
} |
713 |
|
|
|
} |
714 |
|
|
|
|
715 |
|
|
|
|
716 |
|
|
✗ |
static int f_setvbuf (lua_State *L) { |
717 |
|
|
|
static const int mode[] = {_IONBF, _IOFBF, _IOLBF}; |
718 |
|
|
|
static const char *const modenames[] = {"no", "full", "line", NULL}; |
719 |
|
|
✗ |
FILE *f = tofile(L); |
720 |
|
|
✗ |
int op = luaL_checkoption(L, 2, NULL, modenames); |
721 |
|
|
✗ |
lua_Integer sz = luaL_optinteger(L, 3, LUAL_BUFFERSIZE); |
722 |
|
|
✗ |
int res = setvbuf(f, NULL, mode[op], (size_t)sz); |
723 |
|
|
✗ |
return luaL_fileresult(L, res == 0, NULL); |
724 |
|
|
|
} |
725 |
|
|
|
|
726 |
|
|
|
|
727 |
|
|
|
|
728 |
|
|
✗ |
static int io_flush (lua_State *L) { |
729 |
|
|
✗ |
return luaL_fileresult(L, fflush(getiofile(L, IO_OUTPUT)) == 0, NULL); |
730 |
|
|
|
} |
731 |
|
|
|
|
732 |
|
|
|
|
733 |
|
|
✗ |
static int f_flush (lua_State *L) { |
734 |
|
|
✗ |
return luaL_fileresult(L, fflush(tofile(L)) == 0, NULL); |
735 |
|
|
|
} |
736 |
|
|
|
|
737 |
|
|
|
|
738 |
|
|
|
/* |
739 |
|
|
|
** functions for 'io' library |
740 |
|
|
|
*/ |
741 |
|
|
|
static const luaL_Reg iolib[] = { |
742 |
|
|
|
{"close", io_close}, |
743 |
|
|
|
{"flush", io_flush}, |
744 |
|
|
|
{"input", io_input}, |
745 |
|
|
|
{"lines", io_lines}, |
746 |
|
|
|
{"open", io_open}, |
747 |
|
|
|
{"output", io_output}, |
748 |
|
|
|
{"popen", io_popen}, |
749 |
|
|
|
{"read", io_read}, |
750 |
|
|
|
{"tmpfile", io_tmpfile}, |
751 |
|
|
|
{"type", io_type}, |
752 |
|
|
|
{"write", io_write}, |
753 |
|
|
|
{NULL, NULL} |
754 |
|
|
|
}; |
755 |
|
|
|
|
756 |
|
|
|
|
757 |
|
|
|
/* |
758 |
|
|
|
** methods for file handles |
759 |
|
|
|
*/ |
760 |
|
|
|
static const luaL_Reg meth[] = { |
761 |
|
|
|
{"read", f_read}, |
762 |
|
|
|
{"write", f_write}, |
763 |
|
|
|
{"lines", f_lines}, |
764 |
|
|
|
{"flush", f_flush}, |
765 |
|
|
|
{"seek", f_seek}, |
766 |
|
|
|
{"close", f_close}, |
767 |
|
|
|
{"setvbuf", f_setvbuf}, |
768 |
|
|
|
{NULL, NULL} |
769 |
|
|
|
}; |
770 |
|
|
|
|
771 |
|
|
|
|
772 |
|
|
|
/* |
773 |
|
|
|
** metamethods for file handles |
774 |
|
|
|
*/ |
775 |
|
|
|
static const luaL_Reg metameth[] = { |
776 |
|
|
|
{"__index", NULL}, /* place holder */ |
777 |
|
|
|
{"__gc", f_gc}, |
778 |
|
|
|
{"__close", f_gc}, |
779 |
|
|
|
{"__tostring", f_tostring}, |
780 |
|
|
|
{NULL, NULL} |
781 |
|
|
|
}; |
782 |
|
|
|
|
783 |
|
|
|
|
784 |
|
|
✗ |
static void createmeta (lua_State *L) { |
785 |
|
|
✗ |
luaL_newmetatable(L, LUA_FILEHANDLE); /* metatable for file handles */ |
786 |
|
|
✗ |
luaL_setfuncs(L, metameth, 0); /* add metamethods to new metatable */ |
787 |
|
|
✗ |
luaL_newlibtable(L, meth); /* create method table */ |
788 |
|
|
✗ |
luaL_setfuncs(L, meth, 0); /* add file methods to method table */ |
789 |
|
|
✗ |
lua_setfield(L, -2, "__index"); /* metatable.__index = method table */ |
790 |
|
|
✗ |
lua_pop(L, 1); /* pop metatable */ |
791 |
|
|
✗ |
} |
792 |
|
|
|
|
793 |
|
|
|
|
794 |
|
|
|
/* |
795 |
|
|
|
** function to (not) close the standard files stdin, stdout, and stderr |
796 |
|
|
|
*/ |
797 |
|
|
✗ |
static int io_noclose (lua_State *L) { |
798 |
|
|
✗ |
LStream *p = tolstream(L); |
799 |
|
|
✗ |
p->closef = &io_noclose; /* keep file opened */ |
800 |
|
|
✗ |
luaL_pushfail(L); |
801 |
|
|
✗ |
lua_pushliteral(L, "cannot close standard file"); |
802 |
|
|
✗ |
return 2; |
803 |
|
|
|
} |
804 |
|
|
|
|
805 |
|
|
|
|
806 |
|
|
✗ |
static void createstdfile (lua_State *L, FILE *f, const char *k, |
807 |
|
|
|
const char *fname) { |
808 |
|
|
✗ |
LStream *p = newprefile(L); |
809 |
|
|
✗ |
p->f = f; |
810 |
|
|
✗ |
p->closef = &io_noclose; |
811 |
|
|
✗ |
if (k != NULL) { |
812 |
|
|
✗ |
lua_pushvalue(L, -1); |
813 |
|
|
✗ |
lua_setfield(L, LUA_REGISTRYINDEX, k); /* add file to registry */ |
814 |
|
|
|
} |
815 |
|
|
✗ |
lua_setfield(L, -2, fname); /* add file to module */ |
816 |
|
|
✗ |
} |
817 |
|
|
|
|
818 |
|
|
|
|
819 |
|
|
✗ |
LUAMOD_API int luaopen_io (lua_State *L) { |
820 |
|
|
✗ |
luaL_newlib(L, iolib); /* new module */ |
821 |
|
|
✗ |
createmeta(L); |
822 |
|
|
|
/* create (and set) default files */ |
823 |
|
|
✗ |
createstdfile(L, stdin, IO_INPUT, "stdin"); |
824 |
|
|
✗ |
createstdfile(L, stdout, IO_OUTPUT, "stdout"); |
825 |
|
|
✗ |
createstdfile(L, stderr, NULL, "stderr"); |
826 |
|
|
✗ |
return 1; |
827 |
|
|
|
} |
828 |
|
|
|
|
829 |
|
|
|
|