/*************************************************************************** gbx_archive.c (c) 2000-2011 BenoƮt 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 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. ***************************************************************************/ #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, const char *path) { ARCHIVE *arch; ALLOC_ZERO(&arch, sizeof(ARCHIVE), "ARCHIVE_create"); arch->name = name; arch->path = path; arch->domain = STRING_new_zero(name ? name : "gb"); 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 DEBUG_COMP fprintf(stderr, "load_exported_class: %s (component: %s)\n", arch->name, COMPONENT_current ? COMPONENT_current->name : "?"); #endif 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_component(char *name) { COMPONENT *comp; comp = COMPONENT_create(name); COMPONENT_load(comp); } static void load_dependencies(ARCHIVE *arch) { char *buffer; int len; STREAM_load(".startup", &buffer, &len); PROJECT_analyze_startup(buffer, len, load_component); FREE(&buffer, "load_dependencies"); } static void load_archive(ARCHIVE *arch, const char *path, bool dep) { arch->arch = ARCH_open(path); load_exported_class(arch); if (dep) load_dependencies(arch); } static char *exist_library(const char *dir, const char *name) { char *path; path = (char *)FILE_cat(dir, name, NULL); if (FILE_exist(path)) return path; else return NULL; } void ARCHIVE_load(ARCHIVE *arch, bool dep) { char *path; if (arch->path) { if (EXEC_debug) path = (char *)arch->path; else { path = exist_library(PROJECT_path, arch->name); if (!path) path = exist_library("/usr/bin", arch->name); if (!path) path = exist_library("/bin", arch->name); } if (!path || !FILE_exist(path)) THROW(E_LIBRARY, arch->name, "cannot find library"); } else { path = FILE_buffer(); sprintf(path, ARCH_PATTERN, COMPONENT_path, arch->name); } load_archive(arch, path, dep); } void ARCHIVE_create_main(const char *path) { ARCHIVE_main = ARCHIVE_create(NULL, 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 if (!PROJECT_path) return TRUE; FILE_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); if (find.arch) fstat(find.arch->arch->fd, &buf); else stat(PROJECT_path, &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; } // ?? "." means that we want to browse the archive. if (!find.sym && !find.arch) // && !(path[0] == '.' && path[1] == 0)) { // 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); arch_pattern = STRING_new_zero(pattern); //if (arch_dir->header.version == 2) //{ if (find.index >= 0) 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) arch_prefix = STRING_new(abs_path, abs_len); } bool ARCHIVE_dir_next(char **name, int *len, int attr) { ARCH_SYMBOL *asym; 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 = &arch_dir->symbol[arch_index].sym; asym = &arch_dir->symbol[arch_dir->sort[arch_index]]; sym = &asym->sym; if (arch_pattern == NULL) break; arch_index++; if (attr == GB_STAT_DIRECTORY && (asym->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; } void ARCHIVE_browse(ARCHIVE *arch, void (*found)(const char *path, int64_t size)) { int i; ARCH_SYMBOL *asym; SYMBOL *sym; ARCH *a = arch->arch; char *path; char *temp; int size; int ip; for (i = 0; i < a->header.n_symbol; i++) { asym = &a->symbol[i]; sym = &asym->sym; size = asym->len; path = STRING_new(sym->name, sym->len); for(;;) { if (*path != '/') break; ip = atoi(&path[1]); sym = &a->symbol[ip].sym; temp = path; path = STRING_new(sym->name, sym->len); if (path[sym->len - 1] != '/') path = STRING_add(path, "/", 1); path = STRING_add(path, strchr(temp, ':') + 1, 0); STRING_free(&temp); } (*found)(path, size); STRING_free(&path); } }