/*************************************************************************** archive.c The archive management routines (c) 2000-2007 Benoit Minisini 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 1, 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 #include #include #include #include #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, 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; }