Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,6 @@ fuzz_fmp_LDADD = libfmptools.la @LIB_FUZZING_ENGINE@
AM_CFLAGS += -fsanitize=fuzzer-no-link -fsanitize=address -g
libfmptools_la_LDFLAGS += -fsanitize=fuzzer -fsanitize=address
endif

TESTS = test/test_fmp2sqlite.sh
EXTRA_DIST = $(TESTS)
46 changes: 46 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,49 @@ API is subject to change.

You might also enjoy [fp5dump](https://github.com/qwesda/fp5dump), although
that project does not read the newer fp7 and fmp12 formats.


Windows cross-compilation
--

The tools can be cross-compiled for 64-bit Windows from Linux using MinGW-w64.

Required components include:

* `x86_64-w64-mingw32-gcc`
* Windows-targeted SQLite
* Windows-targeted iconv

Example configure command:

```sh
CPPFLAGS="-I/path/to/windows/include" \
LDFLAGS="-L/path/to/windows/lib" \
LIBS="-liconv" \
./configure \
--host=x86_64-w64-mingw32 \
--disable-shared \
--enable-static
```

Then build with:

```sh
make
```

This produces Windows executables such as:

```text
fmp2sqlite.exe
fmpdump.exe
```

If iconv is linked dynamically, place `iconv.dll` next to the executable when distributing it.

This fork also includes fixes for:

* opening FileMaker files in binary mode on Windows
* MinGW/iconv compatibility
* nested FMP12 column metadata
* SQLite parameter binding for sparse FileMaker column indexes
22 changes: 19 additions & 3 deletions src/bin/fmp2sqlite.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ typedef struct fmp_sqlite_ctx_s {
sqlite3_stmt *insert_stmt;
char *table_name;
int last_row;
fmp_column_array_t *columns;
} fmp_sqlite_ctx_t;

fmp_handler_status_t handle_value(int row, fmp_column_t *column, const char *value, void *ctxp) {
Expand All @@ -52,7 +53,17 @@ fmp_handler_status_t handle_value(int row, fmp_column_t *column, const char *val
}
sqlite3_clear_bindings(ctx->insert_stmt);
}
int rc = sqlite3_bind_text(ctx->insert_stmt, column->index, value, strlen(value), SQLITE_TRANSIENT);
int bind_index = 0;
for (int i = 0; i < ctx->columns->count; i++) {
if (ctx->columns->columns[i].index == column->index) {
bind_index = i + 1;
break;
}
}
if (bind_index == 0)
return FMP_HANDLER_OK;

int rc = sqlite3_bind_text(ctx->insert_stmt, bind_index, value, strlen(value), SQLITE_TRANSIENT);
if (rc != SQLITE_OK) {
fprintf(stderr, "Error binding parameter: %s\n", sqlite3_errmsg(ctx->db));
return FMP_HANDLER_ABORT;
Expand Down Expand Up @@ -170,7 +181,7 @@ int main(int argc, char *argv[]) {
q += snprintf(q, insert_query_len - (q - insert_query), ") VALUES (");
for (int j=0; j<columns->count; j++) {
fmp_column_t *column = &columns->columns[j];
q += snprintf(q, insert_query_len - (q - insert_query), "?%d", column->index);
q += snprintf(q, insert_query_len - (q - insert_query), "?%d", j + 1);
if (j < columns->count - 1)
q += snprintf(q, insert_query_len - (q - insert_query), ", ");
}
Expand All @@ -192,7 +203,12 @@ int main(int argc, char *argv[]) {
return 1;
}

fmp_sqlite_ctx_t ctx = { .db = db, .table_name = table->utf8_name, .insert_stmt = stmt };
fmp_sqlite_ctx_t ctx = {
.db = db,
.table_name = table->utf8_name,
.insert_stmt = stmt,
.columns = columns
};
fmp_read_values(file, table, &handle_value, &ctx);
if (ctx.last_row) {
int rc = sqlite3_step(stmt);
Expand Down
4 changes: 2 additions & 2 deletions src/fmp.c
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ void convert(iconv_t converter, uint8_t xor_mask,
char *output_bytes = dst;
size_t output_bytes_left = dst_len;
if (converter) {
iconv(converter, &input_bytes, &input_bytes_left, &output_bytes, &output_bytes_left);
iconv(converter, (const char **)&input_bytes, &input_bytes_left, &output_bytes, &output_bytes_left);
} else {
convert_scsu_to_utf8(&input_bytes, &input_bytes_left, &output_bytes, &output_bytes_left);
}
Expand Down Expand Up @@ -377,7 +377,7 @@ fmp_file_t *fmp_open_buffer(const void *buffer, size_t len, fmp_error_t *errorCo

fmp_file_t *fmp_open_file(const char *path, fmp_error_t *errorCode) {
fmp_file_t *file = NULL;
FILE *stream = fopen(path, "r");
FILE *stream = fopen(path, "rb");
if (!stream) {
if (errorCode)
*errorCode = FMP_ERROR_OPEN;
Expand Down
13 changes: 13 additions & 0 deletions src/list_columns.c
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,19 @@ static chunk_status_t handle_chunk_list_columns_v7(fmp_chunk_t *chunk, fmp_list_
if (chunk->type != FMP_CHUNK_FIELD_REF_SIMPLE)
return CHUNK_NEXT;

if (chunk->path_level == 5 &&
path_value(chunk, path_at(chunk, 0)) == ctx->target_table_index + 128 &&
path_is(chunk, path_at(chunk, 1), 3) &&
path_is(chunk, path_at(chunk, 2), 5) &&
path_is(chunk, path_at(chunk, 4), 16) &&
chunk->ref_simple == 1) {
size_t column_index = path_value(chunk, path_at(chunk, 3));
if (column_index > 0 && column_index <= FMP_MAX_INDEX) {
handle_column(column_index, &chunk->data, ctx);
}
return CHUNK_NEXT;
}

if (table_path_match_start2(chunk, 3, 3, 5)) {
fmp_data_t *column_path = path_at(chunk, chunk->path_level-1);
size_t column_index = path_value(chunk, column_path);
Expand Down
47 changes: 47 additions & 0 deletions src/read_values.c
Original file line number Diff line number Diff line change
Expand Up @@ -133,8 +133,11 @@ static chunk_status_t handle_chunk_read_values_v3(fmp_chunk_t *chunk, fmp_read_v
if (column_index == 0 || column_index > FMP_MAX_INDEX)
return CHUNK_NEXT;
if (column_index > ctx->num_columns) {
size_t old_num_columns = ctx->num_columns;
ctx->num_columns = column_index;
ctx->columns = realloc(ctx->columns, ctx->num_columns * sizeof(fmp_column_t));
memset(&ctx->columns[old_num_columns], 0,
(ctx->num_columns - old_num_columns) * sizeof(fmp_column_t));
}
fmp_column_t *current_column = ctx->columns + column_index - 1;
if (chunk->ref_simple == 1) {
Expand Down Expand Up @@ -162,14 +165,58 @@ static chunk_status_t handle_chunk_read_values_v7(fmp_chunk_t *chunk, fmp_read_v
if (chunk->type != FMP_CHUNK_FIELD_REF_SIMPLE && chunk->type != FMP_CHUNK_DATA_SEGMENT)
return CHUNK_NEXT;

if (chunk->path_level == 5 &&
path_value(chunk, path_at(chunk, 0)) == ctx->target_table_index + 128 &&
path_is(chunk, path_at(chunk, 1), 3) &&
path_is(chunk, path_at(chunk, 2), 5) &&
path_is(chunk, path_at(chunk, 4), 16) &&
chunk->ref_simple == 1) {
size_t column_index = path_value(chunk, path_at(chunk, 3));

if (column_index > 0 && column_index <= FMP_MAX_INDEX) {
if (column_index > ctx->num_columns) {
size_t old_num_columns = ctx->num_columns;
ctx->num_columns = column_index;
ctx->columns = realloc(
ctx->columns,
ctx->num_columns * sizeof(fmp_column_t)
);
memset(
&ctx->columns[old_num_columns],
0,
(ctx->num_columns - old_num_columns) * sizeof(fmp_column_t)
);
}

fmp_column_t *current_column =
ctx->columns + column_index - 1;

convert(
ctx->file->converter,
ctx->file->xor_mask,
current_column->utf8_name,
sizeof(current_column->utf8_name),
chunk->data.bytes,
chunk->data.len
);

current_column->index = column_index;
}

return CHUNK_NEXT;
}

if (table_path_match_start2(chunk, 3, 3, 5)) {
fmp_data_t *column_path = path_at(chunk, chunk->path_level-1);
size_t column_index = path_value(chunk, column_path);
if (column_index == 0 || column_index > FMP_MAX_INDEX)
return CHUNK_NEXT;
if (column_index > ctx->num_columns) {
size_t old_num_columns = ctx->num_columns;
ctx->num_columns = column_index;
ctx->columns = realloc(ctx->columns, ctx->num_columns * sizeof(fmp_column_t));
memset(&ctx->columns[old_num_columns], 0,
(ctx->num_columns - old_num_columns) * sizeof(fmp_column_t));
}
fmp_column_t *current_column = ctx->columns + column_index - 1;
if (chunk->ref_simple == 16) {
Expand Down
2 changes: 1 addition & 1 deletion src/scsu.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
*/

#include <sys/types.h>
#include <sys/errno.h>
#include <errno.h>
#include <stdint.h>
#include <stdio.h>

Expand Down
16 changes: 16 additions & 0 deletions test/test_fmp2sqlite.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#!/bin/sh
set -eu

TMP_DB="${TMPDIR:-/tmp}/fmptools-portablecode39.sqlite"
rm -f "$TMP_DB"

./fmp2sqlite test/data/fmp12/PortableCode39-2.fmp12 "$TMP_DB"

if [ ! -s "$TMP_DB" ]; then
echo "SQLite output was not created or is empty"
exit 1
fi

rm -f "$TMP_DB"

echo "fmp2sqlite regression test passed"