This is an automated email from the git hooks/post-receive script. sebastic pushed a commit to branch master in repository osmctools.
commit 45b417a44fd5c612c80dd30bbc52b6623b6a8e15 Author: Bas Couwenberg <sebas...@xs4all.nl> Date: Fri Mar 31 10:51:40 2017 +0200 Imported Upstream version 0.7 --- src/osmconvert.c | 910 ++++++++++++++++++++++++++++++++++++++++++--- src/osmfilter.c | 1093 +++++++++++++++++++++++++++++++++++++++++++++--------- src/osmupdate.c | 149 +++++--- 3 files changed, 1873 insertions(+), 279 deletions(-) diff --git a/src/osmconvert.c b/src/osmconvert.c index 159689b..6fbfb4b 100644 --- a/src/osmconvert.c +++ b/src/osmconvert.c @@ -1,10 +1,10 @@ -// osmconvert 2016-02-12 20:30 -#define VERSION "0.8.5" +// osmconvert 2017-03-30 19:00 +#define VERSION "0.8.7" // // compile this file: // gcc osmconvert.c -lz -O3 -o osmconvert // -// (c) 2011..2016 Markus Weber, Nuernberg +// (c) 2011..2017 Markus Weber, Nuernberg // Richard Russo contributed the initiative to --add-bbox-tags option // // This program is free software; you can redistribute it and/or @@ -44,6 +44,8 @@ const char* shorthelptext= "--drop-nodes delete all nodes\n" "--drop-ways delete all ways\n" "--drop-relations delete all relations\n" +"--modify-tags= define which tags are to be modified\n" +"--modify-...-tags= similar to --keep-...-tags= (see above)\n" "--diff calculate differences between two files\n" "--diff-contents same as before, but compare whole contents\n" "--subtract subtract objects given by following files\n" @@ -192,6 +194,22 @@ const char* helptext= " According to the combination of these parameters, no members\n" " of the referred section will be written.\n" "\n" +"--modify-tags=<tag_modification_list>\n" +" The tag modification list determines which tags will be\n" +" modified. The example\n" +" --modify-tags=\"highway=primary to =secondary\"\n" +" will change every \"primary\" highway into \"secondary\".\n" +" You can also use comparisons or add additional tags:\n" +" --modify-way-tags=\"maxspeed>200 add highspeed=yes\"\n" +"\n" +"--modify-node-tags=TAG_MODIFICATION_LIST\n" +"--modify-way-tags=TAG_MODIFICATION_LIST\n" +"--modify-relation-tags=TAG_MODIFICATION_LIST\n" +"--modify-node-way-tags=TAG_MODIFICATION_LIST\n" +"--modify-node-relation-tags=TAG_MODIFICATION_LIST\n" +"--modify-way-relation-tags=TAG_MODIFICATION_LIST\n" +" Same as above, but just for the specified object types.\n" +"\n" "--diff\n" " Calculate difference between two files and create a new .osc\n" " or .o5c file.\n" @@ -456,6 +474,44 @@ typedef enum {false= 0,true= 1} bool; typedef uint8_t byte; typedef unsigned int uint; #define isdig(x) isdigit((unsigned char)(x)) +static byte isdigi_tab[]= { + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; +#define isdigi(c) (isdigi_tab[(c)]) // digit +static byte digival_tab[]= { + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 1,2,3,4,5,6,7,8,9,10,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; +#define digival(c) (digival_tab[(c)]) + // value of a digit, starting with 1, for comparisons only + static int loglevel= 0; // logging to stderr; // 0: no logging; 1: small logging; 2: normal logging; // 3: extended logging; @@ -1813,9 +1869,8 @@ return false; if(s[0]!=' ' && s[0]!='\t') { // not inside a section if(x0!=nil && x1!=nil && (x1!=x0 || y1!=y0)) { // last polygon was not closed - if(x1==x0) { // the edge would be vertical - // we have to insert an additional edge - x0+= 3; + if(x1!=x0) { // missing edge not in north-south direction + // close the polygon if(x0>x1) { bep->x1= x1; bep->y1= y1; bep->x2= x0; bep->y2= y0; } else @@ -1823,24 +1878,10 @@ return false; bep->chain= NULL; if(loglevel>=1) fprintf(stderr, - "+ %i %"PRIi32",%"PRIi32",%"PRIi32",%"PRIi32"\n", - (int)(bep-border__edge), - bep->x1,bep->y1,bep->x2,bep->y2); + "c %i %"PRIi32",%"PRIi32",%"PRIi32",%"PRIi32"\n", + (int)(bep-border__edge),bep->x1,bep->y1,bep->x2,bep->y2); bep++; - x1= x0; y1= y0; - x0-= 3; - } // the edge would be vertical - // close the polygon - if(x0>x1) - { bep->x1= x1; bep->y1= y1; bep->x2= x0; bep->y2= y0; } - else - { bep->x1= x0; bep->y1= y0; bep->x2= x1; bep->y2= y1; } - bep->chain= NULL; - if(loglevel>=1) - fprintf(stderr, - "c %i %"PRIi32",%"PRIi32",%"PRIi32",%"PRIi32"\n", - (int)(bep-border__edge),bep->x1,bep->y1,bep->x2,bep->y2); - bep++; + } // missing edge not in north-south direction } // end last polygon was not closed x0= x1= nil; } // end not inside a section @@ -1856,20 +1897,22 @@ return false; } if(x!=nil) { // data plausible if(x1!=nil) { // there is a preceding coordinate - if(x==x1) x+= 2; // do not accept exact north-south - // lines, because then we may not be able to determine - // if a point lies inside or outside the polygon; - if(x>x1) - { bep->x1= x1; bep->y1= y1; bep->x2= x; bep->y2= y; } - else - { bep->x1= x; bep->y1= y; bep->x2= x1; bep->y2= y1; } - bep->chain= NULL; - if(loglevel>=1) - fprintf(stderr, - "- %i %"PRIi32",%"PRIi32",%"PRIi32",%"PRIi32"\n", - (int)(bep-border__edge), - bep->x1,bep->y1,bep->x2,bep->y2); - bep++; + if(x1!=x) { // new edge not in north-south direction; + // we do not accept exact north-south lines, + // because then we may not be able to determine + // if a point lies inside or outside the polygon; + if(x>x1) + { bep->x1= x1; bep->y1= y1; bep->x2= x; bep->y2= y; } + else + { bep->x1= x; bep->y1= y; bep->x2= x1; bep->y2= y1; } + bep->chain= NULL; + if(loglevel>=1) + fprintf(stderr, + "- %i %"PRIi32",%"PRIi32",%"PRIi32",%"PRIi32"\n", + (int)(bep-border__edge), + bep->x1,bep->y1,bep->x2,bep->y2); + bep++; + } // new edge not in north-south direction } // end there is a preceding coordinate x1= x; y1= y; if(x0==nil) @@ -2017,20 +2060,18 @@ return false; return true; cross= 0; - /* binary-search the edge with the closest x1 */ { + /* binary-search the edge with the closest x1 | x1<=x */ { int i,i1,i2; // iteration indexes i1= 0; i2= border__edge_n; while(i2>i1+1) { i= (i1+i2)/2; bep= border__edge+i; -//fprintf(stderr,"s %i %i %i %li\n",i1,i,i2,bep->x1); /// if(bep->x1 > x) i2= i; else i1= i; -//fprintf(stderr," %i %i %i\n",i1,i,i2); /// } bep= border__edge+i1; - } // end binary-search the edge with the closest x1 + } // binary-search the edge with the closest x1 | x1<=x bcp= NULL; // (default, because we want to examine the own edge first) @@ -5865,6 +5906,710 @@ static inline void pw_relation_close() { //------------------------------------------------------------ +// Module modi_ OSM tag modification module +//------------------------------------------------------------ + +// this module provides tag modification functionality; +// as usual, all identifiers of a module have the same prefix, +// in this case 'modi'; an underline will follow in case of a +// global accessible object, two underlines in case of objects +// which are not meant to be accessed from outside this module; +// the sections of private and public definitions are separated +// by a horizontal line: ---- + +static inline void modi__stresccpy(char *dest, const char *src, + size_t len) { + // similar as strmpy(), but remove every initial '\\' character; + // len: length of the source string - without terminating zero; + while(len>0) { + if(*src=='\\') { src++; len--; } + if(!(len>0) || *src==0) + break; + len--; + *dest++= *src++; + } + *dest= 0; + } // end modi__stresccpy() + +static inline bool modi__cmp(const char* s1,const char* s2) { + // this procedure compares two character strings; + // s1[]: first string; + // s2[0]: operator which shall be used for comparison; + // 0: '=', and there are wildcards coded in s2[1]: + // s2[1]==1: wildcard at start; + // s2[1]==2: wildcard at end; + // s2[1]==3: wildcard at both, start and end; + // 1: '!=', and there are wildcards coded in s2[1]; + // 2: '=' + // 4: '<' + // 5: '>=' + // 6: '>' + // 7: '<=' + // 8: unused + // 9: unused + // 10: '=', numeric + // 11: '!=', numeric + // 12: '<', numeric + // 13: '>=', numeric + // 14: '>', numeric + // 15: '<=', numeric + // s2+1: string to compare with the first string; + // this string will start at s2+2 if wildcards are supplied; + // return: condition is met; + int op,wc; // operator, wildcard flags + int diff; // (for numeric comparison) + unsigned char s1v,s2v; // (for numeric comparison) + + op= *s2++; + if(op==2) { // '=' + // first we care about the 'equal' operator + // because it's the most frequently used option + while(*s1==*s2 && *s1!=0) { s1++; s2++; } + return *s1==0 && *s2==0; + } + switch(op) { // depending on comparison operator + case 0: // '=', and there are wildcards + wc= *s2++; + if(wc==2) { // wildcard at end + while(*s1==*s2 && *s1!=0) { s1++; s2++; } + return *s2==0; + } // wildcard at end + if(wc==1) { // wildcard at start + const char* s11,*s22; + + while(*s1!=0) { // for all start positions in s1[] + s11= s1; s22= s2; + while(*s11==*s22 && *s11!=0) { s11++; s22++; } + if(*s11==0 && *s22==0) + return true; + s1++; + } // for all start positions in s1[] + return false; + } // wildcard at start + /* wildcards at start and end */ { + const char* s11,*s22; + + while(*s1!=0) { // for all start positions in s1[] + s11= s1; s22= s2; + while(*s11==*s22 && *s11!=0) { s11++; s22++; } + if(*s22==0) + return true; + s1++; + } // for all start positions in s1[] + return false; + } // wildcards at start and end + case 1: // '!=', and there are wildcards + wc= *s2++; + if(wc==2) { // wildcard at end + while(*s1==*s2 && *s1!=0) { s1++; s2++; } + return *s2!=0; + } // wildcard at end + if(wc==1) { // wildcard at start + const char* s11,*s22; + + while(*s1!=0) { // for all start positions in s1[] + s11= s1; s22= s2; + while(*s11==*s22 && *s11!=0) { s11++; s22++; } + if(*s11==0 && *s22==0) + return false; + s1++; + } // for all start positions in s1[] + return true; + } // wildcard at start + /* wildcards at start and end */ { + const char* s11,*s22; + + while(*s1!=0) { // for all start positions in s1[] + s11= s1; s22= s2; + while(*s11==*s22 && *s11!=0) { s11++; s22++; } + if(*s22==0) + return false; + s1++; + } // for all start positions in s1[] + return true; + } // wildcards at start and end + //case 2: // '=' (we already cared about this) + case 3: // '!=' + while(*s1==*s2 && *s1!=0) { s1++; s2++; } + return *s1!=0 || *s2!=0; + case 4: // '<' + while(*s1==*s2 && *s1!=0) { s1++; s2++; } + return *(unsigned char*)s1 < *(unsigned char*)s2; + case 5: // '>=' + while(*s1==*s2 && *s1!=0) { s1++; s2++; } + return *(unsigned char*)s1 >= *(unsigned char*)s2; + case 6: // '>' + while(*s1==*s2 && *s1!=0) { s1++; s2++; } + return *(unsigned char*)s1 > *(unsigned char*)s2; + case 7: // '<=' + while(*s1==*s2 && *s1!=0) { s1++; s2++; } + return *(unsigned char*)s1 <= *(unsigned char*)s2; + case 10: // '=', numeric + while(*s1=='0') s1++; + while(*s2=='0') s2++; + while(*s1==*s2 && isdigi(*(unsigned char*)s1)) + { s1++; s2++; } + if(*s1=='.') { + if(*s2=='.') { + do { s1++; s2++; } + while(*s1==*s2 && isdigi(*(unsigned char*)s1)); + if(!isdigi(*(unsigned char*)s1)) { + while(*s2=='0') s2++; + return !isdigi(*(unsigned char*)s2); + } + if(!isdigi(*(unsigned char*)s2)) { + while(*s1=='0') s1++; + return !isdigi(*(unsigned char*)s1); + } + return !isdigi(*(unsigned char*)s1) && + !isdigi(*(unsigned char*)s2); + } + do s1++; + while(*s1=='0'); + return !isdigi(*(unsigned char*)s1); + } + if(*s2=='.') { + do s2++; + while(*s2=='0'); + return !isdigi(*(unsigned char*)s2); + } + return !isdigi(*(unsigned char*)s1) && !isdigi(*(unsigned char*)s2); + case 11: // '!=', numeric + while(*s1=='0') s1++; + while(*s2=='0') s2++; + while(*s1==*s2 && isdigi(*(unsigned char*)s1)) + { s1++; s2++; } + if(*s1=='.') { + if(*s2=='.') { + do { s1++; s2++; } + while(*s1==*s2 && isdigi(*(unsigned char*)s1)); + if(!isdigi(*(unsigned char*)s1)) { + while(*s2=='0') s2++; + return isdigi(*(unsigned char*)s2); + } + if(!isdigi(*(unsigned char*)s2)) { + while(*s1=='0') s1++; + return isdigi(*(unsigned char*)s1); + } + return isdigi(*(unsigned char*)s1) || + isdigi(*(unsigned char*)s2); + } + do s1++; + while(*s1=='0'); + return isdigi(*(unsigned char*)s1); + } + if(*s2=='.') { + do s2++; + while(*s2=='0'); + return isdigi(*(unsigned char*)s2); + } + return isdigi(*(unsigned char*)s1) || isdigi(*(unsigned char*)s2); + case 12: /* '<', numeric */ + #define Ds1 s1 + #define Ds2 s2 + s1v= *(unsigned char*)Ds1; s2v= *(unsigned char*)Ds2; + if(s1v=='-') { + if(s2v=='-') { + Ds1++; s2v= *(unsigned char*)Ds1; + Ds2++; s1v= *(unsigned char*)Ds2; + goto op_14; + } + return true; + } + else if(s2v=='-') + return false; + op_12: + while(s1v=='0') { Ds1++; s1v= *(unsigned char*)Ds1; } + while(s2v=='0') { Ds2++; s2v= *(unsigned char*)Ds2; } + while(s1v==s2v && isdigi(s1v)) { + Ds1++; s1v= *(unsigned char*)Ds1; + Ds2++; s2v= *(unsigned char*)Ds2; + } + diff= digival(s1v)-digival(s2v); + while(isdigi(s1v) && isdigi(s2v)) { + Ds1++; s1v= *(unsigned char*)Ds1; + Ds2++; s2v= *(unsigned char*)Ds2; + } + if(s1v=='.') { + if(s2v=='.') { + if(diff!=0) + return diff<0; + do { + Ds1++; s1v= *(unsigned char*)Ds1; + Ds2++; s2v= *(unsigned char*)Ds2; + } while(s1v==s2v && isdigi(s1v)); + while(s2v=='0') { Ds2++; s2v= *(unsigned char*)Ds2; } + return digival(s1v) < digival(s2v); + } + return isdigi(s2v) || diff<0; + } + if(s2v=='.') { + if(isdigi(s1v)) + return false; + if(diff!=0) + return diff<0; + do { Ds2++; s2v= *(unsigned char*)Ds2; } while(s2v=='0'); + return isdigi(s2v); + } + return isdigi(s2v) || (!isdigi(s1v) && diff<0); + #undef Ds1 + #undef Ds2 + case 13: /* '>=', numeric */ + #define Ds1 s1 + #define Ds2 s2 + s1v= *(unsigned char*)Ds1; s2v= *(unsigned char*)Ds2; + if(s1v=='-') { + if(s2v=='-') { + Ds1++; s2v= *(unsigned char*)Ds1; + Ds2++; s1v= *(unsigned char*)Ds2; + goto op_15; + } + return false; + } + else if(s2v=='-') + return true; + op_13: + while(s1v=='0') { Ds1++; s1v= *(unsigned char*)Ds1; } + while(s2v=='0') { Ds2++; s2v= *(unsigned char*)Ds2; } + while(s1v==s2v && isdigi(s1v)) { + Ds1++; s1v= *(unsigned char*)Ds1; + Ds2++; s2v= *(unsigned char*)Ds2; + } + diff= digival(s1v)-digival(s2v); + while(isdigi(s1v) && isdigi(s2v)) { + Ds1++; s1v= *(unsigned char*)Ds1; + Ds2++; s2v= *(unsigned char*)Ds2; + } + if(s1v=='.') { + if(s2v=='.') { + if(diff!=0) + return diff>=0; + do { + Ds1++; s1v= *(unsigned char*)Ds1; + Ds2++; s2v= *(unsigned char*)Ds2; + } while(s1v==s2v && isdigi(s1v)); + while(s2v=='0') { Ds2++; s2v= *(unsigned char*)Ds2; } + return digival(s1v) >= digival(s2v); + } + return !isdigi(s2v) && diff>=0; + } + if(s2v=='.') { + if(isdigi(s1v)) + return true; + if(diff!=0) + return diff>=0; + do { Ds2++; s2v= *(unsigned char*)Ds2; } while(s2v=='0'); + return !isdigi(s2v); + } + return !isdigi(s2v) && (isdigi(s1v) || diff>=0); + #undef Ds1 + #undef Ds2 + case 14: /* '>', numeric */ + #define Ds1 s2 + #define Ds2 s1 + s1v= *(unsigned char*)Ds1; s2v= *(unsigned char*)Ds2; + if(s1v=='-') { + if(s2v=='-') { + Ds1++; s2v= *(unsigned char*)Ds1; + Ds2++; s1v= *(unsigned char*)Ds2; + goto op_12; + } + return true; + } + else if(s2v=='-') + return false; + op_14: + while(s1v=='0') { Ds1++; s1v= *(unsigned char*)Ds1; } + while(s2v=='0') { Ds2++; s2v= *(unsigned char*)Ds2; } + while(s1v==s2v && isdigi(s1v)) { + Ds1++; s1v= *(unsigned char*)Ds1; + Ds2++; s2v= *(unsigned char*)Ds2; + } + diff= digival(s1v)-digival(s2v); + while(isdigi(s1v) && isdigi(s2v)) { + Ds1++; s1v= *(unsigned char*)Ds1; + Ds2++; s2v= *(unsigned char*)Ds2; + } + if(s1v=='.') { + if(s2v=='.') { + if(diff!=0) + return diff<0; + do { + Ds1++; s1v= *(unsigned char*)Ds1; + Ds2++; s2v= *(unsigned char*)Ds2; + } while(s1v==s2v && isdigi(s1v)); + while(s2v=='0') { Ds2++; s2v= *(unsigned char*)Ds2; } + return digival(s1v) < digival(s2v); + } + return isdigi(s2v) || diff<0; + } + if(s2v=='.') { + if(isdigi(s1v)) + return false; + if(diff!=0) + return diff<0; + do { Ds2++; s2v= *(unsigned char*)Ds2; } while(s2v=='0'); + return isdigi(s2v); + } + return isdigi(s2v) || (!isdigi(s1v) && diff<0); + #undef Ds1 + #undef Ds2 + case 15: /* '<=', numeric */ + #define Ds1 s2 + #define Ds2 s1 + s1v= *(unsigned char*)Ds1; s2v= *(unsigned char*)Ds2; + if(s1v=='-') { + if(s2v=='-') { + Ds1++; s2v= *(unsigned char*)Ds1; + Ds2++; s1v= *(unsigned char*)Ds2; + goto op_13; + } + return false; + } + else if(s2v=='-') + return true; + op_15: + while(s1v=='0') { Ds1++; s1v= *(unsigned char*)Ds1; } + while(s2v=='0') { Ds2++; s2v= *(unsigned char*)Ds2; } + while(s1v==s2v && isdigi(s1v)) { + Ds1++; s1v= *(unsigned char*)Ds1; + Ds2++; s2v= *(unsigned char*)Ds2; + } + diff= digival(s1v)-digival(s2v); + while(isdigi(s1v) && isdigi(s2v)) { + Ds1++; s1v= *(unsigned char*)Ds1; + Ds2++; s2v= *(unsigned char*)Ds2; + } + if(s1v=='.') { + if(s2v=='.') { + if(diff!=0) + return diff>=0; + do { + Ds1++; s1v= *(unsigned char*)Ds1; + Ds2++; s2v= *(unsigned char*)Ds2; + } while(s1v==s2v && isdigi(s1v)); + while(s2v=='0') { Ds2++; s2v= *(unsigned char*)Ds2; } + return digival(s1v) >= digival(s2v); + } + return !isdigi(s2v) && diff>=0; + } + if(s2v=='.') { + if(isdigi(s1v)) + return true; + if(diff!=0) + return diff>=0; + do { Ds2++; s2v= *(unsigned char*)Ds2; } while(s2v=='0'); + return !isdigi(s2v); + } + return !isdigi(s2v) && (isdigi(s1v) || diff>=0); + #undef Ds1 + #undef Ds2 + // (no default) + } // depending on comparison operator + return false; // (we never get here) + } // end modi__cmp() + +#define modi__pairM 1000 // maximum number of key-val-pairs +#define modi__pairkM 100 // maximum length of key or val; +#define modi__pairtM 3 // maximum number of modification types; + // these modification types are defined as follows: + // 0: modify node tag; + // 1: modify way tag; + // 2: modify relation tag; +struct modi__pair_struct { + // key/val pair for the include filter + char k[modi__pairkM+8]; // key to compare; + // [0]==0 && [1]==0: same key as previous key in list; + char v[modi__pairkM+8]; // value to the key in .k[]; + // the first byte represents a comparison operator, + // see parameter s2[]in modi__cmp() for details; + // [0]==0 && [1]==0: any value will be accepted; + char nk[modi__pairkM+2]; // new key + char nv[modi__pairkM+2]; // new value + bool add; // new key/val pair shall be added instead of replacing + // the old key/val pair + } __attribute__((__packed__)); +typedef struct modi__pair_struct modi__pair_t; +static modi__pair_t modi__pair[modi__pairtM][modi__pairM+2]= + {{{{0},{0},{0},{0}}}}; +static modi__pair_t* modi__paire[modi__pairtM]= + { &modi__pair[0][0],&modi__pair[1][0],&modi__pair[2][0] }; +static modi__pair_t* modi__pairee[modi__pairtM]= + { &modi__pair[0][modi__pairM],&modi__pair[1][modi__pairM], + &modi__pair[2][modi__pairM] }; + +//------------------------------------------------------------ + +static inline void modi_cpy(char *dest, const char *src, + size_t len,int op) { + // similar as strmpy(), but remove every initial '\\' character; + // len: length of the source string - without terminating zero; + // op: comparison operator; + // 2: '=' + // 4: '<' + // 5: '>=' + // 6: '>' + // 7: '<=' + // return: dest[0]: comparison operator; additional possible values: + // 0: '=', and there are wildcards coded in dest[1]: + // dest[1]==1: wildcard at start; + // dest[1]==2: wildcard at end; + // dest[1]==3: wildcard at both, start and end; + // 1: '!=', and there are wildcards coded in dest[1]; + // 10: '=', numeric + // 11: '!=', numeric + // 12: '<', numeric + // 13: '>=', numeric + // 14: '>', numeric + // 15: '<=', numeric + int wc; // wildcard indicator, see modi__cmp() + + if(op<0) { // unknown operator + WARNv("unknown comparison at: %.80s",src) + op= 2; // assume '=' + } + if(len>(modi__pairkM)) { + len= modi__pairkM; // delimit value length + WARNv("modification argument too long: %.*s",modi__pairkM,src) + } + wc= 0; // (default) + if(len>=2 && src[0]=='*') { // wildcard at start + wc|= 1; + src++; len--; + } + if((len>=2 && src[len-1]=='*' && src[len-2]!='\\') || + (len==1 && src[len-1]=='*')) { + // wildcard at end + wc|= 2; + len--; + } + if(wc==0) { // no wildcard(s) + const char* v; + + v= src; + if(*v=='-') v++; // jump over sign + if(isdig(*v)) // numeric value + op+= 8; + dest[0]= op; + modi__stresccpy(dest+1,src,len); // store this value + } // no wildcard(s) + else { // wildcard(s) + dest[0]= op&1; + dest[1]= wc; + modi__stresccpy(dest+2,src,len); // store this value + } // wildcard(s) + } // end modi_cpy() + +static bool modi_active= false; + // there is at least one modify criteria active; + // may be read by everyone but written only by this module; +static bool modi_activetype[modi__pairtM]= {false,false,false}; + // the related modify list has at least one element; + // may be read by everyone but written only by this module; + +static void modi_ini() { + // initialize this mudule; + int i; + + modi_active= false; + for(i= 0; i<modi__pairtM; i++) { + modi__paire[i]= &modi__pair[i][0]; + modi__pairee[i]= &modi__pair[i][modi__pairM]; + modi_activetype[i]= false; + } + } // modi_ini() + +static void modi_parse(int ftype,const char* arg) { + // interprets a command line argument and stores modification + // information; + // ftype: object type; see explanation at modi__pairtM; + // arg[]: modification information; e.g.: + // "amenity=fire_hydrant to emergency=fire_hydrant" + modi__pair_t*fe,*fee; + const char* pk,*pv,*pe; // pointers in parameter for key/val pairs; + // pk: key; pv: val; pe: end of val; + int len; // string length + int op; // operator, see modi__cmp() + + fe= modi__paire[ftype]; + fee= modi__pairee[ftype]; + if(loglevel>0) + PINFOv("Modify: %s tags:",ONAME(ftype%3)) + pk= arg; + while(*pk==' ') pk++; // jump over spaces + + while(pk!=NULL && fe<fee) { // for every key/val pair + while(*pk==' ') pk++; // jump over (additional) spaces + if(*pk==0) + break; + pe= pk; + while((*pe!=' ' || pe[-1]=='\\') && *pe!=0) pe++; + // get end of this pair + len= pe-pk; // length of this argument + pv= pk; + while(((*pv!='=' && *pv!='<' && *pv!='>' && + (*pv!='!' || pv[1]!='=')) || + (pv>pk && pv[-1]=='\\')) && pv<pe) pv++; + // find operator =, <, >, != + if(pv>=pe-1) pv= pe; // there was no operator in this pair + len= pv-pk; // length of this key + if(len>(modi__pairkM)) { + len= modi__pairkM; // delimit key length + WARNv("modification key too long: %.*s",modi__pairkM,pk) + } + op= -1; // 'unknown operator' (default) + if(pv>=pe) { // there is a key but no value + if(len>0 && pk[len-1]=='=') len--; + modi_cpy(fe->k,pk,len,2); // store this key, op='=' + memset(fe->v,0,3); // store empty value + } + else { // key and value + if(len==0) // no key given + memset(fe->k,0,3); // store empty key, + else + modi_cpy(fe->k,pk,len,2); // store this key, op='=' + if(*pv=='=') op= 2; + else if(*pv=='!' && pv[1]=='=') op= 3; + else if(*pv=='<' && pv[1]!='=') op= 4; + else if(*pv=='>' && pv[1]=='=') op= 5; + else if(*pv=='>' && pv[1]!='=') op= 6; + else if(*pv=='<' && pv[1]=='=') op= 7; + if(op<0) { // unknown operator + WARNv("unknown comparison at: %.80s",pv) + op= 2; // assume '=' + } + pv++; // jump over operator + if(pv<pe && *pv=='=') pv++; + // jump over second character of a two-character operator + len= pe-pv; // length of this value + modi_cpy(fe->v,pv,len,op); // store this value + } // key and value + // jump over ' to ' phrase + while(*pe==' ') pe++; // jump over spaces + if((fe->add= strzcmp(pe,"add ")==0)) pe+= 4; + else if(strzcmp(pe,"to ")==0) pe+= 3; + // get destination key/val + pk= pe; // jump to next key/val pair in parameter list + while(*pk==' ') pk++; // jump over (additional) spaces + pe= pk; + while((*pe!=' ' || pe[-1]=='\\') && *pe!=0) pe++; + // get end of this destination pair + len= pe-pk; // length of this argument + pv= pk; + while((*pv!='=' || (pv>pk && pv[-1]=='\\')) && pv<pe) pv++; + // find operator '=' + if(pv>=pe-1) pv= pe; // there was no operator in this pair + len= pv-pk; // length of this key + if(len>(modi__pairkM)) { + len= modi__pairkM; // delimit key length + WARNv("modification key too long: %.*s",modi__pairkM,pk) + } + if(pv>=pe) { // there is a destination key but no value + if(len>0 && pk[len-1]=='=') len--; + modi__stresccpy(fe->nk,pk,len); // store this key + fe->nv[0]= 0; // store empty value + } + else { // destination key and value + if(len==0) // no key given + modi__stresccpy(fe->nk,fe->k[0]<=1? fe->k+2: fe->k+1, + modi__pairkM); + // store source key as destination key + else + modi__stresccpy(fe->nk,pk,len); // store this key + pv++; // jump over equation operator + if(pv<pe && *pv=='=') pv++; + // jump over second character of a two-character operator + len= pe-pv; // length of this value + if(len==0) // no value given + modi__stresccpy(fe->nv,fe->v[0]<=1? fe->v+2: fe->v+1, + modi__pairkM); + // store source value as destination value + else + modi__stresccpy(fe->nv,pv,len); // store this value + } // destination key and value + if(loglevel>0) { + static const char* ops[]= { "?", + "=","!=","=","!=","<",">=",">","<=", + "?","?","=(numeric)","!=(numeric)", + "<(numeric)",">=(numeric)",">(numeric)","<=(numeric)" }; + PINFOv("Modify: %s\"%.80s\"%s %s %s\"%.80s\"%s", + fe->k[0]<=1 && (fe->k[1] & 1)? "*": "", + *(int16_t*)(fe->k)==0? "(last key)": + fe->k[0]>=2? fe->k+1: fe->k+2, + fe->k[0]<=1 && (fe->k[1] & 2)? "*": "", + ops[fe->v[0]+1], + fe->v[0]<=1 && (fe->v[1] & 1)? "*": "", + *(int16_t*)(fe->v)==0? "(anything)": + fe->v[0]>=2? fe->v+1: fe->v+2, + fe->v[0]<=1 && (fe->v[1] & 2)? "*": ""); + } + fe++; // next pair in key/val table + pk= pe; // jump to next key/val pair in parameter list + } // end for every key/val pair + if(fe>=fee) + WARN("too many modification parameters.") + modi__paire[ftype]= fe; + modi_active= true; + modi_activetype[ftype]= true; + } // end modi_parse() + +static char* modi_check_key= "-",*modi_check_val= "-"; + static bool modi_check_add= false; + // return values of procedure modi_check(); + // the values are valid only if the previous call to modi_check() + // has returned 'true'; + +static inline bool modi_check(int otype,char* key,char* val) { + // check if OSM object matches modification criteria; + // otype: 0: node; 1: way; 2: relation; + // key,val: key and value; + // return: given key/val pair matches modification criteria; + // modi_check_key,modi_check_val: destination key/val; + // modi_check_add: the destination key/val shall be added + // instead of replacing the old key/val pair; + modi__pair_t* fp,*fe; + + fp= modi__pair[otype]; fe= modi__paire[otype]; + while(fp<fe) { // for every key/val pair in filter + if(*(int16_t*)(fp->k)==0) { // no key given + if(modi__cmp(val,fp->v)) // just compare the value + goto modi_check_found; + } + else { // key given + if(modi__cmp(key,fp->k) && + (*(int16_t*)(fp->k)==0 || modi__cmp(val,fp->v))) + // compare key and value (if any) + goto modi_check_found; + } + fp++; + } // for every key/val pair in filter + return false; +modi_check_found: + if(fp->nk[0]!=0) // there is a destination key + modi_check_key= fp->nk; // take that destination key + else + modi_check_key= key; + // take source key instead + if(fp->nv[0]!=0) // there is a destination value + modi_check_val= fp->nv; // take that destination value + else + modi_check_val= val; + // take source value instead + modi_check_add= fp->add; // publish key/val add request + return true; + } // end modi_check() + +#define modi_CHECK(ot,k,v) \ + (modi_active && modi_activetype[ot] && modi_check(ot,k,v)) + // prevents procedure call in case there are no modifications applied + +//------------------------------------------------------------ +// end Module modi_ OSM tag modification module +//------------------------------------------------------------ + + + +//------------------------------------------------------------ // Module posi_ OSM position module //------------------------------------------------------------ @@ -10761,8 +11506,15 @@ return 26; wo_node(id, hisver,histime,hiscset,hisuid,hisuser,lon,lat); keyp= key; valp= val; - while(keyp<keye) // for all key/val pairs of this object - wo_node_keyval(*keyp++,*valp++); + while(keyp<keye) { // for all key/val pairs of this object + if(modi_CHECK(otype,*keyp,*valp)) { + if(modi_check_add) wo_node_keyval(*keyp++,*valp++); + else keyp++; valp++; + wo_node_keyval(modi_check_key,modi_check_val); + } + else + wo_node_keyval(*keyp++,*valp++); + } // for all key/val pairs of this object wo_node_close(); } // end not to drop } // end node lies inside @@ -10896,8 +11648,16 @@ return 26; if(global_add) wo_addbboxtags(true,x_min,y_min,x_max,y_max); keyp= key; valp= val; - while(keyp<keye) // for all key/val pairs of this object - wo_node_keyval(*keyp++,*valp++); + while(keyp<keye) { + // for all key/val pairs of this object + if(modi_CHECK(otype,*keyp,*valp)) { + if(modi_check_add) wo_node_keyval(*keyp++,*valp++); + else keyp++; valp++; + wo_node_keyval(modi_check_key,modi_check_val); + } + else + wo_node_keyval(*keyp++,*valp++); + } // for all key/val pairs of this object wo_node_close(); } // there is at least one coordinate available } // convert all objects to nodes @@ -10913,8 +11673,16 @@ return 26; if(global_add) wo_addbboxtags(false,x_min,y_min,x_max,y_max); keyp= key; valp= val; - while(keyp<keye) // for all key/val pairs of this object - wo_wayrel_keyval(*keyp++,*valp++); + while(keyp<keye) { + // for all key/val pairs of this object + if(modi_CHECK(otype,*keyp,*valp)) { + if(modi_check_add) wo_wayrel_keyval(*keyp++,*valp++); + else keyp++; valp++; + wo_wayrel_keyval(modi_check_key,modi_check_val); + } + else + wo_wayrel_keyval(*keyp++,*valp++); + } // for all key/val pairs of this object wo_way_close(); } // objects are not to be converted to nodes } // coordinates of ways shall be calculated @@ -10928,8 +11696,16 @@ return 26; refidp++; } // end for every referenced node keyp= key; valp= val; - while(keyp<keye) // for all key/val pairs of this object - wo_wayrel_keyval(*keyp++,*valp++); + while(keyp<keye) { + // for all key/val pairs of this object + if(modi_CHECK(otype,*keyp,*valp)) { + if(modi_check_add) wo_wayrel_keyval(*keyp++,*valp++); + else keyp++; valp++; + wo_wayrel_keyval(modi_check_key,modi_check_val); + } + else + wo_wayrel_keyval(*keyp++,*valp++); + } // for all key/val pairs of this object wo_way_close(); } // coordinates of ways need not to be calculated } // end not ways to drop @@ -11044,8 +11820,16 @@ return 26; wo_addbboxtags(true, posi_xy[2],posi_xy[3],posi_xy[4],posi_xy[5]); keyp= key; valp= val; - while(keyp<keye) // for all key/val pairs of this object - wo_node_keyval(*keyp++,*valp++); + while(keyp<keye) { + // for all key/val pairs of this object + if(modi_CHECK(otype,*keyp,*valp)) { + if(modi_check_add) wo_node_keyval(*keyp++,*valp++); + else keyp++; valp++; + wo_node_keyval(modi_check_key,modi_check_val); + } + else + wo_node_keyval(*keyp++,*valp++); + } // for all key/val pairs of this object wo_node_close(); } // stored coordinates are valid } // relations are to be converted to nodes @@ -11095,8 +11879,16 @@ return 26; posi_xy[2],posi_xy[3],posi_xy[4],posi_xy[5]); } keyp= key; valp= val; - while(keyp<keye) // for all key/val pairs of this object - wo_wayrel_keyval(*keyp++,*valp++); + while(keyp<keye) { + // for all key/val pairs of this object + if(modi_CHECK(otype,*keyp,*valp)) { + if(modi_check_add) wo_wayrel_keyval(*keyp++,*valp++); + else keyp++; valp++; + wo_wayrel_keyval(modi_check_key,modi_check_val); + } + else + wo_wayrel_keyval(*keyp++,*valp++); + } // for all key/val pairs of this object wo_relation_close(); } // stage!=3 OR not --all-to-nodes } // end no borders OR at least one node inside @@ -11823,6 +12615,7 @@ int main(int argc,char** argv) { // initializations usesstdin= false; h_n= h_w= h_r= 0; + modi_ini(); #if __WIN32__ setmode(fileno(stdout),O_BINARY); setmode(fileno(stdin),O_BINARY); @@ -12245,6 +13038,17 @@ return 4; } // end border consideration by polygon file continue; // take next parameter } + #define F(t) modi_parse(t,a+l); + #define D(p,f) if((l= strzlcmp(a,#p))>0) { f continue; } + D(--modify-tags=,F(0)F(1)F(2)) + D(--modify-node-tags=,F(0)) + D(--modify-way-tags=,F(1)) + D(--modify-relation-tags=,F(2)) + D(--modify-node-way-tags=,F(0)F(1)) + D(--modify-node-relation-tags=,F(0)F(2)) + D(--modify-way-relation-tags=,F(1)F(2)) + #undef D + #undef F if(strcmp(a,"-")==0) { // use standard input usesstdin= true; if(oo_open(NULL)) // file cannot be read diff --git a/src/osmfilter.c b/src/osmfilter.c index adcd71f..946001f 100644 --- a/src/osmfilter.c +++ b/src/osmfilter.c @@ -1,10 +1,10 @@ -// osmfilter 2015-04-14 19:50 -#define VERSION "1.4.0" +// osmfilter 2017-03-30 19:00 +#define VERSION "1.4.2" // // compile this file: // gcc osmfilter.c -O3 -o osmfilter // -// (c) 2011..2015 Markus Weber, Nuernberg +// (c) 2011..2017 Markus Weber, Nuernberg // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU Affero General Public License @@ -41,6 +41,8 @@ const char* shorthelptext= "--keep-way-relation-tags=\n" "--drop-tags= define which tags are to be dropped\n" "--drop-...-tags= similar to --keep-...-tags= (see above)\n" +"--modify-tags= define which tags are to be modified\n" +"--modify-...-tags= similar to --keep-...-tags= (see above)\n" "--drop-author delete changeset and user information\n" "--drop-version same as before, but delete version as well\n" "--drop-nodes delete all nodes\n" @@ -138,6 +140,19 @@ const char* helptext= "--drop-way-relation-tags=TAG_FILTER\n" " Same as above, but just for the specified object types.\n" "\n" +"--modify-tags=TAG_MODIFICATION_LIST\n" +" The specified tags will be modified. This is done after any\n" +" filtering (see --keep, --keep-tags, --drop, --drop-tags).\n" +" Please look below for a description of TAG_MODIFICATION_LIST.\n" +"\n" +"--modify-node-tags=TAG_MODIFICATION_LIST\n" +"--modify-way-tags=TAG_MODIFICATION_LIST\n" +"--modify-relation-tags=TAG_MODIFICATION_LIST\n" +"--modify-node-way-tags=TAG_MODIFICATION_LIST\n" +"--modify-node-relation-tags=TAG_MODIFICATION_LIST\n" +"--modify-way-relation-tags=TAG_MODIFICATION_LIST\n" +" Same as above, but just for the specified object types.\n" +"\n" "--drop-author\n" " For most applications the author tags are not needed. If you\n" " specify this option, no author information will be written:\n" @@ -212,7 +227,8 @@ const char* helptext= " their ids only.\n" "\n" "--out-osh\n" -" For every OSM object, the appropriate \'visible\' tag will be\n" " added to meet \'full planet history\' specification.\n" +" For every OSM object, the appropriate \'visible\' tag will be\n" +" added to meet \'full planet history\' specification.\n" "\n" "--out-o5m\n" " The .o5m format will be used. This format has the same\n" @@ -269,7 +285,7 @@ const char* helptext= " \"amenity=restaurant =pub =bar\"\n" " It is allowed to omit the value. In this case, the program\n" " will accept every value for the defined key. For example:\n" -" \"all highway= lit=yes\"\n" +" \"highway= and lit=yes\"\n" " You may use wildcard characters for key or value, but only at\n" " the beginning and/or at the end. For example:\n" " wikipedia:*= highway=*ary ref_name=*central*\n" @@ -289,6 +305,14 @@ const char* helptext= " tag which is not mentioned in a list, use this example:\n" " all highway= amenity= name=\n" "\n" +"TAG_MODIFICATION_LIST\n" +" The tag modification list determines which tags will be\n" +" modified. The example\n" +" --modify-tags=\"highway=primary to =secondary\"\n" +" will change every \"primary\" highway into \"secondary\".\n" +" You can also use comparisons or add additional tags:\n" +" --modify-way-tags=\"maxspeed>200 add highspeed=yes\"\n" +"\n" "Examples\n" "\n" "./osmfilter europe.o5m --keep=amenity=bar -o=new.o5m\n" @@ -1111,7 +1135,7 @@ static inline bool read_input() { // having available at least read_PREFETCH bytes at address // read_bufp - with one exception: if there are not enough bytes // left to read from standard input, every byte after the end of - // the reminding part of the file in the buffer will be set to + // the remaining part of the file in the buffer will be set to // 0x00 - up to read_bufp+read_PREFETCH; int l,r; @@ -1135,10 +1159,10 @@ static inline bool read_input() { read_infop->eof= true; // memorize that there we are at end of file l= (read__buf+read__bufM)-read_bufe; - // reminding space in buffer + // remaining space in buffer if(l>read_PREFETCH) l= read_PREFETCH; memset(read_bufe,0,l); // 2011-12-24 - // set reminding space up to prefetch bytes in buffer to 0 + // set remaining space up to prefetch bytes in buffer to 0 break; } read_infop->read__counter+= r; @@ -1894,7 +1918,7 @@ static void count_write() { // the sections of private and public definitions are separated // by a horizontal line: ---- -static inline void fil_stresccpy(char *dest, const char *src, +static inline void fil__stresccpy(char *dest, const char *src, size_t len) { // similar as strmpy(), but remove every initial '\\' character; // len: length of the source string - without terminating zero; @@ -1906,7 +1930,7 @@ static inline void fil_stresccpy(char *dest, const char *src, *dest++= *src++; } *dest= 0; - } // end fil_stresccpy() + } // end fil__stresccpy() static inline bool fil__cmp(const char* s1,const char* s2) { // this procedure compares two character strings; @@ -2392,12 +2416,12 @@ static inline void fil_cpy(char *dest, const char *src, if(isdig(*v)) // numeric value op+= 8; dest[0]= op; - fil_stresccpy(dest+1,src,len); // store this value + fil__stresccpy(dest+1,src,len); // store this value } // no wildcard(s) else { // wildcard(s) dest[0]= op&1; dest[1]= wc; - fil_stresccpy(dest+2,src,len); // store this value + fil__stresccpy(dest+2,src,len); // store this value } // wildcard(s) } // end fil_cpy() @@ -2834,160 +2858,864 @@ return result; return result; } // end fil_check0() -static inline bool fil_check1(int otype, - char** key,char** keye,char** val,char** vale) { - // check if OSM object matches filter criteria; - // at this procedure, filter type 4..6 is applied: 'drop object'; - // keyp,keye,valp,vale: tag list; - // otype: 0: node; 1: way; 2: relation; - // return: given tag list matches keep criteria; - bool result; - char** keyp,**valp; - fil__pair_t* fp,*fe; - int bracket_balance; - int bb; // temporary for bracket_balance - char* v; // previous value of a key which compared successfully +static inline bool fil_check1(int otype, + char** key,char** keye,char** val,char** vale) { + // check if OSM object matches filter criteria; + // at this procedure, filter type 4..6 is applied: 'drop object'; + // keyp,keye,valp,vale: tag list; + // otype: 0: node; 1: way; 2: relation; + // return: given tag list matches keep criteria; + bool result; + char** keyp,**valp; + fil__pair_t* fp,*fe; + int bracket_balance; + int bb; // temporary for bracket_balance + char* v; // previous value of a key which compared successfully + + result= false; + v= NULL; // (default) + valp= &v; // (default) + bracket_balance= 0; + fp= fil__pair[3+otype]; fe= fil__paire[3+otype]; + while(fp<fe) { // for every key/val pair in filter + bracket_balance+= fp->left_bracketn; + if(*(int16_t*)(fp->k)==0) { + if(v!=NULL) + result= fil__cmp(v,fp->v); + } + else { + result= false; // (default) + keyp= key; valp= val; + while(keyp<keye) { // for all key/val pairs of this object + if(fil__cmp(*keyp,fp->k)) { // right key + v= *valp; + if(*(int16_t*)(fp->k)==0 || fil__cmp(v,fp->v)) { + // right value + result= true; + break; + } + } + keyp++; valp++; + } // for all key/val pairs of this object + } + #if MAXLOGLEVEL>=3 + if(loglevel>=3) + PINFOv("comparison[%i][%i]==%i", + 3+otype,fp-fil__pair[3+otype],result) + #endif + if(result) { // comparison satisfied + if(fp->operator) { // Boolean operator is AND + // (continue with next comparison) + } // Boolean operator is AND + else { // Boolean operator is OR + // at each encountered 'or': + // jump to after next operand at lower layer + bracket_balance-= fp->right_bracketn; + if(bracket_balance<=0) // we already are at lowest level +return result; + bb= bracket_balance; + fp++; + while(fp<fe) { + bracket_balance+= fp->left_bracketn; + bracket_balance-= fp->right_bracketn; + if(bracket_balance>=bb) { // same level or higher + fp++; + continue; + } + if(fp->operator) { // next operator is 'and' + fp++; + break; // go on by evaluating this operator + } + // here: next operator is an 'or' + if(bracket_balance<=0) // we are at lowest level +return result; + bb= bracket_balance; // from now on ignore this level + fp++; + } + v= NULL; // previous value no longer valid + continue; + } // Boolean operator is OR + } // comparison satisfied + else { // comparison not satisfied + if(fp->operator) { // Boolean operator is AND + // jump to after next 'or' within same brackets or + // lower layer, but not into the space between new brackets + bracket_balance-= fp->right_bracketn; + bb= bracket_balance; + fp++; + while(fp<fe) { + bracket_balance+= fp->left_bracketn; + bracket_balance-= fp->right_bracketn; + if(bracket_balance<bb) + bb= bracket_balance; + if(bracket_balance<=bb && !fp->operator) { + // not in a new bracket AND next operator is 'or' + fp++; + break; + } + fp++; + } + v= NULL; // previous value no longer valid + continue; + } // Boolean operator is AND + else { // Boolean operator is OR + // (continue with next comparison) + } // Boolean operator is OR + } // comparison not satisfied + bracket_balance-= fp->right_bracketn; + fp++; + } // for every key/val pair in filter + return result; + } // end fil_check1() + +static inline bool fil_check2(int otype, + const char* key,const char* val) { + // test if filter allows this tag to be kept; + // at this procedure, filters type 6..8 and 9..11 are applied: + // 'keep tag'; + // otype: 0: node; 1: way; 2: relation; + // return: given key[] and val[] match keep criteria; + fil__pair_t* fp,*fe; + const char* k; // last key in filter + bool keymatch; + + // apply keep-filter + if(fil_active[6+otype]) { + k= "name"; // (default) + keymatch= false; + fp= &fil__pair[6+otype][0]; fe= fil__paire[6+otype]; + while(fp<fe) { + if(*(int16_t*)(fp->k)!=0) k= fp->k; + keymatch= fil__cmp(key,k); + if(keymatch && (*(int16_t*)(fp->v)==0 || fil__cmp(val,fp->v))) + goto keep; + fp++; + } + if(keymatch || fil_meetall[6+otype]) +return false; + } + keep: + // apply drop-filter + if(fil_active[9+otype]) { + k= "name"; // (default) + fp= &fil__pair[9+otype][0]; fe= fil__paire[9+otype]; + while(fp<fe) { + if(*(int16_t*)(fp->k)!=0) k= fp->k; + if(fil__cmp(key,k) && (*(int16_t*)(fp->v)==0 || + fil__cmp(val,fp->v))) +return false; + fp++; + } + } + return true; + } // end fil_check2() + +//------------------------------------------------------------ +// end Module fil_ osm filter module +//------------------------------------------------------------ + + + +//------------------------------------------------------------ +// Module modi_ OSM tag modification module +//------------------------------------------------------------ + +// this module provides tag modification functionality; +// as usual, all identifiers of a module have the same prefix, +// in this case 'modi'; an underline will follow in case of a +// global accessible object, two underlines in case of objects +// which are not meant to be accessed from outside this module; +// the sections of private and public definitions are separated +// by a horizontal line: ---- + +static inline void modi__stresccpy(char *dest, const char *src, + size_t len) { + // similar as strmpy(), but remove every initial '\\' character; + // len: length of the source string - without terminating zero; + while(len>0) { + if(*src=='\\') { src++; len--; } + if(!(len>0) || *src==0) + break; + len--; + *dest++= *src++; + } + *dest= 0; + } // end modi__stresccpy() + +static inline bool modi__cmp(const char* s1,const char* s2) { + // this procedure compares two character strings; + // s1[]: first string; + // s2[0]: operator which shall be used for comparison; + // 0: '=', and there are wildcards coded in s2[1]: + // s2[1]==1: wildcard at start; + // s2[1]==2: wildcard at end; + // s2[1]==3: wildcard at both, start and end; + // 1: '!=', and there are wildcards coded in s2[1]; + // 2: '=' + // 4: '<' + // 5: '>=' + // 6: '>' + // 7: '<=' + // 8: unused + // 9: unused + // 10: '=', numeric + // 11: '!=', numeric + // 12: '<', numeric + // 13: '>=', numeric + // 14: '>', numeric + // 15: '<=', numeric + // s2+1: string to compare with the first string; + // this string will start at s2+2 if wildcards are supplied; + // return: condition is met; + int op,wc; // operator, wildcard flags + int diff; // (for numeric comparison) + unsigned char s1v,s2v; // (for numeric comparison) + + op= *s2++; + if(op==2) { // '=' + // first we care about the 'equal' operator + // because it's the most frequently used option + while(*s1==*s2 && *s1!=0) { s1++; s2++; } + return *s1==0 && *s2==0; + } + switch(op) { // depending on comparison operator + case 0: // '=', and there are wildcards + wc= *s2++; + if(wc==2) { // wildcard at end + while(*s1==*s2 && *s1!=0) { s1++; s2++; } + return *s2==0; + } // wildcard at end + if(wc==1) { // wildcard at start + const char* s11,*s22; + + while(*s1!=0) { // for all start positions in s1[] + s11= s1; s22= s2; + while(*s11==*s22 && *s11!=0) { s11++; s22++; } + if(*s11==0 && *s22==0) + return true; + s1++; + } // for all start positions in s1[] + return false; + } // wildcard at start + /* wildcards at start and end */ { + const char* s11,*s22; + + while(*s1!=0) { // for all start positions in s1[] + s11= s1; s22= s2; + while(*s11==*s22 && *s11!=0) { s11++; s22++; } + if(*s22==0) + return true; + s1++; + } // for all start positions in s1[] + return false; + } // wildcards at start and end + case 1: // '!=', and there are wildcards + wc= *s2++; + if(wc==2) { // wildcard at end + while(*s1==*s2 && *s1!=0) { s1++; s2++; } + return *s2!=0; + } // wildcard at end + if(wc==1) { // wildcard at start + const char* s11,*s22; + + while(*s1!=0) { // for all start positions in s1[] + s11= s1; s22= s2; + while(*s11==*s22 && *s11!=0) { s11++; s22++; } + if(*s11==0 && *s22==0) + return false; + s1++; + } // for all start positions in s1[] + return true; + } // wildcard at start + /* wildcards at start and end */ { + const char* s11,*s22; + + while(*s1!=0) { // for all start positions in s1[] + s11= s1; s22= s2; + while(*s11==*s22 && *s11!=0) { s11++; s22++; } + if(*s22==0) + return false; + s1++; + } // for all start positions in s1[] + return true; + } // wildcards at start and end + //case 2: // '=' (we already cared about this) + case 3: // '!=' + while(*s1==*s2 && *s1!=0) { s1++; s2++; } + return *s1!=0 || *s2!=0; + case 4: // '<' + while(*s1==*s2 && *s1!=0) { s1++; s2++; } + return *(unsigned char*)s1 < *(unsigned char*)s2; + case 5: // '>=' + while(*s1==*s2 && *s1!=0) { s1++; s2++; } + return *(unsigned char*)s1 >= *(unsigned char*)s2; + case 6: // '>' + while(*s1==*s2 && *s1!=0) { s1++; s2++; } + return *(unsigned char*)s1 > *(unsigned char*)s2; + case 7: // '<=' + while(*s1==*s2 && *s1!=0) { s1++; s2++; } + return *(unsigned char*)s1 <= *(unsigned char*)s2; + case 10: // '=', numeric + while(*s1=='0') s1++; + while(*s2=='0') s2++; + while(*s1==*s2 && isdigi(*(unsigned char*)s1)) + { s1++; s2++; } + if(*s1=='.') { + if(*s2=='.') { + do { s1++; s2++; } + while(*s1==*s2 && isdigi(*(unsigned char*)s1)); + if(!isdigi(*(unsigned char*)s1)) { + while(*s2=='0') s2++; + return !isdigi(*(unsigned char*)s2); + } + if(!isdigi(*(unsigned char*)s2)) { + while(*s1=='0') s1++; + return !isdigi(*(unsigned char*)s1); + } + return !isdigi(*(unsigned char*)s1) && + !isdigi(*(unsigned char*)s2); + } + do s1++; + while(*s1=='0'); + return !isdigi(*(unsigned char*)s1); + } + if(*s2=='.') { + do s2++; + while(*s2=='0'); + return !isdigi(*(unsigned char*)s2); + } + return !isdigi(*(unsigned char*)s1) && !isdigi(*(unsigned char*)s2); + case 11: // '!=', numeric + while(*s1=='0') s1++; + while(*s2=='0') s2++; + while(*s1==*s2 && isdigi(*(unsigned char*)s1)) + { s1++; s2++; } + if(*s1=='.') { + if(*s2=='.') { + do { s1++; s2++; } + while(*s1==*s2 && isdigi(*(unsigned char*)s1)); + if(!isdigi(*(unsigned char*)s1)) { + while(*s2=='0') s2++; + return isdigi(*(unsigned char*)s2); + } + if(!isdigi(*(unsigned char*)s2)) { + while(*s1=='0') s1++; + return isdigi(*(unsigned char*)s1); + } + return isdigi(*(unsigned char*)s1) || + isdigi(*(unsigned char*)s2); + } + do s1++; + while(*s1=='0'); + return isdigi(*(unsigned char*)s1); + } + if(*s2=='.') { + do s2++; + while(*s2=='0'); + return isdigi(*(unsigned char*)s2); + } + return isdigi(*(unsigned char*)s1) || isdigi(*(unsigned char*)s2); + case 12: /* '<', numeric */ + #define Ds1 s1 + #define Ds2 s2 + s1v= *(unsigned char*)Ds1; s2v= *(unsigned char*)Ds2; + if(s1v=='-') { + if(s2v=='-') { + Ds1++; s2v= *(unsigned char*)Ds1; + Ds2++; s1v= *(unsigned char*)Ds2; + goto op_14; + } + return true; + } + else if(s2v=='-') + return false; + op_12: + while(s1v=='0') { Ds1++; s1v= *(unsigned char*)Ds1; } + while(s2v=='0') { Ds2++; s2v= *(unsigned char*)Ds2; } + while(s1v==s2v && isdigi(s1v)) { + Ds1++; s1v= *(unsigned char*)Ds1; + Ds2++; s2v= *(unsigned char*)Ds2; + } + diff= digival(s1v)-digival(s2v); + while(isdigi(s1v) && isdigi(s2v)) { + Ds1++; s1v= *(unsigned char*)Ds1; + Ds2++; s2v= *(unsigned char*)Ds2; + } + if(s1v=='.') { + if(s2v=='.') { + if(diff!=0) + return diff<0; + do { + Ds1++; s1v= *(unsigned char*)Ds1; + Ds2++; s2v= *(unsigned char*)Ds2; + } while(s1v==s2v && isdigi(s1v)); + while(s2v=='0') { Ds2++; s2v= *(unsigned char*)Ds2; } + return digival(s1v) < digival(s2v); + } + return isdigi(s2v) || diff<0; + } + if(s2v=='.') { + if(isdigi(s1v)) + return false; + if(diff!=0) + return diff<0; + do { Ds2++; s2v= *(unsigned char*)Ds2; } while(s2v=='0'); + return isdigi(s2v); + } + return isdigi(s2v) || (!isdigi(s1v) && diff<0); + #undef Ds1 + #undef Ds2 + case 13: /* '>=', numeric */ + #define Ds1 s1 + #define Ds2 s2 + s1v= *(unsigned char*)Ds1; s2v= *(unsigned char*)Ds2; + if(s1v=='-') { + if(s2v=='-') { + Ds1++; s2v= *(unsigned char*)Ds1; + Ds2++; s1v= *(unsigned char*)Ds2; + goto op_15; + } + return false; + } + else if(s2v=='-') + return true; + op_13: + while(s1v=='0') { Ds1++; s1v= *(unsigned char*)Ds1; } + while(s2v=='0') { Ds2++; s2v= *(unsigned char*)Ds2; } + while(s1v==s2v && isdigi(s1v)) { + Ds1++; s1v= *(unsigned char*)Ds1; + Ds2++; s2v= *(unsigned char*)Ds2; + } + diff= digival(s1v)-digival(s2v); + while(isdigi(s1v) && isdigi(s2v)) { + Ds1++; s1v= *(unsigned char*)Ds1; + Ds2++; s2v= *(unsigned char*)Ds2; + } + if(s1v=='.') { + if(s2v=='.') { + if(diff!=0) + return diff>=0; + do { + Ds1++; s1v= *(unsigned char*)Ds1; + Ds2++; s2v= *(unsigned char*)Ds2; + } while(s1v==s2v && isdigi(s1v)); + while(s2v=='0') { Ds2++; s2v= *(unsigned char*)Ds2; } + return digival(s1v) >= digival(s2v); + } + return !isdigi(s2v) && diff>=0; + } + if(s2v=='.') { + if(isdigi(s1v)) + return true; + if(diff!=0) + return diff>=0; + do { Ds2++; s2v= *(unsigned char*)Ds2; } while(s2v=='0'); + return !isdigi(s2v); + } + return !isdigi(s2v) && (isdigi(s1v) || diff>=0); + #undef Ds1 + #undef Ds2 + case 14: /* '>', numeric */ + #define Ds1 s2 + #define Ds2 s1 + s1v= *(unsigned char*)Ds1; s2v= *(unsigned char*)Ds2; + if(s1v=='-') { + if(s2v=='-') { + Ds1++; s2v= *(unsigned char*)Ds1; + Ds2++; s1v= *(unsigned char*)Ds2; + goto op_12; + } + return true; + } + else if(s2v=='-') + return false; + op_14: + while(s1v=='0') { Ds1++; s1v= *(unsigned char*)Ds1; } + while(s2v=='0') { Ds2++; s2v= *(unsigned char*)Ds2; } + while(s1v==s2v && isdigi(s1v)) { + Ds1++; s1v= *(unsigned char*)Ds1; + Ds2++; s2v= *(unsigned char*)Ds2; + } + diff= digival(s1v)-digival(s2v); + while(isdigi(s1v) && isdigi(s2v)) { + Ds1++; s1v= *(unsigned char*)Ds1; + Ds2++; s2v= *(unsigned char*)Ds2; + } + if(s1v=='.') { + if(s2v=='.') { + if(diff!=0) + return diff<0; + do { + Ds1++; s1v= *(unsigned char*)Ds1; + Ds2++; s2v= *(unsigned char*)Ds2; + } while(s1v==s2v && isdigi(s1v)); + while(s2v=='0') { Ds2++; s2v= *(unsigned char*)Ds2; } + return digival(s1v) < digival(s2v); + } + return isdigi(s2v) || diff<0; + } + if(s2v=='.') { + if(isdigi(s1v)) + return false; + if(diff!=0) + return diff<0; + do { Ds2++; s2v= *(unsigned char*)Ds2; } while(s2v=='0'); + return isdigi(s2v); + } + return isdigi(s2v) || (!isdigi(s1v) && diff<0); + #undef Ds1 + #undef Ds2 + case 15: /* '<=', numeric */ + #define Ds1 s2 + #define Ds2 s1 + s1v= *(unsigned char*)Ds1; s2v= *(unsigned char*)Ds2; + if(s1v=='-') { + if(s2v=='-') { + Ds1++; s2v= *(unsigned char*)Ds1; + Ds2++; s1v= *(unsigned char*)Ds2; + goto op_13; + } + return false; + } + else if(s2v=='-') + return true; + op_15: + while(s1v=='0') { Ds1++; s1v= *(unsigned char*)Ds1; } + while(s2v=='0') { Ds2++; s2v= *(unsigned char*)Ds2; } + while(s1v==s2v && isdigi(s1v)) { + Ds1++; s1v= *(unsigned char*)Ds1; + Ds2++; s2v= *(unsigned char*)Ds2; + } + diff= digival(s1v)-digival(s2v); + while(isdigi(s1v) && isdigi(s2v)) { + Ds1++; s1v= *(unsigned char*)Ds1; + Ds2++; s2v= *(unsigned char*)Ds2; + } + if(s1v=='.') { + if(s2v=='.') { + if(diff!=0) + return diff>=0; + do { + Ds1++; s1v= *(unsigned char*)Ds1; + Ds2++; s2v= *(unsigned char*)Ds2; + } while(s1v==s2v && isdigi(s1v)); + while(s2v=='0') { Ds2++; s2v= *(unsigned char*)Ds2; } + return digival(s1v) >= digival(s2v); + } + return !isdigi(s2v) && diff>=0; + } + if(s2v=='.') { + if(isdigi(s1v)) + return true; + if(diff!=0) + return diff>=0; + do { Ds2++; s2v= *(unsigned char*)Ds2; } while(s2v=='0'); + return !isdigi(s2v); + } + return !isdigi(s2v) && (isdigi(s1v) || diff>=0); + #undef Ds1 + #undef Ds2 + // (no default) + } // depending on comparison operator + return false; // (we never get here) + } // end modi__cmp() + +#define modi__pairM 1000 // maximum number of key-val-pairs +#define modi__pairkM 100 // maximum length of key or val; +#define modi__pairtM 3 // maximum number of modification types; + // these modification types are defined as follows: + // 0: modify node tag; + // 1: modify way tag; + // 2: modify relation tag; +struct modi__pair_struct { + // key/val pair for the include filter + char k[modi__pairkM+8]; // key to compare; + // [0]==0 && [1]==0: same key as previous key in list; + char v[modi__pairkM+8]; // value to the key in .k[]; + // the first byte represents a comparison operator, + // see parameter s2[]in modi__cmp() for details; + // [0]==0 && [1]==0: any value will be accepted; + char nk[modi__pairkM+2]; // new key + char nv[modi__pairkM+2]; // new value + bool add; // new key/val pair shall be added instead of replacing + // the old key/val pair + } __attribute__((__packed__)); +typedef struct modi__pair_struct modi__pair_t; +static modi__pair_t modi__pair[modi__pairtM][modi__pairM+2]= + {{{{0},{0},{0},{0}}}}; +static modi__pair_t* modi__paire[modi__pairtM]= + { &modi__pair[0][0],&modi__pair[1][0],&modi__pair[2][0] }; +static modi__pair_t* modi__pairee[modi__pairtM]= + { &modi__pair[0][modi__pairM],&modi__pair[1][modi__pairM], + &modi__pair[2][modi__pairM] }; + +//------------------------------------------------------------ + +static inline void modi_cpy(char *dest, const char *src, + size_t len,int op) { + // similar as strmpy(), but remove every initial '\\' character; + // len: length of the source string - without terminating zero; + // op: comparison operator; + // 2: '=' + // 4: '<' + // 5: '>=' + // 6: '>' + // 7: '<=' + // return: dest[0]: comparison operator; additional possible values: + // 0: '=', and there are wildcards coded in dest[1]: + // dest[1]==1: wildcard at start; + // dest[1]==2: wildcard at end; + // dest[1]==3: wildcard at both, start and end; + // 1: '!=', and there are wildcards coded in dest[1]; + // 10: '=', numeric + // 11: '!=', numeric + // 12: '<', numeric + // 13: '>=', numeric + // 14: '>', numeric + // 15: '<=', numeric + int wc; // wildcard indicator, see modi__cmp() + + if(op<0) { // unknown operator + WARNv("unknown comparison at: %.80s",src) + op= 2; // assume '=' + } + if(len>(modi__pairkM)) { + len= modi__pairkM; // delimit value length + WARNv("modification argument too long: %.*s",modi__pairkM,src) + } + wc= 0; // (default) + if(len>=2 && src[0]=='*') { // wildcard at start + wc|= 1; + src++; len--; + } + if((len>=2 && src[len-1]=='*' && src[len-2]!='\\') || + (len==1 && src[len-1]=='*')) { + // wildcard at end + wc|= 2; + len--; + } + if(wc==0) { // no wildcard(s) + const char* v; + + v= src; + if(*v=='-') v++; // jump over sign + if(isdig(*v)) // numeric value + op+= 8; + dest[0]= op; + modi__stresccpy(dest+1,src,len); // store this value + } // no wildcard(s) + else { // wildcard(s) + dest[0]= op&1; + dest[1]= wc; + modi__stresccpy(dest+2,src,len); // store this value + } // wildcard(s) + } // end modi_cpy() + +static bool modi_active= false; + // there is at least one modify criteria active; + // may be read by everyone but written only by this module; +static bool modi_activetype[modi__pairtM]= {false,false,false}; + // the related modify list has at least one element; + // may be read by everyone but written only by this module; - result= false; - v= NULL; // (default) - valp= &v; // (default) - bracket_balance= 0; - fp= fil__pair[3+otype]; fe= fil__paire[3+otype]; - while(fp<fe) { // for every key/val pair in filter - bracket_balance+= fp->left_bracketn; - if(*(int16_t*)(fp->k)==0) { - if(v!=NULL) - result= fil__cmp(v,fp->v); +static void modi_ini() { + // initialize this mudule; + int i; + + modi_active= false; + for(i= 0; i<modi__pairtM; i++) { + modi__paire[i]= &modi__pair[i][0]; + modi__pairee[i]= &modi__pair[i][modi__pairM]; + modi_activetype[i]= false; + } + } // modi_ini() + +static void modi_parse(int ftype,const char* arg) { + // interprets a command line argument and stores modification + // information; + // ftype: object type; see explanation at modi__pairtM; + // arg[]: modification information; e.g.: + // "amenity=fire_hydrant to emergency=fire_hydrant" + modi__pair_t*fe,*fee; + const char* pk,*pv,*pe; // pointers in parameter for key/val pairs; + // pk: key; pv: val; pe: end of val; + int len; // string length + int op; // operator, see modi__cmp() + + fe= modi__paire[ftype]; + fee= modi__pairee[ftype]; + if(loglevel>0) + PINFOv("Modify: %s tags:",ONAME(ftype%3)) + pk= arg; + while(*pk==' ') pk++; // jump over spaces + + while(pk!=NULL && fe<fee) { // for every key/val pair + while(*pk==' ') pk++; // jump over (additional) spaces + if(*pk==0) + break; + pe= pk; + while((*pe!=' ' || pe[-1]=='\\') && *pe!=0) pe++; + // get end of this pair + len= pe-pk; // length of this argument + pv= pk; + while(((*pv!='=' && *pv!='<' && *pv!='>' && + (*pv!='!' || pv[1]!='=')) || + (pv>pk && pv[-1]=='\\')) && pv<pe) pv++; + // find operator =, <, >, != + if(pv>=pe-1) pv= pe; // there was no operator in this pair + len= pv-pk; // length of this key + if(len>(modi__pairkM)) { + len= modi__pairkM; // delimit key length + WARNv("modification key too long: %.*s",modi__pairkM,pk) } - else { - result= false; // (default) - keyp= key; valp= val; - while(keyp<keye) { // for all key/val pairs of this object - if(fil__cmp(*keyp,fp->k)) { // right key - v= *valp; - if(*(int16_t*)(fp->k)==0 || fil__cmp(v,fp->v)) { - // right value - result= true; - break; - } - } - keyp++; valp++; - } // for all key/val pairs of this object + op= -1; // 'unknown operator' (default) + if(pv>=pe) { // there is a key but no value + if(len>0 && pk[len-1]=='=') len--; + modi_cpy(fe->k,pk,len,2); // store this key, op='=' + memset(fe->v,0,3); // store empty value } - #if MAXLOGLEVEL>=3 - if(loglevel>=3) - PINFOv("comparison[%i][%i]==%i", - 3+otype,fp-fil__pair[3+otype],result) - #endif - if(result) { // comparison satisfied - if(fp->operator) { // Boolean operator is AND - // (continue with next comparison) - } // Boolean operator is AND - else { // Boolean operator is OR - // at each encountered 'or': - // jump to after next operand at lower layer - bracket_balance-= fp->right_bracketn; - if(bracket_balance<=0) // we already are at lowest level -return result; - bb= bracket_balance; - fp++; - while(fp<fe) { - bracket_balance+= fp->left_bracketn; - bracket_balance-= fp->right_bracketn; - if(bracket_balance>=bb) { // same level or higher - fp++; - continue; - } - if(fp->operator) { // next operator is 'and' - fp++; - break; // go on by evaluating this operator - } - // here: next operator is an 'or' - if(bracket_balance<=0) // we are at lowest level -return result; - bb= bracket_balance; // from now on ignore this level - fp++; - } - v= NULL; // previous value no longer valid - continue; - } // Boolean operator is OR - } // comparison satisfied - else { // comparison not satisfied - if(fp->operator) { // Boolean operator is AND - // jump to after next 'or' within same brackets or - // lower layer, but not into the space between new brackets - bracket_balance-= fp->right_bracketn; - bb= bracket_balance; - fp++; - while(fp<fe) { - bracket_balance+= fp->left_bracketn; - bracket_balance-= fp->right_bracketn; - if(bracket_balance<bb) - bb= bracket_balance; - if(bracket_balance<=bb && !fp->operator) { - // not in a new bracket AND next operator is 'or' - fp++; - break; - } - fp++; - } - v= NULL; // previous value no longer valid - continue; - } // Boolean operator is AND - else { // Boolean operator is OR - // (continue with next comparison) - } // Boolean operator is OR - } // comparison not satisfied - bracket_balance-= fp->right_bracketn; - fp++; - } // for every key/val pair in filter - return result; - } // end fil_check1() - -static inline bool fil_check2(int otype, - const char* key,const char* val) { - // test if filter allows this tag to be kept; - // at this procedure, filters type 6..8 and 9..11 are applied: - // 'keep tag'; + else { // key and value + if(len==0) // no key given + memset(fe->k,0,3); // store empty key, + else + modi_cpy(fe->k,pk,len,2); // store this key, op='=' + if(*pv=='=') op= 2; + else if(*pv=='!' && pv[1]=='=') op= 3; + else if(*pv=='<' && pv[1]!='=') op= 4; + else if(*pv=='>' && pv[1]=='=') op= 5; + else if(*pv=='>' && pv[1]!='=') op= 6; + else if(*pv=='<' && pv[1]=='=') op= 7; + if(op<0) { // unknown operator + WARNv("unknown comparison at: %.80s",pv) + op= 2; // assume '=' + } + pv++; // jump over operator + if(pv<pe && *pv=='=') pv++; + // jump over second character of a two-character operator + len= pe-pv; // length of this value + modi_cpy(fe->v,pv,len,op); // store this value + } // key and value + // jump over ' to ' phrase + while(*pe==' ') pe++; // jump over spaces + if((fe->add= strzcmp(pe,"add ")==0)) pe+= 4; + else if(strzcmp(pe,"to ")==0) pe+= 3; + // get destination key/val + pk= pe; // jump to next key/val pair in parameter list + while(*pk==' ') pk++; // jump over (additional) spaces + pe= pk; + while((*pe!=' ' || pe[-1]=='\\') && *pe!=0) pe++; + // get end of this destination pair + len= pe-pk; // length of this argument + pv= pk; + while((*pv!='=' || (pv>pk && pv[-1]=='\\')) && pv<pe) pv++; + // find operator '=' + if(pv>=pe-1) pv= pe; // there was no operator in this pair + len= pv-pk; // length of this key + if(len>(modi__pairkM)) { + len= modi__pairkM; // delimit key length + WARNv("modification key too long: %.*s",modi__pairkM,pk) + } + if(pv>=pe) { // there is a destination key but no value + if(len>0 && pk[len-1]=='=') len--; + modi__stresccpy(fe->nk,pk,len); // store this key + fe->nv[0]= 0; // store empty value + } + else { // destination key and value + if(len==0) // no key given + modi__stresccpy(fe->nk,fe->k[0]<=1? fe->k+2: fe->k+1, + modi__pairkM); + // store source key as destination key + else + modi__stresccpy(fe->nk,pk,len); // store this key + pv++; // jump over equation operator + if(pv<pe && *pv=='=') pv++; + // jump over second character of a two-character operator + len= pe-pv; // length of this value + if(len==0) // no value given + modi__stresccpy(fe->nv,fe->v[0]<=1? fe->v+2: fe->v+1, + modi__pairkM); + // store source value as destination value + else + modi__stresccpy(fe->nv,pv,len); // store this value + } // destination key and value + if(loglevel>0) { + static const char* ops[]= { "?", + "=","!=","=","!=","<",">=",">","<=", + "?","?","=(numeric)","!=(numeric)", + "<(numeric)",">=(numeric)",">(numeric)","<=(numeric)" }; + PINFOv("Modify: %s\"%.80s\"%s %s %s\"%.80s\"%s", + fe->k[0]<=1 && (fe->k[1] & 1)? "*": "", + *(int16_t*)(fe->k)==0? "(last key)": + fe->k[0]>=2? fe->k+1: fe->k+2, + fe->k[0]<=1 && (fe->k[1] & 2)? "*": "", + ops[fe->v[0]+1], + fe->v[0]<=1 && (fe->v[1] & 1)? "*": "", + *(int16_t*)(fe->v)==0? "(anything)": + fe->v[0]>=2? fe->v+1: fe->v+2, + fe->v[0]<=1 && (fe->v[1] & 2)? "*": ""); + } + fe++; // next pair in key/val table + pk= pe; // jump to next key/val pair in parameter list + } // end for every key/val pair + if(fe>=fee) + WARN("too many modification parameters.") + modi__paire[ftype]= fe; + modi_active= true; + modi_activetype[ftype]= true; + } // end modi_parse() + +static char* modi_check_key= "-",*modi_check_val= "-"; + static bool modi_check_add= false; + // return values of procedure modi_check(); + // the values are valid only if the previous call to modi_check() + // has returned 'true'; + +static inline bool modi_check(int otype,char* key,char* val) { + // check if OSM object matches modification criteria; // otype: 0: node; 1: way; 2: relation; - // return: given key[] and val[] match keep criteria; - fil__pair_t* fp,*fe; - const char* k; // last key in filter - bool keymatch; - - // apply keep-filter - if(fil_active[6+otype]) { - k= "name"; // (default) - keymatch= false; - fp= &fil__pair[6+otype][0]; fe= fil__paire[6+otype]; - while(fp<fe) { - if(*(int16_t*)(fp->k)!=0) k= fp->k; - keymatch= fil__cmp(key,k); - if(keymatch && (*(int16_t*)(fp->v)==0 || fil__cmp(val,fp->v))) - goto keep; - fp++; + // key,val: key and value; + // return: given key/val pair matches modification criteria; + // modi_check_key,modi_check_val: destination key/val; + // modi_check_add: the destination key/val shall be added + // instead of replacing the old key/val pair; + modi__pair_t* fp,*fe; + + fp= modi__pair[otype]; fe= modi__paire[otype]; + while(fp<fe) { // for every key/val pair in filter + if(*(int16_t*)(fp->k)==0) { // no key given + if(modi__cmp(val,fp->v)) // just compare the value + goto modi_check_found; } - if(keymatch || fil_meetall[6+otype]) -return false; - } - keep: - // apply drop-filter - if(fil_active[9+otype]) { - k= "name"; // (default) - fp= &fil__pair[9+otype][0]; fe= fil__paire[9+otype]; - while(fp<fe) { - if(*(int16_t*)(fp->k)!=0) k= fp->k; - if(fil__cmp(key,k) && (*(int16_t*)(fp->v)==0 || - fil__cmp(val,fp->v))) -return false; - fp++; + else { // key given + if(modi__cmp(key,fp->k) && + (*(int16_t*)(fp->k)==0 || modi__cmp(val,fp->v))) + // compare key and value (if any) + goto modi_check_found; } - } + fp++; + } // for every key/val pair in filter + return false; +modi_check_found: + if(fp->nk[0]!=0) // there is a destination key + modi_check_key= fp->nk; // take that destination key + else + modi_check_key= key; + // take source key instead + if(fp->nv[0]!=0) // there is a destination value + modi_check_val= fp->nv; // take that destination value + else + modi_check_val= val; + // take source value instead + modi_check_add= fp->add; // publish key/val add request return true; - } // end fil_check2() + } // end modi_check() + +#define modi_CHECK(ot,k,v) \ + (modi_active && modi_activetype[ot] && modi_check(ot,k,v)) + // prevents procedure call in case there are no modifications applied //------------------------------------------------------------ -// end Module fil_ osm filter module +// end Module modi_ OSM tag modification module //------------------------------------------------------------ @@ -5159,7 +5887,7 @@ static void oo__close() { if(!oo__ifp->endoffile && oo_ifn>0) // missing logical end of file fprintf(stderr,"osmfilter Warning: " "unexpected end of input file: %.80s\n",oo__ifp->filename); - read_close(oo__ifp->ri); + read_close(); //oo__ifp->ri); oo__ifp->ri= NULL; oo_ifn--; } @@ -5652,10 +6380,12 @@ return 18; } // end xml // care about possible array overflows - if(refide>refidee) - WARNv("way %"PRIi64" has too many noderefs.",id) - if(refide>refidee) - WARNv("relation %"PRIi64" has too many refs.",id) + if(refide>=refidee) { + if(otype==1) + WARNv("way %"PRIi64" has too many noderefs.",id) + else + WARNv("relation %"PRIi64" has too many refs.",id) + } if(keye>=keyee) WARNv("%s %"PRIi64" has too many key/val pairs.", ONAME(otype),id) @@ -5779,8 +6509,14 @@ return 18; hisver,histime,hiscset,hisuid,hisuser,lon,lat); keyp= key; valp= val; while(keyp<keye) { // for all key/val pairs of this object - if(!fil_activeo[otype] || fil_check2(otype,*keyp,*valp)) - wo_keyval(*keyp,*valp); + if(!fil_activeo[otype] || fil_check2(otype,*keyp,*valp)) { + if(modi_CHECK(otype,*keyp,*valp)) { + if(modi_check_add) wo_keyval(*keyp,*valp); + wo_keyval(modi_check_key,modi_check_val); + } + else + wo_keyval(*keyp,*valp); + } keyp++; valp++; } } // end not to drop @@ -5793,8 +6529,14 @@ return 18; wo_noderef(*refidp++); keyp= key; valp= val; while(keyp<keye) { // for all key/val pairs of this object - if(!fil_activeo[otype] || fil_check2(otype,*keyp,*valp)) + if(!fil_activeo[otype] || fil_check2(otype,*keyp,*valp)) { + if(modi_CHECK(otype,*keyp,*valp)) { + if(modi_check_add) wo_keyval(*keyp,*valp); + wo_keyval(modi_check_key,modi_check_val); + } + else wo_keyval(*keyp,*valp); + } keyp++; valp++; } } // end not ways to drop @@ -5807,8 +6549,14 @@ return 18; wo_ref(*refidp++,*reftypep++,*refrolep++); keyp= key; valp= val; while(keyp<keye) { // for all key/val pairs of this object - if(!fil_activeo[otype] || fil_check2(otype,*keyp,*valp)) + if(!fil_activeo[otype] || fil_check2(otype,*keyp,*valp)) { + if(modi_CHECK(otype,*keyp,*valp)) { + if(modi_check_add) wo_keyval(*keyp,*valp); + wo_keyval(modi_check_key,modi_check_val); + } + else wo_keyval(*keyp,*valp); + } keyp++; valp++; } } // end not relations to drop @@ -5864,6 +6612,7 @@ int main(int argc,const char** argv) { // initializations h_n= h_w= h_r= 0; fil_ini(); + modi_ini(); #if __WIN32__ setmode(fileno(stdout),O_BINARY); setmode(fileno(stdin),O_BINARY); @@ -6162,6 +6911,17 @@ return 0; D(--drop-way-relation-tags=,F(10)F(11)) #undef D #undef F + #define F(t) modi_parse(t,a+l); + #define D(p,f) if((l= strzlcmp(a,#p))>0) { f continue; } + D(--modify-tags=,F(0)F(1)F(2)) + D(--modify-node-tags=,F(0)) + D(--modify-way-tags=,F(1)) + D(--modify-relation-tags=,F(2)) + D(--modify-node-way-tags=,F(0)F(1)) + D(--modify-node-relation-tags=,F(0)F(2)) + D(--modify-way-relation-tags=,F(1)F(2)) + #undef D + #undef F if(a[0]=='-') { PERRv("unrecognized option: %.80s",a) return 1; @@ -6257,3 +7017,4 @@ return 5; } // verbose mode return r; } // end main() + diff --git a/src/osmupdate.c b/src/osmupdate.c index d27ec84..5a6e737 100644 --- a/src/osmupdate.c +++ b/src/osmupdate.c @@ -1,10 +1,10 @@ -// osmupdate 2015-04-15 10:00 -#define VERSION "0.4.1" +// osmupdate 2017-02-26 16:40 +#define VERSION "0.4.4" // // compile this file: // gcc osmupdate.c -o osmupdate // -// (c) 2011..2015 Markus Weber, Nuernberg +// (c) 2011..2017 Markus Weber, Nuernberg // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU Affero General Public License @@ -131,6 +131,14 @@ const char* helptext= " change file sources (option --base-url). This would cause\n" " severe data corruption.\n" "\n" +"--trust-tempfiles\n" +" Use this option if you want to use the saved local copies\n" +" of already downloaded changefiles without checking their\n" +" lengths against to their server-hosted originals.\n" +" Downloads will be limited to files not saved yet.\n" +" Do not invoke this option if you suspect incomplete\n" +" downloads.\n" +"\n" "--compression-level=LEVEL\n" " Define level for gzip compression. Values between 1 (low\n" " compression, but fast) and 9 (high compression, but slow).\n" @@ -160,7 +168,6 @@ const char* helptext= "Please send any bug reports to markus.we...@gmx.com\n\n"; #define _FILE_OFFSET_BITS 64 -#include <zlib.h> #include <inttypes.h> #include <stdlib.h> #include <string.h> @@ -498,6 +505,8 @@ static char global_tempfile_name[450]= ""; // prefix of names for temporary files static bool global_keep_tempfiles= false; // temporary files shall not be deleted at program end +static bool global_trust_tempfiles= false; + // Cached files are considered to be intact static char global_osmconvert_arguments[2000]= ""; // general command line arguments for osmconvert; #define max_number_of_changefiles_in_cache 100 @@ -917,58 +926,70 @@ static void process_changefile( // assemble the URL and download the changefile old_file_length= file_length(this_cachefile_name); - if(loglevel>0 && old_file_length<10) - // verbose mode AND file not downloaded yet - PINFOv("%s changefile %i: downloading", - CFTNAME(changefile_type),file_sequence_number) - command_p= command; - stecpy(&command_p,command_e,"wget -nv -c "); - stecpy(&command_p,command_e,global_base_url); - switch(changefile_type) { // changefile type - case cft_MINUTELY: - stecpy(&command_p,command_e,"/minute"); - break; - case cft_HOURLY: - stecpy(&command_p,command_e,"/hour"); - break; - case cft_DAILY: - stecpy(&command_p,command_e,"/day"); - break; - case cft_SPORADIC: - break; - default: // invalid change file type - return; - } // changefile type - stecpy(&command_p,command_e,global_base_url_suffix); - stecpy(&command_p,command_e,"/"); - - /* process sequence number */ { - int l; - l= sprintf(command_p,"%03i/%03i/%03i.osc.gz", - file_sequence_number/1000000,file_sequence_number/1000%1000, - file_sequence_number%1000); - command_p+= l; - } // process sequence number - - stecpy(&command_p,command_e," -O \""); - steesccpy(&command_p,command_e,this_cachefile_name); - stecpy(&command_p,command_e,"\" 2>&1 && echo \"Wget Command Ok\""); - shell_command(command,result); - if(strstr(result,"Wget Command Ok")==NULL) { // download error - PERRv("Could not download %s changefile %i", - CFTNAME(changefile_type),file_sequence_number) - PINFOv("wget Error message:\n%s",result) -exit(1); - } - if(loglevel>0 && old_file_length>=10) { - // verbose mode AND file was already in cache - if(file_length(this_cachefile_name)!=old_file_length) - PINFOv("%s changefile %i: download completed", + if(global_trust_tempfiles && old_file_length>=10) { + // trusted file already in cache + if(loglevel>0) // verbose mode + PINFOv("%s changefile %i: trusting local copy", CFTNAME(changefile_type),file_sequence_number) - else - PINFOv("%s changefile %i: already in cache", + } // trusted file already in cache + else { // file not in cache or not trusted + if(loglevel>0) { // verbose mode + if(old_file_length<10) // file not downloaded yet + PINFOv("%s changefile %i: downloading", + CFTNAME(changefile_type),file_sequence_number) + else // file had been downloaded at least partially + PINFOv("%s changefile %i: checking", + CFTNAME(changefile_type),file_sequence_number) + } // verbose mode + command_p= command; + stecpy(&command_p,command_e,"wget -nv -c "); + stecpy(&command_p,command_e,global_base_url); + switch(changefile_type) { // changefile type + case cft_MINUTELY: + stecpy(&command_p,command_e,"/minute"); + break; + case cft_HOURLY: + stecpy(&command_p,command_e,"/hour"); + break; + case cft_DAILY: + stecpy(&command_p,command_e,"/day"); + break; + case cft_SPORADIC: + break; + default: // invalid change file type + return; + } // changefile type + stecpy(&command_p,command_e,global_base_url_suffix); + stecpy(&command_p,command_e,"/"); + + /* process sequence number */ { + int l; + l= sprintf(command_p,"%03i/%03i/%03i.osc.gz", + file_sequence_number/1000000,file_sequence_number/1000%1000, + file_sequence_number%1000); + command_p+= l; + } // process sequence number + + stecpy(&command_p,command_e," -O \""); + steesccpy(&command_p,command_e,this_cachefile_name); + stecpy(&command_p,command_e,"\" 2>&1 && echo \"Wget Command Ok\""); + shell_command(command,result); + if(strstr(result,"Wget Command Ok")==NULL) { // download error + PERRv("Could not download %s changefile %i", CFTNAME(changefile_type),file_sequence_number) - } // verbose mode + PINFOv("wget Error message:\n%s",result) +exit(1); + } + if(loglevel>0 && old_file_length>=10) { + // verbose mode AND file was already in cache + if(file_length(this_cachefile_name)!=old_file_length) + PINFOv("%s changefile %i: download completed", + CFTNAME(changefile_type),file_sequence_number) + else + PINFOv("%s changefile %i: already in cache", + CFTNAME(changefile_type),file_sequence_number) + } // verbose mode + } // file not in cache or not trusted number_of_changefiles_in_cache++; } // changefile download requested @@ -1007,7 +1028,9 @@ exit(1); shell_command(command,result); if(file_length(master_cachefile_name_temp)<10 || strstr(result,"Error")!=NULL || - strstr(result,"error")!=NULL) { // merging failed + strstr(result,"error")!=NULL || + strstr(result,"Warning")!=NULL || + strstr(result,"warning")!=NULL) { // merging failed PERRv("Merging of changefiles failed:\n%s",command) if(result[0]!=0) PERRv("%s",result) @@ -1015,7 +1038,7 @@ exit(1); } // merging failed unlink(master_cachefile_name); rename(master_cachefile_name_temp,master_cachefile_name); - } // at lease one change files must be merged + } // at least one change file must be merged } // process_changefile() #if !__WIN32__ @@ -1105,7 +1128,7 @@ int main(int argc,const char** argv) { // read command line parameters if(argc<=1) { // no command line parameters given fprintf(stderr,"osmupdate " VERSION "\n" - "Updates .osm and .o5m files, downloads .osc and o5c files.\n" + "Updates .osm, .o5m, .pbf files, downloads .osc, .o5c files.\n" "To get detailed help, please enter: ./osmupdate -h\n"); return 0; // end the program, because without having parameters // we do not know what to do; @@ -1146,7 +1169,7 @@ return 0; } if((strzcmp(a,"-t=")==0 || strzcmp(a,"--tempfiles=")==0) && global_tempfile_name[0]==0) { - // user-defined prefix for names of temorary files + // user-defined prefix for names of temporary files strmcpy(global_tempfile_name,strchr(a,'=')+1, sizeof(global_tempfile_name)-50); continue; // take next parameter @@ -1156,6 +1179,11 @@ return 0; global_keep_tempfiles= true; continue; // take next parameter } + if(strzcmp(a,"--trust-tempfiles")==0) { + // cached files are considered to be intact + global_trust_tempfiles= true; + continue; // take next parameter + } if(strzcmp(a,"--compression-level=")==0) { // gzip compression level static char gzip_par[3]= ""; @@ -1364,7 +1392,7 @@ return 1; // care about user defined processing categories if(process_minutely || process_hourly || process_daily || process_sporadic) { - // user wants specific type(s) of chancefiles to be processed + // user wants specific type(s) of changefiles to be processed if(!process_minutely) no_minutely= true; if(!process_hourly) no_hourly= true; if(!process_daily) no_daily= true; @@ -1646,4 +1674,5 @@ return 21; PINFO("Completed successfully.") return main_return_value; - } // end main() \ No newline at end of file + } // end main() + -- Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-grass/osmctools.git _______________________________________________ Pkg-grass-devel mailing list Pkg-grass-devel@lists.alioth.debian.org http://lists.alioth.debian.org/cgi-bin/mailman/listinfo/pkg-grass-devel