Gedcom object model in C
Index
Main functions
There are two ways to start with a GEDCOM object model (after having called gedcom_init
): either by starting from scratch, or by starting from a given GEDCOM file. This is done via the following two functions:
int gom_parse_file (const char* file_name);
This initializes the object model by parsing the GEDCOM file given by file_name
. It returns 0 on success and 1 on failure.
int gom_new_model ();
This starts an empty model. Actually, this is done by processing the file "new.ged
" in the gedcom-parse data directory.
In the GEDCOM object model, all the data is immediately available after calling gom_parse_file()
or gom_new_model()
. For this, an entire model based on C structs is used. These structs are documented here,
and follow the GEDCOM syntax quite closely. Each of the records in
a GEDCOM file are modelled by a separate struct, and some common sub-structures
have their own struct definition.
The following functions are available to get at these structs:
- First, there are two functions to get the header record and the submission
record (there can be only one of them in a GEDCOM file):
struct header* gom_get_header();
struct submission* gom_get_submission();
- Further, for each of the other records, there are two functions, one
to get the first of such records, and one to get a record via its cross-reference
tag in the GEDCOM file:
struct XXX* gom_get_first_XXX();
struct XXX* gom_get_XXX_by_xref(char* xref);
The XXX
stands for one of the following: family,
individual, multimedia, note, repository, source, submitter, user_rec
.
Object model structure
Object lists
All records of a certain type are linked together in a linked list. The
above functions only give access to the first record of each linked list.
The others can be accessed by traversing the linked list via the next
member of the structs. This means that e.g. the following piece of code will traverse the linked list of family records:
struct family* fam;
for (fam = gom_get_first_family() ; fam ; fam = fam->next) {
...
}
The next
member of the last element in the list is guaranteed to have the NULL
value.
Actually, the linked list is a doubly-linked list: each record also has a previous
member. But for implementation reasons the behaviour of this previous
member on the edges of the linked list will not be guaranteed, i.e. it can be circular or terminated with NULL
, no assumptions can be made in the application code.
This linked-list model applies also to all sub-structures of the main record structs, i.e. each struct that has a next
and previous
member follows the above conventions. This means that the following
piece of code traverses all children of a family (see the details of the
different structs here):
struct family* fam = ...;
struct xref_list* xrl;
for (xrl = fam->children ; xrl ; xrl = xrl->next) {
...
}
Note that all character strings in the object model are encoded in UTF-8 (Why UTF-8?), but see below for how to convert these automatically.
User data
Each of the structs has an extra member called extra
(of type struct user_data*
).
This gathers all non-standard GEDCOM tags within the scope of the struct
in a flat linked list, no matter what the internal structure of the non-standard
tags is. Each element of the linked list has:
- a level: the level number in the GEDCOM file
- a tag: the tag given in the GEDCOM file
- a value: the value, which can be a string value or a cross-reference value (one of the two will be non-NULL)
This way, none of the information in the GEDCOM file is lost, even the non-standard information.
Modifying the object model
Currently, there are only functions available to modify strings in the model (and the date manipulation functions described here).
In principle it is possible to add new records, notes, ... yourself,
but you have to know what you are doing, because you should keep the model
consistent. In the next release, functions will be available to add,
remove and modify anything in the model.
Manipulating strings
There are some functions available to retrieve and change strings in the
Gedcom object model, depending whether you use UTF-8 strings in your application
or locale-defined strings.
The following functions retrieve and set the string in UTF-8 encoding:
char* gom_get_string (char* data);
char* gom_set_string (char** data, const char* str_in_utf8);
The first function is in fact superfluous, because it just returns the data
, but it is there for symmetry with the functions given below for the locale-defined input and output.
The second function returns the new value if successful, or NULL
if an error occurred (e.g. failure to allocate memory or the given string is not a valid UTF-8 string). It makes a
copy of the input string to store it in the object model. It also takes
care of deallocating the old value of the data if needed. Note that
the set function needs the address of the data variable, to be able to modify
it. In the case of an error, the target data variable is not modified.
Examples of use of these strings would be, e.g. for retrieving and setting the system ID in the header:
struct header* head = gom_get_header();
char* oldvalue = gom_get_string(head->source.id);
char* newvalue = "My_Gedcom_Tool";
if (gom_set_string(&head->source.id, newvalue)) {
printf("Modified system id from %s to %s\n", oldvalue, newvalue);
}
A second couple of functions retrieve and set the string in the format defined by the current locale:
char* gom_get_string_for_locale (char* data, int* conversion_failures);
char* gom_set_string_for_locale (char** data, const char* str_in_locale)
;
The use of these functions is the same as the previous ones, but e.g. in
the "en_US" locale the string will be returned by the first function in the
ISO-8859-1 encoding and the second function expects the str_in_locale
to be in this encoding. Conversion to and from UTF-8 for the object model is done on the fly.
Since the conversion from UTF-8 to the locale encoding is not always possible,
the get function has a second parameter that can return the number of conversion
failures for the result string. Pass a pointer to an integer if you
want to know this. You can pass NULL
if you're not interested. The function returns NULL
if an error occurred (e.g. if the given string is not a valid string for
the current locale); in that case the target data variable is not modified.
Writing the object model to file
Writing the current object model to a file is simply done using the following function:
int gom_write_file (const char* filename, int* total_conv_fails);
This writes the model to the file filename
. The second parameter can return the total number of conversion failures (pass NULL
if you're not interested). The functions in this section can be used before gom_write_file
to control some settings.
Before you write the file, you can update the timestamp in the header using the following function:
int gom_header_update_timestamp (time_t tval);
This sets the date
and time
fields of the header to the time indicated by tval
.
The function returns 0 on success, non-zero if an error occurred. Typically,
the function would be used as follows, to set the current time in the timestamp:
int result;
result = gom_header_update_timestamp(time(NULL));
$Id$
$Name$