libtool-patches
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

rewrite try_dlopen (2)


From: Ralf Wildenhues
Subject: rewrite try_dlopen (2)
Date: Tue, 5 Oct 2004 15:27:55 +0200
User-agent: Mutt/1.4.1i

Ok, I've already threatened I'd further rewrite the .la file
parsing.  Here it is.  I haven't had time to split this patch in
nice little pieces, so if you want me to do that, please say so.

This splits pars_dotla_file in a bunch of small functions, each of
which does one thing.  It introduces fairly strict checking of the
.la file syntax (not its semantics), adds an error reporting a
malformed .la file, groups .la file information into an internal
structure, which is also used by find_module.  The error reporting
could easily be improved now (e.g., report where the parse error
occured and what caused it), but that would probably imply some sort
of ltdl interface change, so I'd like to solicit if that is wanted.

My splitting between parsing and evaluating might be a little too
strict, but I think the code is easier to understand that way.

The getline clone get_line that fell out of this change could in
principle be replaced by the glibc one (and LIBOBJed on non-glibc
systems), and its current implementation is still ugly, but I did
not want to work on this now.  I'd rather do that together with
switching ltdl from stdio to fd IO sometime soon (as requested).

As as related side note, I have not made use of *scanf on purpose.  
Together with fd IO I'd really like to also remove any usage of
*scanf() or *printf().  This really saves on static library size,
and libltdl IMHO deserves to be a small library.

What's missing from this patch is a necessary change in the library
version of libltdl.  That depends on whether the error message
addition is backwards-compatible or not (a question I pointed out in
some other thread already).

Further missing bits:
- Currently no check is made for any garbage after an assignment.
E.g.,
   foo1=bar foo2=baz
would only pick up the first assignment.  A check could be added.
- Trailing white space in assignment values is not checked for.
Most notably,
   library_names='a b c '
would fail to do the right thing.  One could remove leading and
trailing white space in the value (disallowing is not possible
due to existing use).
BTW, the code now ignores white space before assignment.

Next step would be a further tackle of the rest of try_dlopen, still
ugly 300 lines of code.  Oh yeah, the documentation of the .la file
syntax..

If this patch pleases the audience, I'll write a (or several) nice
ChangeLog entry and commit this.  For better reading, the patch is
separated into the part removing the old cruft and the part adding
the new cruft ;-)

Thanks for reading this much,
Ralf


Index: libltdl/ltdl.c
===================================================================
RCS file: /cvsroot/libtool/libtool/libltdl/ltdl.c,v
retrieving revision 1.219
diff -u -r1.219 ltdl.c
--- libltdl/ltdl.c      5 Oct 2004 12:48:55 -0000       1.219
+++ libltdl/ltdl.c      5 Oct 2004 13:06:03 -0000
@@ -112,7 +112,6 @@
                                       const char *libdir, const char *dlname,
                                       const char *old_name, int installed);
 static int     load_deplibs          (lt_dlhandle handle,  char *deplibs);
-static int     trim                  (char **dest, const char *str);
 static int     try_dlopen            (lt_dlhandle *handle,
                                       const char *filename);
 static int     tryall_dlopen         (lt_dlhandle *handle,
@@ -884,145 +883,6 @@
   return errors;
 }
 
