Diff of /trunk/src/osm.c

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 6 by achadwick, Thu Dec 11 13:26:13 2008 UTC revision 8 by harbaum, Thu Dec 11 20:42:10 2008 UTC
# Line 17  Line 17 
17   * along with OSM2Go.  If not, see <http://www.gnu.org/licenses/>.   * along with OSM2Go.  If not, see <http://www.gnu.org/licenses/>.
18   */   */
19    
20    #define OSM_STREAM_PARSER
21    
22  #include <stdio.h>  #include <stdio.h>
23  #include <stdlib.h>  #include <stdlib.h>
24  #include <string.h>  #include <string.h>
# Line 52  static void osm_bounds_dump(bounds_t *bo Line 54  static void osm_bounds_dump(bounds_t *bo
54           bounds->ll_min.lon, bounds->ll_max.lon);           bounds->ll_min.lon, bounds->ll_max.lon);
55  }  }
56    
57    #ifndef OSM_STREAM_PARSER
58  static bounds_t *osm_parse_osm_bounds(osm_t *osm,  static bounds_t *osm_parse_osm_bounds(osm_t *osm,
59                       xmlDocPtr doc, xmlNode *a_node) {                       xmlDocPtr doc, xmlNode *a_node) {
60    char *prop;    char *prop;
# Line 92  static bounds_t *osm_parse_osm_bounds(os Line 95  static bounds_t *osm_parse_osm_bounds(os
95             bounds->ll_min.lat, bounds->ll_min.lon,             bounds->ll_min.lat, bounds->ll_min.lon,
96             bounds->ll_max.lat, bounds->ll_max.lon);             bounds->ll_max.lat, bounds->ll_max.lon);
97    
98      g_free(bounds);      osm_bounds_free(bounds);
99      return NULL;      return NULL;
100    }    }
101    
# Line 122  static bounds_t *osm_parse_osm_bounds(os Line 125  static bounds_t *osm_parse_osm_bounds(os
125    
126    return bounds;    return bounds;
127  }  }
128    #endif
129    
130  /* ------------------------- user handling --------------------- */  /* ------------------------- user handling --------------------- */
131    
# Line 319  void osm_nodes_dump(node_t *node) { Line 323  void osm_nodes_dump(node_t *node) {
323    }    }
324  }  }
325    
326    #ifndef OSM_STREAM_PARSER
327  static node_t *osm_parse_osm_node(osm_t *osm,  static node_t *osm_parse_osm_node(osm_t *osm,
328                            xmlDocPtr doc, xmlNode *a_node) {                            xmlDocPtr doc, xmlNode *a_node) {
329    xmlNode *cur_node = NULL;    xmlNode *cur_node = NULL;
# Line 375  static node_t *osm_parse_osm_node(osm_t Line 380  static node_t *osm_parse_osm_node(osm_t
380    
381    return node;    return node;
382  }  }
383    #endif
384    
385  /* ------------------- way handling ------------------- */  /* ------------------- way handling ------------------- */
386    
# Line 485  node_chain_t *osm_parse_osm_way_nd(osm_t Line 491  node_chain_t *osm_parse_osm_way_nd(osm_t
491    return NULL;    return NULL;
492  }  }
493    
494    #ifndef OSM_STREAM_PARSER
495  static way_t *osm_parse_osm_way(osm_t *osm,  static way_t *osm_parse_osm_way(osm_t *osm,
496                            xmlDocPtr doc, xmlNode *a_node) {                            xmlDocPtr doc, xmlNode *a_node) {
497    xmlNode *cur_node = NULL;    xmlNode *cur_node = NULL;
# Line 534  static way_t *osm_parse_osm_way(osm_t *o Line 541  static way_t *osm_parse_osm_way(osm_t *o
541    
542    return way;    return way;
543  }  }
544    #endif
545    
546  /* ------------------- relation handling ------------------- */  /* ------------------- relation handling ------------------- */
547    
# Line 688  member_t *osm_parse_osm_relation_member( Line 696  member_t *osm_parse_osm_relation_member(
696    return member;    return member;
697  }  }
698    
699    #ifndef OSM_STREAM_PARSER
700  static relation_t *osm_parse_osm_relation(osm_t *osm,  static relation_t *osm_parse_osm_relation(osm_t *osm,
701                            xmlDocPtr doc, xmlNode *a_node) {                            xmlDocPtr doc, xmlNode *a_node) {
702    xmlNode *cur_node = NULL;    xmlNode *cur_node = NULL;
# Line 854  static osm_t *osm_parse_doc(xmlDocPtr do Line 863  static osm_t *osm_parse_doc(xmlDocPtr do
863    
864    return osm;    return osm;
865  }  }
866    #endif
867    
868  /* ------------------ osm handling ----------------- */  /* ------------------ osm handling ----------------- */
869    
# Line 876  void osm_dump(osm_t *osm) { Line 886  void osm_dump(osm_t *osm) {
886    osm_relations_dump(osm->relation);    osm_relations_dump(osm->relation);
887  }  }
888    
889    #ifdef OSM_STREAM_PARSER
890    /* -------------------------- stream parser tests ------------------- */
891    
892    #include <libxml/xmlreader.h>
893    
894    static gint my_strcmp(const xmlChar *a, const xmlChar *b) {
895      if(!a && !b) return 0;
896      if(!a) return -1;
897      if(!b) return +1;
898      return strcmp((char*)a,(char*)b);
899    }
900    
901    /* skip current element incl. everything below (mainly for testing) */
902    /* returns FALSE if something failed */
903    static gboolean skip_element(xmlTextReaderPtr reader) {
904      g_assert(xmlTextReaderNodeType(reader) == XML_READER_TYPE_ELEMENT);
905      const xmlChar *name = xmlTextReaderConstName(reader);
906      g_assert(name);
907      int depth = xmlTextReaderDepth(reader);
908    
909      if(xmlTextReaderIsEmptyElement(reader))
910        return TRUE;
911    
912      int ret = xmlTextReaderRead(reader);
913      while((ret == 1) &&
914            ((xmlTextReaderNodeType(reader) != XML_READER_TYPE_END_ELEMENT) ||
915             (xmlTextReaderDepth(reader) > depth) ||
916             (my_strcmp(xmlTextReaderConstName(reader), name) != 0))) {
917        ret = xmlTextReaderRead(reader);
918      }
919      return(ret == 1);
920    }
921    
922    /* parse bounds */
923    static bounds_t *process_bounds(xmlTextReaderPtr reader) {
924      char *prop = NULL;
925      bounds_t *bounds = g_new0(bounds_t, 1);
926    
927      bounds->ll_min.lat = bounds->ll_min.lon = NAN;
928      bounds->ll_max.lat = bounds->ll_max.lon = NAN;
929    
930      if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "minlat"))) {
931        bounds->ll_min.lat = g_ascii_strtod(prop, NULL);
932        xmlFree(prop);
933      }
934    
935      if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "maxlat"))) {
936        bounds->ll_max.lat = g_ascii_strtod(prop, NULL);
937        xmlFree(prop);
938      }
939    
940      if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "minlon"))) {
941        bounds->ll_min.lon = g_ascii_strtod(prop, NULL);
942        xmlFree(prop);
943      }
944    
945      if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "maxlon"))) {
946        bounds->ll_max.lon = g_ascii_strtod(prop, NULL);
947        xmlFree(prop);
948      }
949    
950      if(isnan(bounds->ll_min.lat) || isnan(bounds->ll_min.lon) ||
951         isnan(bounds->ll_max.lat) || isnan(bounds->ll_max.lon)) {
952        errorf(NULL, "Invalid coordinate in bounds (%f/%f/%f/%f)",
953               bounds->ll_min.lat, bounds->ll_min.lon,
954               bounds->ll_max.lat, bounds->ll_max.lon);
955    
956        osm_bounds_free(bounds);
957        return NULL;
958      }
959    
960      /* skip everything below */
961      skip_element(reader);
962    
963      /* calculate map zone which will be used as a reference for all */
964      /* drawing/projection later on */
965      pos_t center = { (bounds->ll_max.lat + bounds->ll_min.lat)/2,
966                       (bounds->ll_max.lon + bounds->ll_min.lon)/2 };
967    
968      pos2lpos_center(&center, &bounds->center);
969    
970      /* the scale is needed to accomodate for "streching" */
971      /* by the mercartor projection */
972      bounds->scale = cos(DEG2RAD(center.lat));
973    
974      pos2lpos_center(&bounds->ll_min, &bounds->min);
975      bounds->min.x -= bounds->center.x;
976      bounds->min.y -= bounds->center.y;
977      bounds->min.x *= bounds->scale;
978      bounds->min.y *= bounds->scale;
979    
980      pos2lpos_center(&bounds->ll_max, &bounds->max);
981      bounds->max.x -= bounds->center.x;
982      bounds->max.y -= bounds->center.y;
983      bounds->max.x *= bounds->scale;
984      bounds->max.y *= bounds->scale;
985    
986      return bounds;
987    }
988    
989    static tag_t *process_tag(xmlTextReaderPtr reader) {
990      /* allocate a new tag structure */
991      tag_t *tag = g_new0(tag_t, 1);
992    
993      char *prop;
994      if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "k"))) {
995        if(strlen(prop) > 0) tag->key = g_strdup(prop);
996        xmlFree(prop);
997      }
998    
999      if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "v"))) {
1000        if(strlen(prop) > 0) tag->value = g_strdup(prop);
1001        xmlFree(prop);
1002      }
1003    
1004      if(!tag->key || !tag->value) {
1005        printf("incomplete tag key/value %s/%s\n", tag->key, tag->value);
1006        osm_tags_free(tag);
1007        tag = NULL;
1008      }
1009    
1010      skip_element(reader);
1011      return tag;
1012    }
1013    
1014    static node_t *process_node(xmlTextReaderPtr reader, osm_t *osm) {
1015    
1016      /* allocate a new node structure */
1017      node_t *node = g_new0(node_t, 1);
1018      node->pos.lat = node->pos.lon = NAN;
1019    
1020      char *prop;
1021      if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "id"))) {
1022        node->id = strtoul(prop, NULL, 10);
1023        xmlFree(prop);
1024      }
1025    
1026      if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "lat"))) {
1027        node->pos.lat = g_ascii_strtod(prop, NULL);
1028        xmlFree(prop);
1029      }
1030    
1031      if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "lon"))) {
1032        node->pos.lon = g_ascii_strtod(prop, NULL);
1033        xmlFree(prop);
1034      }
1035    
1036      if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "user"))) {
1037        node->user = osm_user(osm, prop);
1038        xmlFree(prop);
1039      }
1040    
1041      if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "visible"))) {
1042        node->visible = (strcasecmp(prop, "true") == 0);
1043        xmlFree(prop);
1044      }
1045    
1046      if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "timestamp"))) {
1047        node->time = convert_iso8601(prop);
1048        xmlFree(prop);
1049      }
1050    
1051      pos2lpos(osm->bounds, &node->pos, &node->lpos);
1052    
1053      /* just an empty element? then return the node as it is */
1054      if(xmlTextReaderIsEmptyElement(reader))
1055        return node;
1056    
1057      /* parse tags if present */
1058      int depth = xmlTextReaderDepth(reader);
1059    
1060      /* scan all elements on same level or its children */
1061      tag_t **tag = &node->tag;
1062      int ret = xmlTextReaderRead(reader);
1063      while((ret == 1) &&
1064            ((xmlTextReaderNodeType(reader) != XML_READER_TYPE_END_ELEMENT) ||
1065             (xmlTextReaderDepth(reader) != depth))) {
1066    
1067        if(xmlTextReaderNodeType(reader) == XML_READER_TYPE_ELEMENT) {
1068          char *subname = (char*)xmlTextReaderConstName(reader);
1069          if(strcasecmp(subname, "tag") == 0) {
1070            *tag = process_tag(reader);
1071            if(*tag) tag = &(*tag)->next;
1072          } else
1073            skip_element(reader);
1074        }
1075    
1076        ret = xmlTextReaderRead(reader);
1077      }
1078    
1079      return node;
1080    }
1081    
1082    static node_chain_t *process_nd(xmlTextReaderPtr reader, osm_t *osm) {
1083      char *prop;
1084    
1085      if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "ref"))) {
1086        item_id_t id = strtoul(prop, NULL, 10);
1087        node_chain_t *node_chain = g_new0(node_chain_t, 1);
1088    
1089        /* search matching node */
1090        node_chain->node = osm->node;
1091        while(node_chain->node && node_chain->node->id != id)
1092          node_chain->node = node_chain->node->next;
1093    
1094        if(!node_chain->node) printf("Node id %lu not found\n", id);
1095    
1096        if(node_chain->node)
1097          node_chain->node->ways++;
1098    
1099        xmlFree(prop);
1100    
1101        skip_element(reader);
1102        return node_chain;
1103      }
1104    
1105      skip_element(reader);
1106      return NULL;
1107    }
1108    
1109    static way_t *process_way(xmlTextReaderPtr reader, osm_t *osm) {
1110      /* allocate a new way structure */
1111      way_t *way = g_new0(way_t, 1);
1112    
1113      char *prop;
1114      if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "id"))) {
1115        way->id = strtoul(prop, NULL, 10);
1116        xmlFree(prop);
1117      }
1118    
1119      if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "user"))) {
1120        way->user = osm_user(osm, prop);
1121        xmlFree(prop);
1122      }
1123    
1124      if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "visible"))) {
1125        way->visible = (strcasecmp(prop, "true") == 0);
1126        xmlFree(prop);
1127      }
1128    
1129      if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "timestamp"))) {
1130        way->time = convert_iso8601(prop);
1131        xmlFree(prop);
1132      }
1133    
1134      /* just an empty element? then return the way as it is */
1135      /* (this should in fact never happen as this would be a way without nodes) */
1136      if(xmlTextReaderIsEmptyElement(reader))
1137        return way;
1138    
1139      /* parse tags/nodes if present */
1140      int depth = xmlTextReaderDepth(reader);
1141    
1142      /* scan all elements on same level or its children */
1143      tag_t **tag = &way->tag;
1144      node_chain_t **node_chain = &way->node_chain;
1145      int ret = xmlTextReaderRead(reader);
1146      while((ret == 1) &&
1147            ((xmlTextReaderNodeType(reader) != XML_READER_TYPE_END_ELEMENT) ||
1148             (xmlTextReaderDepth(reader) != depth))) {
1149    
1150        if(xmlTextReaderNodeType(reader) == XML_READER_TYPE_ELEMENT) {
1151          char *subname = (char*)xmlTextReaderConstName(reader);
1152          if(strcasecmp(subname, "nd") == 0) {
1153            *node_chain = process_nd(reader, osm);
1154            if(*node_chain) node_chain = &(*node_chain)->next;
1155          } else if(strcasecmp(subname, "tag") == 0) {
1156            *tag = process_tag(reader);
1157            if(*tag) tag = &(*tag)->next;
1158          } else
1159            skip_element(reader);
1160        }
1161        ret = xmlTextReaderRead(reader);
1162      }
1163    
1164      return way;
1165    }
1166    
1167    static member_t *process_member(xmlTextReaderPtr reader, osm_t *osm) {
1168      char *prop;
1169      member_t *member = g_new0(member_t, 1);
1170      member->type = ILLEGAL;
1171    
1172      if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "type"))) {
1173        if(strcasecmp(prop, "way") == 0)           member->type = WAY;
1174        else if(strcasecmp(prop, "node") == 0)     member->type = NODE;
1175        else if(strcasecmp(prop, "relation") == 0) member->type = RELATION;
1176        xmlFree(prop);
1177      }
1178    
1179      if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "ref"))) {
1180        item_id_t id = strtoul(prop, NULL, 10);
1181    
1182        switch(member->type) {
1183        case ILLEGAL:
1184          printf("Unable to store illegal type\n");
1185          break;
1186    
1187        case WAY:
1188          /* search matching way */
1189          member->way = osm->way;
1190          while(member->way && member->way->id != id)
1191            member->way = member->way->next;
1192    
1193          if(!member->way) {
1194            member->type = WAY_ID;
1195            member->id = id;
1196          }
1197          break;
1198    
1199        case NODE:
1200          /* search matching node */
1201          member->node = osm->node;
1202          while(member->node && member->node->id != id)
1203            member->node = member->node->next;
1204    
1205          if(!member->node) {
1206            member->type = NODE_ID;
1207            member->id = id;
1208          }
1209          break;
1210    
1211        case RELATION:
1212          /* search matching relation */
1213          member->relation = osm->relation;
1214          while(member->relation && member->relation->id != id)
1215            member->relation = member->relation->next;
1216    
1217          if(!member->relation) {
1218            member->type = NODE_ID;
1219            member->id = id;
1220          }
1221          break;
1222    
1223        case WAY_ID:
1224        case NODE_ID:
1225        case RELATION_ID:
1226          break;
1227        }
1228    
1229        xmlFree(prop);
1230      }
1231    
1232      if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "role"))) {
1233        if(strlen(prop) > 0) member->role = g_strdup(prop);
1234        xmlFree(prop);
1235      }
1236    
1237      return member;
1238    }
1239    
1240    static relation_t *process_relation(xmlTextReaderPtr reader, osm_t *osm) {
1241      /* allocate a new relation structure */
1242      relation_t *relation = g_new0(relation_t, 1);
1243    
1244      char *prop;
1245      if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "id"))) {
1246        relation->id = strtoul(prop, NULL, 10);
1247        xmlFree(prop);
1248      }
1249    
1250      if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "user"))) {
1251        relation->user = osm_user(osm, prop);
1252        xmlFree(prop);
1253      }
1254    
1255      if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "visible"))) {
1256        relation->visible = (strcasecmp(prop, "true") == 0);
1257        xmlFree(prop);
1258      }
1259    
1260      if((prop = (char*)xmlTextReaderGetAttribute(reader, BAD_CAST "timestamp"))) {
1261        relation->time = convert_iso8601(prop);
1262        xmlFree(prop);
1263      }
1264    
1265      /* just an empty element? then return the relation as it is */
1266      /* (this should in fact never happen as this would be a relation */
1267      /* without members) */
1268      if(xmlTextReaderIsEmptyElement(reader))
1269        return relation;
1270    
1271      /* parse tags/member if present */
1272      int depth = xmlTextReaderDepth(reader);
1273    
1274      /* scan all elements on same level or its children */
1275      tag_t **tag = &relation->tag;
1276      member_t **member = &relation->member;
1277      int ret = xmlTextReaderRead(reader);
1278      while((ret == 1) &&
1279            ((xmlTextReaderNodeType(reader) != XML_READER_TYPE_END_ELEMENT) ||
1280             (xmlTextReaderDepth(reader) != depth))) {
1281    
1282        if(xmlTextReaderNodeType(reader) == XML_READER_TYPE_ELEMENT) {
1283          char *subname = (char*)xmlTextReaderConstName(reader);
1284          if(strcasecmp(subname, "nd") == 0) {
1285            *member = process_member(reader, osm);
1286            if(*member) member = &(*member)->next;
1287          } else if(strcasecmp(subname, "tag") == 0) {
1288            *tag = process_tag(reader);
1289            if(*tag) tag = &(*tag)->next;
1290          } else
1291            skip_element(reader);
1292        }
1293        ret = xmlTextReaderRead(reader);
1294      }
1295    
1296      return relation;
1297    }
1298    
1299    static osm_t *process_osm(xmlTextReaderPtr reader) {
1300      /* alloc osm structure */
1301      osm_t *osm = g_new0(osm_t, 1);
1302    
1303      node_t **node = &osm->node;
1304      way_t **way = &osm->way;
1305      relation_t **relation = &osm->relation;
1306    
1307      /* no attributes of interest */
1308    
1309      const xmlChar *name = xmlTextReaderConstName(reader);
1310      g_assert(name);
1311    
1312      /* read next node */
1313      int ret = xmlTextReaderRead(reader);
1314      while(ret == 1) {
1315    
1316        switch(xmlTextReaderNodeType(reader)) {
1317        case XML_READER_TYPE_ELEMENT:
1318    
1319          g_assert(xmlTextReaderDepth(reader) == 1);
1320          char *name = (char*)xmlTextReaderConstName(reader);
1321          if(strcasecmp(name, "bounds") == 0) {
1322            osm->bounds = process_bounds(reader);
1323          } else if(strcasecmp(name, "node") == 0) {
1324            *node = process_node(reader, osm);
1325            if(*node) node = &(*node)->next;
1326          } else if(strcasecmp(name, "way") == 0) {
1327            *way = process_way(reader, osm);
1328            if(*way) way = &(*way)->next;
1329          } else if(strcasecmp(name, "relation") == 0) {
1330            *relation = process_relation(reader, osm);
1331            if(*relation) relation = &(*relation)->next;
1332          } else {
1333            printf("something unknown found\n");
1334            g_assert(0);
1335            skip_element(reader);
1336          }
1337          break;
1338    
1339        case XML_READER_TYPE_END_ELEMENT:
1340          /* end element must be for the current element */
1341          g_assert(xmlTextReaderDepth(reader) == 0);
1342          return osm;
1343          break;
1344    
1345        default:
1346          break;
1347        }
1348        ret = xmlTextReaderRead(reader);
1349      }
1350    
1351      g_assert(0);
1352      return NULL;
1353    }
1354    
1355    static osm_t *process_file(const char *filename) {
1356      osm_t *osm = NULL;
1357      xmlTextReaderPtr reader;
1358      int ret;
1359    
1360      reader = xmlReaderForFile(filename, NULL, 0);
1361      if (reader != NULL) {
1362        ret = xmlTextReaderRead(reader);
1363        if(ret == 1) {
1364          char *name = (char*)xmlTextReaderConstName(reader);
1365          if(name && strcasecmp(name, "osm") == 0)
1366            osm = process_osm(reader);
1367        } else
1368          printf("file empty\n");
1369    
1370        xmlFreeTextReader(reader);
1371      } else {
1372        fprintf(stderr, "Unable to open %s\n", filename);
1373      }
1374      return osm;
1375    }
1376    
1377    /* ----------------------- end of stream parser tests ------------------- */
1378    #endif
1379    
1380    #include <sys/time.h>
1381    
1382  osm_t *osm_parse(char *filename) {  osm_t *osm_parse(char *filename) {
1383    xmlDoc *doc = NULL;  
1384      struct timeval start;
1385      gettimeofday(&start, NULL);
1386    
1387    LIBXML_TEST_VERSION;    LIBXML_TEST_VERSION;
1388    
1389    #ifdef OSM_STREAM_PARSER
1390      // use stream parser
1391      osm_t *osm = process_file(filename);
1392      xmlCleanupParser();
1393    
1394    #else
1395      // parse into a tree
1396    /* parse the file and get the DOM */    /* parse the file and get the DOM */
1397      xmlDoc *doc = NULL;
1398    if ((doc = xmlReadFile(filename, NULL, 0)) == NULL) {    if ((doc = xmlReadFile(filename, NULL, 0)) == NULL) {
1399      xmlErrorPtr errP = xmlGetLastError();      xmlErrorPtr errP = xmlGetLastError();
1400      errorf(NULL, "While parsing \"%s\":\n\n%s", filename, errP->message);      errorf(NULL, "While parsing \"%s\":\n\n%s", filename, errP->message);
1401      return NULL;      return NULL;
1402    }    }
1403    
1404    return osm_parse_doc(doc);    osm_t *osm = osm_parse_doc(doc);
1405    #endif
1406    
1407      struct timeval end;
1408      gettimeofday(&end, NULL);
1409    
1410      printf("total parse time: %ldms\n",
1411             (end.tv_usec - start.tv_usec)/1000 +
1412             (end.tv_sec - start.tv_sec)*1000);
1413    
1414      return osm;
1415  }  }
1416    
1417  gboolean osm_sanity_check(GtkWidget *parent, osm_t *osm) {  gboolean osm_sanity_check(GtkWidget *parent, osm_t *osm) {
# Line 1468  way_chain_t *osm_node_to_way(osm_t *osm, Line 1991  way_chain_t *osm_node_to_way(osm_t *osm,
1991        cur_chain = &((*cur_chain)->next);        cur_chain = &((*cur_chain)->next);
1992      }      }
1993    
1994      way = way->next;       way = way->next;
1995    }    }
1996    
1997    return chain;    return chain;

Legend:
Removed from v.6  
changed lines
  Added in v.8