]> git.street.me.uk Git - andy/viking.git/commitdiff
SF#2998555: Control of saving Absolute vs Relative Paths in .vik files
authorRob Norris <rw_norris@hotmail.com>
Sun, 24 Mar 2013 17:05:20 +0000 (17:05 +0000)
committerRob Norris <rw_norris@hotmail.com>
Wed, 17 Apr 2013 22:15:19 +0000 (23:15 +0100)
For any file name paths potentially save in relative (../../ etc...) style if specified by the preferences.

Affects:
. Waypoint images
. DEM file list
. Map directory cache
. GeoRef file image

A new level of 'Advanced' preferences tab has been created to store the controlling setting.

src/file.c
src/file.h
src/globals.c
src/globals.h
src/gpspoint.c
src/vikdemlayer.c
src/vikgeoreflayer.c
src/vikmapslayer.c

index b2be51f1448fa750e8f2f44547bf927200711231..bd5021ae58b1f8d1366b9629d5a61988a129d786 100644 (file)
@@ -820,3 +820,121 @@ char *file_realpath ( const char *path, char *real )
   return realpath ( path, real );
 }
 
+/**
+ * Permission granted to use this code after personal correspondance
+ * Slightly reworked for better cross platform use, glibisms, function rename and a compacter format
+ *
+ * FROM http://www.codeguru.com/cpp/misc/misc/fileanddirectorynaming/article.php/c263
+ */
+
+// GetRelativeFilename(), by Rob Fisher.
+// rfisher@iee.org
+// http://come.to/robfisher
+
+// defines
+#ifndef MAXPATHLEN
+#define MAXPATHLEN 1024
+#endif
+// The number of characters at the start of an absolute filename.  e.g. in DOS,
+// absolute filenames start with "X:\" so this value should be 3, in UNIX they start
+// with "\" so this value should be 1.
+#ifdef WINDOWS
+#define ABSOLUTE_NAME_START 3
+#else
+#define ABSOLUTE_NAME_START 1
+#endif
+
+// Given the absolute current directory and an absolute file name, returns a relative file name.
+// For example, if the current directory is C:\foo\bar and the filename C:\foo\whee\text.txt is given,
+// GetRelativeFilename will return ..\whee\text.txt.
+
+const gchar *file_GetRelativeFilename ( gchar *currentDirectory, gchar *absoluteFilename )
+{
+  gint afMarker = 0, rfMarker = 0;
+  gint cdLen = 0, afLen = 0;
+  gint i = 0;
+  gint levels = 0;
+  static gchar relativeFilename[MAXPATHLEN+1];
+
+  cdLen = strlen(currentDirectory);
+  afLen = strlen(absoluteFilename);
+
+  // make sure the names are not too long or too short
+  if (cdLen > MAXPATHLEN || cdLen < ABSOLUTE_NAME_START+1 ||
+      afLen > MAXPATHLEN || afLen < ABSOLUTE_NAME_START+1) {
+    return NULL;
+  }
+
+  // Handle DOS names that are on different drives:
+  if (currentDirectory[0] != absoluteFilename[0]) {
+    // not on the same drive, so only absolute filename will do
+    strcpy(relativeFilename, absoluteFilename);
+    return relativeFilename;
+  }
+
+  // they are on the same drive, find out how much of the current directory
+  // is in the absolute filename
+  i = ABSOLUTE_NAME_START;
+  while (i < afLen && i < cdLen && currentDirectory[i] == absoluteFilename[i]) {
+    i++;
+  }
+
+  if (i == cdLen && (absoluteFilename[i] == G_DIR_SEPARATOR || absoluteFilename[i-1] == G_DIR_SEPARATOR)) {
+    // the whole current directory name is in the file name,
+    // so we just trim off the current directory name to get the
+    // current file name.
+    if (absoluteFilename[i] == G_DIR_SEPARATOR) {
+      // a directory name might have a trailing slash but a relative
+      // file name should not have a leading one...
+      i++;
+    }
+
+    strcpy(relativeFilename, &absoluteFilename[i]);
+    return relativeFilename;
+  }
+
+  // The file is not in a child directory of the current directory, so we
+  // need to step back the appropriate number of parent directories by
+  // using "..\"s.  First find out how many levels deeper we are than the
+  // common directory
+  afMarker = i;
+  levels = 1;
+
+  // count the number of directory levels we have to go up to get to the
+  // common directory
+  while (i < cdLen) {
+    i++;
+    if (currentDirectory[i] == G_DIR_SEPARATOR) {
+      // make sure it's not a trailing slash
+      i++;
+      if (currentDirectory[i] != '\0') {
+       levels++;
+      }
+    }
+  }
+
+  // move the absolute filename marker back to the start of the directory name
+  // that it has stopped in.
+  while (afMarker > 0 && absoluteFilename[afMarker-1] != G_DIR_SEPARATOR) {
+    afMarker--;
+  }
+
+  // check that the result will not be too long
+  if (levels * 3 + afLen - afMarker > MAXPATHLEN) {
+    return NULL;
+  }
+
+  // add the appropriate number of "..\"s.
+  rfMarker = 0;
+  for (i = 0; i < levels; i++) {
+    relativeFilename[rfMarker++] = '.';
+    relativeFilename[rfMarker++] = '.';
+    relativeFilename[rfMarker++] = G_DIR_SEPARATOR;
+  }
+
+  // copy the rest of the filename into the result string
+  strcpy(&relativeFilename[rfMarker], &absoluteFilename[afMarker]);
+
+  return relativeFilename;
+}
+/* END http://www.codeguru.com/cpp/misc/misc/fileanddirectorynaming/article.php/c263 */
index 7625ec339b6250c5f1dad32a5112a020c39c1f1f..0cbf98fe9eb48ac4e0a1acfbcd4265f8a171548c 100644 (file)
@@ -65,6 +65,8 @@ void file_write_layer_param ( FILE *f, const gchar *name, VikLayerParamType type
 
 char *file_realpath ( const char *path, char *real );
 
+const gchar *file_GetRelativeFilename ( gchar *currentDirectory, gchar *absoluteFilename );
+
 G_END_DECLS
 
 #endif
index 510fe48f4d28875b3ff1b50f2dd3d5ce499a79e5..e5d614792acf738b985fe9af1e41337a9f351334 100644 (file)
@@ -39,7 +39,8 @@ static gchar * params_units_speed[] = {"km/h", "mph", "m/s", "knots", NULL};
 static gchar * params_units_height[] = {"Metres", "Feet", NULL};
 static VikLayerParamScale params_scales_lat[] = { {-90.0, 90.0, 0.05, 2} };
 static VikLayerParamScale params_scales_long[] = { {-180.0, 180.0, 0.05, 2} };
+static gchar * params_vik_fileref[] = {N_("Absolute"), N_("Relative"), NULL};
+
 static VikLayerParam prefs1[] = {
   { VIK_LAYER_NUM_TYPES, VIKING_PREFERENCES_NAMESPACE "degree_format", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, N_("Degree format:"), VIK_LAYER_WIDGET_COMBOBOX, params_degree_formats, NULL, NULL },
 };
@@ -91,6 +92,11 @@ static VikLayerParam io_prefs_external_gpx[] = {
   { VIK_LAYER_NUM_TYPES, VIKING_PREFERENCES_IO_NAMESPACE "external_gpx_2", VIK_LAYER_PARAM_STRING, VIK_LAYER_GROUP_NONE, N_("External GPX Program 2:"), VIK_LAYER_WIDGET_FILEENTRY, NULL, NULL, NULL },
 };
 
+static VikLayerParam prefs_advanced[] = {
+  { VIK_LAYER_NUM_TYPES, VIKING_PREFERENCES_ADVANCED_NAMESPACE "save_file_reference_mode", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, N_("Save File Reference Mode:"), VIK_LAYER_WIDGET_COMBOBOX, params_vik_fileref, NULL,
+    N_("When saving a Viking .vik file, this determines how the directory paths of filenames are written."), NULL },
+};
+
 /* End of Options static stuff */
 
 void a_vik_preferences_init ()
@@ -146,6 +152,9 @@ void a_vik_preferences_init ()
 
   // 'Advanced' Properties
   a_preferences_register_group ( VIKING_PREFERENCES_ADVANCED_GROUP_KEY, _("Advanced") );
+
+  tmp.u = VIK_FILE_REF_FORMAT_ABSOLUTE;
+  a_preferences_register(&prefs_advanced[0], tmp, VIKING_PREFERENCES_ADVANCED_GROUP_KEY);
 }
 
 vik_degree_format_t a_vik_get_degree_format ( )