-static int
-trim (char **dest, const char *str)
-{
-  /* remove the leading and trailing "'" from str
-     and store the result in dest */
-  const char *end   = strrchr (str, '\'');
-  size_t len       = LT_STRLEN (str);
-  char *tmp;
-
-  FREE (*dest);
-
-  if (len > 3 && str[0] == '\'')
-    {
-      tmp = MALLOC (char, end - str);
-      if (!tmp)
-       return 1;
-
-      strncpy(tmp, &str[1], (end - str) - 1);
-      tmp[len-3] = LT_EOS_CHAR;
-      *dest = tmp;
-    }
-  else
-    {
-      *dest = 0;
-    }
-
-  return 0;
-}
-
-/* Read the .la file FILE. */
-static int
-parse_dotla_file(FILE *file, char **dlname, char **libdir, char **deplibs,
-    char **old_name, int *installed)
-{
-  int          errors = 0;
-  size_t       line_len = LT_FILENAME_MAX;
-  char *       line = MALLOC (char, line_len);
-
-  if (!line)
-    {
-      LT__SETERROR (FILE_NOT_FOUND);
-      return 1;
-    }
-
-  while (!feof (file))
-    {
-      if (!fgets (line, (int) line_len, file))
-       {
-         break;
-       }
-
-      /* Handle the case where we occasionally need to read a line
-        that is longer than the initial buffer size.  */
-      while ((line[LT_STRLEN(line) -1] != '\n') && (!feof (file)))
-       {
-         line = REALLOC (char, line, line_len *2);
-         if (!line)
-           {
-             fclose (file);
-             ++errors;
-             goto cleanup;
-           }
-         if (!fgets (&line[line_len -1], (int) line_len +1, file))
-           {
-             break;
-           }
-         line_len *= 2;
-       }
-
-      if (line[0] == '\n' || line[0] == '#')
-       {
-         continue;
-       }
-
-#undef  STR_DLNAME
-#define STR_DLNAME     "dlname="
-      if (strncmp (line, STR_DLNAME, sizeof (STR_DLNAME) - 1) == 0)
-       {
-         errors += trim (dlname, &line[sizeof (STR_DLNAME) - 1]);
-       }
-
-#undef  STR_OLD_LIBRARY
-#define STR_OLD_LIBRARY        "old_library="
-      else if (strncmp (line, STR_OLD_LIBRARY,
-           sizeof (STR_OLD_LIBRARY) - 1) == 0)
-       {
-         errors += trim (old_name, &line[sizeof (STR_OLD_LIBRARY) - 1]);
-       }
-#undef  STR_LIBDIR
-#define STR_LIBDIR     "libdir="
-      else if (strncmp (line, STR_LIBDIR, sizeof (STR_LIBDIR) - 1) == 0)
-       {
-         errors += trim (libdir, &line[sizeof(STR_LIBDIR) - 1]);
-       }
-
-#undef  STR_DL_DEPLIBS
-#define STR_DL_DEPLIBS "dependency_libs="
-      else if (strncmp (line, STR_DL_DEPLIBS,
-           sizeof (STR_DL_DEPLIBS) - 1) == 0)
-       {
-         errors += trim (deplibs, &line[sizeof (STR_DL_DEPLIBS) - 1]);
-       }
-      else if (streq (line, "installed=yes\n"))
-       {
-         *installed = 1;
-       }
-      else if (streq (line, "installed=no\n"))
-       {
-         *installed = 0;
-       }
-
-#undef  STR_LIBRARY_NAMES
-#define STR_LIBRARY_NAMES "library_names="
-      else if (!*dlname && strncmp (line, STR_LIBRARY_NAMES,
-           sizeof (STR_LIBRARY_NAMES) - 1) == 0)
-       {
-         char *last_libname;
-         errors += trim (dlname, &line[sizeof (STR_LIBRARY_NAMES) - 1]);
-         if (!errors
-             && *dlname
-             && (last_libname = strrchr (*dlname, ' ')) != 0)
-           {
-             last_libname = lt__strdup (last_libname + 1);
-             if (!last_libname)
-               {
-                 ++errors;
-                 goto cleanup;
-               }
-             MEMREASSIGN (*dlname, last_libname);
-           }
-       }
-
-      if (errors)
-       return 1;
-    }
-cleanup:
-  return errors;
-}
-
 /* Try to open FILENAME as a module. */
 static int
 try_dlopen (lt_dlhandle *phandle, const char *filename)
--- libltdl/ltdl.c      2004-10-05 15:05:30.000000000 +0200
+++ libltdl/ltdl.c-changed      2004-10-05 15:11:21.000000000 +0200
@@ -82,6 +82,14 @@
 
 /* --- DYNAMIC MODULE LOADING --- */
 
