Re: [AD] Relative paths in the grabber

[ Thread Index | Date Index | More lists.liballeg.org/allegro-developers Archives ]


> And I think I found a bug in your algorithm, for example for
>
> 	path1 = /home/eric/
> 	path2 = /home/eric/src/test.c
>
> the result will lack the leading './'.

Hmm... I'm being overzealous here, the leading './' is not required so I 
withdraw my objection: your algorithm is fine.

Revised path attached.

-- 
Eric Botcazou
--- /home/eric/cvs/allegro/src/file.c	Mon May  5 18:44:04 2003
+++ allegro/src/file.c	Fri May 16 10:40:39 2003
@@ -14,6 +14,10 @@
  *
  *      _pack_fdopen() and related modifications by Annie Testes.
  *
+ *      Evert Glebbeek added the support for relative filenames:
+ *      make_absolute_filename(), make_relative_filename() and
+ *      is_relative_filename().
+ *
  *      See readme.txt for copyright information.
  */
 
@@ -328,6 +332,160 @@
 }
 
 
+
+/* make_absolute_filename:
+ *  Makes the absolute filename corresponding to the specified relative
+ *  filename using the specified base (PATH is absolute and represents
+ *  the base, FILENAME is the relative filename), stores it in DEST
+ *  whose size in bytes is SIZE and returns a pointer to it.
+ *  It does not append '/' to the path.
+ */
+char *make_absolute_filename(char *dest, AL_CONST char *path, AL_CONST char *filename, int size)
+{
+   char tmp[1024];
+   ASSERT(dest);
+   ASSERT(path);
+   ASSERT(filename);
+   ASSERT(size >= 0);
+
+   replace_filename(tmp, path, filename, sizeof(tmp));
+
+   fix_filename_path(dest, tmp, size);
+
+   return dest;
+}
+
+
+
+/* make_relative_filename:
+ *  Makes the relative filename corresponding to the specified absolute
+ *  filename using the specified base (PATH is absolute and represents
+ *  the base, FILENAME is the absolute filename), stores it in DEST
+ *  whose size in bytes is SIZE and returns a pointer to it, or returns
+ *  NULL if it cannot do so.
+ *  It does not append '/' to the path.
+ */
+char *make_relative_filename(char *dest, AL_CONST char *path, AL_CONST char *filename, int size)
+{
+   char *my_path, *my_filename;
+   char *reduced_path = NULL, *reduced_filename = NULL;
+   char *p1, *p2;
+   int c, c1, c2, pos;
+   ASSERT(dest);
+   ASSERT(path);
+   ASSERT(filename);
+   ASSERT(size >= 0);
+
+   /* The first check under DOS/Windows would be for the drive: since the
+    * paths are absolute, they will always contain a drive letter. Do this
+    * check under Unix too where the first character should always be '/'
+    * in order not to screw up existing DOS/Windows paths.
+    */
+   if (ugetc(path) != ugetc(filename))
+      return NULL;
+
+   my_path = ustrdup(path);
+   if (!my_path)
+      return NULL;
+
+   my_filename = ustrdup(filename);
+   if (!my_filename) {
+      free(my_path);
+      return NULL;
+   }
+
+   /* Strip the filenames to keep only the directories. */
+   usetc(get_filename(my_path), 0);
+   usetc(get_filename(my_filename), 0);
+
+   /* Both paths are on the same device. There are three cases:
+    *  - the filename is a "child" of the path in the directory tree,
+    *  - the filename is a "brother" of the path,
+    *  - the filename is only a "cousin" of the path.
+    * In the two former cases, we will only need to keep a suffix of the
+    * filename. In the latter case, we will need to back-paddle through
+    * the directory tree.
+    */
+   p1 = my_path;
+   p2 = my_filename;
+   while (((c1=ugetx(&p1)) == (c2=ugetx(&p2))) && c1 && c2) {
+      if ((c1 == '/') || (c1 == OTHER_PATH_SEPARATOR)) {
+	 reduced_path = p1;
+	 reduced_filename = p2;
+      }
+   }
+
+   if (!c1) {
+      /* If the path is exhausted, we are in one of the two former cases. */
+
+      if (!c2) {
+	 /* If the filename is also exhausted, we are in the second case.
+	  * Prepend './' to the reduced filename.
+	  */
+	 pos = usetc(dest, '.');
+	 pos += usetc(dest+pos, OTHER_PATH_SEPARATOR);
+	 usetc(dest+pos, 0);
+      }
+      else {
+	 /* Otherwise we are in the first case. Nothing to do. */
+	 usetc(dest, 0);
+      }
+   }
+   else {
+      /* Otherwise, we are in the latter case and need to count the number
+       * of remaining directories in the reduced path and prepend the same
+       * number of '../' to the reduced filename.
+       */
+      pos = 0;
+      while ((c=ugetx(&reduced_path))) {
+	 if ((c == '/') || (c == OTHER_PATH_SEPARATOR)) {
+	    pos += usetc(dest+pos, '.');
+	    pos += usetc(dest+pos, '.');
+	    pos += usetc(dest+pos, OTHER_PATH_SEPARATOR);
+	 }
+      }
+
+      usetc(dest+pos, 0);
+   }
+
+   ustrzcat(dest, size, reduced_filename);
+   ustrzcat(dest, size, get_filename(filename));
+
+   free(my_path);
+   free(my_filename);
+
+   /* Harmonize path separators. */
+   return fix_filename_slashes(dest);
+}
+
+
+
+/* is_relative_filename:
+ *  Checks whether the specified filename is relative.
+ */
+int is_relative_filename(AL_CONST char *filename)
+{
+   ASSERT(filename);
+
+   /* All filenames that start with a '.' are relative. */
+   if (ugetc(filename) == '.')
+      return TRUE;
+
+   /* Filenames that contain a device separator (DOS/Windows)
+    * or start with a '/' (Unix) are considered absolute.
+    */
+#if (defined ALLEGRO_DOS) || (defined ALLEGRO_WINDOWS)
+   if (ustrchr(filename, DEVICE_SEPARATOR)) 
+      return FALSE;
+#endif
+
+   if ((ugetc(filename) == '/') || (ugetc(filename) == OTHER_PATH_SEPARATOR))
+      return FALSE;
+
+   return TRUE;
+}
+
+
 
 /* replace_filename:
  *  Replaces filename in path with different one.
--- /home/eric/cvs/allegro/include/allegro/file.h	Thu Jan 23 14:13:07 2003
+++ allegro/include/allegro/file.h	Mon May  5 21:20:00 2003
@@ -28,6 +28,9 @@
 AL_FUNC(char *, fix_filename_case, (char *path));
 AL_FUNC(char *, fix_filename_slashes, (char *path));
 AL_FUNC(char *, fix_filename_path, (char *dest, AL_CONST char *path, int size));
+AL_FUNC(char *, make_absolute_filename, (char *dest, AL_CONST char *path, AL_CONST char *filename, int size));
+AL_FUNC(char *, make_relative_filename, (char *dest, AL_CONST char *path, AL_CONST char *filename, int size));
+AL_FUNC(int, is_relative_filename, (AL_CONST char *filename));
 AL_FUNC(char *, replace_filename, (char *dest, AL_CONST char *path, AL_CONST char *filename, int size));
 AL_FUNC(char *, replace_extension, (char *dest, AL_CONST char *filename, AL_CONST char *ext, int size));
 AL_FUNC(char *, append_filename, (char *dest, AL_CONST char *path, AL_CONST char *filename, int size));
--- /home/eric/cvs/allegro/docs/src/allegro._tx	Thu May 15 23:40:40 2003
+++ allegro/docs/src/allegro._tx	Fri May 16 00:51:14 2003
@@ -6320,6 +6320,22 @@
    Converts a partial filename into a full path, storing at most size bytes 
    into the dest buffer. Returns a copy of the dest parameter.
 
+@@char *@make_absolute_filename(char *dest, const char *path, const char *filename, int size);
+@xref make_relative_filename, is_relative_filename
+   Makes an absolute filename from an absolute path and a relative filename,
+   storing at most size bytes into the dest buffer. Returns a copy of the
+   dest parameter.
+
+@@char *@make_relative_filename(char *dest, const char *path, const char *filename, int size);
+@xref make_absolute_filename, is_relative_filename
+   Attempts to make a relative filename from an absolute path and an absolute
+   filename, storing at most size bytes into the dest buffer. Returns a copy
+   of the dest parameter if it succeeds or NULL if it fails.
+
+@@int @is_relative_filename(const char *filename);
+@xref make_absolute_filename, make_relative_filename
+   Returns TRUE if the filename is relative or FALSE if it is absolute.
+
 @\char *@replace_filename(char *dest, const char *path, 
 @@                       const char *filename, int size);
 @xref get_filename, replace_extension, append_filename


Mail converted by MHonArc 2.6.19+ http://listengine.tuxfamily.org/