/* Support for "OpenStreetMap" data files (.xml) Copyright (C) 2008 Olaf Klein, o.b.klein@gpsbabel.org This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111 USA */ #include "defs.h" #include "avltree.h" #include "xmlgeneric.h" static char* opt_tag, *opt_tagnd, *created_by; static arglist_t osm_args[] = { { "tag", &opt_tag, "Write additional way tag key/value pairs", NULL, ARGTYPE_STRING, ARG_NOMINMAX }, { "tagnd", &opt_tagnd, "Write additional node tag key/value pairs", NULL, ARGTYPE_STRING, ARG_NOMINMAX }, { "created_by", &created_by, "Use this value as custom created_by value","GPSBabel", ARGTYPE_STRING, ARG_NOMINMAX }, ARG_TERMINATOR }; #define MYNAME "osm" static avltree_t* waypoints; /* AVL tree */ static avltree_t* keys = NULL; static avltree_t* values = NULL; static avltree_t* icons = NULL; static gbfile* fout; static int node_id; static int skip_rte; #if HAVE_LIBEXPAT static route_head* rte; static waypoint* wpt; static int wpt_loaded, rte_loaded; static xg_callback osm_node, osm_node_tag, osm_node_end; static xg_callback osm_way, osm_way_nd, osm_way_tag, osm_way_end; static xg_tag_mapping osm_map[] = { { osm_node, cb_start, "/osm/node" }, { osm_node_tag, cb_start, "/osm/node/tag" }, { osm_node_end, cb_end, "/osm/node" }, { osm_way, cb_start, "/osm/way" }, { osm_way_nd, cb_start, "/osm/way/nd" }, { osm_way_tag, cb_start, "/osm/way/tag" }, { osm_way_end, cb_end, "/osm/way" }, { NULL, (xg_cb_type)0, NULL } }; #endif // HAVE_LIBEXPAT static const char* osm_features[] = { "- dummy -", /* 0 */ "aeroway", /* 1 */ "amenity", /* 2 */ "building", /* 3 */ "cycleway", /* 4 */ "railway", /* 5 */ "highway", /* 6 */ "historic", /* 7 */ "landuse", /* 8 */ "leisure", /* 9 */ "man_made", /* 10 */ "military", /* 11 */ "natural", /* 12 */ "place", /* 13 */ "power", /* 14 */ "shop", /* 15 */ "sport", /* 16 */ "tourism", /* 17 */ "waterway", /* 18 */ "aerialway", /* 19 */ NULL }; typedef struct osm_icon_mapping_s { const int key; const char* value; const char* icon; } osm_icon_mapping_t; /* based on */ static osm_icon_mapping_t osm_icon_mappings[] = { /* cycleway ...*/ /* highway ...*/ // { 6, "mini_roundabout", "?" }, // { 6, "stop", "?" }, // { 6, "traffic_signals", "?" }, // { 6, "crossing", "?" }, // { 6, "gate", "?" }, // { 6, "stile", "?" }, // { 6, "cattle_grid", "?" }, // { 6, "toll_booth", "?" }, // { 6, "incline", "?" }, // { 6, "incline_steep", "?" }, // { 6, "viaduct", "?" }, // { 6, "motorway_junction", "?" }, // { 6, "services", "?" }, // { 6, "ford", "?" }, // { 6, "bus_stop", "?" }, // { 6, "turning_circle", "?" }, // { 6, "User Defined", "?" }, /* waterway ... */ { 18, "dock", "Dock" }, // { 18, "lock_gate", "?" }, // { 18, "turning_point", "?" }, // { 18, "aqueduct", "?" }, // { 18, "boatyard", "?" }, // { 18, "water_point", "?" }, // { 18, "waste_disposal", "?" }, // { 18, "mooring", "?" }, // { 18, "weir", "?" }, // { 18, "User Defined", "?" }, /* railway ... */ // { 5, "station", "?" }, // { 5, "halt", "?" }, // { 5, "tram_stop", "?" }, // { 5, "viaduct", "?" }, { 5, "crossing", "Crossing" }, // { 5, "level_crossing", "?" }, // { 5, "subway_entrance", "?" }, // { 5, "turntable", "?" }, // { 5, "User Defined", "?" }, /* aeroway ... */ { 1, "aerodrome", "Airport" }, { 1, "terminal", "Airport" }, { 1, "helipad", "Heliport" }, // { 1, "User Defined", "?" }, /* aerialway ... */ // { 19, "User Defined", "?" }, /* power ... */ // { 14, "tower", "?" }, // { 14, "sub_station", "?" }, // { 14, "generator", "?" }, /* man_made ... */ // { 10, "works", "?" }, // { 10, "beacon", "?" }, // { 10, "survey_point", "?" }, // { 10, "power_wind", "?" }, // { 10, "power_hydro", "?" }, // { 10, "power_fossil", "?" }, // { 10, "power_nuclear", "?" }, // { 10, "tower", "?" }, // { 10, "water_tower", "?" }, // { 10, "gasometer", "?" }, // { 10, "reservoir_covered", "?" }, // { 10, "lighthouse", "?" }, // { 10, "windmill", "?" }, // { 10, "wastewater_plant", "?" }, // { 10, "crane", "?" }, // { 10, "User Defined", "?" }, /* building ... */ { 3, "yes", "Building" }, // { 3, "User Defined", "?" }, /* leisure ... */ // { 9, "sports_centre", "?" }, { 9, "golf_course", "Golf Course" }, { 9, "stadium", "Stadium" }, // { 9, "track", "?" }, // { 9, "pitch", "?" }, // { 9, "water_park", "?" }, { 9, "marina", "Marina" }, // { 9, "slipway", "?" }, { 9, "fishing", "Fishing Area" }, // { 9, "nature_reserve", "?" }, { 9, "park", "Park" }, // { 9, "playground", "?" }, // { 9, "garden", "?" }, // { 9, "common", "?" }, // { 9, "User Defined", "?" }, /* amenity ... */ { 2, "pub", "Bar" }, // { 2, "biergarten", "?" }, { 2, "nightclub", "Bar" }, // { 2, "cafe", "?" }, { 2, "restaurant", "Restaurant" }, { 2, "fast_food", "Fast Food" }, { 2, "parking", "Parking Area" }, // { 2, "bicycle_parking", "?" }, // { 2, "bicycle_rental", "?" }, { 2, "car_rental", "Car Rental" }, // { 2, "car_sharing", "?" }, // { 2, "taxi", "?" }, { 2, "fuel", "Gas Station" }, { 2, "telephone", "Telephone" }, { 2, "toilets", "Restroom" }, // { 2, "recycling", "?" }, // { 2, "public_building", "?" }, { 2, "townhall", "City Hall" }, // { 2, "place_of_worship", "?" }, // { 2, "grave_yard", "?" }, { 2, "post_office", "Post Office" }, // { 2, "post_box", "?" }, { 2, "school", "School" }, // { 2, "university", "?" }, // { 2, "college", "?" }, { 2, "pharmacy", "Pharmacy" }, { 2, "hospital", "Medical Facility" }, // { 2, "library", "?" }, { 2, "police", "Police Station" }, // { 2, "fire_station", "?" }, // { 2, "bus_station", "?" }, // { 2, "theatre", "?" }, // { 2, "cinema", "?" }, // { 2, "arts_centre", "?" }, // { 2, "courthouse", "?" }, // { 2, "prison", "?" }, { 2, "bank", "Bank" }, // { 2, "bureau_de_change", "?" }, // { 2, "atm", "?" }, // { 2, "fountain", "?" }, // { 2, "User Defined", "?" }, /* shop ... */ // { 15, "supermarket", "?" }, { 15, "convenience", "Convenience Store" }, // { 15, "butcher", "?" }, // { 15, "bicycle", "?" }, // { 15, "doityourself", "?" }, // { 15, "dry_cleaning", "?" }, // { 15, "laundry", "?" }, // { 15, "outdoor", "?" }, // { 15, "kiosk", "?" }, // { 15, "User Defined", "?" }, /* tourism ... */ { 17, "information", "Information" }, { 17, "hotel", "Hotel" }, { 17, "motel", "Lodging" }, { 17, "guest_house", "Lodging" }, { 17, "hostel", "Lodging" }, { 17, "camp_site", "Campground" }, { 17, "caravan_site", "RV Park" }, { 17, "picnic_site", "Picnic Area" }, { 17, "viewpoint", "Scenic Area" }, // { 17, "theme_park", "?" }, // { 17, "attraction", "?" }, { 17, "zoo", "Zoo" }, // { 17, "artwork", "?" }, { 17, "museum", "Museum" }, // { 17, "User Defined", "?" }, /* historic ... */ // { 7, "castle", "?" }, // { 7, "monument", "?" }, // { 7, "memorial", "?" }, // { 7, "archaeological_site", "?" }, // { 7, "ruins", "?" }, // { 7, "battlefield", "?" }, // { 7, "User Defined", "?" }, /* landuse ... */ // { 8, "farm", "?" }, // { 8, "quarry", "?" }, // { 8, "landfill", "?" }, // { 8, "basin", "?" }, // { 8, "reservoir", "?" }, { 8, "forest", "Forest" }, // { 8, "allotments", "?" }, // { 8, "residential", "?" }, // { 8, "retail", "?" }, // { 8, "commercial", "?" }, // { 8, "industrial", "?" }, // { 8, "brownfield", "?" }, // { 8, "greenfield", "?" }, // { 8, "railway", "?" }, // { 8, "construction", "?" }, { 8, "military", "Military" }, { 8, "cemetery", "Cemetery" }, // { 8, "village_green", "?" }, // { 8, "recreation_ground", "?" }, // { 8, "User Defined", "?" }, /* military ... */ // { 11, "airfield", "?" }, // { 11, "bunker", "?" }, // { 11, "barracks", "?" }, // { 11, "danger_area", "?" }, // { 11, "range", "?" }, // { 11, "naval_base", "?" }, // { 11, "User Defined", "?" }, /* natural ... */ // { 12, "spring", "?" }, // { 12, "peak", "?" }, // { 12, "glacier", "?" }, // { 12, "volcano", "?" }, // { 12, "cliff", "?" }, // { 12, "scree", "?" }, // { 12, "scrub", "?" }, // { 12, "fell", "?" }, // { 12, "heath", "?" }, // { 12, "wood", "?" }, // { 12, "marsh", "?" }, // { 12, "water", "?" }, // { 12, "coastline", "?" }, // { 12, "mud", "?" }, { 12, "beach", "Beach" }, // { 12, "bay", "?" }, // { 12, "land", "?" }, // { 12, "cave_entrance", "?" }, // { 12, "User Defined", "?" }, /* sport ... */ // { 16, "10pin", "?" }, // { 16, "athletics", "?" }, // { 16, "australian_football", "?" }, // { 16, "baseball", "?" }, // { 16, "basketball", "?" }, // { 16, "boules", "?" }, // { 16, "bowls", "?" }, // { 16, "climbing", "?" }, // { 16, "cricket", "?" }, // { 16, "cricket_nets", "?" }, // { 16, "croquet", "?" }, // { 16, "cycling", "?" }, // { 16, "dog_racing", "?" }, // { 16, "equestrian", "?" }, // { 16, "football", "?" }, // { 16, "golf", "?" }, // { 16, "gymnastics", "?" }, // { 16, "hockey", "?" }, // { 16, "horse_racing", "?" }, // { 16, "motor", "?" }, // { 16, "multi", "?" }, // { 16, "pelota", "?" }, // { 16, "racquet", "?" }, // { 16, "rugby", "?" }, // { 16, "skating", "?" }, // { 16, "skateboard", "?" }, // { 16, "soccer", "?" }, { 16, "swimming", "Swimming Area" }, { 16, "skiing", "Skiing Area" }, // { 16, "table_tennis", "?" }, // { 16, "tennis", "?" }, // { 16, "orienteering", "?" }, // { 16, "User Defined", "?" }, /* place ... */ // { 13, "continent", "?" }, // { 13, "country", "?" }, // { 13, "state", "?" }, // { 13, "region", "?" }, // { 13, "county", "?" }, { 13, "city", "City (Large)" }, { 13, "town", "City (Medium)" }, { 13, "village", "City (Small)" }, // { 13, "hamlet", "?" }, // { 13, "suburb", "?" }, // { 13, "locality", "?" }, // { 13, "island", "?" }, // { 13, "User Defined", "?" }, { -1, NULL, NULL } }; #if ! HAVE_LIBEXPAT void osm_rd_init(const char* fname) { fatal(MYNAME ": This build excluded \" MYNAME \" support because expat was not installed.\n"); } void osm_read(void) { } #else /*******************************************************************************/ /* READER */ /*-----------------------------------------------------------------------------*/ static void osm_features_init(void) { int i; keys = avltree_init(AVLTREE_STATIC_KEYS, MYNAME); values = avltree_init(0, MYNAME); /* the first of osm_features is a place holder */ for (i = 1; osm_features[i]; i++) { avltree_insert(keys, osm_features[i], gb_int2ptr(i)); } for (i = 0; osm_icon_mappings[i].value; i++) { char buff[128]; buff[0] = osm_icon_mappings[i].key; strncpy(&buff[1], osm_icon_mappings[i].value, sizeof(buff) - 1); avltree_insert(values, buff, (const void*)&osm_icon_mappings[i]); } } static char osm_feature_ikey(const char* key) { int result; const void* p; if (avltree_find(keys, key, &p)) { result = gb_ptr2int(p); } else { result = -1; } return result; } static char* osm_feature_symbol(const int ikey, const char* value) { char* result; char buff[128]; osm_icon_mapping_t* data; buff[0] = ikey; strncpy(&buff[1], value, sizeof(buff) - 1); if (avltree_find(values, buff, (const void**)&data)) { result = xstrdup(data->icon); } else { xasprintf(&result, "%s:%s", osm_features[ikey], value); } return result; } static char* osm_strip_html(const char* str) { utf_string utf; utf.is_html = 1; utf.utfstring = (char*)str; return strip_html(&utf); // util.c } static void osm_node_end(const char* args, const char** unused) { if (wpt) { if (wpt->wpt_flags.fmt_use) { waypt_add(wpt); } else { waypt_free(wpt); } wpt = NULL; } } static void osm_node(const char* args, const char** attrv) { const char** avp = &attrv[0]; wpt = waypt_new(); while (*avp) { if (strcmp(avp[0], "id") == 0) { xasprintf(&wpt->description, "osm-id %s", avp[1]); if (! avltree_insert(waypoints, avp[1], (void*)wpt)) { warning(MYNAME ": Duplicate osm-id %s!\n", avp[1]); } else { wpt->wpt_flags.fmt_use = 1; } } else if (strcmp(avp[0], "user") == 0) ; else if (strcmp(avp[0], "lat") == 0) { wpt->latitude = atof(avp[1]); } else if (strcmp(avp[0], "lon") == 0) { wpt->longitude = atof(avp[1]); } else if (strcmp(avp[0], "timestamp") == 0) { wpt->creation_time = xml_parse_time(avp[1], &wpt->microseconds); } avp += 2; } } static void osm_node_tag(const char* args, const char** attrv) { const char** avp = &attrv[0]; const char* key = "", *value = ""; char* str; signed char ikey; while (*avp) { if (strcmp(avp[0], "k") == 0) { key = avp[1]; } else if (strcmp(avp[0], "v") == 0) { value = avp[1]; } avp+=2; } str = osm_strip_html(value); if (strcmp(key, "name") == 0) { if (! wpt->shortname) { wpt->shortname = xstrdup(str); } } else if (strcmp(key, "name:en") == 0) { if (wpt->shortname) { xfree(wpt->shortname); } wpt->shortname = xstrdup(str); } else if ((ikey = osm_feature_ikey(key)) >= 0) { wpt->icon_descr = osm_feature_symbol(ikey, value); wpt->wpt_flags.icon_descr_is_dynamic = 1; } else if (strcmp(key, "note") == 0) { if (wpt->notes) { char* tmp; xasprintf(&tmp, "%s; %s", wpt->notes, str); xfree(wpt->notes); wpt->notes = tmp; } else { wpt->notes = xstrdup(str); } } else if (strcmp(key, "gps:hdop") == 0) { wpt->hdop = atof(str); } else if (strcmp(key, "gps:vdop") == 0) { wpt->vdop = atof(str); } else if (strcmp(key, "gps:pdop") == 0) { wpt->pdop = atof(str); } else if (strcmp(key, "gps:sat") == 0) { wpt->sat = atoi(str); } else if (strcmp(key, "gps:fix") == 0) { if (strcmp(str, "2d") == 0) { wpt->fix = fix_2d; } else if (strcmp(str, "3d") == 0) { wpt->fix = fix_3d; } else if (strcmp(str, "dgps") == 0) { wpt->fix = fix_dgps; } else if (strcmp(str, "pps") == 0) { wpt->fix = fix_pps; } else if (strcmp(str, "none") == 0) { wpt->fix = fix_none; } } xfree(str); } static void osm_way(const char* args, const char** attrv) { const char** avp = &attrv[0]; rte = route_head_alloc(); while (*avp) { if (strcmp(avp[0], "id") == 0) { xasprintf(&rte->rte_desc, "osm-id %s", avp[1]); } avp += 2; } } static void osm_way_nd(const char* args, const char** attrv) { const char** avp = &attrv[0]; while (*avp) { if (strcmp(avp[0], "ref") == 0) { waypoint* tmp; if (avltree_find(waypoints, avp[1], (const void**)&tmp)) { tmp = waypt_dupe(tmp); route_add_wpt(rte, tmp); } else { warning(MYNAME ": Way reference id \"%s\" wasn't listed under nodes!\n", avp[1]); } } avp += 2; } } static void osm_way_tag(const char* args, const char** attrv) { const char** avp = &attrv[0]; const char* key = "", *value = ""; char* str; while (*avp) { if (strcmp(avp[0], "k") == 0) { key = avp[1]; } else if (strcmp(avp[0], "v") == 0) { value = avp[1]; } avp += 2; } str = osm_strip_html(value); if (strcmp(key, "name") == 0) { if (! rte->rte_name) { rte->rte_name = xstrdup(str); } } else if (strcmp(key, "name:en") == 0) { if (rte->rte_name) { xfree(rte->rte_name); } rte->rte_name = xstrdup(str); } xfree(str); } static void osm_way_end(const char* args, const char** unused) { if (rte) { route_add_head(rte); rte = NULL; } } static void osm_rd_init(const char* fname) { wpt = NULL; rte = NULL; wpt_loaded = 0; rte_loaded = 0; waypoints = avltree_init(0, MYNAME); if (! keys) { osm_features_init(); } xml_init(fname, osm_map, NULL); } static void osm_read(void) { xml_read(); } #endif static void osm_rd_deinit(void) { xml_deinit(); avltree_done(waypoints); } /*******************************************************************************/ /* WRITER */ /*-----------------------------------------------------------------------------*/ static void osm_init_icons(void) { int i; if (icons) { return; } icons = avltree_init(AVLTREE_STATIC_KEYS, MYNAME); for (i = 0; osm_icon_mappings[i].value; i++) { avltree_insert(icons, osm_icon_mappings[i].icon, (const void*)&osm_icon_mappings[i]); } } static void osm_write_tag(const char* key, const char* value) { if (value && *value) { char* str = xml_entitize(value); gbfprintf(fout, " \n", key, str); xfree(str); } } static void osm_disp_feature(const waypoint* wpt) { osm_icon_mapping_t* map; if (avltree_find(icons, wpt->icon_descr, (const void**) &map)) { osm_write_tag(osm_features[map->key], map->value); } } static void osm_write_opt_tag(const char* atag) { char* tag, *cin, *ce; if (!atag) { return; } tag = cin = xstrdup(atag); ce = cin + strlen(cin); while (cin < ce) { char* sc, *dp; if ((sc = strchr(cin, ';'))) { *sc = '\0'; } if ((dp = strchr(cin, ':'))) { *dp++ = '\0'; osm_write_tag(cin, dp); } cin += strlen(cin) + 1; } xfree(tag); } static void osm_release_ids(const waypoint* wpt) { if (wpt && wpt->extra_data) { waypoint* tmp = (waypoint*)wpt; xfree(tmp->extra_data); tmp->extra_data = NULL; } } static void osm_waypt_disp(const waypoint* wpt) { char* buff; xasprintf(&buff, "%s\01%f\01%f", (wpt->shortname) ? wpt->shortname : "", wpt->latitude, wpt->longitude); if (avltree_insert(waypoints, buff, (const void*) wpt)) { int* id; id = (int*) xmalloc(sizeof(*id)); *id = --node_id; ((waypoint*)(wpt))->extra_data = id; gbfprintf(fout, " latitude, wpt->longitude); if (wpt->creation_time) { char time_string[64]; xml_fill_in_time(time_string, wpt->creation_time, wpt->microseconds, XML_LONG_TIME); gbfprintf(fout, " timestamp='%s'", time_string); } gbfprintf(fout, ">\n"); if (wpt->hdop) { gbfprintf(fout, " \n", wpt->hdop); } if (wpt->vdop) { gbfprintf(fout, " \n", wpt->vdop); } if (wpt->pdop) { gbfprintf(fout, " \n", wpt->pdop); } if (wpt->sat > 0) { gbfprintf(fout, " \n", wpt->sat); } switch (wpt->fix) { case fix_2d: gbfprintf(fout, " \n"); break; case fix_3d: gbfprintf(fout, " \n"); break; case fix_dgps: gbfprintf(fout, " \n"); break; case fix_pps: gbfprintf(fout, " \n"); break; case fix_none: gbfprintf(fout, " \n"); break; case fix_unknown: default: break; } if (strlen(created_by) !=0) { gbfprintf(fout, " \n"); } osm_write_tag("name", wpt->shortname); osm_write_tag("note", (wpt->notes) ? wpt->notes : wpt->description); if (wpt->icon_descr) { osm_disp_feature(wpt); } osm_write_opt_tag(opt_tagnd); gbfprintf(fout, " \n"); } xfree(buff); } static void osm_rte_disp_head(const route_head* rte) { skip_rte = (rte->rte_waypt_ct <= 0); if (skip_rte) { return; } gbfprintf(fout, " \n", --node_id); } static void osm_rtept_disp(const waypoint* wpt_ref) { char* buff; waypoint* wpt; if (skip_rte) { return; } xasprintf(&buff, "%s\01%f\01%f", (wpt_ref->shortname) ? wpt_ref->shortname : "", wpt_ref->latitude, wpt_ref->longitude); if (avltree_find(waypoints, buff, (const void**) &wpt)) { int* id = (int*) wpt->extra_data; gbfprintf(fout, " \n", *id); } xfree(buff); } static void osm_rte_disp_trail(const route_head* rte) { if (skip_rte) { return; } if (strlen(created_by) !=0) { gbfprintf(fout, " \n"); } osm_write_tag("name", rte->rte_name); osm_write_tag("note", rte->rte_desc); if (opt_tag && (case_ignore_strncmp(opt_tag, "tagnd", 5) != 0)) { osm_write_opt_tag(opt_tag); } gbfprintf(fout, " \n"); } /*-----------------------------------------------------------------------------*/ static void osm_wr_init(const char* fname) { fout = gbfopen(fname, "w", MYNAME); osm_init_icons(); waypoints = avltree_init(0, MYNAME); node_id = 0; } static void osm_write(void) { gbfprintf(fout, "\n"); gbfprintf(fout, "\n"); waypt_disp_all(osm_waypt_disp); route_disp_all(NULL, NULL, osm_waypt_disp); track_disp_all(NULL, NULL, osm_waypt_disp); route_disp_all(osm_rte_disp_head, osm_rte_disp_trail, osm_rtept_disp); track_disp_all(osm_rte_disp_head, osm_rte_disp_trail, osm_rtept_disp); gbfprintf(fout, "\n"); } static void osm_wr_deinit(void) { gbfclose(fout); waypt_disp_all(osm_release_ids); route_disp_all(NULL, NULL, osm_release_ids); track_disp_all(NULL, NULL, osm_release_ids); avltree_done(waypoints); } static void osm_exit(void) { if (keys) { avltree_done(keys); } if (values) { avltree_done(values); } if (icons) { avltree_done(icons); } } /*-----------------------------------------------------------------------------*/ ff_vecs_t osm_vecs = { ff_type_file, { (ff_cap)(ff_cap_read | ff_cap_write) /* waypoints */, ff_cap_write /* tracks */, (ff_cap)(ff_cap_read | ff_cap_write) /* routes */, }, osm_rd_init, osm_wr_init, osm_rd_deinit, osm_wr_deinit, osm_read, osm_write, osm_exit, osm_args, CET_CHARSET_UTF8, 0 };