+/* The contents of a .la file interesting to ltdl. */
+typedef struct {
+  char *dlname;
+  char *libdir;
+  char *deplibs;
+  char *old_name;
+  int installed;
+} lt_module_data;
 
 /* The type of a function used at each iteration of  foreach_dirinpath().  */
 typedef int    foreach_callback_func (char *filename, void *data1,
@@ -109,14 +117,23 @@
                                       const char *base_name,
                                       lt_dlhandle *handle);
 static int     find_module           (lt_dlhandle *handle, const char *dir,
-                                      const char *libdir, const char *dlname,
-                                      const char *old_name, int installed);
-static int     load_deplibs          (lt_dlhandle handle,  char *deplibs);
+                                      const lt_module_data *parm);
+static int     load_deplibs          (lt_dlhandle handle, char *deplibs);
 static int     try_dlopen            (lt_dlhandle *handle,
                                       const char *filename);
 static int     tryall_dlopen         (lt_dlhandle *handle,
                                       const char *filename);
 static int     unload_deplibs        (lt_dlhandle handle);
+
+static ssize_t get_line              (char **linep, size_t *len, FILE *file);
+static int     parse_dotla_line      (char *line, char **var, char **value);
+static int     parse_dotla_file      (FILE *file, lt_module_data *parm);
+
+static void    init_module_parm      (lt_module_data *parm);
+static void    cleanup_module_parm   (lt_module_data *parm);
+static int     set_module_parm       (lt_module_data *parm,
+                                      const char *var, const char *value);
+
 static int     lt_argz_insert        (char **pargz, size_t *pargz_len,
                                       char *before, const char *entry);
 static int     lt_argz_insertinorder (char **pargz, size_t *pargz_len,
@@ -435,39 +452,40 @@
 }
 
 static int
-find_module (lt_dlhandle *handle, const char *dir, const char *libdir,
-            const char *dlname,  const char *old_name, int installed)
+find_module (lt_dlhandle *handle, const char *dir, const lt_module_data *parm)
 {
+  assert(parm != NULL);
+
   /* Try to open the old library first; if it was dlpreopened,
      we want the preopened version of it, even if a dlopenable
      module is available.  */
-  if (old_name && tryall_dlopen (handle, old_name) == 0)
+  if (parm->old_name && tryall_dlopen (handle, parm->old_name) == 0)
     {
       return 0;
     }
 
   /* Try to open the dynamic library.  */
-  if (dlname)
+  if (parm->dlname)
     {
       /* try to open the installed module */
-      if (installed && libdir)
+      if (parm->installed && parm->libdir)
        {
          if (tryall_dlopen_module (handle,
-                                   (const char *) 0, libdir, dlname) == 0)
+                                   (const char *) 0, parm->libdir, 
parm->dlname) == 0)
            return 0;
        }
 
       /* try to open the not-installed module */
-      if (!installed)
+      if (!parm->installed)
        {
-         if (tryall_dlopen_module (handle, dir, objdir, dlname) == 0)
+         if (tryall_dlopen_module (handle, dir, objdir, parm->dlname) == 0)
            return 0;
        }
 
       /* maybe it was moved to another directory */
       {
          if (tryall_dlopen_module (handle,
-                                   (const char *) 0, dir, dlname) == 0)
+                                   (const char *) 0, dir, parm->dlname) == 0)
            return 0;
       }
     }
@@ -883,6 +901,203 @@
   return errors;
 }
 
