Re: [AD] Relative paths in the grabber

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


> Maybe "make" after all, or "build"...

I eventually settled for 'make' and also decided to add 
is_relative_filename() to the API. Patch attached, as well as a little test 
to exercise it.

Evert, as the functions will be in the library, I stripped the non strictly 
necessary parts so as to make them as much orthogonal to the other functions 
as possible. Of course the grabber will have to combine several of these to 
be robust.

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 './'.

-- 
Eric Botcazou
--- /home/eric/cvs/allegro/src/file.c	Mon May  5 18:44:04 2003
+++ allegro/src/file.c	Fri May 16 02:36:42 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,151 @@
 }
 
 
+
+/* 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 two cases:
+    *  - the filename is a "child" of the path in the directory tree,
+    *  - the filename is only a "cousin" of the path.
+    * In the former case, 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 the former case. We only need
+       * to 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 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);
+
+   return 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;
+
+   /* Assume relativeness by default. */
+   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
/* Usage: ./test a|r|i [path] filename */

#include <allegro.h>
#include <stdio.h>

#define ALLEGRO_USE_CONSOLE

int main(int argc, char *argv[])
{
   char tmp[1024];

   set_uformat(U_ASCII);
   install_allegro(SYSTEM_NONE, &errno, NULL);

   if (argc == 1)
      return -1;
   
   switch (argv[1][0]) {

      case 'a':  /* absolute */
         if (argc != 4)
	    return -1;

         puts(make_absolute_filename(tmp, argv[2], argv[3], sizeof(tmp)));
	 putchar('\n');
         break;

      case 'r':  /* relative */
         if (argc != 4)
	    return -1;

         if (make_relative_filename(tmp, argv[2], argv[3], sizeof(tmp))) {
            puts(tmp);
	    putchar('\n');
	 }
         break;

      case 'i':  /* is relative ? */
	 if (argc != 3)
	    return -1;

	 puts(is_relative_filename(argv[2]) ? "relative\n" : "absolute\n");
	 break;
   }

   return 0;
}
END_OF_MAIN()


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