Package: release.debian.org
Severity: normal
X-Debbugs-Cc: rout...@packages.debian.org
Control: affects -1 + src:routino
User: release.debian....@packages.debian.org
Usertags: unblock

Please unblock package routino

[ Reason ]
The new upstream release fixes a crash which we'd like to get into trixie.

Because of the soft freeze the package was intended to stay in experimental 
until after the release, but users of the package who experienced these crashes 
have contacted me privately to ask about getting the fix in trixie.

[ Impact ]
Using routino directly or via QMapShack to process OSM data will result in 
crashes.

[ Tests ]
User tests have reported the crashes to not occur with 3.4.3.

[ Risks ]
Low risk, the new release only addresses this crash. The diff is a little 
cluttered by the inclusion of the #nodesx.c# junk file.

[ Checklist ]
  [x] all changes are documented in the d/changelog
  [x] I reviewed all changes and I approve them
  [x] attach debdiff against the package in testing

[ Other info ]
N/A

unblock routino/3.4.3-1

Kind Regards,

Bas
diff -Nru routino-3.4.2/ChangeLog routino-3.4.3/ChangeLog
--- routino-3.4.2/ChangeLog     2025-03-29 11:47:31.000000000 +0100
+++ routino-3.4.3/ChangeLog     2025-04-26 11:39:06.000000000 +0200
@@ -1,3 +1,62 @@
+2025-04-26  Andrew M. Bishop <amb>
+
+       Version 3.4.3 released.
+
+2025-04-26 [r2207]  Andrew M. Bishop <amb>
+
+       * doc/html/readme.html: Fix typo in release notes.
+
+2025-04-26 [r2205]  Andrew M. Bishop <amb>
+
+       * FILES, doc/NEWS.txt, doc/README.txt, doc/html/readme.html,
+         src/version.h: Update to version 3.4.3.
+
+2025-04-09 [r2203]  Andrew M. Bishop <amb>
+
+       * src/files.c, src/files.h, src/sorting.c: Merge the "filebuffer"
+         and "openedfile" data structures. Add a new function for deleting
+         an open buffered file. Improve the new logassert messages to
+         print the filename.
+
+2025-04-08 [r2202]  Andrew M. Bishop <amb>
+
+       * src/files.c: Allocate the string for the filename of the mapped
+         files. Don't allocate a filebuffer structure for mapped files.
+
+2025-04-06 [r2201]  Andrew M. Bishop <amb>
+
+       * src/files.c: Print an error message and exit if read/write fails.
+
+2025-04-06 [r2200]  Andrew M. Bishop <amb>
+
+       * extras/find-fixme/fixme-finder.c, src/files.c, src/logerror.c,
+         src/planetsplitter.c, src/router.c, src/uncompress.c,
+         src/xml/xsd-to-xmlparser.c: Check and update all exits from the
+         program to make sure that there is consistency in the error
+         messages. Use logassert if it's an internal bug, use
+         fprintf();exit() if it's a user error or program failure to
+         route.
+
+2025-04-06 [r2199]  Andrew M. Bishop <amb>
+
+       * src/nodesx.c, src/relationsx.c, src/waysx.c: Revert part of r2189
+         and add extra checks on the sizes of node_t, way_t and relation_t
+         with respect to index_t.
+
+2025-04-05 [r2198]  Andrew M. Bishop <amb>
+
+       * src/logging.c, src/logging.h: Create a logassert function that
+         allows a format string (via a pre-processor hack).
+
+2025-04-05 [r2197]  Andrew M. Bishop <amb>
+
+       * src/version.h: Update the version number to make clear it is
+         modified from the release version.
+
+2025-03-29  Andrew M. Bishop <amb>
+
+       Version 3.4.2 released.
+
 2025-03-29 [r2193]  Andrew M. Bishop <amb>
 
        * FILES, doc/NEWS.txt, doc/README.txt, doc/html/readme.html,
diff -Nru routino-3.4.2/debian/changelog routino-3.4.3/debian/changelog
--- routino-3.4.2/debian/changelog      2025-03-30 07:46:18.000000000 +0200
+++ routino-3.4.3/debian/changelog      2025-04-28 05:43:31.000000000 +0200
@@ -1,3 +1,15 @@
+routino (3.4.3-1) unstable; urgency=medium
+
+  * Move from experimental to unstable.
+
+ -- Bas Couwenberg <sebas...@debian.org>  Mon, 28 Apr 2025 05:43:31 +0200
+
+routino (3.4.3-1~exp1) experimental; urgency=medium
+
+  * New upstream release.
+
+ -- Bas Couwenberg <sebas...@debian.org>  Sun, 27 Apr 2025 06:35:37 +0200
+
 routino (3.4.2-1) unstable; urgency=medium
 
   * New upstream release.
diff -Nru routino-3.4.2/doc/html/readme.html routino-3.4.3/doc/html/readme.html
--- routino-3.4.2/doc/html/readme.html  2025-03-24 19:21:17.000000000 +0100
+++ routino-3.4.3/doc/html/readme.html  2025-04-26 11:35:03.000000000 +0200
@@ -182,6 +182,8 @@
 Version 3.4.1 of Routino was released on 1st July 2023.
 <br>
 Version 3.4.2 of Routino was released on 29th March 2025.
+<br>
+Version 3.4.3 of Routino was released on 26th April 2025.
 
 <p>
 
@@ -247,7 +249,19 @@
 <b>Note:</b> This version is compatible with databases from versions 2.7.1 - 
3.4.1.
 
 
-<h3 id="H_1_5_4" title="Other Versions">Other Versions</h3>
+<h3 id="H_1_5_4" title="Changes 3.4.3">Changes in Version 3.4.3</h3>
+
+<dl>
+  <dt>Bug fixes:
+    <dd>Fix bug that would crash on some turn relations (partial undo of 
v3.4.2).
+</dl>
+
+<p>
+<b>Note:</b> This version is compatible with databases from versions 2.7.1 - 
3.4.2.
+
+
+
+<h3 id="H_1_5_5" title="Other Versions">Other Versions</h3>
 
 There is a version of Routino (in subversion, on the branch called
 "destination-access") that allows the first and last waypoint of a
diff -Nru routino-3.4.2/doc/NEWS.txt routino-3.4.3/doc/NEWS.txt
--- routino-3.4.2/doc/NEWS.txt  2025-03-24 19:20:54.000000000 +0100
+++ routino-3.4.3/doc/NEWS.txt  2025-04-25 19:23:49.000000000 +0200
@@ -1,3 +1,13 @@
+Version 3.4.3 of Routino released : Sat Apr 26 2025
+---------------------------------------------------
+
+Bug fixes:
+  Fix bug that would crash on some turn relations (partial undo of v3.4.2).
+
+
+Note: This version is compatible with databases from versions 2.7.1 - 3.4.2.
+
+
 Version 3.4.2 of Routino released : Sat Mar 29 2025
 ---------------------------------------------------
 
diff -Nru routino-3.4.2/doc/README.txt routino-3.4.3/doc/README.txt
--- routino-3.4.2/doc/README.txt        2025-03-24 19:21:09.000000000 +0100
+++ routino-3.4.3/doc/README.txt        2025-04-25 19:23:41.000000000 +0200
@@ -115,6 +115,7 @@
    Version 3.4 of Routino was released on 11th June 2023.
    Version 3.4.1 of Routino was released on 1st July 2023.
    Version 3.4.2 of Routino was released on 29th March 2025.
+   Version 3.4.3 of Routino was released on 26th April 2025.
 
    The full version history is available in the NEWS.txt file.
 
diff -Nru routino-3.4.2/extras/find-fixme/fixme-finder.c 
routino-3.4.3/extras/find-fixme/fixme-finder.c
--- routino-3.4.2/extras/find-fixme/fixme-finder.c      2023-06-06 
19:59:09.000000000 +0200
+++ routino-3.4.3/extras/find-fixme/fixme-finder.c      2025-04-05 
17:42:11.000000000 +0200
@@ -3,7 +3,7 @@
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2008-2015, 2023 Andrew M. Bishop
+ This file Copyright 2008-2015, 2023, 2025 Andrew M. Bishop
 
  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU Affero General Public License as published by
@@ -211,17 +211,17 @@
     if((p=strstr(filename,".pbf")) && !strcmp(p,".pbf"))
       {
        if(ParsePBFFile(fd,OSMNodes,OSMWays,OSMRelations))
-          exit(EXIT_FAILURE);
+          exit(EXIT_FAILURE); /* no message because one printed in parser 
function */
       }
     else if((p=strstr(filename,".o5m")) && !strcmp(p,".o5m"))
       {
        if(ParseO5MFile(fd,OSMNodes,OSMWays,OSMRelations))
-          exit(EXIT_FAILURE);
+          exit(EXIT_FAILURE); /* no message because one printed in parser 
function */
       }
     else
       {
        if(ParseOSMFile(fd,OSMNodes,OSMWays,OSMRelations))
-          exit(EXIT_FAILURE);
+          exit(EXIT_FAILURE); /* no message because one printed in parser 
function */
       }
 
     CloseFile(fd);
diff -Nru routino-3.4.2/NEWS.txt routino-3.4.3/NEWS.txt
--- routino-3.4.2/NEWS.txt      2025-03-24 19:20:54.000000000 +0100
+++ routino-3.4.3/NEWS.txt      2025-04-25 19:23:49.000000000 +0200
@@ -1,3 +1,13 @@
+Version 3.4.3 of Routino released : Sat Apr 26 2025
+---------------------------------------------------
+
+Bug fixes:
+  Fix bug that would crash on some turn relations (partial undo of v3.4.2).
+
+
+Note: This version is compatible with databases from versions 2.7.1 - 3.4.2.
+
+
 Version 3.4.2 of Routino released : Sat Mar 29 2025
 ---------------------------------------------------
 
diff -Nru routino-3.4.2/README.txt routino-3.4.3/README.txt
--- routino-3.4.2/README.txt    2025-03-24 19:21:09.000000000 +0100
+++ routino-3.4.3/README.txt    2025-04-25 19:23:41.000000000 +0200
@@ -115,6 +115,7 @@
    Version 3.4 of Routino was released on 11th June 2023.
    Version 3.4.1 of Routino was released on 1st July 2023.
    Version 3.4.2 of Routino was released on 29th March 2025.
