Commandline
What is it?
Download
History
Progarmming
Compile
Documentation
For the impatient:
shell$ $MELDIR/mel -I$MELDIR -I/usr/include/libxml2 -r week week.dtd
shell$ cat > reader.c
#include "week.h"
int main() {
FILE * outputfile;
struct week *week = read_week(week.xml);
/* do stuff with the structures returned */
outputfile = fopen("weeknew.xml", "w");
print_week(week, outputfile);
}
^D
That should get you started with mel.
This documentation assumes you know how to read a DTD and programme in C. We'll start with a simple DTD.
This is for a small stock program. It stores a "week"'s worth of data about stockmarket data. Each week is made up of days, each day includes the date and a list of quotes which in turn are have a name, a closing price, a high and a low for the day.<!ELEMENT week (day*)> <!ELEMENT day (date, quote*)> <!ELEMENT date (year, month, dom)> <!ELEMENT month (#PCDATA)> <!ELEMENT year (#PCDATA)> <!ELEMENT dom (#PCDATA)> <!ELEMENT quote (name, close, low, high)> <!ELEMENT name (#PCDATA)> <!ELEMENT close (#PCDATA)> <!ELEMENT low (#PCDATA)> <!ELEMENT high (#PCDATA)>
We store that in the file week.dtd and in the Makefile, we place the line:
week.h week.c:
$(MELDIR)/mel -I$(MELDIR) -I/usr/include/libxml2 -r week week.dtd
The -r means that we want week to be the root (and by implication the
name of the produced .h and .c files). $(MELDIR) is the directory where
mel is installed and includes the files mel, mel.c.template,
mel.h.template, melstack.h, mellib.h and mellib.a
The week.h file will look like this:
#include "melstack.h"
#include
#include
#include
#include
/** THIS FILE IS AUTO-GENERATED
* Do NOT edit!!
* edit the .dtd file instead to alter these structures
*/
struct close {
xmlChar *_chars;
};
struct date {
struct year *year;
struct month *month;
struct dom *dom;
};
struct day {
struct date *date;
Stack(struct quote, quote);
};
struct dom {
xmlChar *_chars;
};
struct high {
xmlChar *_chars;
};
struct low {
xmlChar *_chars;
};
struct month {
xmlChar *_chars;
};
struct name {
xmlChar *_chars;
};
struct quote {
struct name *name;
struct close *close;
struct low *low;
struct high *high;
};
struct week {
Stack(struct day, day);
struct mel_idTree *mel_IDTree;
};
struct year {
xmlChar *_chars;
};
/* order */
enum week_element_order {
MEL_close,
MEL_date,
MEL_day,
MEL_dom,
MEL_high,
MEL_low,
MEL_month,
MEL_name,
MEL_quote,
MEL_week,
MEL_year
};
struct week *read_week(const char * filename);
void print_week(struct week* data, FILE *out);
void print_week_element(void *data, enum week_element_order type, FILE *out);
void debug_week(FILE *out);
void free_week_element(void *data, enum week_element_order type);
void free_week(struct week*data);
struct mel_element *week_idref2Element(struct week *root, const xmlChar * idref);
struct mel_element *week_addIdrefNElement(struct week *root, const xmlChar * idref, struct mel_element *element);
int validate_week(struct week *root);
Let's go over that in pieces
#include "melstack.h"
#include
#include
#include
#include
These are various files that allow it to compile when compiled with
gcc with extreme warnings turned on.
Note "melstack.h". This is a template created with the C preprocessor
to allow collections of items of all the same type.
/** THIS FILE IS AUTO-GENERATED
* Do NOT edit!!
* edit the .dtd file instead to alter these structures
*/
This comment is self-evident. Do what it says and don't edit this file.
struct close {
xmlChar *_chars;
};
Many of the structures in this file look like this. PCDATA types map
nicely to strings of xmlChar (from the libxml2 library). You can read
it, replace it, do whatever you want with it.
struct date {
struct year *year;
struct month *month;
struct dom *dom;
};
This is a more interesting structure. It just says date includes the
year, the month and the day of the month. Same as the DTD said.
struct day {
struct date *date;
Stack(struct quote, quote);
};
This is more interesting. day includes a date and a collection of
quotes. The Stack macro represents an expandable array. You can push
struct quote *
's onto it until you run out of memory (though this is
not recommended since it will spend a long time copying) and then pop
all of the pointers back off. You can also run through the list of
pushed items using a loop like
for (j = 0; j < Stack_size(&day.quote); j += 1){
struct quote * currquote = Stack_get(&day.quote, j);
/* do whatever */
}
which will read through the quotes in the order they were pushed onto
the stack.
We'll skip the next few since they are identical to close
struct week {
Stack(struct day, day);
struct mel_idTree *mel_IDTree;
};
sturct mel_idTree is a tree of ID's to resolve IDREFs. This is one
case where your compiler won't catch you assigning the wrong type to a
pointer since the IDTree stores everything as void *
struct year {
xmlChar *_chars;
};
/* order */
enum week_element_order {
MEL_close,
MEL_date,
MEL_day,
MEL_dom,
MEL_high,
MEL_low,
MEL_month,
MEL_name,
MEL_quote,
MEL_week,
MEL_year
};
This is a the list of types in the DTD. It's used internally by mellib
and so that you can tell mellib to read something that isn't a
week
. More a few lines down.
struct week *read_week(const char * filename);
This is your basic way of reading a XML file. If we had given a
different root name, we would have gotten a different name for this
function. It should be safe to hook up multiple produced readers to
mellib and still get intelligible results, though I admit, I haven't
tried it.
void print_week(struct week* data, FILE *out);
This will print the XML back out.
void print_week_element(void *data, enum week_element_order type, FILE *out);
Here's where that last enumeration is useful. We can tell mellib to
write out just a portion of the XML. Say those parts under
quote
by calling
print_week_element(quote, MEL_quote, stdout);
void debug_week(FILE *out);
This is for debugging mellib. You're welcome to see what it does, but
do expect lots of impossible to read information to pop up.
void free_week_element(void *data, enum week_element_order type);
void free_week(struct week*data);
These two are to free the XML data. You could easily write these
yourself, but I'm too nice a guy.
struct mel_element *week_idref2Element(struct week *root, const xmlChar * idref);
struct mel_element *week_addIdrefNElement(struct week *root, const xmlChar * idref, struct mel_element *element);
These are for looking up and adding IDs to the XML. I've not actually
tried them out other than debugging them. They would be used
like this:
quote = week_idref2Element(week, "BOBS");
assuming that quote had an ID attribute associated with it.
int validate_week(struct week *root);
This will read through a week and verify that all required fields that
mellib understand are set.
So, how do you acutally use it? Try this on for size
#include "week.h"
static struct week * week;
const char * names[] = {
"stock1",
"stock2"
};
static int highs[sizeof(names)/sizeof(const char *)];
static void getHighs(struct quote *quote){
int m;
for (m = 0 ; m < 2; m += 1){
if (!strcmp(names[m], quote->name->_chars)){
int today = atoi(quote->high->_chars);
if (highs[m] < today)
highs[m] = today;
}
}
}
int main(int argc, char ** argv){
int j,k,m;
week = read_week("quotes.xml");
for (j =0 ; j < Stack_size(&week->day); j += 1){
struct day *day = Stack_get(&week->day,j);
for (k = 0; k < Stack_size(&day->quote); k += 1){
struct quote *quote = Stack_get(&day->quote, k);
getHighs(quote);
}
}
free_week(week);
for (m = 0; m < sizeof(names)/sizeof(const char *); m += 1){
printf("%s: %d\n", names[m], highs[m]);
}
}
For more information see the Makefile and main.c in the testdir directory.