@@ -236,3 +245,10 @@ const gchar* a_vik_get_external_gpx_program_2 ( )
 {
   return a_preferences_get(VIKING_PREFERENCES_IO_NAMESPACE "external_gpx_2")->s;
 }
+
+vik_file_ref_format_t a_vik_get_file_ref_format ( )
+{
+  vik_file_ref_format_t format;
+  format = a_preferences_get(VIKING_PREFERENCES_ADVANCED_NAMESPACE "save_file_reference_mode")->u;
+  return format;
+}
index e1449c812326acf53008c60e124a223b951ac821..155c682a69a72f21133039eab73c7c973fd7a999 100644 (file)
@@ -157,6 +157,14 @@ const gchar* a_vik_get_external_gpx_program_1 ( );
 
 const gchar* a_vik_get_external_gpx_program_2 ( );
 
+/* File reference preferences - mainly in saving of a viking file */
+typedef enum {
+  VIK_FILE_REF_FORMAT_ABSOLUTE,
+  VIK_FILE_REF_FORMAT_RELATIVE,
+} vik_file_ref_format_t;
+
+vik_file_ref_format_t a_vik_get_file_ref_format ( );
+
 /* Group for global preferences */
 #define VIKING_PREFERENCES_GROUP_KEY "viking.globals"
 #define VIKING_PREFERENCES_NAMESPACE "viking.globals."
