gambas-source-code/main/gbx/gbx_subr_file.c
Benoît Minisini 18c9b762fc [INTERPRETER]
* BUG: A stream redirected through the 'Begin' method checks that stream is
  ready for writing only when the 'Send' method is called. Between the 
  'Begin' and 'Send' calls, the PRINT and WRITE instructions will always
  succeed.


git-svn-id: svn://localhost/gambas/trunk@5723 867c0c6c-44f3-4631-809d-bfa615b0a4ec
2013-07-06 19:00:41 +00:00

985 lines
16 KiB
C
Executable file

/***************************************************************************
gbx_subr_file.c
(c) 2000-2012 Benoît Minisini <gambas@users.sourceforge.net>
This program 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.
This program 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 this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
MA 02110-1301, USA.
***************************************************************************/
#include "gb_common.h"
#include "gb_common_buffer.h"
#include "gbx_subr.h"
#include "gb_file.h"
#include "gb_list.h"
#include "gbx_stream.h"
#include "gbx_archive.h"
#include "gbx_api.h"
#include "gbx_local.h"
#include "gbx_regexp.h"
#include "gbx_string.h"
#include "gbx_c_file.h"
typedef
struct _stream {
struct _stream *next;
CSTREAM *stream;
}
CSTREAM_NODE;
static void *_default_in = NULL;
static void *_default_out = NULL;
static void *_default_err = NULL;
static GB_ARRAY _result;
static char *_pattern;
static int _len_pattern;
static int _ignore;
static STREAM *_stream;
static void push_stream(void **list, CSTREAM *stream)
{
CSTREAM_NODE *slot;
ALLOC(&slot, sizeof(CSTREAM_NODE));
slot->stream = stream;
//OBJECT_REF(stream);
slot->next = *list;
*list = slot;
}
static CSTREAM *pop_stream(void **list)
{
CSTREAM *stream;
CSTREAM_NODE *slot;
if (!*list)
return NULL;
stream = ((CSTREAM_NODE *)*list)->stream;
slot = *list;
*list = slot->next;
FREE(&slot);
return stream;
}
static STREAM *get_default(intptr_t val)
{
STREAM *stream;
switch(val)
{
case 0:
if (_default_in)
stream = CSTREAM_stream(((CSTREAM_NODE *)_default_in)->stream);
else
stream = CSTREAM_stream(CFILE_in);
break;
case 1:
if (_default_out)
stream = CSTREAM_stream(((CSTREAM_NODE *)_default_out)->stream);
else
stream = CSTREAM_stream(CFILE_out);
break;
case 2:
if (_default_err)
stream = CSTREAM_stream(((CSTREAM_NODE *)_default_err)->stream);
else
stream = CSTREAM_stream(CFILE_err);
break;
default:
stream = NULL;
}
return stream;
}
#define _get_stream(_value, _can_default) \
STREAM *stream; \
\
VARIANT_undo(_value); \
\
if ((_can_default) && TYPE_is_integer((_value)->type) && (_value)->_integer.value >= 0 && (_value)->_integer.value <= 2) \
stream = get_default((intptr_t)(_value)->_integer.value); \
else \
{ \
if (TYPE_is_object((_value)->type) && (_value)->_object.object && OBJECT_class((_value)->_object.object)->is_stream) \
stream = CSTREAM_stream((_value)->_object.object); \
else \
{ \
if (VALUE_is_null(_value)) \
THROW(E_NULL); \
\
VALUE_conv_object((_value), (TYPE)CLASS_Stream); \
stream = NULL; \
} \
}
#define get_stream(_value, _can_default) \
({ \
_get_stream(_value, _can_default); \
\
if (STREAM_is_closed(stream)) \
THROW(E_CLOSED); \
\
stream; \
})
#define get_stream_for_writing(_value, _can_default) \
({ \
_get_stream(_value, _can_default); \
\
if (STREAM_is_closed_for_writing(stream)) \
THROW(E_CLOSED); \
\
stream; \
})
static char *get_path(VALUE *param)
{
char *name;
int len;
SUBR_get_string_len(param, &name, &len);
return STRING_conv_file_name(name, len);
}
void SUBR_open(ushort code)
{
CFILE *file;
STREAM stream;
int mode;
void *addr;
SUBR_ENTER_PARAM(2);
SUBR_check_integer(&PARAM[1]);
mode = PARAM[1]._integer.value;
if (code & 0x3F)
{
if (TYPE_is_pointer(PARAM->type))
addr = (void *)PARAM->_pointer.value;
else
THROW(E_TYPE, "Pointer", TYPE_get_name(PARAM->type));
STREAM_open(&stream, (char *)addr, mode | ST_MEMORY);
}
else
{
STREAM_open(&stream, get_path(PARAM), mode);
}
file = CFILE_create(&stream, mode);
OBJECT_put(RETURN, file);
SUBR_LEAVE();
}
void SUBR_close(void)
{
SUBR_ENTER_PARAM(1);
STREAM_close(get_stream(PARAM, FALSE));
SUBR_LEAVE_VOID();
}
void SUBR_flush(void)
{
SUBR_ENTER_PARAM(1);
STREAM_flush(get_stream(PARAM, TRUE));
SUBR_LEAVE_VOID();
}
/*static void print_it(char *addr, long len)
{
STREAM_write(_stream, addr, len);
}*/
void SUBR_print(ushort code)
{
int i;
char *addr;
int len;
SUBR_ENTER();
if (NPARAM < 1)
THROW(E_NEPARAM);
_stream = get_stream_for_writing(PARAM, TRUE);
//PRINT_init(print_it, FALSE);
for (i = 1; i < NPARAM; i++)
{
PARAM++;
//PRINT_value(PARAM);
VALUE_to_string(PARAM, &addr, &len);
if (len == 1 && *addr == '\n')
STREAM_write_eol(_stream);
else
STREAM_write(_stream, addr, len);
}
SUBR_LEAVE_VOID();
}
void SUBR_linput(void)
{
STREAM *stream;
char *addr;
stream = get_stream(&SP[-1], TRUE);
addr = STREAM_line_input(stream, NULL);
SP--;
if (!TYPE_is_integer(SP->type))
RELEASE_OBJECT(SP);
SP->type = T_STRING;
SP->_string.addr = addr;
SP->_string.start = 0;
SP->_string.len = STRING_length(addr);
SP++;
}
void SUBR_input(ushort code)
{
static STREAM *stream = NULL;
char *addr;
SUBR_ENTER();
if (NPARAM == 1)
stream = get_stream(PARAM, TRUE);
if (stream)
addr = STREAM_input(stream);
else
addr = NULL;
if (NPARAM == 1)
{
SP--;
if (!TYPE_is_integer(SP->type))
RELEASE_OBJECT(SP);
}
if (addr)
{
//VALUE_from_string(SP, addr, STRING_length(addr));
SP->type = T_STRING;
SP->_string.addr = addr;
SP->_string.start = 0;
SP->_string.len = STRING_length(addr);
}
else
SP->type = T_NULL;
SP++;
}
void SUBR_eof(ushort code)
{
STREAM *stream;
bool eof;
SUBR_ENTER();
if (NPARAM == 1)
{
stream = get_stream(PARAM, FALSE);
eof = STREAM_eof(stream);
RELEASE_OBJECT(PARAM);
SP--;
}
else
{
stream = get_default(0);
eof = STREAM_eof(stream);
}
SP->type = T_BOOLEAN;
SP->_boolean.value = (-eof);
SP++;
}
void SUBR_lof(ushort code)
{
STREAM *stream;
SUBR_ENTER();
if (NPARAM == 1)
stream = get_stream(PARAM, FALSE);
else
stream = get_default(0);
RETURN->type = T_LONG;
STREAM_lof(stream, &(RETURN->_long.value));
SUBR_LEAVE();
}
void SUBR_seek(ushort code)
{
STREAM *stream;
int64_t pos;
int64_t len;
int whence = SEEK_SET;
SUBR_ENTER();
stream = get_stream(PARAM, FALSE);
if (NPARAM >= 2)
{
VALUE_conv(&PARAM[1], T_LONG);
pos = PARAM[1]._long.value;
if (NPARAM == 3)
{
VALUE_conv_integer(&PARAM[2]);
whence = PARAM[2]._integer.value;
if (whence != SEEK_SET && whence != SEEK_CUR && whence != SEEK_END)
THROW(E_ARG);
}
else
{
if (pos < 0)
{
STREAM_lof(stream, &len);
pos += len;
}
}
STREAM_seek(stream, pos, (int)whence);
RETURN->type = T_VOID;
}
else
{
RETURN->type = T_LONG;
RETURN->_long.value = STREAM_tell(stream);
}
SUBR_LEAVE();
}
void SUBR_read(ushort code)
{
STREAM *stream;
int len = 0;
SUBR_ENTER_PARAM(2);
stream = get_stream(PARAM, TRUE);
if (code & 0x3F)
{
VALUE_conv_integer(&PARAM[1]);
len = PARAM[1]._integer.value;
if (len == 0)
{
RETURN->type = T_NULL;
}
else if (len > 0)
{
STRING_new_temp_value(RETURN, NULL, len);
STREAM_read(stream, RETURN->_string.addr, len);
}
else
{
len = (-len);
RETURN->type = T_STRING;
RETURN->_string.addr = STRING_new(NULL, len);
RETURN->_string.start = 0;
RETURN->_string.len = len;
STREAM_read_max(stream, RETURN->_string.addr, len);
if (STREAM_eff_read == 0)
{
RETURN->type = T_NULL;
STRING_free(&RETURN->_string.addr);
}
else
{
if (STREAM_eff_read < len)
{
RETURN->_string.addr = STRING_extend(RETURN->_string.addr, STREAM_eff_read);
RETURN->_string.len = STREAM_eff_read;
}
STRING_extend_end(RETURN->_string.addr);
}
}
}
else
{
TYPE type = SUBR_get_type(&PARAM[1]);
STREAM_read_type(stream, type, RETURN);
}
SUBR_LEAVE();
}
void SUBR_write(ushort code)
{
STREAM *stream;
SUBR_ENTER_PARAM(3);
stream = get_stream_for_writing(PARAM, TRUE);
if (code & 0x3F)
{
char *str;
int len;
int lenw;
VALUE_conv_integer(&PARAM[2]);
lenw = PARAM[2]._integer.value;
if (TYPE_is_pointer(PARAM[1].type))
{
if (lenw < 0)
lenw = 0;
len = lenw;
str = (char *)PARAM[1]._pointer.value;
fprintf(stderr, "%p\n", str);
}
else
{
SUBR_get_string_len(&PARAM[1], &str, &len);
if (lenw < 0)
lenw = len;
}
if (lenw > 0)
{
STREAM_write(stream, str, Min(len, lenw));
if (lenw > len)
STREAM_write_zeros(stream, lenw - len);
}
}
else
{
TYPE type;
type = SUBR_get_type(&PARAM[2]);
VALUE_conv(&PARAM[1], type);
STREAM_write_type(stream, type, &PARAM[1]);
}
SUBR_LEAVE_VOID();
}
void SUBR_stat(ushort code)
{
const char *path;
CSTAT *cstat;
FILE_STAT info;
bool follow = FALSE;
SUBR_ENTER();
path = get_path(PARAM);
if (NPARAM == 2)
follow = SUBR_get_boolean(&PARAM[1]);
FILE_stat(path, &info, follow);
cstat = OBJECT_new(CLASS_Stat, NULL, NULL);
OBJECT_UNREF_KEEP(cstat);
cstat->info = info;
cstat->path = STRING_new_zero(path);
RETURN->_object.class = CLASS_Stat;
RETURN->_object.object = cstat;
SUBR_LEAVE();
}
void SUBR_exist(ushort code)
{
bool exist;
const char *path;
bool follow = FALSE;
SUBR_ENTER();
if (!NPARAM)
{
NPARAM++;
PARAM--;
}
path = get_path(PARAM);
if (NPARAM == 2)
follow = SUBR_get_boolean(&PARAM[1]);
exist = FILE_exist_follow(path, follow);
RETURN->type = T_BOOLEAN;
RETURN->_integer.value = exist ? -1 : 0;
SUBR_LEAVE();
}
void SUBR_dir(ushort code)
{
GB_ARRAY array;
const char *path;
char *pattern;
int len_pattern;
char *str;
int attr = 0;
SUBR_ENTER();
path = get_path(PARAM);
if (NPARAM >= 2)
{
pattern = SUBR_get_string(&PARAM[1]);
if (NPARAM == 3)
attr = SUBR_get_integer(&PARAM[2]);
}
else
pattern = NULL;
FILE_dir_first(path, pattern, attr);
GB_ArrayNew(&array, T_STRING, 0);
while (!FILE_dir_next(&pattern, &len_pattern))
{
if (!LOCAL_is_UTF8)
{
if (STRING_conv(&str, pattern, len_pattern, LOCAL_encoding, SC_UTF8, FALSE))
str = STRING_new(pattern, len_pattern);
else
STRING_ref(str);
}
else
str = STRING_new(pattern, len_pattern);
*((char **)GB_ArrayAdd(array)) = str;
}
RETURN->_object.class = OBJECT_class(array);
RETURN->_object.object = array;
SUBR_LEAVE();
}
static void found_file(const char *path)
{
char *str;
int len;
path += _ignore;
len = strlen(path);
if (_pattern && !REGEXP_match(_pattern, _len_pattern, path, len))
return;
if (!LOCAL_is_UTF8)
{
if (STRING_conv(&str, path, len, LOCAL_encoding, SC_UTF8, FALSE))
str = STRING_new(path, len);
else
STRING_ref(str);
}
else
str = STRING_new(path, len);
*((char **)GB_ArrayAdd(_result)) = str;
}
void SUBR_rdir(ushort code)
{
const char *path;
int attr = 0;
bool follow = FALSE;
SUBR_ENTER();
path = get_path(PARAM);
if (NPARAM >= 2)
{
SUBR_get_string_len(&PARAM[1], &_pattern, &_len_pattern);
if (NPARAM >= 3)
{
attr = SUBR_get_integer(&PARAM[2]);
if (NPARAM == 4)
follow = SUBR_get_boolean(&PARAM[3]);
}
}
else
_pattern = NULL;
GB_ArrayNew(&_result, T_STRING, 0);
if (!path || *path == 0)
path = ".";
_ignore = strlen(path);
if (_ignore > 0 && path[_ignore - 1] != '/')
_ignore++;
FILE_recursive_dir(path, found_file, NULL, attr, follow);
RETURN->_object.class = OBJECT_class(_result);
RETURN->_object.object = _result;
SUBR_LEAVE();
}
void SUBR_kill(ushort code)
{
SUBR_ENTER_PARAM(1);
switch(code & 0xFF)
{
case 0:
FILE_unlink(get_path(PARAM));
break;
case 1:
FILE_mkdir(get_path(PARAM));
break;
case 2:
FILE_rmdir(get_path(PARAM));
break;
default:
THROW_ILLEGAL();
}
SUBR_LEAVE_VOID();
}
void SUBR_mkdir(ushort code)
{
SUBR_ENTER_PARAM(1);
switch (code & 0xFF)
{
case 0: // Deprecated Mkdir
SUBR_kill(1);
return;
case 1: // Even
VALUE_conv(PARAM, T_LONG);
PARAM->type = GB_T_BOOLEAN;
PARAM->_boolean.value = (PARAM->_long.value & 1) == 0 ? -1 : 0;
break;
case 2: // Odd
VALUE_conv(PARAM, T_LONG);
PARAM->type = GB_T_BOOLEAN;
PARAM->_boolean.value = (PARAM->_long.value & 1) != 0 ? -1 : 0;
break;
default:
THROW_ILLEGAL();
}
}
void SUBR_rmdir(void)
{
SUBR_kill(2);
}
void SUBR_move(ushort code)
{
char *path;
char *auth;
FILE_STAT info;
SUBR_ENTER_PARAM(2);
path = get_path(&PARAM[0]);
switch (code & 0xFF)
{
case 0: // Move
FILE_rename(path, get_path(&PARAM[1]));
break;
case 1: // Copy
FILE_copy(path, get_path(&PARAM[1]));
break;
case 2: // Link
/* Parameters are NOT inverted ANYMORE! */
FILE_link(path, get_path(&PARAM[1]));
break;
case 3: // Chmod
auth = SUBR_get_string(&PARAM[1]);
FILE_stat(path, &info, TRUE);
FILE_chmod(path, FILE_mode_from_string(info.mode, auth));
break;
case 4: // Chown
auth = SUBR_get_string(&PARAM[1]);
FILE_chown(path, auth);
break;
case 5: // Chgrp
auth = SUBR_get_string(&PARAM[1]);
FILE_chgrp(path, auth);
break;
default:
THROW_ILLEGAL();
}
SUBR_LEAVE_VOID();
}
void SUBR_link(ushort code)
{
SUBR_ENTER_PARAM(1);
switch (code & 0xFF)
{
case 0: // Deprecated Link
SUBR_move(2);
return;
case 1: // IsNan
VALUE_conv(PARAM, T_FLOAT);
PARAM->type = GB_T_BOOLEAN;
PARAM->_boolean.value = isnan(PARAM->_float.value) ? -1 : 0;
break;
case 2: // IsInf
VALUE_conv(PARAM, T_FLOAT);
PARAM->type = GB_T_INTEGER;
PARAM->_integer.value = isinf(PARAM->_float.value);
break;
default:
THROW_ILLEGAL();
}
}
void SUBR_temp(ushort code)
{
char *temp;
int len;
SUBR_ENTER();
if (NPARAM == 0)
temp = FILE_make_temp(&len, NULL);
else
temp = FILE_make_temp(&len, SUBR_get_string(PARAM));
STRING_new_temp_value(RETURN, temp, len);
SUBR_LEAVE();
}
void SUBR_isdir(void)
{
bool isdir;
const char *path;
SUBR_ENTER_PARAM(1);
path = get_path(PARAM);
isdir = FILE_is_dir(path);
RETURN->type = T_BOOLEAN;
RETURN->_integer.value = isdir ? -1 : 0;
SUBR_LEAVE();
}
void SUBR_access(ushort code)
{
int access;
SUBR_ENTER();
if (NPARAM == 1)
access = R_OK;
else
{
VALUE_conv_integer(&PARAM[1]);
access = PARAM[1]._integer.value;
}
RETURN->type = T_BOOLEAN;
RETURN->_integer.value = FILE_access(get_path(PARAM), access) ? -1 : 0;
SUBR_LEAVE();
}
void SUBR_lock(ushort code)
{
SUBR_ENTER_PARAM(1);
if (code & 0x1F)
{
STREAM_close(get_stream(PARAM, FALSE));
SUBR_LEAVE_VOID();
}
else
{
STREAM stream;
CFILE *file;
const char *path = get_path(PARAM);
if (FILE_is_relative(path))
THROW(E_BADPATH);
STREAM_open(&stream, path, ST_WRITE | ST_CREATE | ST_DIRECT);
STREAM_lock(&stream);
file = CFILE_create(&stream, ST_WRITE | ST_CREATE | ST_DIRECT);
OBJECT_put(RETURN, file);
SUBR_LEAVE();
}
}
void SUBR_inp_out(ushort code)
{
CSTREAM *stream;
void **where;
SUBR_ENTER_PARAM(1);
switch(code & 0x1F)
{
case 0: where = &_default_in; break;
case 1: where = &_default_out; break;
default: where = &_default_err; break;
}
if (VALUE_is_null(PARAM))
{
stream = pop_stream(where);
if (stream)
OBJECT_UNREF(stream);
return;
}
VALUE_conv_object(PARAM, (TYPE)CLASS_Stream);
stream = PARAM->_object.object;
OBJECT_REF(stream);
push_stream(where, stream);
SUBR_LEAVE_VOID();
}
static void free_list(void **list)
{
CSTREAM *stream;
for(;;)
{
stream = pop_stream(list);
if (!stream)
return;
OBJECT_UNREF(stream);
}
}
void SUBR_exit_inp_out(void)
{
free_list((void **)&_default_in);
free_list((void **)&_default_out);
free_list((void **)&_default_err);
}
void SUBR_dfree(void)
{
SUBR_ENTER_PARAM(1);
RETURN->type = T_LONG;
RETURN->_long.value = FILE_free(get_path(PARAM));
SUBR_LEAVE();
}
void SUBR_debug(void)
{
const int NPARAM = 0;
STREAM *stream = get_default(2);
const char *s = DEBUG_get_current_position();
STREAM_write(stream, (void *)s, strlen(s));
STREAM_write(stream, ": ", 2);
SUBR_LEAVE();
}