]> git.draconx.ca Git - upkg.git/blobdiff - src/package.c
libupkg: Parameterise I/O functions to allow custom streams.
[upkg.git] / src / package.c
index a0957563bb4246985456542556794420325cb0cd..cbbaf09dd71efbcd6e0fb247cb78001e49e508c9 100644 (file)
@@ -2,9 +2,9 @@
  *  upkg: tool for manipulating Unreal Tournament packages.
  *  Copyright (C) 2009 Nick Bowler
  *
- *  This program is free software; you can redistribute it and/or modify
+ *  This program is free software: you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
+ *  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,
@@ -13,8 +13,7 @@
  *  GNU General Public License for more details.
  *
  *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
 #include <stdio.h>
 
 struct upkg_priv {
        lt_dlhandle native;
+       FILE *pkg_file;
 };
 
 G_DEFINE_TYPE(UPkg, u_pkg, G_TYPE_TYPE_MODULE);
 
+/* Package search path. */
+static char  *search_path;
+static size_t search_path_sz;
+
+/* List of package file extensions, in descending order of precedence. */
+static const char u_pkg_exts[][5] = { ".u", ".utx", ".umx", ".uax", ".unr" };
+
 static char *str_cpy_lower(char *dst, const char *src)
 {
        size_t i;
@@ -47,6 +54,25 @@ static char *str_cpy_lower(char *dst, const char *src)
        return dst;
 }
 
+static int str_cmp_lower(const char *s1, const char *s2)
+{
+       size_t i;
+
+       for (i = 0; s1[i] && s2[i]; i++) {
+               int c1 = tolower(s1[i]), c2 = tolower(s2[i]);
+               if (c1 < c2)
+                       return -1;
+               if (c1 > c2)
+                       return 1;
+       }
+
+       if (s1[i])
+               return 1;
+       if (s2[i])
+               return -1;
+       return 0;
+}
+
 static void dl_print_errors(const char *prefix)
 {
        const char *err;
@@ -56,6 +82,50 @@ static void dl_print_errors(const char *prefix)
        }
 }
 
+/*
+ * Function for use with lt_dlforeachfile.  The user data must point to
+ * a UPkg with the GTypeModule name field initialized.  If a suitable file
+ * is found and could be successfully opened for reading, the pkg_file member
+ * of struct upkg_priv will be filled in.
+ */
+static int find_package_by_name(const char *filename, void *_pkg)
+{
+       struct upkg_priv *priv = U_PKG_GET_PRIV(_pkg);
+       GTypeModule *m = _pkg;
+
+       const char *base;
+       size_t len;
+       char *buf;
+
+       base = strrchr(filename, '/');
+       if (base) {
+               base++;
+       } else {
+               base = filename;
+       }
+
+       if (str_cmp_lower(base, m->name) != 0)
+               return 0;
+
+       len = strlen(filename);
+       buf = malloc(len + sizeof **u_pkg_exts);
+       if (!buf)
+               return 0;
+       strcpy(buf, filename);
+
+       for (unsigned i = 0; i < sizeof u_pkg_exts / sizeof *u_pkg_exts; i++) {
+               strcpy(buf+len, u_pkg_exts[i]);
+               priv->pkg_file = fopen(buf, "rb");
+               if (priv->pkg_file) {
+                       free(buf);
+                       return 1;
+               }
+       }
+
+       free(buf);
+       return 0;
+}
+
 static gboolean u_pkg_load(GTypeModule *m)
 {
        struct upkg_priv *priv = U_PKG_GET_PRIV(m);
@@ -74,6 +144,8 @@ static gboolean u_pkg_load(GTypeModule *m)
                return FALSE;
        }
 
+       lt_dlforeachfile(u_pkg_get_search_path(), find_package_by_name, m);
+
        return TRUE;
 }
 
@@ -92,6 +164,10 @@ static void u_pkg_unload(GTypeModule *m)
                        dl_print_errors(__func__);
                }
        }
+
+       if (priv->pkg_file) {
+               fclose(priv->pkg_file);
+       }
 }
 
 static void u_pkg_init(UPkg *pkg)
@@ -121,6 +197,55 @@ static void u_pkg_class_init(UPkgClass *class)
        modclass->unload = u_pkg_unload;
 }
 
+static int expand_search_path(size_t need)
+{
+       size_t want = search_path_sz;
+       if (want == 0) want = 1;
+
+       while (want < need)
+               want *= 2;
+
+       if (want > search_path_sz) {
+               char *new = realloc(search_path, want);
+               if (!new) {
+                       return -1;
+               }
+
+               search_path    = new;
+               search_path_sz = want;
+       }
+
+       return 0;
+}
+
+const char *u_pkg_get_search_path(void)
+{
+       return search_path ? search_path : "";
+}
+
+int u_pkg_set_search_path(const char *path)
+{
+       if (expand_search_path(strlen(path)+1) != 0)
+               return -1;
+       strcpy(search_path, path);
+       return 0;
+}
+
+int u_pkg_add_search_dir(const char *path)
+{
+       size_t end = search_path ? strlen(search_path) : 0;
+
+       if (end == 0) {
+               return u_pkg_set_search_path(path);
+       }
+
+       if (expand_search_path(end + strlen(path) + 2) != 0)
+               return -1;
+       search_path[end] = LT_PATHSEP_CHAR;
+       strcpy(search_path+end+1, path);
+       return 0;
+}
+
 GTypeModule *u_pkg_new(const char *name)
 {
        g_return_val_if_fail(name != NULL, NULL);