index 41dd2982f35ac3aae596dab8dd1d75f8c1859432..868168b9f5398f8d6a14fc48399989149481debd 100644 (file)
@@ -527,8 +527,23 @@ static void a_gpspoint_write_waypoint ( const gpointer id, const VikWaypoint *wp
   }
   if ( wp->image )
   {
-    gchar *tmp_image = slashdup(wp->image);
-    fprintf ( f, " image=\"%s\"", tmp_image );
+    gchar *tmp_image = NULL;
+    gchar *cwd = NULL;
+    if ( a_vik_get_file_ref_format() == VIK_FILE_REF_FORMAT_RELATIVE ) {
+      cwd = g_get_current_dir();
+      if ( cwd )
+        tmp_image = g_strdup ( file_GetRelativeFilename ( cwd, wp->image ) );
+    }
+
+    // if cwd not available - use image filename as is
+    // this should be an absolute path as set in thumbnails
+    if ( !cwd )
+      tmp_image = slashdup(wp->image);
+
+    if ( tmp_image )
+      fprintf ( f, " image=\"%s\"", tmp_image );
+
+    g_free ( cwd );
     g_free ( tmp_image );
   }
   if ( wp->symbol )
index 5a283ed2b0e996587fa88b783fe0a733c313565e..f59d795e04f4762fc48ac81830bf0f7001620a0c 100644 (file)
@@ -349,6 +349,40 @@ static void dem_layer_thread_cancel ( dem_load_thread_data *data )
   // Thus we can see/use what was done
 }
 
