669820173b
* NEW: The constructor arguments must follow the inheritance order from top to bottom, and not from bottom to top as before. This way, the constructor of the bottom class can have optional arguments. git-svn-id: svn://localhost/gambas/trunk@2448 867c0c6c-44f3-4631-809d-bfa615b0a4ec
479 lines
9.3 KiB
C
479 lines
9.3 KiB
C
/***************************************************************************
|
|
|
|
gbx_archive.c
|
|
|
|
(c) 2000-2009 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
|
|
***************************************************************************/
|
|
|
|
#define __GBX_ARCHIVE_C
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/mman.h>
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
|
|
#include "gb_common.h"
|
|
#include "gb_error.h"
|
|
#include "gb_alloc.h"
|
|
|
|
#include "gbx_string.h"
|
|
#include "gbx_stream.h"
|
|
#include "gbx_component.h"
|
|
#include "gbx_regexp.h"
|
|
#include "gbx_exec.h"
|
|
#include "gbx_class.h"
|
|
#include "gbx_project.h"
|
|
|
|
#include "gbx_archive.h"
|
|
|
|
#include "gb_arch_temp.h"
|
|
|
|
/* main archive project (used only if gbx is run with -x flag) */
|
|
ARCHIVE *ARCHIVE_main = NULL;
|
|
|
|
static ARCHIVE *_archive_list = NULL;
|
|
|
|
static char *arch_pattern = NULL;
|
|
static int arch_index = 0;
|
|
static ARCH *arch_dir = NULL;
|
|
static char *arch_prefix = NULL;
|
|
|
|
ARCHIVE *ARCHIVE_create(const char *name)
|
|
{
|
|
ARCHIVE *arch;
|
|
|
|
ALLOC_ZERO(&arch, sizeof(ARCHIVE), "ARCHIVE_create");
|
|
|
|
arch->name = name;
|
|
|
|
if (name)
|
|
STRING_new(&arch->domain, name, 0);
|
|
else
|
|
STRING_new(&arch->domain, "gb", 0);
|
|
|
|
arch->translation_loaded = FALSE;
|
|
|
|
TABLE_create(&arch->classes, sizeof(CLASS_SYMBOL), TF_IGNORE_CASE);
|
|
|
|
LIST_insert(&_archive_list, arch, &arch->list);
|
|
|
|
return arch;
|
|
}
|
|
|
|
static void load_exported_class(ARCHIVE *arch)
|
|
{
|
|
/*STREAM stream;*/
|
|
char *buffer;
|
|
int len;
|
|
char *name;
|
|
CLASS *class;
|
|
CLASS **exported = NULL;
|
|
int i;
|
|
|
|
/* COMPONENT_current is set => it will look in the archive */
|
|
|
|
if (!FILE_exist(".list"))
|
|
return;
|
|
|
|
STREAM_load(".list", &buffer, &len);
|
|
/* The file must end with a newline !*/
|
|
buffer[len - 1] = 0;
|
|
|
|
#if DEBUG_COMP
|
|
fprintf(stderr, ".list file:\n-----------\n\n%s\n----------\n\n", buffer);
|
|
#endif
|
|
|
|
ARRAY_create(&exported);
|
|
|
|
name = strtok(buffer, "\n");
|
|
while (name)
|
|
{
|
|
#if DEBUG_COMP
|
|
fprintf(stderr, "Check %s global\n", name);
|
|
#endif
|
|
|
|
CLASS_check_global(name);
|
|
|
|
len = strlen(name);
|
|
if (len > 2 && name[len - 1] == '?')
|
|
{
|
|
name[len - 1] = 0;
|
|
if (!CLASS_look_global(name, strlen(name)))
|
|
class = CLASS_find_global(name);
|
|
else
|
|
class = NULL;
|
|
}
|
|
else
|
|
class = CLASS_find_global(name);
|
|
|
|
if (class)
|
|
{
|
|
class->component = COMPONENT_current;
|
|
*((CLASS **)ARRAY_add(&exported)) = class;
|
|
}
|
|
|
|
name = strtok(NULL, "\n");
|
|
}
|
|
|
|
for (i = 0; i < ARRAY_count(exported); i++)
|
|
CLASS_load_without_init(exported[i]);
|
|
|
|
ARRAY_delete(&exported);
|
|
FREE(&buffer, "load_exported_class");
|
|
}
|
|
|
|
static void load_archive(ARCHIVE *arch, const char *path)
|
|
{
|
|
if (path)
|
|
arch->arch = ARCH_open(path);
|
|
else
|
|
arch->arch = NULL;
|
|
|
|
//if (arch != ARCHIVE_main)
|
|
load_exported_class(arch);
|
|
}
|
|
|
|
|
|
void ARCHIVE_load(ARCHIVE *arch)
|
|
{
|
|
char *path = FILE_buffer();
|
|
|
|
sprintf(path, ARCH_PATTERN, COMPONENT_path, arch->name);
|
|
if (!FILE_exist(path))
|
|
sprintf(path, ARCH_PATTERN, COMPONENT_user_path, arch->name);
|
|
|
|
load_archive(arch, path);
|
|
}
|
|
|
|
|
|
void ARCHIVE_create_main(const char *path)
|
|
{
|
|
ARCHIVE_main = ARCHIVE_create(NULL);
|
|
|
|
if (path)
|
|
ARCHIVE_main->arch = ARCH_open(path);
|
|
else
|
|
ARCHIVE_main->arch = NULL;
|
|
}
|
|
|
|
|
|
void ARCHIVE_load_main()
|
|
{
|
|
load_exported_class(ARCHIVE_main);
|
|
}
|
|
|
|
|
|
void ARCHIVE_delete(ARCHIVE *arch)
|
|
{
|
|
LIST_remove(&_archive_list, arch, &arch->list);
|
|
|
|
if (arch->arch)
|
|
ARCH_close(arch->arch);
|
|
|
|
TABLE_delete(&arch->classes);
|
|
STRING_free(&arch->domain);
|
|
|
|
FREE(&arch, "ARCHIVE_delete");
|
|
}
|
|
|
|
|
|
void ARCHIVE_init(void)
|
|
{
|
|
//ARCH_create(path);
|
|
//ARCHIVE_main = _arch_list;
|
|
}
|
|
|
|
|
|
void ARCHIVE_exit(void)
|
|
{
|
|
if (ARCHIVE_main)
|
|
ARCHIVE_delete(ARCHIVE_main);
|
|
|
|
STRING_free(&arch_pattern);
|
|
STRING_free(&arch_prefix);
|
|
}
|
|
|
|
|
|
/* ### *parch must be initialized to NULL or a valid archive */
|
|
/* Returns a true archive, never the main archive if we are not running an executable */
|
|
static bool get_current(ARCHIVE **parch, const char **ppath)
|
|
{
|
|
if (*parch)
|
|
return FALSE;
|
|
|
|
if (strncmp(*ppath, "../", 3) == 0)
|
|
{
|
|
*parch = EXEC_arch ? ARCHIVE_main : NULL;
|
|
*ppath += 3;
|
|
}
|
|
else if (COMPONENT_current && COMPONENT_current->archive)
|
|
*parch = COMPONENT_current->archive;
|
|
else if (CP && CP->component && CP->component->archive)
|
|
*parch = CP->component->archive;
|
|
else
|
|
*parch = EXEC_arch ? ARCHIVE_main : NULL;
|
|
|
|
return *parch == NULL;
|
|
}
|
|
|
|
/* Can return the main archive even if we are not running an executable */
|
|
bool ARCHIVE_get_current(ARCHIVE **parch)
|
|
{
|
|
if (COMPONENT_current && COMPONENT_current->archive)
|
|
*parch = COMPONENT_current->archive;
|
|
else if (CP && CP->component && CP->component->archive)
|
|
*parch = CP->component->archive;
|
|
else
|
|
*parch = ARCHIVE_main;
|
|
|
|
return *parch == NULL;
|
|
}
|
|
|
|
|
|
bool ARCHIVE_get(ARCHIVE *arch, const char **ppath, ARCHIVE_FIND *find)
|
|
{
|
|
ARCH_FIND f;
|
|
struct stat buf;
|
|
|
|
if (!*ppath || **ppath == 0)
|
|
return TRUE;
|
|
|
|
if (get_current(&arch, ppath))
|
|
{
|
|
// no archive found, we try a lstat
|
|
chdir(PROJECT_path);
|
|
if (stat(*ppath, &buf))
|
|
return TRUE;
|
|
|
|
find->pos = S_ISDIR(buf.st_mode) ? -1 : 0;
|
|
find->sym = NULL;
|
|
find->len = (int)buf.st_size;
|
|
find->index = -1;
|
|
find->arch = NULL;
|
|
return FALSE;
|
|
}
|
|
|
|
if (ARCH_find(arch->arch, *ppath, 0, &f))
|
|
return TRUE;
|
|
|
|
find->sym = f.sym;
|
|
find->pos = f.pos;
|
|
find->len = f.len;
|
|
find->index = f.index;
|
|
find->arch = arch;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
bool ARCHIVE_exist(ARCHIVE *arch, const char *path)
|
|
{
|
|
ARCHIVE_FIND find;
|
|
|
|
//if (get_current(&arch, &path))
|
|
// return FALSE;
|
|
|
|
return (!ARCHIVE_get(arch, &path, &find));
|
|
}
|
|
|
|
|
|
bool ARCHIVE_is_dir(ARCHIVE *arch, const char *path)
|
|
{
|
|
ARCHIVE_FIND find;
|
|
|
|
if (ARCHIVE_get(arch, &path, &find))
|
|
return FALSE;
|
|
|
|
return (find.pos < 0);
|
|
}
|
|
|
|
|
|
void ARCHIVE_stat(ARCHIVE *arch, const char *path, FILE_STAT *info)
|
|
{
|
|
ARCHIVE_FIND find;
|
|
struct stat buf;
|
|
|
|
//if (get_current(&arch))
|
|
// THROW_SYSTEM(ENOENT, path);
|
|
|
|
if (ARCHIVE_get(arch, &path, &find))
|
|
THROW_SYSTEM(ENOENT, path);
|
|
|
|
fstat(find.arch->arch->fd, &buf);
|
|
|
|
info->type = find.pos < 0 ? GB_STAT_DIRECTORY : GB_STAT_FILE;
|
|
info->mode = 0400;
|
|
|
|
info->size = find.len;
|
|
info->atime = (int)buf.st_mtime;
|
|
info->mtime = (int)buf.st_mtime;
|
|
info->ctime = (int)buf.st_mtime;
|
|
info->hidden = (*FILE_get_name(path) == '.');
|
|
info->uid = buf.st_uid;
|
|
info->gid = buf.st_gid;
|
|
}
|
|
|
|
|
|
bool ARCHIVE_read(ARCHIVE *arch, int pos, void *buffer, int len)
|
|
{
|
|
return ARCH_read(arch->arch, pos, buffer, len);
|
|
}
|
|
|
|
|
|
void ARCHIVE_dir_first(ARCHIVE *arch, const char *path, const char *pattern, int attr)
|
|
{
|
|
ARCHIVE_FIND find;
|
|
char abs_path[16];
|
|
int abs_len;
|
|
|
|
/*if (get_current(&arch, &path))
|
|
{
|
|
arch_dir = NULL;
|
|
return;
|
|
}*/
|
|
|
|
if (ARCHIVE_get(arch, &path, &find))
|
|
{
|
|
arch_dir = NULL;
|
|
return;
|
|
}
|
|
|
|
if (!find.sym)
|
|
{
|
|
// By calling FILE_dir_first() again with an absolute path, we are sure that next calls to
|
|
// FILE_dir_next() will never call ARCHIVE_dir_next().
|
|
FILE_dir_first(FILE_cat(PROJECT_path, path, NULL), pattern, attr);
|
|
return;
|
|
}
|
|
|
|
if (pattern == NULL)
|
|
pattern = "*";
|
|
|
|
arch = find.arch;
|
|
arch_dir = arch->arch;
|
|
arch_index = 0;
|
|
|
|
STRING_free(&arch_pattern);
|
|
STRING_new(&arch_pattern, pattern, 0);
|
|
|
|
//if (arch_dir->header.version == 2)
|
|
//{
|
|
if (find.index)
|
|
abs_len = sprintf(abs_path, "/%d:", find.index);
|
|
else
|
|
abs_len = 0;
|
|
/*}
|
|
else
|
|
{
|
|
ARCH_get_absolute_path(path, strlen(path), abs_path, &abs_len);
|
|
|
|
if (abs_len && abs_path[abs_len - 1] != '/')
|
|
{
|
|
abs_path[abs_len] = '/';
|
|
abs_len++;
|
|
}
|
|
}*/
|
|
|
|
STRING_free(&arch_prefix);
|
|
if (abs_len)
|
|
STRING_new(&arch_prefix, abs_path, abs_len);
|
|
}
|
|
|
|
|
|
bool ARCHIVE_dir_next(char **name, int *len, int attr)
|
|
{
|
|
SYMBOL *sym;
|
|
char *s = NULL;
|
|
int l = 0;
|
|
int lenp;
|
|
|
|
/*if (arch_fd < 0)
|
|
return FILE_dir_next(name, len);*/
|
|
|
|
if (!arch_dir)
|
|
return TRUE;
|
|
|
|
lenp = STRING_length(arch_prefix);
|
|
|
|
for(;;)
|
|
{
|
|
if (arch_index >= arch_dir->header.n_symbol)
|
|
return TRUE;
|
|
|
|
sym = (SYMBOL *)&arch_dir->symbol[arch_index];
|
|
sym = (SYMBOL *)&arch_dir->symbol[sym->sort];
|
|
|
|
if (arch_pattern == NULL)
|
|
break;
|
|
|
|
arch_index++;
|
|
|
|
if (attr == GB_STAT_DIRECTORY && (((ARCH_SYMBOL *)sym)->pos >= 0))
|
|
continue;
|
|
|
|
if (sym->len < lenp)
|
|
continue;
|
|
|
|
if (lenp)
|
|
{
|
|
if (strncmp(sym->name, arch_prefix, lenp))
|
|
continue;
|
|
}
|
|
else // special case: root directory when header.version == 2
|
|
{
|
|
if (!strncmp(sym->name, "/", 1))
|
|
continue;
|
|
}
|
|
|
|
s = sym->name + lenp;
|
|
l = sym->len - lenp;
|
|
|
|
if (l < 0)
|
|
continue;
|
|
|
|
if (memchr(s, '/', l))
|
|
continue;
|
|
|
|
if (!REGEXP_match(arch_pattern, STRING_length(arch_pattern), s, l))
|
|
continue;
|
|
|
|
break;
|
|
}
|
|
|
|
*name = s;
|
|
*len = l;
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
bool ARCHIVE_check_addr(char *addr)
|
|
{
|
|
ARCHIVE *arch;
|
|
ARCH *a;
|
|
|
|
LIST_for_each(arch, _archive_list)
|
|
{
|
|
a = arch->arch;
|
|
if (a && addr >= a->addr && addr < &a->addr[a->length])
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|