Index: libucdf/tester.c =================================================================== --- libucdf/tester.c (revision 35421) +++ libucdf/tester.c (revision 35422) @@ -1,4 +1,16 @@ #include "ucdf.h" + +static void print_dir(ucdf_file_t *ctx, ucdf_direntry_t *dir, int level) +{ + int n; + ucdf_direntry_t *d; + + for(n = 0; n < level; n++) putc(' ', stdout); + printf("%s [%d] %ld @%ld\n", dir->name, dir->type, dir->size, dir->first); + for(d = dir->children; d != NULL; d = d->next) + print_dir(ctx, d, level+1); +} + int main(int argc, char *argv[]) { char *fn = "A.PcbDoc"; @@ -16,5 +28,7 @@ printf(" ver=%04x rev=%04x %s-endian sect_size=%d,%d\n", ctx.file_ver, ctx.file_rev, ctx.litend ? "little" : "big", ctx.sect_size, ctx.short_sect_size); printf(" sat_len: %ld; dir1=%ld ssat=%ld+%ld msat=%ld+%ld\n", ctx.sat_len, ctx.dir_first, ctx.ssat_first, ctx.ssat_len, ctx.msat_first, ctx.msat_len); + print_dir(&ctx, ctx.root, 0); + return 0; } Index: libucdf/ucdf.c =================================================================== --- libucdf/ucdf.c (revision 35421) +++ libucdf/ucdf.c (revision 35422) @@ -47,6 +47,8 @@ "broken MSAT chain", "broken SAT chain", "broken short SAT chain", + "broken direcotry chain", + "failed to allocate memory", NULL }; @@ -257,11 +259,103 @@ if (next != UCDF_SECT_EOC) error(UCDF_ERR_BAD_SSAT); + return 0; +} +static void utf16_to_ascii(char *dst, int dstlen, const unsigned char *src) +{ + for(dstlen--; (*src != '\0') && (dstlen > 0); dstlen--, dst++, src+=2) + *dst = *src; + *dst = '\0'; +} +typedef struct { + long num_dirsects, dirs_per_sect; + long *diroffs; +} dir_ctx_t; + +static int ucdf_read_dir(ucdf_file_t *ctx, dir_ctx_t *dctx, long dirid, ucdf_direntry_t *parent, ucdf_direntry_t **de_out) +{ + long page = dirid / dctx->dirs_per_sect; + long offs = dirid % dctx->dirs_per_sect; + long leftid, rightid, rootid; + unsigned char buf[128]; + ucdf_direntry_t *de = malloc(sizeof(ucdf_direntry_t)); + + if ((page < 0) || (page >= dctx->num_dirsects)) + error(UCDF_ERR_BAD_DIRCHAIN); + + safe_seek(dctx->diroffs[page] + offs * 128); + safe_read(buf, 128); + + utf16_to_ascii(de->name, sizeof(de->name), buf); + de->size = load_int(ctx, buf+120, 4); + leftid = load_int(ctx, buf+68, 4); + rightid = load_int(ctx, buf+72, 4); + rootid = load_int(ctx, buf+76, 4); + de->first = load_int(ctx, buf+116, 4); + de->type = buf[66]; + de->parent = parent; + + if (parent != NULL) { + de->next = parent->children; + parent->children = de; + } + else + de->next = NULL; + + if (de_out != NULL) + *de_out = de; + +/* printf("dir: %d:'%s' (%ld %ld r=%ld) sec=%ld\n", de->type, de->name, leftid, rightid, rootid, de->first);*/ + + if (leftid >= 0) + ucdf_read_dir(ctx, dctx, leftid, de->parent, NULL); + if (rightid >= 0) + ucdf_read_dir(ctx, dctx, rightid, de->parent, NULL); + if (rootid >= 0) + ucdf_read_dir(ctx, dctx, rootid, de, NULL); + return 0; } +static int ucdf_read_dirs(ucdf_file_t *ctx) +{ + int res; + long n, next; + dir_ctx_t dctx; + + /* count directory sectors */ + next = ctx->dir_first; + dctx.num_dirsects = 0; + dctx.dirs_per_sect = ctx->sect_size >> 7; + while(next >= 0) { + next = ctx->sat[next]; + dctx.num_dirsects++; + } + if (next != UCDF_SECT_EOC) + error(UCDF_ERR_BAD_DIRCHAIN); + + /* build a temporary offset list of dir pages */ + dctx.diroffs = malloc(sizeof(long) * dctx.num_dirsects); + if (dctx.diroffs == NULL) + error(UCDF_ERR_BAD_MALLOC); + + next = ctx->dir_first; + n = 0; + while(next >= 0) { + dctx.diroffs[n] = sect_id2offs(ctx, next); + next = ctx->sat[next]; + n++; + } + + /* start reading from the root dir */ + res = ucdf_read_dir(ctx, &dctx, 0, NULL, &ctx->root); + + free(dctx.diroffs); + return res; +} + int ucdf_open(ucdf_file_t *ctx, const char *path) { ctx->f = fopen(path, "rb"); @@ -276,6 +370,9 @@ if (ucdf_read_sats(ctx) != 0) goto error; + if (ucdf_read_dirs(ctx) != 0) + goto error; + return 0; error:; Index: libucdf/ucdf.h =================================================================== --- libucdf/ucdf.h (revision 35421) +++ libucdf/ucdf.h (revision 35422) @@ -16,10 +16,34 @@ UCDF_ERR_BAD_MSAT, UCDF_ERR_BAD_SAT, UCDF_ERR_BAD_SSAT, + UCDF_ERR_BAD_DIRCHAIN, + UCDF_ERR_BAD_MALLOC } ucdf_error_t; extern const char *ucdf_error_str[]; /* indexed by ucdf_error_t */ +typedef enum { + UCDF_DE_EMPTY = 0, + UCDF_DE_DIR = 1, + UCDF_DE_FILE = 2, /* "stream" */ + UCDF_DE_LOCK = 3, + UCDF_DE_PROP = 4, + UCDF_DE_ROOT = 5 +} ucdf_detype_t; + +typedef struct ucdf_direntry_s ucdf_direntry_t; + +struct ucdf_direntry_s { + char name[32]; /* converted to ASCII */ + ucdf_detype_t type; + long size; + unsigned is_short:1; /* if set: file is short, using short sectors */ + long first; /* sector id (long or short) of the first sector */ + ucdf_direntry_t *parent; + ucdf_direntry_t *children; + ucdf_direntry_t *next; /* within children */ +}; + typedef struct { ucdf_error_t error; /* last error experienced in parsing */ int file_ver, file_rev, sect_size, short_sect_size; @@ -26,6 +50,8 @@ unsigned litend:1; /* set if file is little endian */ + ucdf_direntry_t *root; + /* cache/interanl */ FILE *f; /* the file we are reading from */ int ssz, sssz;