]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
PR 60324 Handle long path names, don't use PATH_MAX.
authorJanne Blomqvist <jb@gcc.gnu.org>
Thu, 22 May 2014 03:51:25 +0000 (06:51 +0300)
committerJanne Blomqvist <jb@gcc.gnu.org>
Thu, 22 May 2014 03:51:25 +0000 (06:51 +0300)
From-SVN: r210738

12 files changed:
libgfortran/ChangeLog
libgfortran/config.h.in
libgfortran/configure
libgfortran/configure.ac
libgfortran/io/close.c
libgfortran/io/open.c
libgfortran/io/unit.c
libgfortran/io/unix.c
libgfortran/io/unix.h
libgfortran/libgfortran.h
libgfortran/runtime/main.c
libgfortran/runtime/string.c

index 94789e8f3a108bbc5d47a63e1ecabc610e7026e8..9f5a39bfcd6808f8b0ccb05f9c8c8a36a9e86144 100644 (file)
@@ -1,3 +1,35 @@
+2014-05-22  Janne Blomqvist  <jb@gcc.gnu.org>
+
+       PR libfortran/60324
+       * config.h.in: Regenerated.
+       * configure: Regenerated.
+       * configure.ac (AC_CHECK_FUNCS_ONCE): Check for strnlen and
+       strndup.
+       * libgfortran.h (fc_strdup): New prototype.
+       * runtime/string.c (strnlen): New fallback function.
+       (strndup): New fallback function.
+       (fc_strdup): New function.
+       * io/close.c (st_close): Use fc_strdup.
+       * io/open.c (new_unit): Likewise.
+       (already_open): Likewise.
+       * io/unit.c (filename_from_unit): Likewise.
+       * io/unix.c (unpack_filename): Remove function.
+       (regular_file): Rename to regular_file2, add path argument.
+       (regular_file): New function calling regular_file2.
+       (compare_file_filename): Use fc_strdup.
+       (find_file): Likewise.
+       (delete_file): Likewise.
+       (file_exists): Likewise.
+       (file_size): Likewise.
+       (inquire_sequential): Likewise.
+       (inquire_direct): Likewise.
+       (inquire_formatted): Likewise.
+       (inquire_access): Likewise.
+       * io/unix.h (unpack_filename): Remove prototype.
+       * runtime/main.c (please_free_exe_path_when_done): Change type to
+       bool.
+       (store_exe_path): Use malloced buffer, grow as needed.
+
 2014-05-17  Jerry DeLisle  <jvdelisle@gcc.gnu>
 
        PR libfortran/52539
index 8298f8d7812939fbdf84c8a68b67a8ff13b1a79b..110fb6d5b9a87552eb1278d918086caa1fdc8ce7 100644 (file)
 /* Define to 1 if you have the <string.h> header file. */
 #undef HAVE_STRING_H
 
+/* Define to 1 if you have the `strndup' function. */
+#undef HAVE_STRNDUP
+
+/* Define to 1 if you have the `strnlen' function. */
+#undef HAVE_STRNLEN
+
 /* Define to 1 if you have the `strtof' function. */
 #undef HAVE_STRTOF
 
index 8e5db19c8a93a72371ebf17d43ee0647bac25bdc..d46a6e2d796055e495d510de496aac83fd3c5057 100755 (executable)
@@ -2599,6 +2599,8 @@ as_fn_append ac_func_list " getegid"
 as_fn_append ac_func_list " secure_getenv"
 as_fn_append ac_func_list " __secure_getenv"
 as_fn_append ac_func_list " mkostemp"
+as_fn_append ac_func_list " strnlen"
+as_fn_append ac_func_list " strndup"
 as_fn_append ac_header_list " math.h"
 # Check that the precious variables saved in the cache have kept the same
 # value.
@@ -12344,7 +12346,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 12347 "configure"
+#line 12349 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -12450,7 +12452,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 12453 "configure"
+#line 12455 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -16601,6 +16603,10 @@ done
 
 
 
+
+
+
+
 
 
 
