]> sjero.net Git - wget/blobdiff - src/utils.c
[svn] Concatenate strings using concat_strings.
[wget] / src / utils.c
index 7259d019197c8081700d5a3c342f683f41ea065d..e5098be38a5cbc4780d0381068adb873973b27eb 100644 (file)
@@ -226,6 +226,54 @@ aprintf (const char *fmt, ...)
     }
   return NULL;                 /* unreached */
 }
+
+/* Concatenate the NULL-terminated list of string arguments into
+   freshly allocated space.  */
+
+char *
+concat_strings (const char *str0, ...)
+{
+  va_list args;
+  int saved_lengths[5];                /* inspired by Apache's apr_pstrcat */
+  char *ret, *p;
+
+  const char *next_str;
+  int total_length = 0;
+  int argcount;
+
+  /* Calculate the length of and allocate the resulting string. */
+
+  argcount = 0;
+  VA_START (args, str0);
+  for (next_str = str0; next_str != NULL; next_str = va_arg (args, char *))
+    {
+      int len = strlen (next_str);
+      if (argcount < countof (saved_lengths))
+       saved_lengths[argcount++] = len;
+      total_length += len;
+    }
+  va_end (args);
+  p = ret = xmalloc (total_length + 1);
+
+  /* Copy the strings into the allocated space. */
+
+  argcount = 0;
+  VA_START (args, str0);
+  for (next_str = str0; next_str != NULL; next_str = va_arg (args, char *))
+    {
+      int len;
+      if (argcount < countof (saved_lengths))
+       len = saved_lengths[argcount++];
+      else
+       len = strlen (next_str);
+      memcpy (p, next_str, len);
+      p += len;
+    }
+  va_end (args);
+  *p = '\0';
+
+  return ret;
+}
 \f
 /* Return pointer to a static char[] buffer in which zero-terminated
    string-representation of TM (in form hh:mm:ss) is printed.
@@ -283,12 +331,21 @@ fork_to_background (void)
 {
   pid_t pid;
   /* Whether we arrange our own version of opt.lfilename here.  */
-  int changedp = 0;
+  int logfile_changed = 0;
 
   if (!opt.lfilename)
     {
-      opt.lfilename = unique_name (DEFAULT_LOGFILE, 0);
-      changedp = 1;
+      /* We must create the file immediately to avoid either a race
+        condition (which arises from using unique_name and failing to
+        use fopen_excl) or lying to the user about the log file name
+        (which arises from using unique_name, printing the name, and
+        using fopen_excl later on.)  */
+      FILE *new_log_fp = unique_create (DEFAULT_LOGFILE, 0, &opt.lfilename);
+      if (new_log_fp)
+       {
+         logfile_changed = 1;
+         fclose (new_log_fp);
+       }
     }
   pid = fork ();
   if (pid < 0)
@@ -301,7 +358,7 @@ fork_to_background (void)
     {
       /* parent, no error */
       printf (_("Continuing in background, pid %d.\n"), (int)pid);
-      if (changedp)
+      if (logfile_changed)
        printf (_("Output will be written to `%s'.\n"), opt.lfilename);
       exit (0);                        /* #### should we use _exit()? */
     }
@@ -439,7 +496,7 @@ unique_name_1 (const char *prefix)
    exist at the point in time when the function was called.
    Therefore, where security matters, don't rely that the file created
    by this function exists until you open it with O_EXCL or
-   something.
+   equivalent.
 
    If ALLOW_PASSTHROUGH is 0, it always returns a freshly allocated
    string.  Otherwise, it may return FILE if the file doesn't exist
@@ -457,6 +514,66 @@ unique_name (const char *file, int allow_passthrough)
      and return it.  */
   return unique_name_1 (file);
 }
+
+/* Create a file based on NAME, except without overwriting an existing
+   file with that name.  Providing O_EXCL is correctly implemented,
+   this function does not have the race condition associated with
+   opening the file returned by unique_name.  */
+
+FILE *
+unique_create (const char *name, int binary, char **opened_name)
+{
+  /* unique file name, based on NAME */
+  char *uname = unique_name (name, 0);
+  FILE *fp;
+  while ((fp = fopen_excl (uname, binary)) == NULL && errno == EEXIST)
+    {
+      xfree (uname);
+      uname = unique_name (name, 0);
+    }
+  if (opened_name && fp != NULL)
+    {
+      if (fp)
+       *opened_name = uname;
+      else
+       {
+         *opened_name = NULL;
+         xfree (uname);
+       }
+    }
+  else
+    xfree (uname);
+  return fp;
+}
+
+/* Open the file for writing, with the addition that the file is
+   opened "exclusively".  This means that, if the file already exists,
+   this function will *fail* and errno will be set to EEXIST.  If
+   BINARY is set, the file will be opened in binary mode, equivalent
+   to fopen's "wb".
+
+   If opening the file fails for any reason, including the file having
+   previously existed, this function returns NULL and sets errno
+   appropriately.  */
+   
+FILE *
+fopen_excl (const char *fname, int binary)
+{
+  int fd;
+#ifdef O_EXCL
+  int flags = O_WRONLY | O_CREAT | O_EXCL;
+# ifdef O_BINARY
+  if (binary)
+    flags |= O_BINARY;
+# endif
+  fd = open (fname, flags, 0666);
+  if (fd < 0)
+    return NULL;
+  return fdopen (fd, binary ? "wb" : "w");
+#else  /* not O_EXCL */
+  return fopen (fname, binary ? "wb" : "w");
+#endif /* not O_EXCL */
+}
 \f
 /* Create DIRECTORY.  If some of the pathname components of DIRECTORY
    are missing, create them first.  In case any mkdir() call fails,
@@ -467,9 +584,7 @@ unique_name (const char *file, int allow_passthrough)
 int
 make_directory (const char *directory)
 {
-  int quit = 0;
-  int i;
-  int ret = 0;
+  int i, ret, quit = 0;
   char *dir;
 
   /* Make a copy of dir, to be able to write to it.  Otherwise, the
@@ -807,7 +922,7 @@ read_file (const char *file)
 {
   int fd;
   struct file_memory *fm;
-  wgint size;
+  long size;
   int inhibit_close = 0;
 
   /* Some magic in the finest tradition of Perl and its kin: if FILE
@@ -1263,9 +1378,8 @@ numdigit (wgint number)
 #  define C100000000000000000 100000000000000000LL
 #  define C1000000000000000000 1000000000000000000LL
 # else
-#  if defined(_MSC_VER) || defined(__WATCOM__)
-/* Otherwise, if __int64 is available (under Windows), use __int64
-   constants. */
+#  if defined(WINDOWS)
+/* Use __int64 constants under Windows. */
 #   define C10000000000 10000000000I64
 #   define C100000000000 100000000000I64
 #   define C1000000000000 1000000000000I64
@@ -1287,7 +1401,7 @@ numdigit (wgint number)
 # if SIZEOF_LONG_LONG >= SIZEOF_WGINT
 #   define SPRINTF_WGINT(buf, n) sprintf(buf, "%lld", (long long) (n))
 # else
-#  ifdef _MSC_VER
+#  ifdef WINDOWS
 #   define SPRINTF_WGINT(buf, n) sprintf(buf, "%I64", (__int64) (n))
 #  endif
 # endif