+
+/* Similar to GNU getline. */
+static ssize_t
+get_line(char **linep, size_t *len, FILE *file)
+{
+  char *line;
+
+  assert(linep);
+  assert(len);
+  assert(file);
+
+  if (*linep == NULL || *len == 0) 
+    {
+      *len = LT_FILENAME_MAX;
+      *linep = MALLOC (char, *len);
+      if (*linep == NULL)
+       {
+         LT__SETERROR (NO_MEMORY);
+         return -1;
+       }
+    }
+    
+  line = *linep;
+  if (!fgets (line, (int) *len, file))
+    return LT_STRLEN(line);
+
+  while ((line[LT_STRLEN(line) -1] != '\n') && (!feof (file)))
+    {
+      *linep = REALLOC (char, *linep, *len *2);
+      line = *linep;
+      if (!line)
+         return -1;
+      *len *= 2;
+      if (!fgets (&line[*len -1], (int) *len +1, file))
+       break;
+    }
+  return LT_STRLEN(line);
+}
+
+/* Parse LINE, destructively, into VAR=VALUE.  Return 0 for success.
+   Allow singly, doubly quoted or unquoted right hand side.
+   Remove quotes.  In the unquoted case, VALUE is first word
+   on the right hand side (like in a shell). */
+static int
+parse_dotla_line(char *line, char **var, char **value)
+{
+  char *p      = line;
+  char *p_end;
+
+  assert(line);
+  assert(var);
+  assert(value);
+
+  while (isspace((unsigned char)*p))
+    p++;
+
+  if (!isalpha((unsigned char)*p))
+    return -1;
+
+  *var = p++;
+  while (isalnum((unsigned char)*p) || *p == '_')
+    p++;
+  if (*p != '=')
+    return -1;
+  *p++ = LT_EOS_CHAR;
+
+  if (*p == '"' || *p == '\'')
+    {
+      *value = ++p;
+      p_end = strchr(p, p[-1]);
+      if (p_end == NULL)
+       return -1;
+    }
+  else
+    {
+      *value = p;
+      while (*p && !isspace((unsigned char)*p))
+       p++;
+      p_end = p;
+    }
+  /* FIXME: Should we check for stuff after the end? */
+  *p_end = LT_EOS_CHAR;
+  return 0;
+}
+
+ 
+static void
+init_module_parm(lt_module_data *parm)
+{
+  assert(parm);
+  parm->dlname = NULL;
+  parm->libdir = NULL;
+  parm->deplibs = NULL;
+  parm->old_name = NULL;
+  parm->installed = 0;
+}
+
+static void
+cleanup_module_parm(lt_module_data *parm)
+{
+  assert(parm);
+  FREE (parm->dlname);
+  FREE (parm->libdir);
+  FREE (parm->deplibs);
+  FREE (parm->old_name);
+}
+
+static int
+set_module_parm(lt_module_data *parm, const char *var, const char *value)
+{
+  if (!*value)
+    return 0;  /* we ignore setting empty values */
+
+  if (!strcmp(var, "dlname"))
+    {
+      FREE (parm->dlname);
+      parm->dlname = lt__strdup(value);
+    }
+  else if (!strcmp(var, "old_library"))
+    {
+      FREE (parm->old_name);
+      parm->old_name = lt__strdup(value);
+    }
+  else if (!strcmp(var, "libdir"))
+    {
+      FREE (parm->libdir);
+      parm->libdir = lt__strdup(value);
+    }
+  else if (!strcmp(var, "dependency_libs"))
+    {
+      FREE (parm->deplibs);
+      parm->deplibs = lt__strdup(value);
+    }
+  else if (!strcmp(var, "installed"))
+    {
+      if (!strcmp(value, "yes"))
+       parm->installed = 1;
+      else if (!strcmp(value, "no"))
+       parm->installed = 0;
+      else
+       return -1;
+    }
+  else if (!strcmp(var, "library_names") && parm->dlname == NULL)
+    {
+      /* Backward compatibility:
+        Take the last word in library_names as dlname. */
+      char *last_libname = strrchr (value, ' ');
+      /* FIXME: trailing white space? */
+      if (last_libname && last_libname[1] != LT_EOS_CHAR)
+       {
+         parm->dlname = lt__strdup(last_libname + 1);
+       }
+      else
+       {
+         parm->dlname = lt__strdup(value);
+       }
+    }
+  /* We do not recognize all .la variables,
+     so we do not fail on unmatched VAR. */
+
+  return 0;
+}
+
+/* Read the .la file FILE. */
+static int
+parse_dotla_file(FILE *file, lt_module_data *parm)
+{
+  int          errors = 0;
+  char *       line = NULL;
+  size_t       line_len = 0;
+  ssize_t      cur_len = 0;
+
+  for (;;)
+    {
+      char *var = NULL;
+      char *value = NULL;
+
+      cur_len = get_line(&line, &line_len, file);
+
+      if (cur_len == -1)
+       break;
+
+      if (cur_len == 0 || line[0] == '\n' || line[0] == '#')
+       continue;
+
+      if (parse_dotla_line(line, &var, &value) != 0 ||
+         set_module_parm(parm, var, value) != 0)
+       {
+         LT__SETERROR(INVALID_FILE);
+         break;
+       }
+    }
+
+  FREE(line);
+  return !feof(file);
+}
+
 /* Try to open FILENAME as a module. */
 static int
 try_dlopen (lt_dlhandle *phandle, const char *filename)
