X-Git-Url: http://git.draconx.ca/gitweb/upkg.git/blobdiff_plain/33c1796d255a720ad076160efd6859ca74d0792f..07c7ad2a8ae59fd0159c52c27c263f034aa7746a:/src/libupkg.c diff --git a/src/libupkg.c b/src/libupkg.c index ae48820..b87c5dd 100644 --- a/src/libupkg.c +++ b/src/libupkg.c @@ -25,6 +25,19 @@ #define MIN(a, b) ((a) < (b) ? (a) : (b)) +/* + * Print a message and execute some statement(s) if the expression evaluates + * to zero. Intended to help verify that assumed constraints on the file + * format actually are not violated. + */ +#define format_assert(expr, body) do { \ + if (!(expr)) { \ + fprintf(stderr, "%s: %d: %s: format assertion failed: %s\n", \ + __FILE__, __LINE__, __func__, #expr); \ + body; \ + } \ +} while (0) + struct upkg_name { unsigned long flags; char *name; @@ -45,6 +58,7 @@ struct upkg_import { struct upkg_private { const struct upkg_file_ops *fops; + int (*dtor)(void *handle); void *f; struct upkg_file *last_file; @@ -60,22 +74,27 @@ struct upkg_private { /* Default I/O operations for ordinary files. */ static size_t file_read(void *buf, size_t size, void *handle) { - return fread(buf, 1, size, handle); + return fread(buf, 1, size, (FILE *)handle); } static int file_seek(void *handle, long offset, int whence) { - return fseek(handle, offset, whence); + return fseek((FILE *)handle, offset, whence); } static long file_tell(void *handle) { - return ftell(handle); + return ftell((FILE *)handle); } static int file_eof(void *handle) { - return feof(handle); + return feof((FILE *)handle); +} + +static int file_close(void *handle) +{ + return fclose((FILE *)handle); } const struct upkg_file_ops upkg_default_fops = { @@ -351,12 +370,12 @@ static int pkg_init_imports(struct upkg *pkg) len = 0; rc = upkg_decode_index(&tmp, buf+len, nbuf-len); - if (rc == 0 || len < 0 || len >= pkg->name_count) goto err; + if (rc == 0 || len >= pkg->name_count) goto err; import->class_package = pkg->priv->names[tmp].name; len += rc; rc = upkg_decode_index(&tmp, buf+len, nbuf-len); - if (rc == 0 || len < 0 || len >= pkg->name_count) goto err; + if (rc == 0 || len >= pkg->name_count) goto err; import->class_name = pkg->priv->names[tmp].name; len += rc; @@ -365,7 +384,7 @@ static int pkg_init_imports(struct upkg *pkg) len += 4; rc = upkg_decode_index(&tmp, buf+len, nbuf-len); - if (rc == 0 || len < 0 || len >= pkg->name_count) goto err; + if (rc == 0 || len >= pkg->name_count) goto err; import->object_name = pkg->priv->names[tmp].name; len += rc; @@ -380,7 +399,8 @@ err: return -1; } -struct upkg *upkg_open(void *f, const struct upkg_file_ops *fops) +struct upkg *upkg_open(void *f, const struct upkg_file_ops *fops, + int (*destructor)(void *handle)) { unsigned char hdr_buf[UPKG_HDR_SIZE]; struct upkg *pkg; @@ -398,6 +418,7 @@ struct upkg *upkg_open(void *f, const struct upkg_file_ops *fops) return NULL; } pkg->priv->fops = fops; + pkg->priv->dtor = destructor; pkg->priv->f = f; if (pkg_init_guid(pkg) != 0) { @@ -438,7 +459,7 @@ struct upkg *upkg_fopen(const char *path) return NULL; } - pkg = upkg_open(f, &upkg_default_fops); + pkg = upkg_open(f, &upkg_default_fops, file_close); if (!pkg) { fclose(f); } @@ -450,12 +471,12 @@ int upkg_close(struct upkg *pkg) { int rc = 0; - if (pkg->priv->f) { - rc = fclose(pkg->priv->f); + if (pkg->priv->dtor) { + rc = pkg->priv->dtor(pkg->priv->f); + } - for (unsigned i = 0; i < pkg->name_count; i++) { - free(pkg->priv->names[i].name); - } + for (unsigned i = 0; i < pkg->name_count; i++) { + free(pkg->priv->names[i].name); } free(pkg->priv->imports); @@ -473,13 +494,17 @@ const char *upkg_get_name(struct upkg *pkg, unsigned long idx) return pkg->priv->names[idx].name; } -long upkg_export_find(struct upkg *pkg, const char *name) +long upkg_export_find(struct upkg *pkg, long parent, const char *name) { - /* This is wrong. - * Export names are not necessarily unique within a package. */ - for (unsigned i = 0; i < pkg->export_count; i++) { - struct upkg_export *export = &pkg->priv->exports[i]; - if (strcmp(export->name, name) == 0) { + /* This only makes sense if the assertion below is not violated. */ + long package = parent < 0 ? 0 : parent + 1; + + for (unsigned long i = 0; i < pkg->export_count; i++) { + struct upkg_export *e = &pkg->priv->exports[i]; + + /* Assertion: an object's package is an export. */ + format_assert(e->package >= 0, continue); + if (e->package == package && strcmp(e->name, name) == 0) { return i; } } @@ -513,11 +538,8 @@ const char *upkg_export_class(struct upkg *pkg, unsigned long idx, export = &pkg->priv->exports[idx]; - /* ASSUMPTION: class references are always imports */ - if (export->class > 0) { - fprintf(stderr, "Assumption Violated: class not import\n"); - return NULL; - } + /* Assumption: class references are always imports. */ + format_assert(export->class <= 0, return NULL); /* Get the class. */ if (export->class == 0) { @@ -530,17 +552,12 @@ const char *upkg_export_class(struct upkg *pkg, unsigned long idx, return NULL; iclass = &pkg->priv->imports[pkg_idx]; - /* ASSUMPTION: Class references are always Core.Class */ - if (strcmp(iclass->class_name, "Class") || strcmp(iclass->class_package, "Core")) { - fprintf(stderr, "Assumption Violated: class not Core.Class\n"); - return NULL; - } + /* Assumption: class references are always Core.Class. */ + format_assert(!strcmp(iclass->class_package, "Core"), return NULL); + format_assert(!strcmp(iclass->class_name, "Class"), return NULL); - /* ASSUMPTION: Package references are always imports */ - if (iclass->package >= 0) { - fprintf(stderr, "Assumption Violated: package not import\n"); - return NULL; - } + /* Assumption: package references are always imports. */ + format_assert(iclass->package <= 0, return NULL); /* Get the package. */ pkg_idx = -(iclass->package + 1); @@ -548,11 +565,9 @@ const char *upkg_export_class(struct upkg *pkg, unsigned long idx, return NULL; ipackage = &pkg->priv->imports[pkg_idx]; - /* ASSUMPTION: Package references are always Core.Package */ - if (strcmp(ipackage->class_name, "Package") || strcmp(ipackage->class_package, "Core")) { - fprintf(stderr, "Assumption Violated: package not Core.Package\n"); - return NULL; - } + /* Assumption: package references are always Core.Package. */ + format_assert(!strcmp(ipackage->class_package, "Core"), return NULL); + format_assert(!strcmp(ipackage->class_name, "Package"), return NULL); if (package) *package = ipackage->object_name; return iclass->object_name;