diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..b26f7c4 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "python.pythonPath": "/bin/python" +} \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..3820c8e --- /dev/null +++ b/Makefile @@ -0,0 +1,14 @@ +OBJ:= read.o +FILE := coil.gds test_text.gds +CFLAGS += -g -Wall -Werror +CC := clang + +run: test + ./test $(FILE) + +test: $(OBJ) + $(CC) $^ -o $@ + + +clean: + $(RM) $(OBJ) test diff --git a/__pycache__/__init__.cpython-37.pyc b/__pycache__/__init__.cpython-37.pyc new file mode 100644 index 0000000..beb026d Binary files /dev/null and b/__pycache__/__init__.cpython-37.pyc differ diff --git a/__pycache__/import_gds.cpython-37.pyc b/__pycache__/import_gds.cpython-37.pyc new file mode 100644 index 0000000..5b33708 Binary files /dev/null and b/__pycache__/import_gds.cpython-37.pyc differ diff --git a/gds/library.py b/gds/library.py index 4dc6261..0fa5000 100644 --- a/gds/library.py +++ b/gds/library.py @@ -19,6 +19,17 @@ class Library(object): class LinkError(Exception): element = None pass + + + """ + normalizes given coordinate according to library units + """ + def normalize_coord(self, coord, to_meters=False): + fact = self.units_per_dbunit + if to_meters: + fact *= self.meters_per_unit + + return (coord[0] * fact, coord[1] * fact) def link_refs(self, progress_callback=None): class Progress(ProgressGetter): diff --git a/gdsimporter.code-workspace b/gdsimporter.code-workspace new file mode 100644 index 0000000..e8343ff --- /dev/null +++ b/gdsimporter.code-workspace @@ -0,0 +1,15 @@ +{ + "folders": [ + { + "path": "." + } + ], + "settings": { + "files.associations": { + "limits": "c", + "memory": "c", + "optional": "c", + "type_traits": "c" + } + } +} \ No newline at end of file diff --git a/import_gds.py b/import_gds.py index 796d4ef..7378408 100644 --- a/import_gds.py +++ b/import_gds.py @@ -49,7 +49,52 @@ class Progressor: def __call__(self, update): self.total = update.total self.current = update.current + +def build_object(context, layergroups, lib, struct): + name = struct.name + + verts = {} + faces = {} + + print("structure name: {}".format(name)) + + root = bpy.data.objects.new(name, None) + context.scene.objects.link(root) + + for element in struct.elements: + # handle all elements + if isinstance(element, gds.Boundary): + if element.layer not in faces: + verts[element.layer] = [] + faces[element.layer] = [] + + indices = [] + for point in element.points[:-1]: + pos = lib.normalize_coord(point) + verts[element.layer].append((pos[0], pos[1], 0)) + + indices.append(len(verts[element.layer])-1) + + faces[element.layer].append(tuple(indices)) + # build structure object + for layer in verts.keys(): + if layer not in layergroups: + layergroups[layer] = bpy.data.groups.new("layer{}".format(layer)) + + meshname = "{}[{}]".format(name, layer) + + mesh = bpy.data.meshes.new(meshname) + mesh.from_pydata(verts[layer], [], faces[layer]) + mesh.update() + + obj = bpy.data.objects.new(meshname, mesh) + + context.scene.objects.link(obj) + layergroups[layer].objects.link(obj) + obj.parent = root + + return root def load(context, filepath, @@ -84,47 +129,28 @@ def load(context, layergroups = {} for name, value in lib.structures.items(): - if not len(value.elements): - continue + build_object(context, layergroups, lib, value) - print("structure name: {}".format(name)) + # process all references + for name, value in lib.structures.items(): + root = bpy.data.objects[name] - verts = {} - faces = {} + for reference in value.references: + sname = reference.structure.name - root = bpy.data.objects.new(name, None) - context.scene.objects.link(root) - - for element in value.elements: - # handle all elements - if isinstance(element, gds.Boundary): - if element.layer not in faces: - verts[element.layer] = [] - faces[element.layer] = [] - - indices = [] - for point in element.points[:-1]: - verts[element.layer].append((point[0] * lib.units_per_dbunit, point[1]*lib.units_per_dbunit, 0)) - indices.append(len(verts[element.layer])-1) - - faces[element.layer].append(tuple(indices)) - - # build structure object - for layer in verts.keys(): - if layer not in layergroups: - layergroups[layer] = bpy.data.groups.new("layer{}".format(layer)) - - meshname = "{}[{}]".format(name, layer) - - mesh = bpy.data.meshes.new(meshname) - mesh.from_pydata(verts[layer], [], faces[layer]) - mesh.update() - - obj = bpy.data.objects.new(meshname, mesh) + if not sname in bpy.data.objects: + print("object not build for referenced structure \"{}\"".format(sname)) + obj = build_object(context, layergroups, lib, reference.structure) + else: + obj = bpy.data.objects[sname] - context.scene.objects.link(obj) - layergroups[layer].objects.link(obj) + #create copy of object + obj = obj.copy() + + # reparent new instance obj.parent = root + pos = lib.normalize_coord(reference.position) + obj.position = (pos[0], pos[1], 0) progress.leave_substeps("Done") diff --git a/read.c b/read.c new file mode 100644 index 0000000..a8763f4 --- /dev/null +++ b/read.c @@ -0,0 +1,863 @@ +#include // for printf +#include // for uintxx_t +#include // for assert + +#include // for perror +#include // for memcpy +#include // for malloc, free + +#define LIST_ELEMENTS(a) \ +/* file header records */ \ +a(HEADER , 0x0002) \ +a(BGNLIB , 0x0102) \ +a(LIBNAME , 0x0206) \ +a(REFLIBS , 0x1F06) \ +a(FONTS , 0x2006) \ +a(ATTRTABLE , 0x2306) \ +a(GENERATIONS , 0x2202) \ +a(FORMAT , 0x3602) \ +a(MASK , 0x3706) \ +a(ENDMASKS , 0x3800) \ +a(UNITS , 0x0305) \ +/* file tail records */ \ +a(ENDLIB , 0x0400) \ +/* structure header records */ \ +a(BGNSTR , 0x0502) \ +a(STRNAME , 0x0606) \ +a(ENDEL , 0x1100) \ +/* structure tail records */ \ +a(ENDSTR , 0x0700) \ +/* element header records */ \ +a(BOUNDARY , 0x0800) \ +a(PATH , 0x0900) \ +a(SREF , 0x0A00) \ +a(AREF , 0x0B00) \ +a(TEXT , 0x0C00) \ +a(NODE , 0x1500) \ +a(BOX , 0x2D00) \ +/* element contents records */ \ +a(ELFLAGS , 0x2601) \ +a(PLEX , 0x2F03) \ +a(LAYER , 0x0D02) \ +a(DATATYPE , 0x0E02) \ +a(XY , 0x1003) \ +a(PATHTYPE , 0x2102) \ +a(WIDTH , 0x0F03) \ +a(SNAME , 0x1206) \ +a(STRANS , 0x1A01) \ +a(MAG , 0x1B05) \ +a(ANGLE , 0x1C05) \ +a(COLROW , 0x1302) \ +a(TEXTTYPE , 0x1602) \ +a(PRESENTATION, 0x1701) \ +a(ASCII_STRING, 0x1906) \ +a(NODETYPE , 0x2A02) \ +a(BOXTYPE , 0x2E02) \ +a(STRING , 0x1906) \ +a(PROPATTR , 0x2B02) \ +a(PROPVALUE , 0x2C06) + +#define a(a,b) \ +{ \ + .name = #a, \ + .ident = b \ +}, + +#define e(a,b) \ + a = b, + +struct { + const char * name; + uint16_t ident; +} header_elements[] = { + LIST_ELEMENTS(a) +}; + +enum RECORD_IDENTIFIERS { + LIST_ELEMENTS(e) +}; + +#undef a +#undef e +#undef LIST_ELEMENTS + +typedef struct record_header { + uint16_t len; + uint16_t ident; +} record_header_t; + +typedef struct date { + uint16_t year, month, day, hour, minute, second; +} date_t; + +void swap(uint8_t * a, uint8_t * b) +{ + assert(a); + assert(b); + + uint8_t temp = *a; + *a = *b; + *b = temp; +} + +void invert(uint8_t * data, size_t s) +{ + assert(data); + + uint8_t * a = data; + uint8_t * b = data + s - 1; + + s /= 2; + + while(s--) + { + swap(a,b); + a++; + b--; + } +} + +const char * name_from_ident(uint16_t ident) +{ + for (int i = 0; i < sizeof(header_elements)/sizeof(header_elements[0]); ++i) + { + if (header_elements[i].ident == ident) + return header_elements[i].name; + } + + return NULL; +} + +typedef enum { + GSD_ELEM_UNKNOWN = 0, + GDS_ELEM_BOUNDARY, + GDS_ELEM_PATH, + GDS_ELEM_SREF, + GDS_ELEM_AREF, + GDS_ELEM_TEXT, + GDS_ELEM_NODE, + GDS_ELEM_BOX, +} gds_element_type_t; + +#define GDS_ELEM_IS_GRAPHICAL(t) ((t == GDS_ELEM_BOUNDARY) || (t == GDS_ELEM_PATH) || (t == GDS_ELEM_SREF) || (t == GDS_ELEM_AREF) || (t == GDS_ELEM_TEXT) || (t == GDS_ELEM_NODE) || (t == GDS_ELEM_BOX)) +#define GDS_ELEM_HAS_LAYER(t) ((t == GDS_ELEM_BOUNDARY) || (t == GDS_ELEM_PATH) || (t == GDS_ELEM_TEXT) || (t == GDS_ELEM_NODE) || (t == GDS_ELEM_BOX)) + +typedef struct { + gds_element_type_t type; + uint16_t elfclass; + int16_t plex; +} gds_element_base_t; + +typedef struct { + int32_t x,y; +} gds_coord_t; + +typedef struct { + gds_element_base_t base; + + size_t point_count; + gds_coord_t * points; +} gds_element_graphical_t; + +typedef struct { + gds_element_graphical_t base; + int16_t layer; + int16_t datatype; +} gds_element_boundary_t; + +typedef enum { + GDS_PATH_SQUARE = 0, + GDS_PATH_ROUNDED = 1, + GDS_PATH_SQUARE_EXTEND = 2 +} gds_path_type_t; + +typedef struct { + gds_element_graphical_t base; + int16_t layer; + int16_t datatype; + + gds_path_type_t pathtype; + + int32_t width; +} gds_element_path_t; + +typedef struct { + gds_element_graphical_t base; + + int16_t layer; + int16_t nodetype; +} gds_element_node_t; + +typedef struct { + gds_element_graphical_t base; + + int16_t layer; + int16_t texttype; + + int32_t width; + gds_path_type_t pathtype; + + uint8_t font_number:2; + + struct + { + enum + { + GDS_TEXT_TOP = 0b00, + GDS_TEXT_MIDDLE = 0b01, + GDS_TEXT_BOTTOM = 0b10, + } vertical; + + enum + { + GDS_TEXT_LEFT = 0b00, + GDS_TEXT_CENTER = 0b01, + GDS_TEXT_RIGHT = 0b10 + } horizontal; + } justification; + + char * string; +} gds_element_text_t; + +typedef union { + gds_element_type_t type; + gds_element_graphical_t graphical; + + /* element containers */ + gds_element_boundary_t boundary; + gds_element_path_t path; + gds_element_node_t node; + gds_element_text_t text; +} gds_element_t; + +typedef struct structure { + date_t creation, last_access; + char * name; + + size_t element_count; + gds_element_t * elements; +} gds_structure_t; + +typedef struct gds_file { + uint16_t version; // HEADER data + date_t last_mod, last_access; // BGNLIB data + char * libname; // LIBNAME data, must be freed + + enum { + GDS_FMT_ARCHIVED = 0, + GDS_FMT_FILTERED = 1, + } format; + + struct { + double units_per_dbunits; + double meters_per_unit; + } units; // UNITS data + + size_t structure_count; + gds_structure_t * structures; +} gds_file_t; + +/* parser errors */ +#define LIST_ERRORS(a) \ + a(GDS_NO_ERR , "Success") \ + a(GDS_ERR_MISSING_ARGS, "Missing argument(s) to record type") \ + a(GDS_ERR_NOMEM , "out of memory") \ + a(GDS_ERR_EOF , "unexpected EOF") \ + a(GDS_ERR_MISSING_HEADER , "file is missing HEADER record") \ + a(GDS_ERR_MISSING_BEGELEM , "found element records without an element start") \ + a(GDS_ERR_INVALID_FILE , "file found to be invalid") + +#define STR(a, b) #a ": " b, +#define ENUM(a, b) a, + +static const char * gds_parser_errstr[] = { LIST_ERRORS(STR) }; +typedef enum { + LIST_ERRORS(ENUM) + GDS_ERR_COUNT, +} gds_parser_err_t; + +#undef STR +#undef ENUM +#undef LIST_ERRORS + +const char * gds_strerror(gds_parser_err_t err) { + if (err >= GDS_ERR_COUNT) { + return "unknown error"; + } + + return gds_parser_errstr[err]; +} + +typedef struct parser_state { + uint8_t has_header:1; + gds_structure_t * current_structure; + gds_element_t * current_element; + + gds_parser_err_t error; + gds_file_t root; +} parser_state_t; + + +void free_gds_element(gds_element_t * this) +{ + switch(this->type) + { + default: + case GSD_ELEM_UNKNOWN: + break; + case GDS_ELEM_BOUNDARY: + case GDS_ELEM_PATH: + // clear all points + free(this->graphical.points); + this->graphical.point_count = 0; + break; + case GDS_ELEM_TEXT: + free(this->text.string); + break; + } + + // clear type + this->type = GSD_ELEM_UNKNOWN; +} + +void free_gds_structure(gds_structure_t * this) +{ + for (int i = 0; i < this->element_count; ++i) + free_gds_element(this->elements + i); + + free(this->name); + free(this->elements); +} + +void free_gds_file(gds_file_t * file) +{ + + for (int i = 0; i < file->structure_count; ++i) + free_gds_structure(file->structures + i); + + free(file->libname); + free(file->structures); +} + +// cleanup parser state from mem +void free_parser_state(parser_state_t * state) +{ + free_gds_file(&state->root); +} + +#define READ_STRUCT(file, dest) (fread((dest), sizeof(*(dest)), 1, (file))) + +int read_ushort(uint16_t * dest, FILE * file) { + if (!READ_STRUCT(file, dest)) { + return 0; + } + + invert((uint8_t*)dest, sizeof(*dest)); + return 1; +} + +int read_short(int16_t * dest, FILE * file) { + if (!READ_STRUCT(file, dest)) { + return 0; + } + + invert((uint8_t*)dest, sizeof(*dest)); + return 1; +} + +int read_int(int32_t * dest, FILE * file) { + if (!READ_STRUCT(file, dest)) + return 0; + + invert((uint8_t*)dest, sizeof(*dest)); + return 1; +} +int read_date(date_t * dest, FILE * src) { + if (!READ_STRUCT(src, dest)) { + return 0; + } + + invert((uint8_t*)&dest->day, 2); + invert((uint8_t*)&dest->month, 2); + invert((uint8_t*)&dest->year, 2); + + invert((uint8_t*)&dest->hour, 2); + invert((uint8_t*)&dest->minute, 2); + invert((uint8_t*)&dest->second, 2); + + return 1; +} + +// reads a 8 byte double value as it is represented in +// a gds file +// from spec: 1 bit sign | 7 bit exp (in 64-excess) | 7 byte mantisse +// with double = sign * 1.mantisse * 64 ** exp +int read_double(double * dest, FILE * file) { + uint8_t src[8]; + + if (!fread(src, 8, 1, file)) { + return 0; + } + + // special value, ZERO + if (*(uint64_t*)(src) == 0) { + *dest = 0; + return 1; + } + + // mantissa first + *dest = 1; + for (int i = 1; i < 8; ++i) { + *dest += ((double)src[8-i])/(1L< 0) + { + while(exp) + { + *dest *= 16.0; + exp--; + } + } + else + { + while(exp) + { + *dest /= 16.0; + exp++; + } + } + + // negation + if (src[0] & 0x80) { + *dest *= -1; + } + + return 1; +} + +int read_record_header(record_header_t * header, FILE * file) { + if (!read_ushort(&header->len, file) || + !read_ushort(&header->ident, file)) + { + return 0; + } + + header->len -= 4; + return 1; +} + +int read_coord(gds_coord_t * coord, FILE * file) +{ + if (!read_int(&coord->x, file) || !read_int(&coord->y, file)) + return 0; + + return 1; +} + +/* allocate a new element in a structure */ +gds_element_t * alloc_elem(gds_structure_t * this, gds_element_type_t type) +{ + gds_element_t * new = realloc(this->elements, (this->element_count+1)*sizeof(gds_element_t)); + + if (new == NULL) + return NULL; + + this->elements = new; + + new += this->element_count++; + + // initialize new element + *new = (gds_element_t){}; + new->type = type; + + return new; +} + +#define DATE_FMT "%2d.%02d.%04d" +#define DATE_ARGS(a) (a).day, (a).month, (a).year + +int parse_record(parser_state_t * state, FILE * file) +{ + record_header_t header = {}; + + if (!read_record_header(&header, file)) + { + perror("could not read file record"); + return 0; + } + +#define ASSERT(a) if (!(a)) do { printf("ASSERT %s:%d\n", __FILE__, __LINE__); state->error = GDS_ERR_INVALID_FILE; return 0; } while(0) + +#if 0 + printf("record %s (%04x)\n", name_from_ident(header.ident), header.ident); +#endif + + if (!state->has_header && header.ident != HEADER) + { + state->error = GDS_ERR_MISSING_HEADER; + return 0; + } + + switch (header.ident) + { + case HEADER: + ASSERT(header.len == 2); + + read_ushort(&state->root.version, file); + state->has_header = 1; + break; + + case BGNLIB: + ASSERT(header.len == 2* sizeof(date_t)); + + read_date(&state->root.last_mod , file); + read_date(&state->root.last_access, file); + + break; + + case ENDLIB: + if (state->current_structure) + printf("missing (last) ENDSTR\n"); // fixme: better warning system + + return 0; + + case LIBNAME: + state->root.libname = calloc(1, header.len + 1); + if (state->root.libname == NULL) { + state->error = GDS_ERR_NOMEM; + return 0; + } + + // copy libname to new string + if (!fread(state->root.libname, header.len, 1, file)) { + state->error = GDS_ERR_EOF; + return 0; + } + + break; + + // case REFLIBS: + // break; + // case FONTS: + // break; + // case ATTRTABLE: + // break; + // case GENERATIONS: + // break; + + case FORMAT: + ASSERT(header.len); + + uint16_t temp; + read_ushort(&temp, file); + + if (temp) + state->root.format = GDS_FMT_FILTERED; + + break; + + case UNITS: + ASSERT(header.len == 16); + + read_double(&state->root.units.units_per_dbunits, file); + read_double(&state->root.units.meters_per_unit , file); + + // now comes the first structure definition + break; + + case BGNSTR: + ASSERT(header.len == 2 * sizeof(date_t)); + + // create new structure, apppend to list + gds_structure_t * list = realloc(state->root.structures, (state->root.structure_count+1)*sizeof(gds_structure_t)); + if (!list) + { + state->error = GDS_ERR_NOMEM; + return 0; + } + + // set new list pointer + state->root.structures = list; + state->current_structure = &state->root.structures[state->root.structure_count++]; + + // read dates + *state->current_structure = (gds_structure_t){}; + read_date(&state->current_structure->creation, file); + read_date(&state->current_structure->last_access, file); + break; + + case ENDSTR: + // done with structure + state->current_structure = NULL; + break; + + case STRNAME: + state->current_structure->name = calloc(1, header.len + 1); + + if (state->current_structure->name == NULL) + { + state->error = GDS_ERR_NOMEM; + return 0; + } + + if (!fread(state->current_structure->name, header.len, 1, file)) + { + state->error = GDS_ERR_EOF; + return 0; + } + + break; + + + case BOUNDARY: + ASSERT(state->current_structure); + + if ((state->current_element = alloc_elem(state->current_structure, GDS_ELEM_BOUNDARY)) == NULL) + { + state->error = GDS_ERR_NOMEM; + return 0; + } + break; + case PATH: + ASSERT(state->current_structure); + + if ((state->current_element = alloc_elem(state->current_structure, GDS_ELEM_PATH)) == NULL) + { + state->error = GDS_ERR_NOMEM; + return 0; + } + break; + case TEXT: + ASSERT(state->current_structure); + + if ((state->current_element = alloc_elem(state->current_structure, GDS_ELEM_TEXT)) == NULL) + { + state->error = GDS_ERR_NOMEM; + return 0; + } + break; + case NODE: + ASSERT(state->current_structure); + + if ((state->current_element = alloc_elem(state->current_structure, GDS_ELEM_NODE)) == NULL) + { + state->error = GDS_ERR_NOMEM; + return 0; + } + break; + + case ENDEL: + state->current_element = NULL; + break; + + case DATATYPE: + if (!state->current_element) { + state->error = GDS_ERR_MISSING_BEGELEM; + return 0; + } + + switch (state->current_element->type) { + case GDS_ELEM_PATH: + read_short(&state->current_element->path.datatype, file); + break; + case GDS_ELEM_BOUNDARY: + read_short(&state->current_element->boundary.datatype, file); + break; + default: + state->error = GDS_ERR_INVALID_FILE; + return 0; + } + + break; + + case LAYER: + if (!state->current_element) + { + state->error = GDS_ERR_MISSING_BEGELEM; + return 0; + } + + ASSERT(GDS_ELEM_HAS_LAYER(state->current_element->type)); + + switch(state->current_element->type) + { + case GDS_ELEM_BOUNDARY: + read_short(&state->current_element->boundary.layer, file); + break; + case GDS_ELEM_PATH: + read_short(&state->current_element->path.layer, file); + break; + default: + // fixme: we should read a layer here, but + // its probably not implemented yet + fseek(file, header.len, SEEK_CUR); + } + break; + + case XY: + ASSERT(state->current_element); + ASSERT(GDS_ELEM_IS_GRAPHICAL(state->current_element->type)); + + if(header.len % sizeof(gds_coord_t)) + { + // alignment error + return -1; + } + + { + gds_coord_t * current = state->current_element->graphical.points = malloc(header.len); + if (!current) + { + state->error = GDS_ERR_NOMEM; + return 0; + } + + size_t size = header.len / sizeof(gds_coord_t); + state->current_element->graphical.point_count = size; + + // read all points + for(;size && read_coord(current, file);--size, ++current); + + if (size) + { + state->error = GDS_ERR_EOF; + return 0; + } + } + break; + + case WIDTH: + ASSERT(state->current_element); + + switch(state->current_element->type) { + case GDS_ELEM_PATH: + read_int(&state->current_element->path.width, file); + break; + case GDS_ELEM_TEXT: + read_int(&state->current_element->text.width, file); + default: + state->error = GDS_ERR_INVALID_FILE; + return 0; + } + + break; + case PATHTYPE: + ASSERT(state->current_element); + + { + int16_t temp; + read_short(&temp, file); + if (temp > GDS_PATH_SQUARE_EXTEND || temp < 0) + { + printf("inalid pathtype found\n"); + } + + else { + switch(state->current_element->type) { + case GDS_ELEM_PATH: + state->current_element->path.pathtype = temp; + break; + case GDS_ELEM_TEXT: + state->current_element->text.pathtype = temp; + break; + default: + printf("misplaced pathtype\n"); + break; + } + + } + + } + break; + case NODETYPE: + ASSERT(state->current_element); + ASSERT(state->current_element->type == GDS_ELEM_NODE); + + read_short(&state->current_element->node.nodetype, file); + break; + + default: + // skip record + printf("unhandled record with ident %04x (%s)\n", header.ident, name_from_ident(header.ident)); + fseek(file, header.len, SEEK_CUR); + break; + } + + return 1; +} + +gds_parser_err_t parse_gds(FILE * f, gds_file_t * file) +{ + parser_state_t state = {}; + + // go to beginning of file (just in case) + fseek(f, 0, SEEK_SET); + + // parse all records + while(!feof(f) && parse_record(&state, f)); + + // transfer ownership of file + *file = state.root; + + // reset file of parser state + state.root = (gds_file_t){}; + + // cleanup parser memory + free_parser_state(&state); + + return state.error; +} + +int main(int argc, char ** argv) +{ + printf("start\n"); + + for (char ** filename = argv + 1; filename != argv + argc; ++filename) + { + printf("reading %s...\n", *filename); + + gds_file_t file; + FILE * f = fopen(*filename, "rb"); + + if (!f) + { + perror("could not open file"); + continue; + } + + gds_parser_err_t err = parse_gds(f, &file); + + if (err != GDS_NO_ERR) { + fprintf(stderr, "error during parsing of file: %s\n", gds_strerror(err)); + } + + printf("read file\n"); + printf("last accessed: " DATE_FMT "\n", DATE_ARGS(file.last_access)); + printf("last modification: " DATE_FMT "\n", DATE_ARGS(file.last_mod)); + printf("libname: %s\n", file.libname); + + printf("units per db units: %g\n", file.units.units_per_dbunits); + printf("m/units : %g\n", file.units.meters_per_unit); + printf("file contains %ld structures\n", file.structure_count); + + for (gds_structure_t * it = file.structures; it != file.structures + file.structure_count; ++it) + { + printf("structure with name \"%s\" ", it->name); + printf("contains %ld elements\n", it->element_count); + } + + // cleanup + free_gds_file(&file); + fclose(f); + } + + printf("done\n"); + return 0; +} \ No newline at end of file diff --git a/tests/test_aref.gds b/tests/test_aref.gds new file mode 100644 index 0000000..3caa67c Binary files /dev/null and b/tests/test_aref.gds differ diff --git a/tests/test_coords.gds b/tests/test_coords.gds new file mode 100644 index 0000000..f4021aa Binary files /dev/null and b/tests/test_coords.gds differ diff --git a/tests/test_sref.gds b/tests/test_sref.gds new file mode 100644 index 0000000..4292f62 Binary files /dev/null and b/tests/test_sref.gds differ