@@ -991,16 +1206,15 @@
   if (ext && streq (ext, archive_ext))
     {
       /* this seems to be a libtool module */
-      FILE *   file     = 0;
-      char *   dlname   = 0;
-      char *   old_name = 0;
-      char *   libdir   = 0;
-      char *   deplibs  = 0;
+      FILE *file = 0;
+      lt_module_data parm;
+
+      init_module_parm(&parm);
 
       /* if we can't find the installed flag, it is probably an
         installed libtool archive, produced with an old version
         of libtool */
-      int      installed = 1;
+      parm.installed = 1;
 
 
       /* Now try to open the .la file.  If there is no directory name
@@ -1052,8 +1266,7 @@
        }
 
       /* read the .la file */
-      if (parse_dotla_file(file, &dlname, &libdir, &deplibs,
-           &old_name, &installed) != 0)
+      if (parse_dotla_file(file, &parm) != 0)
        errors++;
 
       fclose (file);
@@ -1065,21 +1278,18 @@
 
       if (errors)
        {
-         FREE (dlname);
-         FREE (old_name);
-         FREE (libdir);
-         FREE (deplibs);
+         cleanup_module_parm(&parm);
          FREE (*phandle);
          goto cleanup;
        }
 
       assert (*phandle);
 
-      if (load_deplibs (*phandle, deplibs) == 0)
+      if (load_deplibs (*phandle, parm.deplibs) == 0)
        {
          newhandle = *phandle;
          /* find_module may replace newhandle */
-         if (find_module (&newhandle, dir, libdir, dlname, old_name, 
installed))
+         if (find_module (&newhandle, dir, &parm))
            {
              unload_deplibs (*phandle);
              ++errors;
@@ -1090,10 +1300,7 @@
          ++errors;
        }
 
-      FREE (dlname);
-      FREE (old_name);
-      FREE (libdir);
-      FREE (deplibs);
+      cleanup_module_parm(&parm);
 
       if (errors)
        {
Index: libltdl/lt_error.h
===================================================================
RCS file: /cvsroot/libtool/libtool/libltdl/lt_error.h,v
retrieving revision 1.4
diff -u -r1.4 lt_error.h
--- libltdl/lt_error.h  15 Jul 2004 12:14:47 -0000      1.4
+++ libltdl/lt_error.h  5 Oct 2004 13:06:03 -0000
@@ -57,7 +57,8 @@
     LT_ERROR(SHUTDOWN,             "library already shutdown")         \
     LT_ERROR(CLOSE_RESIDENT_MODULE, "can't close resident module")     \
     LT_ERROR(INVALID_MUTEX_ARGS,    "internal error (code withdrawn)")  \
-    LT_ERROR(INVALID_POSITION,     "invalid search path insert position")
+    LT_ERROR(INVALID_POSITION,     "invalid search path insert position") \
+    LT_ERROR(INVALID_FILE,          "malformed library file")
 
 /* Enumerate the symbolic error names. */
 enum {




reply via email to

[Prev in Thread] Current Thread [Next in Thread]