index 212628540a24f14065e47341a3e726affcd4cbe3..fb29c147666fe5dc0f655e443a693f5104d2feb6 100644 (file)
@@ -287,7 +287,7 @@ else
    strcasestr getrlimit gettimeofday stat fstat lstat getpwuid vsnprintf dup \
    getcwd localtime_r gmtime_r getpwuid_r ttyname_r clock_gettime \
    readlink getgid getpid getppid getuid geteuid umask getegid \
-   secure_getenv __secure_getenv mkostemp)
+   secure_getenv __secure_getenv mkostemp strnlen strndup)
 fi
 
 # Check strerror_r, cannot be above as versions with two and three arguments exist
index 63da0c420cd9b6fab84b519cd42e06b2ccf39a17..55f49da831ecb07b4a687d62eeaad09f45cb3b56 100644 (file)
@@ -72,8 +72,7 @@ st_close (st_parameter_close *clp)
            generate_error (&clp->common, LIBERROR_BAD_OPTION,
                            "Can't KEEP a scratch file on CLOSE");
 #if !HAVE_UNLINK_OPEN_FILE
-         path = (char *) gfc_alloca (u->file_len + 1);
-          unpack_filename (path, u->file, u->file_len);
+         path = fc_strdup (u->file, u->file_len);
 #endif
        }
       else
@@ -83,8 +82,7 @@ st_close (st_parameter_close *clp)
 #if HAVE_UNLINK_OPEN_FILE
              delete_file (u);
 #else
-             path = (char *) gfc_alloca (u->file_len + 1);
-              unpack_filename (path, u->file, u->file_len);
+             path = fc_strdup (u->file, u->file_len);
 #endif
             }
        }
@@ -93,7 +91,10 @@ st_close (st_parameter_close *clp)
 
 #if !HAVE_UNLINK_OPEN_FILE
       if (path != NULL)
-        unlink (path);
+       {
+         unlink (path);
+         free (path);
+       }
 #endif
     }
 
index 06fd59415fe66fe1f2e6d41753db2286fdb0038b..b803859ac3d8f45318c149007e0d655163a38589 100644 (file)
@@ -502,12 +502,9 @@ new_unit (st_parameter_open *opp, gfc_unit *u, unit_flags * flags)
   s = open_external (opp, flags);
   if (s == NULL)
     {
-      char *path, *msg;
-      size_t msglen;
-      path = (char *) gfc_alloca (opp->file_len + 1);
-      msglen = opp->file_len + 51;
-      msg = (char *) gfc_alloca (msglen);
-      unpack_filename (path, opp->file, opp->file_len);
+      char *path = fc_strdup (opp->file, opp->file_len);
+      size_t msglen = opp->file_len + 51;
+      char *msg = xmalloc (msglen);
 
       switch (errno)
        {
@@ -529,10 +526,13 @@ new_unit (st_parameter_open *opp, gfc_unit *u, unit_flags * flags)
          break;
 
        default:
+         free (msg);
          msg = NULL;
        }
 
       generate_error (&opp->common, LIBERROR_OS, msg);
+      free (msg);
+      free (path);
       goto cleanup;
     }
 
@@ -676,15 +676,6 @@ already_open (st_parameter_open *opp, gfc_unit * u, unit_flags * flags)
 
   if (!compare_file_filename (u, opp->file, opp->file_len))
     {
-#if !HAVE_UNLINK_OPEN_FILE
-      char *path = NULL;
-      if (u->file && u->flags.status == STATUS_SCRATCH)
-       {
-         path = (char *) gfc_alloca (u->file_len + 1);
-         unpack_filename (path, u->file, u->file_len);
-       }
-#endif
-
       if (sclose (u->s) == -1)
        {
          unlock_unit (u);
@@ -699,8 +690,14 @@ already_open (st_parameter_open *opp, gfc_unit * u, unit_flags * flags)
       u->file_len = 0;
 
 #if !HAVE_UNLINK_OPEN_FILE
+      char *path = NULL;
+      if (u->file && u->flags.status == STATUS_SCRATCH)
+       path = fc_strdup (u->file, u->file_len);
       if (path != NULL)
-       unlink (path);
+       {
+         unlink (path);
+         free (path);
+       }
 #endif
 
       u = new_unit (opp, u, flags);
index 385818adc1f5d0c00493f0f5a1b1f656ba3000ac..a406c9e8be91972e6658cc351cc2983ca9144b8c 100644 (file)
@@ -786,7 +786,6 @@ unit_truncate (gfc_unit * u, gfc_offset pos, st_parameter_common * common)
 char *
 filename_from_unit (int n)
 {
-  char *filename;
   gfc_unit *u;
   int c;
 
@@ -805,11 +804,7 @@ filename_from_unit (int n)
 
   /* Get the filename.  */
   if (u != NULL)
-    {
-      filename = (char *) xmalloc (u->file_len + 1);
-      unpack_filename (filename, u->file, u->file_len);
-      return filename;
-    }
+    return fc_strdup (u->file, u->file_len);
   else
     return (char *) NULL;
 }
index 76ed84effbf6348d0faf7aa753b398e732ee6281..3721b71f9948cd3080734fce982dc1a0a5611411 100644 (file)
@@ -114,9 +114,6 @@ id_from_fd (const int fd)
     typeof (b) _b = (b);       \
     _a < _b ? _a : _b; })
 
