void mdb_iconv_init(MdbHandle *mdb) { const char *iconv_code; /* check environment variable */ if (!(iconv_code=getenv("MDBICONV"))) { iconv_code="UTF-8"; } #ifdef HAVE_ICONV if (IS_JET4(mdb)) { mdb->iconv_out = iconv_open("UCS-2LE", iconv_code); mdb->iconv_in = iconv_open(iconv_code, "UCS-2LE"); } else { /* According to Microsoft Knowledge Base pages 289525 and */ /* 202427, code page info is not contained in the database */ const char *jet3_iconv_code; /* check environment variable */ if (!(jet3_iconv_code=getenv("MDB_JET3_CHARSET"))) { jet3_iconv_code="CP1252"; } mdb->iconv_out = iconv_open(jet3_iconv_code, iconv_code); mdb->iconv_in = iconv_open(iconv_code, jet3_iconv_code); } #endif }
int main(int argc, char **argv) { MdbHandle *mdb; int print_mdbver = 0; int opt; /* setlocale (LC_ALL, ""); */ bindtextdomain (PACKAGE, LOCALEDIR); textdomain (PACKAGE); while ((opt=getopt(argc, argv, "M"))!=-1) { switch (opt) { case 'M': print_mdbver = 1; break; default: break; } } if (print_mdbver) { fprintf(stdout,"%s\n", MDB_FULL_VERSION); if (argc-optind < 1) exit(0); } /* ** optind is now the position of the first non-option arg, ** see getopt(3) */ if (argc-optind < 1) { fprintf(stderr,_("Usage: %s [-M] <file>\n"),argv[0]); exit(1); } mdb_init(); if (!(mdb = mdb_open(argv[optind], MDB_NOFLAGS))) { fprintf(stderr,_("Error: unable to open file %s\n"),argv[optind]); mdb_exit(); exit(1); } if (IS_JET3(mdb)) { printf("JET3\n"); } else if (IS_JET4(mdb)) { printf("JET4\n"); } else { printf(_("unknown database version\n")); } mdb_close(mdb); mdb_exit(); exit(0); }
/** * mdb_open: * @filename: path to MDB (database) file * @flags: MDB_NOFLAGS for read-only, MDB_WRITABLE for read/write * * Opens an MDB file and returns an MdbHandle to it. MDB File may be relative * to the current directory, a full path to the file, or relative to a * component of $MDBPATH. * * Return value: pointer to MdbHandle structure. **/ MdbHandle *mdb_open(char *filename, MdbFileFlags flags) { MdbHandle *mdb; mdb = (MdbHandle *) g_malloc0(sizeof(MdbHandle)); mdb_set_default_backend(mdb, "access"); /* need something to bootstrap with, reassign after page 0 is read */ mdb->fmt = &MdbJet3Constants; mdb->f = (MdbFile *) g_malloc0(sizeof(MdbFile)); mdb->f->refs = 1; mdb->f->fd = -1; mdb->f->filename = (char *) mdb_find_file(filename); if (!mdb->f->filename) { fprintf(stderr, "Can't alloc filename\n"); mdb_close(mdb); return NULL; } if (flags & MDB_WRITABLE) { mdb->f->writable = TRUE; mdb->f->fd = open(mdb->f->filename,O_RDWR); } else { mdb->f->fd = open(mdb->f->filename,O_RDONLY); } if (mdb->f->fd==-1) { fprintf(stderr,"Couldn't open file %s\n",mdb->f->filename); mdb_close(mdb); return NULL; } if (!mdb_read_pg(mdb, 0)) { fprintf(stderr,"Couldn't read first page.\n"); mdb_close(mdb); return NULL; } if (mdb->pg_buf[0] != 0) { mdb_close(mdb); return NULL; } mdb->f->jet_version = mdb_pg_get_int32(mdb, 0x14); if (IS_JET4(mdb)) { mdb->fmt = &MdbJet4Constants; } else if (IS_JET3(mdb)) { mdb->fmt = &MdbJet3Constants; } else { fprintf(stderr,"Unknown Jet version.\n"); mdb_close(mdb); return NULL; } return mdb; }
/* * This function is used in reading text data from an MDB table. */ int mdb_unicode2ascii(MdbHandle *mdb, char *src, size_t slen, char *dest, size_t dlen) { char *tmp = NULL; size_t tlen = 0; size_t len_in, len_out; char *in_ptr, *out_ptr; if ((!src) || (!dest) || (!dlen)) return 0; /* Uncompress 'Unicode Compressed' string into tmp */ if (IS_JET4(mdb) && (slen>=2) && ((src[0]&0xff)==0xff) && ((src[1]&0xff)==0xfe)) { unsigned int compress=1; src += 2; slen -= 2; tmp = (char *)g_malloc(slen*2); while (slen) { if (*src == 0) { compress = (compress) ? 0 : 1; src++; slen--; } else if (compress) { tmp[tlen++] = *src++; tmp[tlen++] = 0; slen--; } else if (slen >= 2){ tmp[tlen++] = *src++; tmp[tlen++] = *src++; slen-=2; } } } in_ptr = (tmp) ? tmp : src; out_ptr = dest; len_in = (tmp) ? tlen : slen; len_out = dlen; #if HAVE_ICONV //printf("1 len_in %d len_out %d\n",len_in, len_out); while (1) { iconv(mdb->iconv_in, &in_ptr, &len_in, &out_ptr, &len_out); if ((!len_in) || (errno == E2BIG)) break; /* Don't bail if impossible conversion is encountered */ in_ptr += (IS_JET4(mdb)) ? 2 : 1; len_in -= (IS_JET4(mdb)) ? 2 : 1; *out_ptr++ = '?'; len_out--; } //printf("2 len_in %d len_out %d\n",len_in, len_out); dlen -= len_out; #else if (IS_JET3(mdb)) { strncpy(out_ptr, in_ptr, len_in); dlen = len_in; } else { /* rough UCS-2LE to ISO-8859-1 conversion */ unsigned int i; for (i=0; i<len_in; i+=2) dest[i/2] = (in_ptr[i+1] == 0) ? in_ptr[i] : '?'; dlen = len_in/2; } #endif if (tmp) g_free(tmp); dest[dlen]='\0'; //printf("dest %s\n",dest); return dlen; }
/* * This function is used in writing text data to an MDB table. * If slen is 0, strlen will be used to calculate src's length. */ int mdb_ascii2unicode(MdbHandle *mdb, char *src, size_t slen, char *dest, size_t dlen) { size_t len_in, len_out; char *in_ptr, *out_ptr; if ((!src) || (!dest) || (!dlen)) return 0; in_ptr = src; out_ptr = dest; len_in = (slen) ? slen : strlen(in_ptr); len_out = dlen; #ifdef HAVE_ICONV iconv(mdb->iconv_out, &in_ptr, &len_in, &out_ptr, &len_out); //printf("len_in %d len_out %d\n", len_in, len_out); dlen -= len_out; #else if (IS_JET3(mdb)) { dlen = MIN(len_in, len_out); strncpy(out_ptr, in_ptr, dlen); } else { unsigned int i; slen = MIN(len_in, len_out/2); dlen = slen*2; for (i=0; i<slen; i++) { out_ptr[i*2] = in_ptr[i]; out_ptr[i*2+1] = 0; } } #endif /* Unicode Compression */ if(IS_JET4(mdb) && (dlen>4)) { unsigned char *tmp = g_malloc(dlen); unsigned int tptr = 0, dptr = 0; int comp = 1; tmp[tptr++] = 0xff; tmp[tptr++] = 0xfe; while((dptr < dlen) && (tptr < dlen)) { if (((dest[dptr+1]==0) && (comp==0)) || ((dest[dptr+1]!=0) && (comp==1))) { /* switch encoding mode */ tmp[tptr++] = 0; comp = (comp) ? 0 : 1; } else if (dest[dptr]==0) { /* this string cannot be compressed */ tptr = dlen; } else if (comp==1) { /* encode compressed character */ tmp[tptr++] = dest[dptr]; dptr += 2; } else if (tptr+1 < dlen) { /* encode uncompressed character */ tmp[tptr++] = dest[dptr]; tmp[tptr++] = dest[dptr+1]; dptr += 2; } else { /* could not encode uncompressed character * into single byte */ tptr = dlen; } } if (tptr < dlen) { memcpy(dest, tmp, tptr); dlen = tptr; } g_free(tmp); } return dlen; }
/** * mdb_open: * @filename: path to MDB (database) file * @flags: MDB_NOFLAGS for read-only, MDB_WRITABLE for read/write * * Opens an MDB file and returns an MdbHandle to it. MDB File may be relative * to the current directory, a full path to the file, or relative to a * component of $MDBPATH. * * Return value: pointer to MdbHandle structure. **/ MdbHandle *mdb_open(const char *filename, MdbFileFlags flags) { MdbHandle *mdb; int open_flags; mdb = (MdbHandle *) g_malloc0(sizeof(MdbHandle)); mdb_set_default_backend(mdb, "access"); #ifdef HAVE_ICONV mdb->iconv_in = (iconv_t)-1; mdb->iconv_out = (iconv_t)-1; #endif /* need something to bootstrap with, reassign after page 0 is read */ mdb->fmt = &MdbJet3Constants; mdb->f = (MdbFile *) g_malloc0(sizeof(MdbFile)); mdb->f->refs = 1; mdb->f->fd = -1; mdb->f->filename = mdb_find_file(filename); if (!mdb->f->filename) { fprintf(stderr, "Can't alloc filename\n"); mdb_close(mdb); return NULL; } if (flags & MDB_WRITABLE) { mdb->f->writable = TRUE; open_flags = O_RDWR; } else { open_flags = O_RDONLY; } #ifdef _WIN32 open_flags |= O_BINARY; #endif mdb->f->fd = open(mdb->f->filename, open_flags); if (mdb->f->fd==-1) { fprintf(stderr,"Couldn't open file %s\n",mdb->f->filename); mdb_close(mdb); return NULL; } if (!mdb_read_pg(mdb, 0)) { fprintf(stderr,"Couldn't read first page.\n"); mdb_close(mdb); return NULL; } if (mdb->pg_buf[0] != 0) { mdb_close(mdb); return NULL; } mdb->f->jet_version = mdb_get_int32(mdb->pg_buf, 0x14); if (IS_JET4(mdb)) { mdb->fmt = &MdbJet4Constants; } else if (IS_JET3(mdb)) { mdb->fmt = &MdbJet3Constants; } else { fprintf(stderr,"Unknown Jet version.\n"); mdb_close(mdb); return NULL; } mdb_iconv_init(mdb); return mdb; }
GPtrArray *mdb_read_columns(MdbTableDef *table) { MdbHandle *mdb = table->entry->mdb; MdbFormatConstants *fmt = mdb->fmt; MdbColumn *pcol; unsigned char *col; unsigned int i; int cur_pos; size_t name_sz; table->columns = g_ptr_array_new(); col = (unsigned char *) g_malloc(fmt->tab_col_entry_size); cur_pos = fmt->tab_cols_start_offset + (table->num_real_idxs * fmt->tab_ridx_entry_size); /* new code based on patch submitted by Tim Nelson 2000.09.27 */ /* ** column attributes */ for (i=0;i<table->num_cols;i++) { #ifdef MDB_DEBUG /* printf("column %d\n", i); buffer_dump(mdb->pg_buf, cur_pos, fmt->tab_col_entry_size); */ #endif read_pg_if_n(mdb, col, &cur_pos, fmt->tab_col_entry_size); pcol = (MdbColumn *) g_malloc0(sizeof(MdbColumn)); pcol->col_type = col[0]; pcol->col_num = col[fmt->col_num_offset]; pcol->var_col_num = mdb_get_int16(col, fmt->tab_col_offset_var); pcol->row_col_num = mdb_get_int16(col, fmt->tab_row_col_num_offset); /* FIXME: can this be right in Jet3 and Jet4? */ if (pcol->col_type == MDB_NUMERIC) { pcol->col_prec = col[11]; pcol->col_scale = col[12]; } pcol->is_fixed = col[fmt->col_fixed_offset] & 0x01 ? 1 : 0; pcol->fixed_offset = mdb_get_int16(col, fmt->tab_col_offset_fixed); if (pcol->col_type != MDB_BOOL) { pcol->col_size = mdb_get_int16(col, fmt->col_size_offset); } else { pcol->col_size=0; } g_ptr_array_add(table->columns, pcol); } g_free (col); /* ** column names - ordered the same as the column attributes table */ for (i=0;i<table->num_cols;i++) { char *tmp_buf; pcol = g_ptr_array_index(table->columns, i); if (IS_JET4(mdb)) { name_sz = read_pg_if_16(mdb, &cur_pos); } else if (IS_JET3(mdb)) { name_sz = read_pg_if_8(mdb, &cur_pos); } else { fprintf(stderr,"Unknown MDB version\n"); continue; } tmp_buf = (char *) g_malloc(name_sz); read_pg_if_n(mdb, tmp_buf, &cur_pos, name_sz); mdb_unicode2ascii(mdb, tmp_buf, name_sz, pcol->name, MDB_MAX_OBJ_NAME); g_free(tmp_buf); } /* Sort the columns by col_num */ g_ptr_array_sort(table->columns, (GCompareFunc)mdb_col_comparer); table->index_start = cur_pos; return table->columns; }
GPtrArray * mdb_read_indices(MdbTableDef *table) { MdbCatalogEntry *entry = table->entry; MdbHandle *mdb = entry->mdb; MdbFormatConstants *fmt = mdb->fmt; MdbIndex *pidx; unsigned int i, j; int idx_num, key_num, col_num; int cur_pos, name_sz, idx2_sz, type_offset; int index_start_pg = mdb->cur_pg; guchar *tmpbuf; table->indices = g_ptr_array_new(); if (IS_JET4(mdb)) { cur_pos = table->index_start + 52 * table->num_real_idxs; idx2_sz = 28; type_offset = 23; } else { cur_pos = table->index_start + 39 * table->num_real_idxs; idx2_sz = 20; type_offset = 19; } tmpbuf = (guchar *) g_malloc(idx2_sz); for (i=0;i<table->num_idxs;i++) { read_pg_if_n(mdb, tmpbuf, &cur_pos, idx2_sz); cur_pos += idx2_sz; pidx = (MdbIndex *) g_malloc0(sizeof(MdbIndex)); pidx->table = table; pidx->index_num = mdb_get_int16(tmpbuf, 4); pidx->index_type = tmpbuf[type_offset]; g_ptr_array_add(table->indices, pidx); } g_free(tmpbuf); for (i=0;i<table->num_idxs;i++) { pidx = g_ptr_array_index (table->indices, i); if (IS_JET4(mdb)) { name_sz=read_pg_if_16(mdb, &cur_pos); cur_pos += 2; tmpbuf = g_malloc(name_sz); read_pg_if_n(mdb, tmpbuf, &cur_pos, name_sz); cur_pos += name_sz; mdb_unicode2ascii(mdb, tmpbuf, 0, name_sz, pidx->name); g_free(tmpbuf); } else { read_pg_if(mdb, &cur_pos, 0); name_sz=mdb->pg_buf[cur_pos++]; read_pg_if_n(mdb, (unsigned char *) pidx->name, &cur_pos, name_sz); cur_pos += name_sz; pidx->name[name_sz]='\0'; } //fprintf(stderr, "index name %s\n", pidx->name); } mdb_read_alt_pg(mdb, entry->table_pg); mdb_read_pg(mdb, index_start_pg); cur_pos = table->index_start; idx_num=0; for (i=0;i<table->num_real_idxs;i++) { if (IS_JET4(mdb)) cur_pos += 4; do { pidx = g_ptr_array_index (table->indices, idx_num++); } while (pidx && pidx->index_type==2); /* if there are more real indexes than index entries left after removing type 2's decrement real indexes and continue. Happens on Northwind Orders table. */ if (!pidx) { table->num_real_idxs--; continue; } pidx->num_rows = mdb_get_int32(mdb->alt_pg_buf, fmt->tab_cols_start_offset + (i*fmt->tab_ridx_entry_size)); key_num=0; for (j=0;j<MDB_MAX_IDX_COLS;j++) { col_num=read_pg_if_16(mdb,&cur_pos); cur_pos += 2; read_pg_if(mdb, &cur_pos, 0); cur_pos++; if (col_num == 0xFFFF) continue; /* set column number to a 1 based column number and store */ pidx->key_col_num[key_num] = col_num + 1; pidx->key_col_order[key_num] = (mdb->pg_buf[cur_pos-1]) ? MDB_ASC : MDB_DESC; key_num++; } pidx->num_keys = key_num; cur_pos += 4; pidx->first_pg = read_pg_if_32(mdb, &cur_pos); cur_pos += 4; read_pg_if(mdb, &cur_pos, 0); pidx->flags = mdb->pg_buf[cur_pos++]; if (IS_JET4(mdb)) cur_pos += 9; } return NULL; }