863 lines
20 KiB
C
863 lines
20 KiB
C
|
#include <stdio.h> // for printf
|
||
|
#include <stdint.h> // for uintxx_t
|
||
|
#include <assert.h> // for assert
|
||
|
|
||
|
#include <errno.h> // for perror
|
||
|
#include <memory.h> // for memcpy
|
||
|
#include <stdlib.h> // 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<<i*8);
|
||
|
}
|
||
|
|
||
|
// exp
|
||
|
int exp = src[0] & 0x7F;
|
||
|
exp -= 64;
|
||
|
|
||
|
// exp is base 64
|
||
|
// so implement pow here :)
|
||
|
if (exp > 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;
|
||
|
}
|