-#ifndef PATH_MAX
-#define PATH_MAX 1024
-#endif
 
 /* These flags aren't defined on all targets (mingw32), so provide them
    here.  */
@@ -1060,26 +1057,6 @@ unit_to_fd (int unit)
 }
 
 
-/* unpack_filename()-- Given a fortran string and a pointer to a
- * buffer that is PATH_MAX characters, convert the fortran string to a
- * C string in the buffer.  Returns nonzero if this is not possible.  */
-
-int
-unpack_filename (char *cstring, const char *fstring, int len)
-{
-  if (fstring == NULL)
-    return EFAULT;
-  len = fstrlen (fstring, len);
-  if (len >= PATH_MAX)
-    return ENAMETOOLONG;
-
-  memmove (cstring, fstring, len);
-  cstring[len] = '\0';
-
-  return 0;
-}
-
-
 /* Set the close-on-exec flag for an existing fd, if the system
    supports such.  */
 
@@ -1244,27 +1221,18 @@ tempfile (st_parameter_open *opp)
 }
 
 
-/* regular_file()-- Open a regular file.
+/* regular_file2()-- Open a regular file.
  * Change flags->action if it is ACTION_UNSPECIFIED on entry,
  * unless an error occurs.
  * Returns the descriptor, which is less than zero on error. */
 
 static int
-regular_file (st_parameter_open *opp, unit_flags *flags)
+regular_file2 (const char *path, st_parameter_open *opp, unit_flags *flags)
 {
-  char path[min(PATH_MAX, opp->file_len + 1)];
   int mode;
   int rwflag;
   int crflag, crflag2;
   int fd;
-  int err;
-
-  err = unpack_filename (path, opp->file, opp->file_len);
-  if (err)
-    {
-      errno = err;             /* Fake an OS error */
-      return -1;
-    }
 
 #ifdef __CYGWIN__
   if (opp->file_len == 7)
@@ -1404,6 +1372,18 @@ regular_file (st_parameter_open *opp, unit_flags *flags)
 }
 
 
+/* Wrapper around regular_file2, to make sure we free the path after
+   we're done.  */
+
+static int
+regular_file (st_parameter_open *opp, unit_flags *flags)
+{
+  char *path = fc_strdup (opp->file, opp->file_len);
+  int fd = regular_file2 (path, opp, flags);
+  free (path);
+  return fd;
+}
+
 /* open_external()-- Open an external file, unix specific version.
  * Change flags->action if it is ACTION_UNSPECIFIED on entry.
  * Returns NULL on operating system error. */
