1
/* Jim - ANSI I/O extension
2
* Copyright 2005 Salvatore Sanfilippo <antirez@invece.org>
4
* $Id: jim-aio.c,v 1.1 2006/04/13 19:06:27 arjenmarkus Exp $
6
* Licensed under the Apache License, Version 2.0 (the "License");
7
* you may not use this file except in compliance with the License.
8
* You may obtain a copy of the License at
10
* http://www.apache.org/licenses/LICENSE-2.0
12
* A copy of the license is also included in the source distribution
13
* of Jim, as a TXT file name called LICENSE.
15
* Unless required by applicable law or agreed to in writing, software
16
* distributed under the License is distributed on an "AS IS" BASIS,
17
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18
* See the License for the specific language governing permissions and
19
* limitations under the License.
29
#define AIO_CMD_LEN 128
30
#define AIO_BUF_LEN 1024
32
typedef struct AioFile {
34
int keepOpen; /* If set, the file is not fclosed on cleanup (stdin, ...) */
37
static void JimAioSetError(Jim_Interp *interp)
39
Jim_SetResultString(interp, strerror(errno), -1);
42
static void JimAioDelProc(Jim_Interp *interp, void *privData)
44
AioFile *af = privData;
52
/* Calls to [aio.file] create commands that are implemented by this
54
static int JimAioHandlerCommand(Jim_Interp *interp, int argc,
57
AioFile *af = Jim_CmdPrivData(interp);
59
const char *options[] = {
60
"close", "seek", "tell", "gets", "read", "puts", "flush", "eof", NULL
62
enum {OPT_CLOSE, OPT_SEEK, OPT_TELL, OPT_GETS, OPT_READ, OPT_PUTS,
66
Jim_WrongNumArgs(interp, 1, argv, "method ?args ...?");
69
if (Jim_GetEnum(interp, argv[1], options, &option, "AIO method",
70
JIM_ERRMSG) != JIM_OK)
73
if (option == OPT_CLOSE) {
75
Jim_WrongNumArgs(interp, 2, argv, "");
78
Jim_DeleteCommand(interp, Jim_GetString(argv[0], NULL));
80
} else if (option == OPT_SEEK) {
85
if (argc != 3 && argc != 4) {
86
Jim_WrongNumArgs(interp, 2, argv, "offset ?origin?");
90
if (Jim_CompareStringImmediate(interp, argv[3], "start"))
92
else if (Jim_CompareStringImmediate(interp, argv[3], "current"))
94
else if (Jim_CompareStringImmediate(interp, argv[3], "end"))
97
Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
98
Jim_AppendStrings(interp, Jim_GetResult(interp),
99
"bad origin \"", Jim_GetString(argv[3], NULL),
100
"\" must be: start, current, or end", NULL);
104
if (Jim_GetLong(interp, argv[2], &offset) != JIM_OK)
106
if (fseek(af->fp, offset, orig) == -1) {
107
JimAioSetError(interp);
111
} else if (option == OPT_TELL) {
116
Jim_WrongNumArgs(interp, 2, argv, "");
119
position = ftell(af->fp);
120
Jim_SetResult(interp, Jim_NewIntObj(interp, position));
122
} else if (option == OPT_GETS) {
124
char buf[AIO_BUF_LEN];
127
if (argc != 2 && argc != 3) {
128
Jim_WrongNumArgs(interp, 2, argv, "?varName?");
131
objPtr = Jim_NewStringObj(interp, NULL, 0);
134
buf[AIO_BUF_LEN-1] = '_';
135
if (fgets(buf, AIO_BUF_LEN, af->fp) == NULL)
137
if (buf[AIO_BUF_LEN-1] == '\0' && buf[AIO_BUF_LEN] == '\n')
140
Jim_AppendString(interp, objPtr, buf, AIO_BUF_LEN-1);
143
Jim_AppendString(interp, objPtr, buf, strlen(buf)-1);
148
if (ferror(af->fp)) {
150
Jim_IncrRefCount(objPtr);
151
Jim_DecrRefCount(interp, objPtr);
152
JimAioSetError(interp);
155
/* On EOF returns -1 if varName was specified, or the empty string. */
156
if (feof(af->fp) && Jim_Length(objPtr) == 0) {
157
Jim_IncrRefCount(objPtr);
158
Jim_DecrRefCount(interp, objPtr);
160
Jim_SetResult(interp, Jim_NewIntObj(interp, -1));
166
Jim_GetString(objPtr, &totLen);
167
if (Jim_SetVariable(interp, argv[2], objPtr) != JIM_OK) {
168
Jim_IncrRefCount(objPtr);
169
Jim_DecrRefCount(interp, objPtr);
172
Jim_SetResult(interp, Jim_NewIntObj(interp, totLen));
174
Jim_SetResult(interp, objPtr);
177
} else if (option == OPT_READ) {
179
char buf[AIO_BUF_LEN];
182
int neededLen = -1; /* -1 is "read as much as possible" */
184
if (argc != 2 && argc != 3) {
185
Jim_WrongNumArgs(interp, 2, argv, "?-nonewline? ?len?");
189
Jim_CompareStringImmediate(interp, argv[2], "-nonewline"))
197
if (Jim_GetWide(interp, argv[2], &wideValue) != JIM_OK)
200
Jim_SetResultString(interp, "invalid parameter: negative len",
204
neededLen = (int) wideValue;
206
objPtr = Jim_NewStringObj(interp, NULL, 0);
207
while (neededLen != 0) {
211
if (neededLen == -1) {
212
readlen = AIO_BUF_LEN;
214
readlen = (neededLen > AIO_BUF_LEN ? AIO_BUF_LEN : neededLen);
216
retval = fread(buf, 1, readlen, af->fp);
218
Jim_AppendString(interp, objPtr, buf, retval);
219
if (neededLen != -1) {
223
if (retval != readlen) break;
225
/* Check for error conditions */
226
if (ferror(af->fp)) {
228
Jim_FreeNewObj(interp, objPtr);
229
JimAioSetError(interp);
234
const char *s = Jim_GetString(objPtr, &len);
236
if (len > 0 && s[len-1] == '\n') {
238
objPtr->bytes[objPtr->length] = '\0';
241
Jim_SetResult(interp, objPtr);
243
} else if (option == OPT_PUTS) {
248
if (argc != 3 && (argc != 4 || !Jim_CompareStringImmediate(
249
interp, argv[2], "-nonewline"))) {
250
Jim_WrongNumArgs(interp, 2, argv, "?-nonewline? string");
253
wdata = Jim_GetString(argv[2+(argc==4)], &wlen);
254
if (fwrite(wdata, 1, wlen, af->fp) != wlen ||
255
(argc == 3 && fwrite("\n", 1, 1, af->fp) != 1)) {
256
JimAioSetError(interp);
260
} else if (option == OPT_FLUSH) {
263
Jim_WrongNumArgs(interp, 2, argv, "");
266
if (fflush(af->fp) == EOF) {
267
JimAioSetError(interp);
271
} else if (option == OPT_EOF) {
274
Jim_WrongNumArgs(interp, 2, argv, "");
277
Jim_SetResult(interp, Jim_NewIntObj(interp, feof(af->fp)));
283
static int JimAioOpenCommand(Jim_Interp *interp, int argc,
284
Jim_Obj *const *argv)
288
char buf[AIO_CMD_LEN];
289
const char *mode = "r";
292
const char *options[] = {"input", "output", "error"};
293
enum {OPT_INPUT, OPT_OUTPUT, OPT_ERROR};
294
int keepOpen = 0, modeLen;
296
if (argc != 2 && argc != 3) {
297
Jim_WrongNumArgs(interp, 1, argv, "filename ?mode?");
301
mode = Jim_GetString(argv[2], &modeLen);
302
if (argc == 3 && Jim_CompareStringImmediate(interp, argv[1], "standard") &&
305
if (Jim_GetEnum(interp, argv[2], options, &option, "standard channel",
306
JIM_ERRMSG) != JIM_OK)
310
case OPT_INPUT: fp = stdin; break;
311
case OPT_OUTPUT: fp = stdout; break;
312
case OPT_ERROR: fp = stderr; break;
313
default: fp = NULL; Jim_Panic("default reached in JimAioOpenCommand()");
317
fp = fopen(Jim_GetString(argv[1], NULL), mode);
319
JimAioSetError(interp);
323
/* Get the next file id */
324
if (Jim_EvalGlobal(interp,
325
"if {[catch {incr aio.fileId}]} {set aio.fileId 0}") != JIM_OK)
327
objPtr = Jim_GetGlobalVariableStr(interp, "aio.fileId", JIM_ERRMSG);
328
if (objPtr == NULL) return JIM_ERR;
329
if (Jim_GetLong(interp, objPtr, &fileId) != JIM_OK) return JIM_ERR;
331
/* Create the file command */
332
af = Jim_Alloc(sizeof(*af));
334
af->keepOpen = keepOpen;
335
sprintf(buf, "aio.handle%ld", fileId);
336
Jim_CreateCommand(interp, buf, JimAioHandlerCommand, af, JimAioDelProc);
337
Jim_SetResultString(interp, buf, -1);
342
__declspec(dllexport)
344
int Jim_OnLoad(Jim_Interp *interp)
346
Jim_InitExtension(interp);
347
if (Jim_PackageProvide(interp, "aio", "1.0", JIM_ERRMSG) != JIM_OK)
349
Jim_CreateCommand(interp, "aio.open", JimAioOpenCommand, NULL, NULL);