+   Version 3.4.3 of Routino was released on 26th April 2025.
 
    The full version history is available in the NEWS.txt file.
 
diff -Nru routino-3.4.2/src/files.c routino-3.4.3/src/files.c
--- routino-3.4.2/src/files.c   2024-08-29 19:34:20.000000000 +0200
+++ routino-3.4.3/src/files.c   2025-04-08 19:53:49.000000000 +0200
@@ -3,7 +3,7 @@
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2008-2015, 2024 Andrew M. Bishop
+ This file Copyright 2008-2015, 2024, 2025 Andrew M. Bishop
 
  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU Affero General Public License as published by
@@ -63,10 +63,10 @@
 /*+ A structure to contain the list of memory mapped files. +*/
 struct mmapinfo
 {
- const char  *filename;         /*+ The name of the file (the index of the 
list). +*/
-       int    fd;               /*+ The file descriptor used when it was 
opened. +*/
-       char  *address;          /*+ The address the file was mapped to. +*/
-       size_t length;           /*+ The length of the file. +*/
+ char  *filename;         /*+ The name of the file (the index of the list). +*/
+ int    fd;               /*+ The file descriptor used when it was opened. +*/
+ char  *address;          /*+ The address the file was mapped to. +*/
+ size_t length;           /*+ The length of the file. +*/
 };
 
 /*+ The list of memory mapped files. +*/
@@ -81,10 +81,14 @@
 /*+ A structure to contain the list of file buffers. +*/
 struct filebuffer
 {
- char   buffer[BUFFLEN];        /*+ The data buffer. +*/
- size_t pointer;                /*+ The read/write pointer for the file 
buffer. +*/
- size_t length;                 /*+ The read pointer for the file buffer. +*/
- int    reading;                /*+ A flag to indicate if the file is for 
reading. +*/
+ char   buffer[BUFFLEN];   /*+ The data buffer. +*/
+ size_t pointer;           /*+ The read/write pointer for the file buffer. +*/
+ size_t length;            /*+ The read pointer for the file buffer. +*/
+ int    reading;           /*+ A flag to indicate if the file is for reading. 
+*/
+ char  *filename;          /*+ The name of the file. +*/
+#if defined(_MSC_VER) || defined(__MINGW32__)
+ int    delete;            /*+ Set to non-zero value if the file is to be 
deleted when closed. +*/
+#endif
 };
 
 /*+ The list of file buffers. +*/
@@ -94,33 +98,9 @@
 static int nfilebuffers=0;
 
 
-#if defined(_MSC_VER) || defined(__MINGW32__)
-
-/*+ A structure to contain the list of opened files to record which are to be 
deleted when closed. +*/
-struct openedfile
-{
- const char *filename;          /*+ The name of the file. +*/
-       int   delete;            /*+ Set to non-zero value if the file is to be 
deleted when closed. +*/
-};
-
-/*+ The list of opened files. +*/
-static struct openedfile **openedfiles=NULL;
-
-/*+ The number of allocated opened file buffer pointers. +*/
-static int nopenedfiles=0;
-
-#endif
-
-
 /* Local functions */
 
-static void CreateFileBuffer(int fd,int read_write);
-
-#if defined(_MSC_VER) || defined(__MINGW32__)
-
-static void CreateOpenedFile(int fd,const char *filename);
-
-#endif
+static void CreateFileBuffer(int fd,int read_write,const char *filename);
 
 
 /*++++++++++++++++++++++++++++++++++++++
@@ -173,8 +153,7 @@
 #ifdef LIBROUTINO
     return(NULL);
 #else
-    fprintf(stderr,"Cannot open file '%s' for reading 
[%s].\n",filename,strerror(errno));
-    exit(EXIT_FAILURE);
+    logassert_format(0,("Cannot open file '%s' for reading 
[%s].",filename,strerror(errno)));
 #endif
    }
 
@@ -185,8 +164,7 @@
 #ifdef LIBROUTINO
     return(NULL);
 #else
-    fprintf(stderr,"Cannot stat file '%s' [%s].\n",filename,strerror(errno));
-    exit(EXIT_FAILURE);
+    logassert_format(0,("Cannot stat file '%s' 
[%s].",filename,strerror(errno)));
 #endif
    }
 
@@ -203,8 +181,7 @@
 #ifdef LIBROUTINO
     return(NULL);
 #else
-    fprintf(stderr,"Cannot mmap file '%s' for reading 
[%s].\n",filename,strerror(errno));
-    exit(EXIT_FAILURE);
+    logassert_format(0,("Cannot mmap file '%s' for reading 
[%s].",filename,strerror(errno)));
 #endif
    }
 
@@ -216,7 +193,7 @@
 
  mappedfiles=(struct 
mmapinfo*)realloc((void*)mappedfiles,(nmappedfiles+1)*sizeof(struct mmapinfo));
 
- mappedfiles[nmappedfiles].filename=filename;
+ 
mappedfiles[nmappedfiles].filename=strcpy(malloc(strlen(filename)+1),filename);
  mappedfiles[nmappedfiles].fd=fd;
  mappedfiles[nmappedfiles].address=address;
  mappedfiles[nmappedfiles].length=size;
@@ -255,8 +232,7 @@
 #ifdef LIBROUTINO
     return(NULL);
 #else
-    fprintf(stderr,"Cannot open file '%s' for reading and writing 
[%s].\n",filename,strerror(errno));
-    exit(EXIT_FAILURE);
+    logassert_format(0,("Cannot open file '%s' for reading and writing 
[%s].",filename,strerror(errno)));
 #endif
    }
 
@@ -267,8 +243,7 @@
 #ifdef LIBROUTINO
     return(NULL);
 #else
-    fprintf(stderr,"Cannot stat file '%s' [%s].\n",filename,strerror(errno));
-    exit(EXIT_FAILURE);
+    logassert_format(0,("Cannot stat file '%s' 
[%s].",filename,strerror(errno)));
 #endif
    }
 
@@ -285,8 +260,7 @@
 #ifdef LIBROUTINO
     return(NULL);
 #else
-    fprintf(stderr,"Cannot mmap file '%s' for reading and writing 
[%s].\n",filename,strerror(errno));
-    exit(EXIT_FAILURE);
+    logassert_format(0,("Cannot mmap file '%s' for reading and writing 
[%s].",filename,strerror(errno)));
 #endif
    }
 
@@ -298,7 +272,7 @@
 
  mappedfiles=(struct 
mmapinfo*)realloc((void*)mappedfiles,(nmappedfiles+1)*sizeof(struct mmapinfo));
 
- mappedfiles[nmappedfiles].filename=filename;
+ 
mappedfiles[nmappedfiles].filename=strcpy(malloc(strlen(filename)+1),filename);
  mappedfiles[nmappedfiles].fd=fd;
  mappedfiles[nmappedfiles].address=address;
  mappedfiles[nmappedfiles].length=size;
@@ -330,8 +304,7 @@
 #ifdef LIBROUTINO
     return(NULL);
 #else
-    fprintf(stderr,"The data at address %p was not mapped using 
MapFile().\n",address);
-    exit(EXIT_FAILURE);
+    logassert_format(0,("The data at address %p was not mapped using MapFile() 
- report a bug.",address));
 #endif
    }
 
@@ -339,6 +312,8 @@
 
  close(mappedfiles[i].fd);
 
+ free(mappedfiles[i].filename);
+
  /* Unmap the file */
 
  munmap(mappedfiles[i].address,mappedfiles[i].length);
@@ -383,13 +358,10 @@
 #ifdef LIBROUTINO
     return(-1);
 #else