@@ -1494,8 +1474,8 @@ error_stream (void)
 int
 compare_file_filename (gfc_unit *u, const char *name, int len)
 {
-  char path[min(PATH_MAX, len + 1)];
   struct stat st;
+  int ret;
 #ifdef HAVE_WORKING_STAT
   unix_stream *s;
 #else
@@ -1504,18 +1484,21 @@ compare_file_filename (gfc_unit *u, const char *name, int len)
 # endif
 #endif
 
-  if (unpack_filename (path, name, len))
-    return 0;                  /* Can't be the same */
+  char *path = fc_strdup (name, len);
 
   /* If the filename doesn't exist, then there is no match with the
    * existing file. */
 
   if (stat (path, &st) < 0)
-    return 0;
+    {
+      ret = 0;
+      goto done;
+    }
 
 #ifdef HAVE_WORKING_STAT
   s = (unix_stream *) (u->s);
-  return (st.st_dev == s->st_dev) && (st.st_ino == s->st_ino);
+  ret = (st.st_dev == s->st_dev) && (st.st_ino == s->st_ino);
+  goto done;
 #else
 
 # ifdef __MINGW32__
@@ -1525,13 +1508,20 @@ compare_file_filename (gfc_unit *u, const char *name, int len)
   id1 = id_from_path (path);
   id2 = id_from_fd (((unix_stream *) (u->s))->fd);
   if (id1 || id2)
-    return (id1 == id2);
+    {
+      ret = (id1 == id2);
+      goto done;
+    }
 # endif
 
   if (len != u->file_len)
-    return 0;
-  return (memcmp(path, u->file, len) == 0);
+    ret = 0;
+  else
+    ret = (memcmp(path, u->file, len) == 0);
 #endif
+ done:
+  free (path);
+  return ret;
 }
 
 
@@ -1594,18 +1584,19 @@ find_file0 (gfc_unit *u, FIND_FILE0_DECL)
 gfc_unit *
 find_file (const char *file, gfc_charlen_type file_len)
 {
-  char path[min(PATH_MAX, file_len + 1)];
   struct stat st[1];
   gfc_unit *u;
 #if defined(__MINGW32__) && !HAVE_WORKING_STAT
   uint64_t id = 0ULL;
 #endif
 
-  if (unpack_filename (path, file, file_len))
-    return NULL;
+  char *path = fc_strdup (file, file_len);
 
   if (stat (path, &st[0]) < 0)
-    return NULL;
+    {
+      u = NULL;
+      goto done;
+    }
 
 #if defined(__MINGW32__) && !HAVE_WORKING_STAT
   id = id_from_path (path);
@@ -1621,7 +1612,7 @@ retry:
        {
          /* assert (u->closed == 0); */
          __gthread_mutex_unlock (&unit_lock);
-         return u;
+         goto done;
        }
 
       inc_waiting_locked (u);
@@ -1641,6 +1632,8 @@ retry:
 
       dec_waiting_unlocked (u);
     }
+ done:
+  free (path);
   return u;
 }
 
@@ -1713,16 +1706,10 @@ flush_all_units (void)
 int
 delete_file (gfc_unit * u)
 {
-  char path[min(PATH_MAX, u->file_len + 1)];
-  int err = unpack_filename (path, u->file, u->file_len);
-
-  if (err)
-    {                          /* Shouldn't be possible */
-      errno = err;
-      return 1;
-    }
-
-  return unlink (path);
+  char *path = fc_strdup (u->file, u->file_len);
+  int err = unlink (path);
+  free (path);
+  return err;
 }
 
 
@@ -1732,12 +1719,10 @@ delete_file (gfc_unit * u)
 int
 file_exists (const char *file, gfc_charlen_type file_len)
 {
-  char path[min(PATH_MAX, file_len + 1)];
-
-  if (unpack_filename (path, file, file_len))
-    return 0;
-
-  return !(access (path, F_OK));
+  char *path = fc_strdup (file, file_len);
+  int res = !(access (path, F_OK));
+  free (path);
+  return res;
 }
 
 
@@ -1746,15 +1731,12 @@ file_exists (const char *file, gfc_charlen_type file_len)
 GFC_IO_INT
 file_size (const char *file, gfc_charlen_type file_len)
 {
-  char path[min(PATH_MAX, file_len + 1)];
+  char *path = fc_strdup (file, file_len);
   struct stat statbuf;
-
-  if (unpack_filename (path, file, file_len))
-    return -1;
-
-  if (stat (path, &statbuf) < 0)
+  int err = stat (path, &statbuf);
+  free (path);
+  if (err == -1)
     return -1;
-
   return (GFC_IO_INT) statbuf.st_size;
 }
 
