[osmctools] 01/09: Imported Upstream version 0.7

Bas Couwenberg sebastic at debian.org
Fri Mar 31 09:34:49 UTC 2017


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 <sebastic at 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.weber at 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



More information about the Pkg-grass-devel mailing list