+/**
+ * Process the list of DEM files and convert each one to a relative path
+ */
+static GList *dem_layer_convert_to_relative_filenaming ( GList *files )
+{
+  gchar *cwd = g_get_current_dir();
+  if ( !cwd )
+    return files;
+
+  GList *relfiles = NULL;
+
+  while ( files ) {
+    gchar *file = g_strdup ( file_GetRelativeFilename ( cwd, files->data ) );
+    relfiles = g_list_prepend ( relfiles, file );
+    files = files->next;
+  }
+
+  g_free ( cwd );
+
+  if ( relfiles ) {
+    // Replacing current list, so delete old values first.
+    GList *iter = files;
+    while ( iter ) {
+      g_free ( iter->data );
+      iter = iter->next;
+    }
+    g_list_free ( files );
+
+    return relfiles;
+  }
+
+  return files;
+}
+
 gboolean dem_layer_set_param ( VikDEMLayer *vdl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation )
 {
   switch ( id )
@@ -404,7 +438,13 @@ static VikLayerParamData dem_layer_get_param ( VikDEMLayer *vdl, guint16 id, gbo
   VikLayerParamData rv;
   switch ( id )
   {
-    case PARAM_FILES: rv.sl = vdl->files; break;
+    case PARAM_FILES:
+      rv.sl = vdl->files;
+      if ( is_file_operation )
+        // Save in relative format if necessary
+        if ( a_vik_get_file_ref_format() == VIK_FILE_REF_FORMAT_RELATIVE )
+          rv.sl = dem_layer_convert_to_relative_filenaming ( rv.sl );
+      break;
     case PARAM_SOURCE: rv.u = vdl->source; break;
     case PARAM_TYPE: rv.u = vdl->type; break;
     case PARAM_COLOR: rv.c = vdl->color; break;
@@ -1063,17 +1103,17 @@ static void weak_ref_cb ( gpointer ptr, GObject * dead_vdl )
 }
 
 /* Try to add file full_path.
- * full_path will be copied.
+ * filename will be copied.
  * returns FALSE if file does not exists, TRUE otherwise.
  */
-static gboolean dem_layer_add_file ( VikDEMLayer *vdl, const gchar *full_path )
+static gboolean dem_layer_add_file ( VikDEMLayer *vdl, const gchar *filename )
 {
-  if ( g_file_test(full_path, G_FILE_TEST_EXISTS ) == TRUE ) {
+  if ( g_file_test(filename, G_FILE_TEST_EXISTS) == TRUE ) {
     /* only load if file size is not 0 (not in progress) */
     struct stat sb;
-    stat (full_path, &sb);
+    stat ( filename, &sb );
     if ( sb.st_size ) {
-      gchar *duped_path = g_strdup(full_path);
+      gchar *duped_path = g_strdup(filename);
       vdl->files = g_list_prepend ( vdl->files, duped_path );
       a_dems_load ( duped_path );
       g_debug("%s: %s", __FUNCTION__, duped_path);
index b5e7c591fcad1e809a029871c38fe9e42f8ed4cc..ad93f72de5926ee53974bb7c8783dc4c829ee653 100644 (file)
@@ -227,7 +227,22 @@ static VikLayerParamData georef_layer_get_param ( VikGeorefLayer *vgl, guint16 i
   VikLayerParamData rv;
   switch ( id )
   {
-    case PARAM_IMAGE: rv.s = vgl->image ? vgl->image : ""; break;
+    case PARAM_IMAGE: {
+      gboolean set = FALSE;
+      if ( is_file_operation ) {
+        if ( a_vik_get_file_ref_format() == VIK_FILE_REF_FORMAT_RELATIVE ) {
+          gchar *cwd = g_get_current_dir();
+          if ( cwd ) {
+            rv.s = file_GetRelativeFilename ( cwd, vgl->image );
+           if ( !rv.s ) rv.s = "";
+            set = TRUE;
+         }
+       }
+      }
+      if ( !set )
+        rv.s = vgl->image ? vgl->image : "";
+      break;
+    }
     case PARAM_CN: rv.d = vgl->corner.northing; break;
     case PARAM_CE: rv.d = vgl->corner.easting; break;
     case PARAM_MN: rv.d = vgl->mpp_northing; break;
index a0287cdc3d8d13e8a509a45c614e167f8e9b801e..bf078aded138d3477c1277b71c055aab153dbd4f 100644 (file)
@@ -521,14 +521,29 @@ static VikLayerParamData maps_layer_get_param ( VikMapsLayer *vml, guint16 id, g
   switch ( id )
   {
     case PARAM_CACHE_DIR:
+    {
+      gboolean set = FALSE;
       /* Only save a blank when the map cache location equals the default
           On reading in, when it is blank then the default is reconstructed
           Since the default changes dependent on the user and OS, it means the resultant file is more portable */
-      if ( is_file_operation && vml->cache_dir && strcmp ( vml->cache_dir, MAPS_CACHE_DIR ) == 0 )
+      if ( is_file_operation && vml->cache_dir && strcmp ( vml->cache_dir, MAPS_CACHE_DIR ) == 0 ) {
         rv.s = "";
-      else
-        rv.s = vml->cache_dir ? vml->cache_dir : "";
+        set = TRUE;
+      }
+      else if ( is_file_operation ) {
+        if ( a_vik_get_file_ref_format() == VIK_FILE_REF_FORMAT_RELATIVE ) {
+          gchar *cwd = g_get_current_dir();
+          if ( cwd ) {
+            rv.s = file_GetRelativeFilename ( cwd, vml->cache_dir );
+            if ( !rv.s ) rv.s = "";
+            set = TRUE;
+         }
+       }
+      }
+      if ( !set )
+       rv.s = vml->cache_dir ? vml->cache_dir : "";
       break;
+    }
     case PARAM_MAPTYPE: rv.u = map_index_to_uniq_id ( vml->maptype ); break;
     case PARAM_ALPHA: rv.u = vml->alpha; break;
     case PARAM_AUTODOWNLOAD: rv.u = vml->autodownload; break;