@@ -1767,11 +1749,15 @@ static const char yes[] = "YES", no[] = "NO", unknown[] = "UNKNOWN";
 const char *
 inquire_sequential (const char *string, int len)
 {
-  char path[min(PATH_MAX, len + 1)];
   struct stat statbuf;
 
-  if (string == NULL ||
-      unpack_filename (path, string, len) || stat (path, &statbuf) < 0)
+  if (string == NULL)
+    return unknown;
+
+  char *path = fc_strdup (string, len);
+  int err = stat (path, &statbuf);
+  free (path);
+  if (err == -1)
     return unknown;
 
   if (S_ISREG (statbuf.st_mode) ||
@@ -1791,11 +1777,15 @@ inquire_sequential (const char *string, int len)
 const char *
 inquire_direct (const char *string, int len)
 {
-  char path[min(PATH_MAX, len + 1)];
   struct stat statbuf;
 
-  if (string == NULL ||
-      unpack_filename (path, string, len) || stat (path, &statbuf) < 0)
+  if (string == NULL)
+    return unknown;
+
+  char *path = fc_strdup (string, len);
+  int err = stat (path, &statbuf);
+  free (path);
+  if (err == -1)
     return unknown;
 
   if (S_ISREG (statbuf.st_mode) || S_ISBLK (statbuf.st_mode))
@@ -1815,11 +1805,15 @@ inquire_direct (const char *string, int len)
 const char *
 inquire_formatted (const char *string, int len)
 {
-  char path[min(PATH_MAX, len + 1)];
   struct stat statbuf;
 
-  if (string == NULL ||
-      unpack_filename (path, string, len) || stat (path, &statbuf) < 0)
+  if (string == NULL)
+    return unknown;
+
+  char *path = fc_strdup (string, len);
+  int err = stat (path, &statbuf);
+  free (path);
+  if (err == -1)
     return unknown;
 
   if (S_ISREG (statbuf.st_mode) ||
@@ -1850,10 +1844,12 @@ inquire_unformatted (const char *string, int len)
 static const char *
 inquire_access (const char *string, int len, int mode)
 {
-  char path[min(PATH_MAX, len + 1)];
-
-  if (string == NULL || unpack_filename (path, string, len) ||
-      access (path, mode) < 0)
+  if (string == NULL)
+    return no;
+  char *path = fc_strdup (string, len);
+  int res = access (path, mode);
+  free (path);
+  if (res == -1)
     return no;
 
   return yes;
index 37561108c803d1ccb58f705f36c3f62644ca736a..910f2c2e6507233abb86229c8c15f3ffcbae69b3 100644 (file)
@@ -185,8 +185,4 @@ internal_proto(stream_isatty);
 extern int stream_ttyname (stream *, char *, size_t);
 internal_proto(stream_ttyname);
 
-extern int unpack_filename (char *, const char *, int);
-internal_proto(unpack_filename);
-
-
 #endif
index 0d6f432a24bf1350fd3efa86b8de198e38667ed2..ba6c1e918930f6c285d1523d46b3f34bcebee76a 100644 (file)
@@ -822,6 +822,9 @@ extern gfc_charlen_type string_len_trim_char4 (gfc_charlen_type,
                                               const gfc_char4_t *);
 export_proto(string_len_trim_char4);
 
+extern char *fc_strdup(const char *, gfc_charlen_type);
+internal_proto(fc_strdup);
+
 /* io/intrinsics.c */
 
 extern void flush_all_units (void);
index 58ec6cc49611673b6d5ef6aa338d2fb1a475c9c4..8a572ecd5efa6e30f5189682e5e13abbbdbc4436 100644 (file)
@@ -26,6 +26,7 @@ see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
 #include <stdlib.h>
 #include <string.h>
 #include <limits.h>
+#include <errno.h>
 
 
 #ifdef HAVE_UNISTD_H
@@ -70,23 +71,18 @@ static int argc_save;
 static char **argv_save;
 
 static const char *exe_path;
-static int please_free_exe_path_when_done;
+static bool please_free_exe_path_when_done;
 
 /* Save the path under which the program was called, for use in the
    backtrace routines.  */
 void
 store_exe_path (const char * argv0)
 {
-#ifndef PATH_MAX
-#define PATH_MAX 1024
-#endif
-
 #ifndef DIR_SEPARATOR   
 #define DIR_SEPARATOR '/'
 #endif
 
-  char buf[PATH_MAX], *path;
-  const char *cwd;
+  char *cwd, *path;
 
   /* This can only happen if store_exe_path is called multiple times.  */
   if (please_free_exe_path_when_done)
@@ -95,13 +91,27 @@ store_exe_path (const char * argv0)
   /* Reading the /proc/self/exe symlink is Linux-specific(?), but if
      it works it gives the correct answer.  */
 #ifdef HAVE_READLINK
-  int len;
-  if ((len = readlink ("/proc/self/exe", buf, sizeof (buf) - 1)) != -1)
+  ssize_t len, psize = 256;
+  while (1)
     {
-      buf[len] = '\0';
-      exe_path = strdup (buf);
-      please_free_exe_path_when_done = 1;
-      return;
+      path = xmalloc (psize);
+      len = readlink ("/proc/self/exe", path, psize);
+      if (len < 0)
+       {
+         free (path);
+         break;
+       }
+      else if (len < psize)
+       {
+         path[len] = '\0';
+         exe_path = strdup (path);
+         free (path);
+         please_free_exe_path_when_done = true;
+         return;
+       }
+      /* The remaining option is len == psize.  */
+      free (path);
+      psize *= 4;
     }
 #endif
 
@@ -117,12 +127,29 @@ store_exe_path (const char * argv0)
 #endif
     {
       exe_path = argv0;
-      please_free_exe_path_when_done = 0;
+      please_free_exe_path_when_done = false;
       return;
     }
 
 #ifdef HAVE_GETCWD
-  cwd = getcwd (buf, sizeof (buf));
+  size_t cwdsize = 256;
+  while (1)
+    {
+      cwd = xmalloc (cwdsize);
+      if (getcwd (cwd, cwdsize))
+       break;
+      else if (errno == ERANGE)
+       {
+         free (cwd);
+         cwdsize *= 4;
+       }
+      else
+       {
+         free (cwd);
+         cwd = NULL;
+         break;
+       }
+    }
 #else
   cwd = NULL;
 #endif
@@ -130,7 +157,7 @@ store_exe_path (const char * argv0)
   if (!cwd)
     {
       exe_path = argv0;
-      please_free_exe_path_when_done = 0;
+      please_free_exe_path_when_done = false;
       return;
     }
 
@@ -138,10 +165,11 @@ store_exe_path (const char * argv0)
      if the executable is not in the cwd, but at this point we're out
      of better ideas.  */
   size_t pathlen = strlen (cwd) + 1 + strlen (argv0) + 1;
-  path = malloc (pathlen);
+  path = xmalloc (pathlen);
   snprintf (path, pathlen, "%s%c%s", cwd, DIR_SEPARATOR, argv0);
+  free (cwd);
   exe_path = path;
-  please_free_exe_path_when_done = 1;
+  please_free_exe_path_when_done = true;
 }
 
 
index a7f68bf5aa159bb8aae250b52907877d27c0719e..5beb0fbd97139d670dfd31b81fda53f8804a820c 100644 (file)
@@ -90,6 +90,49 @@ cf_strcpy (char *dest, gfc_charlen_type dest_len, const char *src)
 }
 
 
+#ifndef HAVE_STRNLEN
+static size_t
+strnlen (const char *s, size_t maxlen)
+{
+  for (size_t ii = 0; ii < maxlen; ii++)
+    {
+      if (s[ii] == '\0')
+       return ii;
+    }
+  return maxlen;
+}
+#endif
+
+
+#ifndef HAVE_STRNDUP
+static char *
+strndup (const char *s, size_t n)
+{
+  size_t len = strnlen (s, n);
+  char *p = malloc (len + 1);
+  if (!p)
+    return NULL;
+  memcpy (p, s, len);
+  p[len] = '\0';
+  return p;
+}
+#endif
+
+
+/* Duplicate a non-null-terminated Fortran string to a malloced
+   null-terminated C string.  */
+
+char *
+fc_strdup (const char *src, gfc_charlen_type src_len)
+{
+  gfc_charlen_type n = fstrlen (src, src_len);
+  char *p = strndup (src, n);
+  if (!p)
+    os_error ("Memory allocation failed in fc_strdup");
+  return p;
+}
+
+
 /* Given a fortran string and an array of st_option structures, search through
    the array to find a match.  If the option is not found, we generate an error
    if no default is provided.  */