-    fprintf(stderr,"Cannot open file '%s' for reading 
[%s].\n",filename,strerror(errno));
-    exit(EXIT_FAILURE);
+    logassert_format(0,("Cannot open file '%s' for reading 
[%s].",filename,strerror(errno)));
 #endif
    }
 
- CreateFileBuffer(fd,0);
-
  return(fd);
 }
 
@@ -419,13 +391,10 @@
 #ifdef LIBROUTINO
     return(-1);
 #else
-    fprintf(stderr,"Cannot open file '%s' for reading and writing 
[%s].\n",filename,strerror(errno));
-    exit(EXIT_FAILURE);
+    logassert_format(0,("Cannot open file '%s' for reading and writing 
[%s].",filename,strerror(errno)));
 #endif
    }
 
- CreateFileBuffer(fd,0);
-
  return(fd);
 }
 
@@ -471,16 +440,11 @@
 #ifdef LIBROUTINO
     return(-1);
 #else
-    fprintf(stderr,"Cannot open file '%s' for writing 
[%s].\n",filename,strerror(errno));
-    exit(EXIT_FAILURE);
+    logassert_format(0,("Cannot open file '%s' for writing 
[%s].",filename,strerror(errno)));
 #endif
    }
 
- CreateFileBuffer(fd,-1);
-
-#if defined(_MSC_VER) || defined(__MINGW32__)
- CreateOpenedFile(fd,filename);
-#endif
+ CreateFileBuffer(fd,-1,filename);
 
  return(fd);
 }
@@ -511,16 +475,11 @@
 #ifdef LIBROUTINO
     return(-1);
 #else
-    fprintf(stderr,"Cannot open file '%s' for appending 
[%s].\n",filename,strerror(errno));
-    exit(EXIT_FAILURE);
+    logassert_format(0,("Cannot open file '%s' for appending 
[%s].",filename,strerror(errno)));
 #endif
    }
 
- CreateFileBuffer(fd,-1);
-
-#if defined(_MSC_VER) || defined(__MINGW32__)
- CreateOpenedFile(fd,filename);
-#endif
+ CreateFileBuffer(fd,-1,filename);
 
  return(fd);
 }
@@ -551,16 +510,11 @@
 #ifdef LIBROUTINO
     return(-1);
 #else
-    fprintf(stderr,"Cannot open file '%s' for reading 
[%s].\n",filename,strerror(errno));
-    exit(EXIT_FAILURE);
+    logassert_format(0,("Cannot open file '%s' for reading 
[%s].",filename,strerror(errno)));
 #endif
    }
 
- CreateFileBuffer(fd,1);
-
-#if defined(_MSC_VER) || defined(__MINGW32__)
- CreateOpenedFile(fd,filename);
-#endif
+ CreateFileBuffer(fd,1,filename);
 
  return(fd);
 }
@@ -592,13 +546,13 @@
 
  *oldfd=ReOpenFileBuffered(filename2);
  
- DeleteFile(filename2);
+ DeleteFileBuffered(filename2);
 
 #else
 
  *oldfd=ReOpenFileBuffered(filename);
  
- DeleteFile(filename);
+ DeleteFileBuffered(filename);
 
 #endif
 
@@ -635,7 +589,13 @@
  if((filebuffers[fd]->pointer+length)>BUFFLEN)
    {
     
if(write(fd,filebuffers[fd]->buffer,filebuffers[fd]->pointer)!=(ssize_t)filebuffers[fd]->pointer)
+      {
+#ifdef LIBROUTINO
        return(-1);
+#else
+       logassert_format(0,("Cannot write to file '%s' 
[%s].",filebuffers[fd]->filename,strerror(errno)));
+#endif
+      }
 
     filebuffers[fd]->pointer=0;
    }
@@ -643,7 +603,13 @@
  if(length>=BUFFLEN)
    {
     if(write(fd,address,length)!=(ssize_t)length)
+      {
+#ifdef LIBROUTINO
        return(-1);
+#else
+       logassert_format(0,("Cannot write to file '%s' 
[%s].",filebuffers[fd]->filename,strerror(errno)));
+#endif
+      }
 
     return(0);
    }
@@ -695,7 +661,13 @@
  if(length>=BUFFLEN)
    {
     if(read(fd,address,length)!=(ssize_t)length)
+      {
+#ifdef LIBROUTINO
        return(-1);
+#else
+       logassert_format(0,("Cannot read from file '%s' 
[%s].",filebuffers[fd]->filename,strerror(errno)));
+#endif
+      }
 
     return(0);
    }
@@ -704,7 +676,15 @@
    {
     ssize_t len=read(fd,filebuffers[fd]->buffer,BUFFLEN);
 
-    if(len<=0)
+    if(len<0)
+      {
+#ifdef LIBROUTINO
+       return(-1);
+#else
+       logassert_format(0,("Cannot read from file '%s' 
[%s].",filebuffers[fd]->filename,strerror(errno)));
+#endif
+      }
+    else if(len==0)
        return(-1);
 
     filebuffers[fd]->length=len;
@@ -744,13 +724,25 @@
 
  if(!filebuffers[fd]->reading)
     
if(write(fd,filebuffers[fd]->buffer,filebuffers[fd]->pointer)!=(ssize_t)filebuffers[fd]->pointer)
+      {
+#ifdef LIBROUTINO
        return(-1);
+#else
+       logassert_format(0,("Cannot write to file '%s' 
[%s].",filebuffers[fd]->filename,strerror(errno)));
+#endif
+      }
 
  filebuffers[fd]->pointer=0;
  filebuffers[fd]->length=0;
 
  if(lseek(fd,position,SEEK_SET)!=position)
+   {
+#ifdef LIBROUTINO
     return(-1);
+#else
+    logassert_format(0,("Cannot seek in file '%s' 
[%s].",filebuffers[fd]->filename,strerror(errno)));
+#endif
+   }
 
  return(0);
 }
@@ -786,7 +778,13 @@
     filebuffers[fd]->length=0;
 
     if(lseek(fd,skip,SEEK_CUR)==-1)
+      {
+#ifdef LIBROUTINO
        return(-1);
+#else
+       logassert_format(0,("Cannot seek in file '%s' 
[%s].",filebuffers[fd]->filename,strerror(errno)));
+#endif
+      }
    }
  else
     filebuffers[fd]->pointer+=skip;
@@ -796,6 +794,115 @@
 
 
 /*++++++++++++++++++++++++++++++++++++++
+  Close a file on disk (and flush the buffer).
+
+  int CloseFileBuffered returns -1 (for similarity to the *OpenFileBuffered* 
functions).
+
+  int fd The file descriptor to close.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+int CloseFileBuffered(int fd)
+{
+#ifndef LIBROUTINO
+ logassert(fd<nfilebuffers && filebuffers[fd],"File descriptor has no buffer - 
report a bug");
+#endif
+
+ if(!filebuffers[fd]->reading)
+    
if(write(fd,filebuffers[fd]->buffer,filebuffers[fd]->pointer)!=(ssize_t)filebuffers[fd]->pointer)
+      {
+#ifdef LIBROUTINO
+       return(-1);
+#else
+       logassert_format(0,("Cannot write to file '%s' 
[%s].",filebuffers[fd]->filename,strerror(errno)));
+#endif
+      }
+
+ close(fd);
+
+ /* Delete the file if Windows */
+
+#if defined(_MSC_VER) || defined(__MINGW32__)
+ if(filebuffers[fd]->delete)
+    unlink(filebuffers[fd]->filename);
+#endif
+
+ /* Free the file buffer */
+
+ free(filebuffers[fd]->filename);
+ free(filebuffers[fd]);
+ filebuffers[fd]=NULL;
+
+ return(-1);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Create a file buffer.
+
+  int fd The file descriptor.
+
+  int read_write A flag set to 1 for reading, -1 for writing and 0 for 
unbuffered.
+
+  const char *filename The name of the file.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+static void CreateFileBuffer(int fd,int read_write,const char *filename)
+{
+ if(nfilebuffers<=fd)
+   {
+    int i;
+
+    filebuffers=(struct 
filebuffer**)realloc((void*)filebuffers,(fd+1)*sizeof(struct filebuffer*));
+
+    for(i=nfilebuffers;i<=fd;i++)
+       filebuffers[i]=NULL;
+
+    nfilebuffers=fd+1;
+   }
+
+ filebuffers[fd]=(struct filebuffer*)calloc(1,sizeof(struct filebuffer));
+
+ filebuffers[fd]->reading=(read_write==1);
+
+ filebuffers[fd]->filename=strcpy(malloc(strlen(filename)+1),filename);
+
+#if defined(_MSC_VER) || defined(__MINGW32__)
+ filebuffers[fd]->delete=0;
+#endif
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Delete a file from disk.
+
+  int DeleteFileBuffered Returns 0 if OK.
+
+  const char *filename The name of the file to delete.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+int DeleteFileBuffered(const char *filename)
+{
+ int fd;
+
+ for(fd=0;fd<nfilebuffers;fd++)
+    if(filebuffers[fd] && !strcmp(filebuffers[fd]->filename,filename))
+       break;
+
+#ifndef LIBROUTINO
+ logassert_format(fd<nfilebuffers && filebuffers[fd],("File '%s' is not 
buffered - report a bug.",filebuffers[fd]->filename));
+#endif
+
+#if defined(_MSC_VER) || defined(__MINGW32__)
+ filebuffers[fd]->delete=1;
+#else
+ unlink(filename);
+#endif
+
+ return(0);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
   Get the size of a file.
 
   offset_t SizeFile Returns the file size if OK or exits in case of an error.
@@ -812,8 +919,7 @@
 #ifdef LIBROUTINO
     return(-1);
 #else
-    fprintf(stderr,"Cannot stat file '%s' [%s].\n",filename,strerror(errno));
-    exit(EXIT_FAILURE);
+    logassert_format(0,("Cannot stat file '%s' 
[%s].",filename,strerror(errno)));
 #endif
    }
 
@@ -838,8 +944,7 @@
 #ifdef LIBROUTINO
     return(-1);
 #else
-    fprintf(stderr,"Cannot stat file descriptor '%d' 
[%s].\n",fd,strerror(errno));
-    exit(EXIT_FAILURE);
+    logassert_format(0,("Cannot stat file descriptor '%d' 
[%s].",fd,strerror(errno)));
 #endif
    }
 
@@ -867,47 +972,6 @@
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  Close a file on disk (and flush the buffer).
-
-  int CloseFileBuffered returns -1 (for similarity to the *OpenFileBuffered* 
functions).
-
-  int fd The file descriptor to close.
-  ++++++++++++++++++++++++++++++++++++++*/
-
-int CloseFileBuffered(int fd)
-{
-#ifndef LIBROUTINO
- logassert(fd<nfilebuffers && filebuffers[fd],"File descriptor has no buffer - 
report a bug");
-#endif
-
- if(!filebuffers[fd]->reading)
-    
if(write(fd,filebuffers[fd]->buffer,filebuffers[fd]->pointer)!=(ssize_t)filebuffers[fd]->pointer)
-       return(-1);
-
- close(fd);
-
- free(filebuffers[fd]);
- filebuffers[fd]=NULL;
-
-#if defined(_MSC_VER) || defined(__MINGW32__)
-
-#ifndef LIBROUTINO
- logassert(fd<nopenedfiles && openedfiles[fd],"File descriptor has no record 
of opening - report a bug");
-#endif
-
- if(openedfiles[fd]->delete)
-    unlink(openedfiles[fd]->filename);
-
- free(openedfiles[fd]);
- openedfiles[fd]=NULL;
-
-#endif
-
- return(-1);
-}
-
-
-/*++++++++++++++++++++++++++++++++++++++
   Open an existing file on disk for reading (in a simple mode).
 
   int OpenFile Returns the file descriptor if OK or exits in case of an error.
@@ -932,15 +996,10 @@
 #ifdef LIBROUTINO
     return(-1);
 #else
-    fprintf(stderr,"Cannot open file '%s' for reading 
[%s].\n",filename,strerror(errno));
-    exit(EXIT_FAILURE);
+    logassert_format(0,("Cannot open file '%s' for reading 
[%s].",filename,strerror(errno)));
 #endif
    }
 
-#if defined(_MSC_VER) || defined(__MINGW32__)
- CreateOpenedFile(fd,filename);
-#endif
-
  return(fd);
 }
 
@@ -953,26 +1012,16 @@
 
 void CloseFile(int fd)
 {
- close(fd);
-
-#if defined(_MSC_VER) || defined(__MINGW32__)
-
 #ifndef LIBROUTINO
- logassert(fd<nopenedfiles && openedfiles[fd],"File descriptor has no record 
of opening - report a bug");
+ logassert_format(fd>=nfilebuffers || !filebuffers[fd],("File '%s' is buffered 
- report a bug.",filebuffers[fd]->filename));
 #endif
 
- if(openedfiles[fd]->delete)
-    unlink(openedfiles[fd]->filename);
-
- free(openedfiles[fd]);
- openedfiles[fd]=NULL;
-
-#endif
+ close(fd);
 }
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  Delete a file from disk.
+  Delete a file from disk (that was opened in simple mode).
 
   int DeleteFile Returns 0 if OK.
 
@@ -981,17 +1030,14 @@
 
 int DeleteFile(const char *filename)
 {
-#if defined(_MSC_VER) || defined(__MINGW32__)
-
  int fd;
 
- for(fd=0;fd<nopenedfiles;fd++)
-    if(openedfiles[fd] && !strcmp(openedfiles[fd]->filename,filename))
-      {
-       openedfiles[fd]->delete=1;
-       return(0);
-      }
+ for(fd=0;fd<nfilebuffers;fd++)
+    if(filebuffers[fd] && !strcmp(filebuffers[fd]->filename,filename))
+       break;
 
+#ifndef LIBROUTINO
+ logassert_format(fd==nfilebuffers || !filebuffers[fd],("File '%s' is buffered 
- report a bug.",filebuffers[fd]->filename));
 #endif
 
  unlink(filename);
@@ -1016,67 +1062,3 @@
 
  return(0);
 }
-
-
-/*++++++++++++++++++++++++++++++++++++++
-  Create a file buffer.
-
-  int fd The file descriptor.
-
-  int read_write A flag set to 1 for reading, -1 for writing and 0 for 
unbuffered.
-  ++++++++++++++++++++++++++++++++++++++*/
-
-static void CreateFileBuffer(int fd,int read_write)
-{
- if(nfilebuffers<=fd)
-   {
-    int i;
-
-    filebuffers=(struct 
filebuffer**)realloc((void*)filebuffers,(fd+1)*sizeof(struct filebuffer*));
-
-    for(i=nfilebuffers;i<=fd;i++)
-       filebuffers[i]=NULL;
-
-    nfilebuffers=fd+1;
-   }
-
- if(read_write)
-   {
-    filebuffers[fd]=(struct filebuffer*)calloc(1,sizeof(struct filebuffer));
-
-    filebuffers[fd]->reading=(read_write==1);
-   }
-}
-
-
-#if defined(_MSC_VER) || defined(__MINGW32__)
-
-/*++++++++++++++++++++++++++++++++++++++
-  Create an opened file record.
-
-  int fd The file descriptor.
-
-  const char *filename The name of the file.
-  ++++++++++++++++++++++++++++++++++++++*/
-
-static void CreateOpenedFile(int fd,const char *filename)
-{
- if(nopenedfiles<=fd)
-   {
-    int i;
-
-    openedfiles=(struct 
openedfile**)realloc((void*)openedfiles,(fd+1)*sizeof(struct openedfile*));
-
-    for(i=nopenedfiles;i<=fd;i++)
-       openedfiles[i]=NULL;
-
-    nopenedfiles=fd+1;
-   }
-
- openedfiles[fd]=(struct openedfile*)calloc(sizeof(struct openedfile),1);
-
- openedfiles[fd]->filename=strcpy(malloc(strlen(filename)+1),filename);
- openedfiles[fd]->delete=0;
-}
-
-#endif
diff -Nru routino-3.4.2/src/files.h routino-3.4.3/src/files.h
--- routino-3.4.2/src/files.h   2019-04-08 20:25:23.000000000 +0200
+++ routino-3.4.3/src/files.h   2025-04-08 19:53:32.000000000 +0200
@@ -3,7 +3,7 @@
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2008-2015, 2019 Andrew M. Bishop
+ This file Copyright 2008-2015, 2019, 2025 Andrew M. Bishop
 
  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU Affero General Public License as published by
@@ -65,6 +65,8 @@
 
 char *FileName(const char *dirname,const char *prefix, const char *name);
 
+/* Functions in files.c for mapped files */
+
 void *MapFile(const char *filename);
 void *MapFileWriteable(const char *filename);
 
@@ -75,6 +77,8 @@
 
 int SlimUnmapFile(int fd);
 
+/* Functions in files.c for buffered files */
+
 int OpenFileBufferedNew(const char *filename);
 int OpenFileBufferedAppend(const char *filename);
 
@@ -90,16 +94,20 @@
 
 int CloseFileBuffered(int fd);
 
+int DeleteFileBuffered(const char *filename);
+
+/* Functions in files.c for simple files */
+
 int OpenFile(const char *filename);
 
 void CloseFile(int fd);
 
+int DeleteFile(const char *filename);
+
 offset_t SizeFile(const char *filename);
 offset_t SizeFileFD(int fd);
 int ExistsFile(const char *filename);
 
-int DeleteFile(const char *filename);
-
 int RenameFile(const char *oldfilename,const char *newfilename);
 
 /* Functions in files.h */
diff -Nru routino-3.4.2/src/logerror.c routino-3.4.3/src/logerror.c
--- routino-3.4.2/src/logerror.c        2015-08-15 14:52:59.000000000 +0200
+++ routino-3.4.3/src/logerror.c        2025-04-05 17:31:34.000000000 +0200
@@ -3,7 +3,7 @@
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2013, 2015 Andrew M. Bishop
+ This file Copyright 2013, 2015, 2025 Andrew M. Bishop
 
  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU Affero General Public License as published by
@@ -75,11 +75,7 @@
  errorlogfile=fopen(errorlogfilename,append?"a" :"w" );
 #endif
 
- if(!errorlogfile)
-   {
-    fprintf(stderr,"Cannot open file '%s' for writing 
[%s].\n",errorlogfilename,strerror(errno));
-    exit(EXIT_FAILURE);
-   }
+ logassert_format(errorlogfile,("Cannot open file '%s' for writing 
[%s].",errorlogfilename,strerror(errno)));
 
  /* Binary log file */
 
diff -Nru routino-3.4.2/src/logging.c routino-3.4.3/src/logging.c
--- routino-3.4.2/src/logging.c 2019-07-28 12:21:50.000000000 +0200
+++ routino-3.4.3/src/logging.c 2025-04-05 17:07:30.000000000 +0200
@@ -3,7 +3,7 @@
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2008-2015, 2019 Andrew M. Bishop
+ This file Copyright 2008-2015, 2019, 2025 Andrew M. Bishop
 
  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU Affero General Public License as published by
@@ -590,7 +590,7 @@
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  Log a fatal error and exit
+  Log a fatal error and exit.
 
   const char *message The error message.
 
@@ -607,6 +607,32 @@
 }
 
 
+/*++++++++++++++++++++++++++++++++++++++
+  Create an error message before logging a fatal error and exiting.
+
+  const char *_logassert_format Returns a constant string.
+
+  const char *format The error message format string.
+
+  ... The other arguments.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+const char *_logassert_format(const char *format, ...)
+{
+ static char string[256];
+
+ va_list ap;
+
+ va_start(ap,format);
+
+ vsnprintf(string,sizeof(string),format,ap);
+
+ va_end(ap);
+
+ return(string);
+}
+
+
 /*++++++++++++++++++++++++++++++++++++++
   Allocate some memory by calling calloc() and checking for failures.
 
diff -Nru routino-3.4.2/src/logging.h routino-3.4.3/src/logging.h
--- routino-3.4.2/src/logging.h 2019-07-27 11:50:18.000000000 +0200
+++ routino-3.4.3/src/logging.h 2025-04-05 16:28:50.000000000 +0200
@@ -3,7 +3,7 @@
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2008-2015, 2017, 2019 Andrew M. Bishop
+ This file Copyright 2008-2015, 2017, 2019, 2025 Andrew M. Bishop
 
  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU Affero General Public License as published by
@@ -106,16 +106,21 @@
 
 /* Runtime fatal error assertion in logging.c */
 
-#define logassert(xx,yy) do { if(!(xx)) _logassert(yy,__FILE__,__LINE__); } 
while(0)
+#define logassert(xx,yy)        do { if(!(xx)) _logassert(                  
yy,__FILE__,__LINE__); } while(0)
+#define logassert_format(xx,yy) do { if(!(xx)) _logassert(_logassert_format 
yy,__FILE__,__LINE__); } while(0)
 
 #ifdef __GNUC__
 
 void _logassert(const char *message,const char *file,int line) __attribute__ 
((noreturn));
 
+const char *_logassert_format(const char *format, ...) __attribute__ ((format 
(printf, 1, 2)));
+
 #else
 
 void _logassert(const char *message,const char *file,int line);
 
+const char *_logassert_format(const char *format, ...);
+
 #endif
 
 
diff -Nru routino-3.4.2/src/#nodesx.c# routino-3.4.3/src/#nodesx.c#
--- routino-3.4.2/src/#nodesx.c#        1970-01-01 01:00:00.000000000 +0100
+++ routino-3.4.3/src/#nodesx.c#        2025-04-04 17:52:15.000000000 +0200
@@ -0,0 +1,904 @@
+/***************************************
+ Extented Node data type functions.
+
+ Part of the Routino routing software.
+ ******************/ /******************
+ This file Copyright 2008-2015, 2019, 2020, 2022 Andrew M. Bishop
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ ***************************************/
+
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "types.h"
+#include "nodes.h"
+
+#include "typesx.h"
+#include "nodesx.h"
+#include "segmentsx.h"
+#include "waysx.h"
+
+#include "files.h"
+#include "logging.h"
+#include "sorting.h"
+
+
+/* Global variables */
+
+/*+ The command line '--tmpdir' option or its default value. +*/
+extern char *option_tmpdirname;
+
+/* Local variables */
+
+/*+ Temporary file-local variables for use by the sort functions 
(re-initialised for each sort). +*/
+static NodesX *sortnodesx;
+static latlong_t lat_min,lat_max,lon_min,lon_max;
+
+/* Local functions */
+
+static int sort_by_id(NodeX *a,NodeX *b);
+static int deduplicate_and_index_by_id(NodeX *nodex,index_t index);
+
+static int update_id(NodeX *nodex,index_t index);
+static int sort_by_lat_long(NodeX *a,NodeX *b);
+static int index_by_lat_long(NodeX *nodex,index_t index);
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Allocate a new node list (create a new file or open an existing one).
+
+  NodesX *NewNodeList Returns a pointer to the node list.
+
+  int append Set to 1 if the file is to be opened for appending.
+
+  int readonly Set to 1 if the file is to be opened for reading.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+NodesX *NewNodeList(int append,int readonly)
+{
+ NodesX *nodesx;
+
+ nodesx=(NodesX*)calloc_logassert(1,sizeof(NodesX));
+
+ nodesx->filename    =(char*)malloc_logassert(strlen(option_tmpdirname)+32);
+ nodesx->filename_tmp=(char*)malloc_logassert(strlen(option_tmpdirname)+40); 
/* allow %p to be up to 20 bytes */
+
+ sprintf(nodesx->filename    ,"%s/nodesx.parsed.mem",option_tmpdirname);
+ sprintf(nodesx->filename_tmp,"%s/nodesx.%p.tmp"    
,option_tmpdirname,(void*)nodesx);
+
+ if(append || readonly)
+    if(ExistsFile(nodesx->filename))
+      {
+       offset_t size;
+
+       size=SizeFile(nodesx->filename);
+
+       nodesx->number=size/sizeof(NodeX);
+
+       RenameFile(nodesx->filename,nodesx->filename_tmp);
+      }
+
+ if(append)
+    nodesx->fd=OpenFileBufferedAppend(nodesx->filename_tmp);
+ else if(!readonly)
+    nodesx->fd=OpenFileBufferedNew(nodesx->filename_tmp);
+ else
+    nodesx->fd=-1;
+
+#if SLIM
+ nodesx->cache=NewNodeXCache();
+ log_malloc(nodesx->cache,sizeof(*nodesx->cache));
+#endif
+
+ nodesx->ifilename_tmp=(char*)malloc_logassert(strlen(option_tmpdirname)+40); 
/* allow %p to be up to 20 bytes */
+
+ 
sprintf(nodesx->ifilename_tmp,"%s/nodesx.%p.idx.tmp",option_tmpdirname,(void*)nodesx);
+
+ return(nodesx);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Free a node list.
+
+  NodesX *nodesx The set of nodes to be freed.
+
+  int keep If set then the results file is to be kept.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+void FreeNodeList(NodesX *nodesx,int keep)
+{
+ if(keep)
+    RenameFile(nodesx->filename_tmp,nodesx->filename);
+ else
+    DeleteFile(nodesx->filename_tmp);
+
+ free(nodesx->filename);
+ free(nodesx->filename_tmp);
+
+ DeleteFile(nodesx->ifilename_tmp);
+
+ free(nodesx->ifilename_tmp);
+
+ if(nodesx->gdata)
+   {
+    log_free(nodesx->gdata);
+    free(nodesx->gdata);
+   }
+
+ if(nodesx->pdata)
+   {
+    log_free(nodesx->pdata);
+    free(nodesx->pdata);
+   }
+
+ if(nodesx->super)
+   {
+    log_free(nodesx->super);
+    free(nodesx->super);
+   }
+
+#if SLIM
+ log_free(nodesx->cache);
+ DeleteNodeXCache(nodesx->cache);
+#endif
+
+ free(nodesx);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Append a single node to an unsorted node list.
+
+  NodesX *nodesx The set of nodes to modify.
+
+  node_t id The node identifier from the original OSM data.
+
+  double latitude The latitude of the node.
+
+  double longitude The longitude of the node.
+
+  transports_t allow The allowed traffic types through the node.
+
+  nodeflags_t flags The flags to set for this node.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+void AppendNodeList(NodesX *nodesx,node_t id,double latitude,double 
longitude,transports_t allow,nodeflags_t flags)
+{
+ NodeX nodex={0};
+
+ nodex.id=id;
+ nodex.latitude =radians_to_latlong(latitude);
+ nodex.longitude=radians_to_latlong(longitude);
+ nodex.allow=allow;
+ nodex.flags=flags;
+
+ WriteFileBuffered(nodesx->fd,&nodex,sizeof(NodeX));
+
+ nodesx->number++;
+
+ logassert(nodesx->number<NODE_FAKE,("Too many nodes (change index_t to 
64-bits?)"); /* NODE_FAKE marks the high-water mark for real nodes. */
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Finish appending nodes and change the filename over.
+
+  NodesX *nodesx The nodes that have been appended.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+void FinishNodeList(NodesX *nodesx)
+{
+ if(nodesx->fd!=-1)
+    nodesx->fd=CloseFileBuffered(nodesx->fd);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Find a particular node index.
+
+  index_t IndexNodeX Returns the index of the extended node with the specified 
id.
+
+  NodesX *nodesx The set of nodes to use.
+
+  node_t id The node id to look for.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+index_t IndexNodeX(NodesX *nodesx,node_t id)
+{
+ index_t start=0;
+ index_t end=nodesx->number-1;
+ index_t mid;
+
+ if(nodesx->number==0)          /* No nodes */
+    return(NO_NODE);
+
+ /* Binary search - search key exact match only is required.
+  *
+  *  # <- start  |  Check mid and exit if it matches else move start or end.
+  *  #           |
+  *  #           |  Since an exact match is wanted we can set end=mid-1
+  *  # <- mid    |  or start=mid+1 if we find that mid doesn't match.
+  *  #           |
+  *  #           |  Eventually either end=start or end=start+1 and one of
+  *  # <- end    |  start or end is the wanted one or neither is.
+  */
+
+ while((end-start)>1)
+   {
+    mid=start+(end-start)/2;       /* Choose mid point (avoid overflow) */
+
+    if(nodesx->idata[mid]<id)      /* Mid point is too low */
+       start=mid+1;
+    else if(nodesx->idata[mid]>id) /* Mid point is too high */
+       end=mid-1;
+    else                           /* Mid point is correct */
+       return(mid);
+   }
+
+ if(nodesx->idata[start]==id)      /* Start is correct */
+    return(start);
+
+ if(nodesx->idata[end]==id)        /* End is correct */
+    return(end);
+
+ return(NO_NODE);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Sort the node list.
+
+  NodesX *nodesx The set of nodes to modify.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+void SortNodeList(NodesX *nodesx)
+{
+ int fd;
+ index_t xnumber;
+
+ /* Print the start message */
+
+ printf_first("Sorting Nodes");
+
+ /* Re-open the file read-only and a new file writeable */
+
+ fd=ReplaceFileBuffered(nodesx->filename_tmp,&nodesx->fd);
+
+ /* Open a file for the index */
+
+ nodesx->ifd=OpenFileBufferedNew(nodesx->ifilename_tmp);
+
+ /* Sort the nodes by ID and index them */
+
+ xnumber=nodesx->number;
+
+ sortnodesx=nodesx;
+
+ nodesx->number=filesort_fixed(nodesx->fd,fd,sizeof(NodeX),NULL,
+                                                           (int (*)(const 
void*,const void*))sort_by_id,
+                                                           (int 
(*)(void*,index_t))deduplicate_and_index_by_id);
+
+ nodesx->knumber=nodesx->number;
+
+ /* Close the files */
+
+ nodesx->fd=CloseFileBuffered(nodesx->fd);
+ CloseFileBuffered(fd);
+
+ nodesx->ifd=CloseFileBuffered(nodesx->ifd);
+
+ /* Print the final message */
+
+ printf_last("Sorted Nodes: Nodes=%"Pindex_t" 
Duplicates=%"Pindex_t,xnumber,xnumber-nodesx->number);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Sort the nodes into id order.
+
+  int sort_by_id Returns the comparison of the id fields.
+
+  NodeX *a The first extended node.
+
+  NodeX *b The second extended node.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+static int sort_by_id(NodeX *a,NodeX *b)
+{
+ node_t a_id=a->id;
+ node_t b_id=b->id;
+
+ if(a_id<b_id)
+    return(-1);
+ else if(a_id>b_id)
+    return(1);
+ else
+    return(-FILESORT_PRESERVE_ORDER(a,b)); /* latest version first */
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Create the index of identifiers and discard duplicate nodes.
+
+  int deduplicate_and_index_by_id Return 1 if the value is to be kept, 
otherwise 0.
+
+  NodeX *nodex The extended node.
+
+  index_t index The number of sorted nodes that have already been written to 
the output file.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+static int deduplicate_and_index_by_id(NodeX *nodex,index_t index)
+{
+ static node_t previd; /* internal variable (reset by first call in each sort; 
index==0) */
+
+ if(index==0 || nodex->id!=previd)
+   {
+    previd=nodex->id;
+
+    if(nodex->flags&NODE_DELETED)
+       return(0);
+    else
+      {
+       WriteFileBuffered(sortnodesx->ifd,&nodex->id,sizeof(node_t));
+
+       return(1);
+      }
+   }
+ else
+    return(0);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Remove any nodes that are not part of a highway.
+
+  NodesX *nodesx The set of nodes to modify.
+
+  WaysX *waysx The set of ways to use.
+
+  int keep If set to 1 then keep the old data file otherwise delete it.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+void RemoveNonHighwayNodes(NodesX *nodesx,WaysX *waysx,int keep)
+{
+ BitMask *usednode;
+ NodeX nodex;
+ index_t i,total=0,highway=0,nothighway=0;
+ node_t bitmasklength;
+ int fd;
+
+ /* Print the start message */
+
+ printf_first("Checking Ways for unused Nodes: Ways=0 Highway Nodes=0");
+
+ /* Re-open the file read-only */
+
+ waysx->fd=ReOpenFileBuffered(waysx->filename_tmp);
+
+ /* Map the index into memory */
+
+ nodesx->idata=MapFile(nodesx->ifilename_tmp);
+
+ /* Allocate the node usage bitmask */
+
+#if SLIM
+ bitmasklength=nodesx->number;                     /* The number of nodes in 
the database */
+#else
+ bitmasklength=nodesx->idata[nodesx->number-1]+1;  /* One more than the 
highest OSM node number in the database */
+#endif
+
+ usednode=AllocBitMask(bitmasklength);
+ log_malloc(usednode,SizeBitMask(bitmasklength));
+
+ /* Loop through the ways and mark the used nodes */
+
+ for(i=0;i<waysx->number;i++)
+   {
+    WayX wayx;
+    FILESORT_VARINT waysize;
+    node_t node;
+
+    ReadFileBuffered(waysx->fd,&waysize,FILESORT_VARSIZE);
+
+    ReadFileBuffered(waysx->fd,&wayx,sizeof(WayX));
+
+    while(!ReadFileBuffered(waysx->fd,&node,sizeof(node_t)) && 
node!=NO_NODE_ID)
+      {
+#if SLIM
+       index_t index=IndexNodeX(nodesx,node); /* Index bitmap by node number 
in the database */
+#else
+       node_t index=node;                     /* Index bitmap by OSM node 
number */
+#endif
+
+       waysize-=sizeof(node_t);
+
+#if SLIM
+       if(index==NO_NODE)
+          continue;
+#endif
+
+       if(!IsBitSet(usednode,index))
+          highway++;
+
+       SetBit(usednode,index);
+      }
+
+    waysize-=sizeof(node_t)+sizeof(WayX);
+
+    SkipFileBuffered(waysx->fd,waysize);
+
+    if(!((i+1)%1000))
+       printf_middle("Checking Ways for unused Nodes: Ways=%"Pindex_t" Highway 
Nodes=%"Pindex_t,i+1,highway);
+   }
+
+ /* Close the file */
+
+ waysx->fd=CloseFileBuffered(waysx->fd);
+
+ /* Unmap the index from memory */
+
+ nodesx->idata=UnmapFile(nodesx->idata);
+
+ /* Print the final message */
+
+ printf_last("Checked Ways for unused Nodes: Ways=%"Pindex_t" Highway 
Nodes=%"Pindex_t,waysx->number,highway);
+
+
+ /* Print the start message */
+
+ printf_first("Removing unused Nodes: Nodes=0");
+
+ /* Open a file for the index */
+
+ nodesx->ifd=OpenFileBufferedNew(nodesx->ifilename_tmp);
+
+ highway=0;
+
+ /* Re-open the file read-only and a new file writeable */
+
+ if(keep)
+   {
+    RenameFile(nodesx->filename_tmp,nodesx->filename);
+
+    nodesx->fd=ReOpenFileBuffered(nodesx->filename);
+
+    fd=OpenFileBufferedNew(nodesx->filename_tmp);
+   }
+ else
+    fd=ReplaceFileBuffered(nodesx->filename_tmp,&nodesx->fd);
+
+ /* Modify the on-disk image */
+
+ while(!ReadFileBuffered(nodesx->fd,&nodex,sizeof(NodeX)))
+   {
+#if SLIM
+    index_t node=total;         /* Index by node number in the database */
+#else
+    node_t node=nodex.id;       /* Index by OSM node number */
+#endif
+
+    if(!IsBitSet(usednode,node))
+       nothighway++;
+    else
+      {
+       WriteFileBuffered(fd,&nodex,sizeof(NodeX));
+
+       WriteFileBuffered(nodesx->ifd,&nodex.id,sizeof(node_t));
+
+       highway++;
+      }
+
+    total++;
+
+    if(!(total%10000))
+       printf_middle("Removing unused Nodes: Nodes=%"Pindex_t" 
Highway=%"Pindex_t" not-Highway=%"Pindex_t,total,highway,nothighway);
+   }
+
+ nodesx->number=highway;
+
+ /* Close the files */
+
+ nodesx->fd=CloseFileBuffered(nodesx->fd);
+ CloseFileBuffered(fd);
+
+ nodesx->ifd=CloseFileBuffered(nodesx->ifd);
+
+ /* Free the now-unneeded index */
+
+ log_free(usednode);
+ free(usednode);
+
+ /* Print the final message */
+
+ printf_last("Removed unused Nodes: Nodes=%"Pindex_t" Highway=%"Pindex_t" 
not-Highway=%"Pindex_t,total,highway,nothighway);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Remove any nodes that have been pruned.
+
+  NodesX *nodesx The set of nodes to prune.
+
+  SegmentsX *segmentsx The set of segments to use.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+void RemovePrunedNodes(NodesX *nodesx,SegmentsX *segmentsx)
+{
+ NodeX nodex;
+ index_t total=0,pruned=0,notpruned=0;
+ int fd;
+
+ if(nodesx->number==0)
+    return;
+
+ /* Print the start message */
+
+ printf_first("Deleting Pruned Nodes: Nodes=0 Pruned=0");
+
+ /* Allocate the array of indexes */
+
+ nodesx->pdata=(index_t*)malloc_logassert(nodesx->number*sizeof(index_t));
+ log_malloc(nodesx->pdata,nodesx->number*sizeof(index_t));
+
+ /* Re-open the file read-only and a new file writeable */
+
+ fd=ReplaceFileBuffered(nodesx->filename_tmp,&nodesx->fd);
+
+ /* Modify the on-disk image */
+
+ while(!ReadFileBuffered(nodesx->fd,&nodex,sizeof(NodeX)))
+   {
+    if(segmentsx->firstnode[total]==NO_SEGMENT)
+      {
+       pruned++;
+
+       nodesx->pdata[total]=NO_NODE;
+      }
+    else
+      {
+       nodesx->pdata[total]=notpruned;
+
+       WriteFileBuffered(fd,&nodex,sizeof(NodeX));
+
+       notpruned++;
+      }
+
+    total++;
+
+    if(!(total%10000))
+       printf_middle("Deleting Pruned Nodes: Nodes=%"Pindex_t" 
Pruned=%"Pindex_t,total,pruned);
+   }
+
+ nodesx->number=notpruned;
+
+ /* Close the files */
+
+ nodesx->fd=CloseFileBuffered(nodesx->fd);
+ CloseFileBuffered(fd);
+
+ /* Free the no-longer required memory */
+
+ if(segmentsx->firstnode)
+   {
+    log_free(segmentsx->firstnode);
+    free(segmentsx->firstnode);
+    segmentsx->firstnode=NULL;
+   }
+
+ /* Print the final message */
+
+ printf_last("Deleted Pruned Nodes: Nodes=%"Pindex_t" 
Pruned=%"Pindex_t,total,pruned);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Sort the node list geographically.
+
+  NodesX *nodesx The set of nodes to modify.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+void SortNodeListGeographically(NodesX *nodesx)
+{
+ int fd;
+ ll_bin_t lat_min_bin,lat_max_bin,lon_min_bin,lon_max_bin;
+
+ if(nodesx->number==0)
+    return;
+
+ /* Print the start message */
+
+ printf_first("Sorting Nodes Geographically");
+
+ /* Work out the range of data */
+
+ lat_min=radians_to_latlong( 2);
+ lat_max=radians_to_latlong(-2);
+ lon_min=radians_to_latlong( 4);
+ lon_max=radians_to_latlong(-4);
+
+ /* Allocate the memory for the geographical index array */
+
+ nodesx->gdata=(index_t*)malloc_logassert(nodesx->number*sizeof(index_t));
+ log_malloc(nodesx->gdata,nodesx->number*sizeof(index_t));
+
+ /* Re-open the file read-only and a new file writeable */
+
+ fd=ReplaceFileBuffered(nodesx->filename_tmp,&nodesx->fd);
+
+ /* Sort nodes geographically and index them */
+
+ sortnodesx=nodesx;
+
+ filesort_fixed(nodesx->fd,fd,sizeof(NodeX),(int (*)(void*,index_t))update_id,
+                                            (int (*)(const void*,const 
void*))sort_by_lat_long,
+                                            (int 
(*)(void*,index_t))index_by_lat_long);
+
+ /* Close the files */
+
+ nodesx->fd=CloseFileBuffered(nodesx->fd);
+ CloseFileBuffered(fd);
+
+ /* Work out the number of bins */
+
+ if(nodesx->super)
+   {
+    lat_min_bin=latlong_to_bin(lat_min);
+    lon_min_bin=latlong_to_bin(lon_min);
+    lat_max_bin=latlong_to_bin(lat_max);
+    lon_max_bin=latlong_to_bin(lon_max);
+
+    nodesx->latzero=lat_min_bin;
+    nodesx->lonzero=lon_min_bin;
+
+    nodesx->latbins=(lat_max_bin-lat_min_bin)+1;
+    nodesx->lonbins=(lon_max_bin-lon_min_bin)+1;
+   }
+
+ /* Free the memory */
+
+ if(nodesx->super)
+   {
+    log_free(nodesx->super);
+    free(nodesx->super);
+    nodesx->super=NULL;
+   }
+
+ /* Print the final message */
+
+ printf_last("Sorted Nodes Geographically: Nodes=%"Pindex_t,nodesx->number);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Update the node ids.
+
+  int update_id Return 1 if the value is to be kept, otherwise 0.
+
+  NodeX *nodex The extended node.
+
+  index_t index The number of unsorted nodes that have been read from the 
input file.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+static int update_id(NodeX *nodex,index_t index)
+{
+ nodex->id=index;
+
+ if(sortnodesx->super && IsBitSet(sortnodesx->super,index))
+    nodex->flags|=NODE_SUPER;
+
+ return(1);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Sort the nodes into latitude and longitude order (first by longitude bin
+  number, then by latitude bin number and then by exact longitude and then by
+  exact latitude).
+
+  int sort_by_lat_long Returns the comparison of the latitude and longitude 
fields.
+
+  NodeX *a The first extended node.
+
+  NodeX *b The second extended node.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+static int sort_by_lat_long(NodeX *a,NodeX *b)
+{
+ ll_bin_t a_lon=latlong_to_bin(a->longitude);
+ ll_bin_t b_lon=latlong_to_bin(b->longitude);
+
+ if(a_lon<b_lon)
+    return(-1);
+ else if(a_lon>b_lon)
+    return(1);
+ else
+   {
+    ll_bin_t a_lat=latlong_to_bin(a->latitude);
+    ll_bin_t b_lat=latlong_to_bin(b->latitude);
+
+    if(a_lat<b_lat)
+       return(-1);
+    else if(a_lat>b_lat)
+       return(1);
+    else
+      {
+       if(a->longitude<b->longitude)
+          return(-1);
+       else if(a->longitude>b->longitude)
+          return(1);
+       else
+         {
+          if(a->latitude<b->latitude)
+             return(-1);
+          else if(a->latitude>b->latitude)
+             return(1);
+         }
+
+       return(FILESORT_PRESERVE_ORDER(a,b));
+      }
+   }
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Create the index between the sorted and unsorted nodes.
+
+  int index_by_lat_long Return 1 if the value is to be kept, otherwise 0.
+
+  NodeX *nodex The extended node.
+
+  index_t index The number of sorted nodes that have already been written to 
the output file.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+static int index_by_lat_long(NodeX *nodex,index_t index)
+{
+ sortnodesx->gdata[nodex->id]=index;
+
+ if(sortnodesx->super)
+   {
+    if(nodex->latitude<lat_min)
+       lat_min=nodex->latitude;
+    if(nodex->latitude>lat_max)
+       lat_max=nodex->latitude;
+    if(nodex->longitude<lon_min)
+       lon_min=nodex->longitude;
+    if(nodex->longitude>lon_max)
+       lon_max=nodex->longitude;
+   }
+
+ return(1);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Save the final node list database to a file.
+
+  NodesX *nodesx The set of nodes to save.
+
+  const char *filename The name of the file to save.
+
+  SegmentsX *segmentsx The set of segments to use.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+void SaveNodeList(NodesX *nodesx,const char *filename,SegmentsX *segmentsx)
+{
+ index_t i;
+ int fd;
+ NodesFile nodesfile={0};
+ index_t super_number=0;
+ ll_bin2_t latlonbin=0,maxlatlonbins;
+ index_t *offsets;
+
+ /* Print the start message */
+
+ printf_first("Writing Nodes: Nodes=0");
+
+ /* Allocate the memory for the geographical offsets array */
+
+ 
offsets=(index_t*)malloc_logassert((nodesx->latbins*nodesx->lonbins+1)*sizeof(index_t));
+
+ latlonbin=0;
+
+ /* Re-open the file */
+
+ nodesx->fd=ReOpenFileBuffered(nodesx->filename_tmp);
+
+ /* Write out the nodes data */
+
+ fd=OpenFileBufferedNew(filename);
+
+ 
SeekFileBuffered(fd,sizeof(NodesFile)+(nodesx->latbins*nodesx->lonbins+1)*sizeof(index_t));
+
+ for(i=0;i<nodesx->number;i++)
+   {
+    NodeX nodex;
+    Node node={0};
+    ll_bin_t latbin,lonbin;
+    ll_bin2_t llbin;
+
+    ReadFileBuffered(nodesx->fd,&nodex,sizeof(NodeX));
+
+    /* Create the Node */
+
+    node.latoffset=latlong_to_off(nodex.latitude);
+    node.lonoffset=latlong_to_off(nodex.longitude);
+    node.firstseg=segmentsx->firstnode[i];
+    node.allow=nodex.allow;
+    node.flags=nodex.flags;
+
+    if(node.flags&NODE_SUPER)
+       super_number++;
+
+    /* Work out the offsets */
+
+    latbin=latlong_to_bin(nodex.latitude )-nodesx->latzero;
+    lonbin=latlong_to_bin(nodex.longitude)-nodesx->lonzero;
+    llbin=lonbin*nodesx->latbins+latbin;
+
+    for(;latlonbin<=llbin;latlonbin++)
+       offsets[latlonbin]=i;
+
+    /* Write the data */
+
+    WriteFileBuffered(fd,&node,sizeof(Node));
+
+    if(!((i+1)%10000))
+       printf_middle("Writing Nodes: Nodes=%"Pindex_t,i+1);
+   }
+
+ /* Close the file */
+
+ nodesx->fd=CloseFileBuffered(nodesx->fd);
+
+ /* Finish off the offset indexing and write them out */
+
+ maxlatlonbins=nodesx->latbins*nodesx->lonbins;
+
+ for(;latlonbin<=maxlatlonbins;latlonbin++)
+    offsets[latlonbin]=nodesx->number;
+
+ SeekFileBuffered(fd,sizeof(NodesFile));
+ 
WriteFileBuffered(fd,offsets,(nodesx->latbins*nodesx->lonbins+1)*sizeof(index_t));
+
+ free(offsets);
+
+ /* Write out the header structure */
+
+ nodesfile.number=nodesx->number;
+ nodesfile.snumber=super_number;
+
+ nodesfile.latbins=nodesx->latbins;
+ nodesfile.lonbins=nodesx->lonbins;
+
+ nodesfile.latzero=nodesx->latzero;
+ nodesfile.lonzero=nodesx->lonzero;
+
+ SeekFileBuffered(fd,0);
+ WriteFileBuffered(fd,&nodesfile,sizeof(NodesFile));
+
+ CloseFileBuffered(fd);
+
+ /* Free the memory in the segments */
+
+ log_free(segmentsx->firstnode);
+ free(segmentsx->firstnode);
+ segmentsx->firstnode=NULL;
+
+ /* Print the final message */
+
+ printf_last("Wrote Nodes: Nodes=%"Pindex_t,nodesx->number);
+}
diff -Nru routino-3.4.2/src/nodesx.c routino-3.4.3/src/nodesx.c
--- routino-3.4.2/src/nodesx.c  2022-05-21 19:49:02.000000000 +0200
+++ routino-3.4.3/src/nodesx.c  2025-04-06 15:07:16.000000000 +0200
@@ -3,7 +3,7 @@
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2008-2015, 2019, 2020, 2022 Andrew M. Bishop
+ This file Copyright 2008-2015, 2019, 2020, 2022, 2025 Andrew M. Bishop
 
  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU Affero General Public License as published by
@@ -71,6 +71,8 @@
 {
  NodesX *nodesx;
 
+ logassert(sizeof(node_t)>=sizeof(index_t),"Size of node_t type must be at 
least as large as size of index_t type.");
+
  nodesx=(NodesX*)calloc_logassert(1,sizeof(NodesX));
 
  nodesx->filename    =(char*)malloc_logassert(strlen(option_tmpdirname)+32);
diff -Nru routino-3.4.2/src/planetsplitter.c routino-3.4.3/src/planetsplitter.c
--- routino-3.4.2/src/planetsplitter.c  2024-05-27 17:21:43.000000000 +0200
+++ routino-3.4.3/src/planetsplitter.c  2025-04-05 17:38:54.000000000 +0200
@@ -3,7 +3,7 @@
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2008-2017, 2023 Andrew M. Bishop
+ This file Copyright 2008-2017, 2023, 2025 Andrew M. Bishop
 
  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU Affero General Public License as published by
@@ -278,17 +278,18 @@
 
          if((p=strstr(filename,".pbf")) && !strcmp(p,".pbf"))
            {
-            logassert(0,"Unable to read a PBF file to apply changes (format 
does not permit this)");
+            fprintf(stderr,"Error: Unable to read a PBF file to apply changes 
(format does not permit this).\n");
+            exit(EXIT_FAILURE);
            }
          else if((p=strstr(filename,".o5c")) && !strcmp(p,".o5c"))
            {
             if(ParseO5CFile(fd,OSMNodes,OSMWays,OSMRelations))
-               exit(EXIT_FAILURE);
+               exit(EXIT_FAILURE); /* no message because one printed in parser 
function */
            }
          else
            {
             if(ParseOSCFile(fd,OSMNodes,OSMWays,OSMRelations))
-               exit(EXIT_FAILURE);
+               exit(EXIT_FAILURE); /* no message because one printed in parser 
function */
            }
         }
       else
@@ -299,17 +300,17 @@
          if((p=strstr(filename,".pbf")) && !strcmp(p,".pbf"))
            {
             if(ParsePBFFile(fd,OSMNodes,OSMWays,OSMRelations))
-               exit(EXIT_FAILURE);
+               exit(EXIT_FAILURE); /* no message because one printed in parser 
function */
            }
          else if((p=strstr(filename,".o5m")) && !strcmp(p,".o5m"))
            {
             if(ParseO5MFile(fd,OSMNodes,OSMWays,OSMRelations))
-               exit(EXIT_FAILURE);
+               exit(EXIT_FAILURE); /* no message because one printed in parser 
function */
            }
          else
            {
             if(ParseOSMFile(fd,OSMNodes,OSMWays,OSMRelations))
-               exit(EXIT_FAILURE);
+               exit(EXIT_FAILURE); /* no message because one printed in parser 
function */
            }
         }
 
diff -Nru routino-3.4.2/src/relationsx.c routino-3.4.3/src/relationsx.c
--- routino-3.4.2/src/relationsx.c      2024-08-29 19:34:21.000000000 +0200
+++ routino-3.4.3/src/relationsx.c      2025-04-06 15:07:32.000000000 +0200
@@ -3,7 +3,7 @@
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2010-2015, 2018, 2019, 2020, 2022, 2024 Andrew M. Bishop
+ This file Copyright 2010-2015, 2018, 2019, 2020, 2022, 2024, 2025 Andrew M. 
Bishop
 
  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU Affero General Public License as published by
@@ -75,6 +75,8 @@
 {
  RelationsX *relationsx;
 
+ logassert(sizeof(relation_t)>=sizeof(index_t),"Size of relation_t type must 
be at least as large as size of index_t type.");
+
  relationsx=(RelationsX*)calloc_logassert(1,sizeof(RelationsX));
 
 
@@ -1189,7 +1191,7 @@
     relationx.via =nodesx->pdata[relationx.via];
     relationx.to  =nodesx->pdata[relationx.to];
 
-    if(relationx.from==NO_WAY_ID || relationx.via==NO_NODE_ID || 
relationx.to==NO_WAY_ID)
+    if(relationx.from==NO_WAY || relationx.via==NO_NODE || 
relationx.to==NO_WAY)
        pruned++;
     else
       {
diff -Nru routino-3.4.2/src/router.c routino-3.4.3/src/router.c
--- routino-3.4.2/src/router.c  2018-09-23 11:15:07.000000000 +0200
+++ routino-3.4.3/src/router.c  2025-04-06 16:22:07.000000000 +0200
@@ -3,7 +3,7 @@
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2008-2016, 2018 Andrew M. Bishop
+ This file Copyright 2008-2016, 2018, 2025 Andrew M. Bishop
 
  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU Affero General Public License as published by
@@ -625,7 +625,7 @@
     
results[nresults]=CalculateRoute(OSMNodes,OSMSegments,OSMWays,OSMRelations,profile,start_node,join_segment,finish_node,start_waypoint,finish_waypoint);
 
     if(!results[nresults])
-       exit(EXIT_FAILURE);
+       exit(EXIT_FAILURE); /* no message because one printed in route 
calculator */
 
     join_segment=results[nresults]->last_segment;
 
@@ -648,7 +648,7 @@
     
results[nresults]=CalculateRoute(OSMNodes,OSMSegments,OSMWays,OSMRelations,profile,start_node,join_segment,finish_node,start_waypoint,finish_waypoint);
 
     if(!results[nresults])
-       exit(EXIT_FAILURE);
+       exit(EXIT_FAILURE); /* no message because one printed in route 
calculator */
 
     nresults++;
    }
diff -Nru routino-3.4.2/src/sorting.c routino-3.4.3/src/sorting.c
--- routino-3.4.2/src/sorting.c 2023-07-16 17:50:20.000000000 +0200
+++ routino-3.4.3/src/sorting.c 2025-04-08 19:52:05.000000000 +0200
@@ -3,7 +3,7 @@
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2009-2015, 2017, 2019, 2023 Andrew M. Bishop
+ This file Copyright 2009-2015, 2017, 2019, 2023, 2025 Andrew M. Bishop
 
  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU Affero General Public License as published by
@@ -921,7 +921,7 @@
 
     fds[i]=ReOpenFileBuffered(filename);
 
-    DeleteFile(filename);
+    DeleteFileBuffered(filename);
    }
 
  /* Fill the heap to start with */
diff -Nru routino-3.4.2/src/uncompress.c routino-3.4.3/src/uncompress.c
--- routino-3.4.2/src/uncompress.c      2015-06-09 19:48:56.000000000 +0200
+++ routino-3.4.3/src/uncompress.c      2025-04-05 17:31:33.000000000 +0200
@@ -3,7 +3,7 @@
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2012-2015 Andrew M. Bishop
+ This file Copyright 2012-2015, 2025 Andrew M. Bishop
 
  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU Affero General Public License as published by
@@ -96,7 +96,7 @@
 
 #else /* USE_BZIP2 */
 
- logassert(0,"No bzip2 compression support available (re-compile and try 
again)");
+ logassert(0,"No bzip2 compression support available (re-compile and try 
again).");
 
  return(0);
 
@@ -127,7 +127,7 @@
 
 #else /* USE_GZIP */
 
- logassert(0,"No gzip compression support available (re-compile and try 
again)");
+ logassert(0,"No gzip compression support available (re-compile and try 
again).");
 
  return(0);
 
@@ -158,7 +158,7 @@
 
 #else /* USE_XZ */
 
- logassert(0,"No xz compression support available (re-compile and try again)");
+ logassert(0,"No xz compression support available (re-compile and try 
again).");
 
  return(0);
 
@@ -264,7 +264,7 @@
  int state;
 
  if(BZ2_bzDecompressInit(&bz,0,0)!=BZ_OK)
-    exit(EXIT_FAILURE);
+    exit(EXIT_FAILURE); /* no error message because in sub-process */
 
  do
    {
@@ -287,7 +287,7 @@
     state=BZ2_bzDecompress(&bz);
 
     if(state!=BZ_OK && state!=BZ_STREAM_END)
-       exit(EXIT_FAILURE);
+       exit(EXIT_FAILURE); /* no error message because in sub-process */
 
     if(bz.avail_out<sizeof(outbuffer))
       {
@@ -302,7 +302,7 @@
           m=write(pipefd,p,n);
 
           if(m<=0)
-             exit(EXIT_FAILURE);
+             exit(EXIT_FAILURE); /* no error message because in sub-process */
 
           p+=m;
           n-=m;
@@ -312,7 +312,7 @@
  while(state!=BZ_STREAM_END);
 
  if(BZ2_bzDecompressEnd(&bz)!=BZ_OK)
-    exit(EXIT_FAILURE);
+    exit(EXIT_FAILURE); /* no error message because in sub-process */
 
  exit(EXIT_SUCCESS);
 }
@@ -338,7 +338,7 @@
  int state;
 
  if(inflateInit2(&z,15+32)!=Z_OK)
-    exit(EXIT_FAILURE);
+    exit(EXIT_FAILURE); /* no error message because in sub-process */
 
  do
    {
@@ -361,9 +361,7 @@
     state=inflate(&z,Z_NO_FLUSH);
 
     if(state!=Z_OK && state!=Z_STREAM_END)
-      {
-       exit(EXIT_FAILURE);
-      }
+       exit(EXIT_FAILURE); /* no error message because in sub-process */
 
     if(z.avail_out<sizeof(outbuffer))
       {
@@ -378,7 +376,7 @@
           m=write(pipefd,p,n);
 
           if(m<=0)
-             exit(EXIT_FAILURE);
+             exit(EXIT_FAILURE); /* no error message because in sub-process */
 
           p+=m;
           n-=m;
@@ -388,7 +386,7 @@
  while(state!=Z_STREAM_END);
 
  if(inflateEnd(&z)!=Z_OK)
-    exit(EXIT_FAILURE);
+    exit(EXIT_FAILURE); /* no error message because in sub-process */
 
  exit(EXIT_SUCCESS);
 }
@@ -414,7 +412,7 @@
  lzma_ret retval;
 
  if(lzma_stream_decoder(&lzma,UINT64_MAX,0)!=LZMA_OK)
-    exit(EXIT_FAILURE);
+    exit(EXIT_FAILURE); /* no error message because in sub-process */
 
  do
    {
@@ -437,9 +435,7 @@
     retval=lzma_code(&lzma,LZMA_RUN);
 
     if(retval!=LZMA_OK && retval!=LZMA_STREAM_END)
-      {
-       exit(EXIT_FAILURE);
-      }
+       exit(EXIT_FAILURE); /* no error message because in sub-process */
 
     if(lzma.avail_out<sizeof(outbuffer))
       {
@@ -454,7 +450,7 @@
           m=write(pipefd,p,n);
 
           if(m<=0)
-             exit(EXIT_FAILURE);
+             exit(EXIT_FAILURE); /* no error message because in sub-process */
 
           p+=m;
           n-=m;
@@ -465,7 +461,7 @@
 
  lzma_end(&lzma);
 
- exit(EXIT_SUCCESS);
+ exit(EXIT_SUCCESS); /* no error message because in sub-process */
 }
 
 #endif /* USE_XZ */
diff -Nru routino-3.4.2/src/version.h routino-3.4.3/src/version.h
--- routino-3.4.2/src/version.h 2025-03-23 17:22:16.000000000 +0100
+++ routino-3.4.3/src/version.h 2025-04-25 19:25:14.000000000 +0200
@@ -23,7 +23,7 @@
 #ifndef VERSION_H
 #define VERSION_H    /*+ To stop multiple inclusions. +*/
 
-#define ROUTINO_VERSION "3.4.2"
+#define ROUTINO_VERSION "3.4.3"
 
 #define ROUTINO_URL     "<http://www.routino.org/>"
 
diff -Nru routino-3.4.2/src/waysx.c routino-3.4.3/src/waysx.c
--- routino-3.4.2/src/waysx.c   2024-08-29 19:30:18.000000000 +0200
+++ routino-3.4.3/src/waysx.c   2025-04-06 15:07:24.000000000 +0200
@@ -3,7 +3,7 @@
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2008-2015, 2018, 2019, 2020, 2022, 2024 Andrew M. Bishop
+ This file Copyright 2008-2015, 2018, 2019, 2020, 2022, 2024, 2025 Andrew M. 
Bishop
 
  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU Affero General Public License as published by
@@ -73,6 +73,8 @@
 {
  WaysX *waysx;
 
+ logassert(sizeof(way_t)>=sizeof(index_t),"Size of way_t type must be at least 
as large as size of index_t type.");
+
  waysx=(WaysX*)calloc_logassert(1,sizeof(WaysX));
 
  waysx->filename    =(char*)malloc_logassert(strlen(option_tmpdirname)+32);
diff -Nru routino-3.4.2/src/xml/xsd-to-xmlparser.c 
routino-3.4.3/src/xml/xsd-to-xmlparser.c
--- routino-3.4.2/src/xml/xsd-to-xmlparser.c    2019-04-07 17:53:23.000000000 
+0200
+++ routino-3.4.3/src/xml/xsd-to-xmlparser.c    2025-04-05 17:36:07.000000000 
+0200
@@ -5,7 +5,7 @@
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2010-2015, 2019 Andrew M. Bishop
+ This file Copyright 2010-2015, 2019, 2025 Andrew M. Bishop
 
  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU Affero General Public License as published by
@@ -236,7 +236,10 @@
        tagsx[i]->nsubtagsx++;
 
        if(tagsx[i]->nsubtagsx==XMLPARSE_MAX_SUBTAGS)
-         {fprintf(stderr,"Too many subtags seen for type 
'%s'.\n",currenttype); exit(1);}
+         {
+          fprintf(stderr,"Error: Too many subtags (%d) seen for type 
'%s'.\n",XMLPARSE_MAX_SUBTAGS,currenttype);
+          exit(EXIT_FAILURE);
+         }
       }
 
  return(0);
@@ -310,7 +313,10 @@
        tagsx[i]->nattributes++;
 
        if(tagsx[i]->nattributes==XMLPARSE_MAX_ATTRS)
-         {fprintf(stderr,"Too many attributes seen for type 
'%s'.\n",currenttype); exit(1);}
+         {
+          fprintf(stderr,"Error: Too many attributes (%d) seen for type 
'%s'.\n",XMLPARSE_MAX_ATTRS,currenttype);
+          exit(EXIT_FAILURE);
+         }
       }
 
  return(0);
@@ -329,8 +335,8 @@
 
  if(ParseXML(STDIN_FILENO,xml_toplevel_tags,XMLPARSE_UNKNOWN_ATTR_IGNORE))
    {
-    fprintf(stderr,"Cannot parse XML file - exiting.\n");
-    exit(1);
+    fprintf(stderr,"Error: Cannot parse XML file - exiting.\n");
+    exit(EXIT_FAILURE);
    }
 
  /* Print the header */

Reply via email to