]> sjero.net Git - wget/blobdiff - src/utils.c
[svn] Rewrite parsing and handling of URLs.
[wget] / src / utils.c
index 9075e3ad0913e106d56d504338673164e42e3058..01cc422ee1d33898920ab1882985be861a9d047a 100644 (file)
@@ -1,20 +1,21 @@
 /* Various functions of utilitarian nature.
-   Copyright (C) 1995, 1996, 1997, 1998, 2000 Free Software Foundation, Inc.
+   Copyright (C) 1995, 1996, 1997, 1998, 2000, 2001
+   Free Software Foundation, Inc.
 
-This file is part of Wget.
+This file is part of GNU Wget.
 
-This program is free software; you can redistribute it and/or modify
+GNU Wget 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
 (at your option) any later version.
 
-This program is distributed in the hope that it will be useful,
+GNU Wget is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 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
+along with Wget; if not, write to the Free Software
 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
 
 #include <config.h>
@@ -352,79 +353,62 @@ sepstring (const char *s)
 }
 \f
 /* Return pointer to a static char[] buffer in which zero-terminated
-   string-representation of TM (in form hh:mm:ss) is printed.  It is
-   shamelessly non-reentrant, but it doesn't matter, really.
+   string-representation of TM (in form hh:mm:ss) is printed.
+
+   If TM is non-NULL, the current time-in-seconds will be stored
+   there.
+
+   (#### This is misleading: one would expect TM would be used instead
+   of the current time in that case.  This design was probably
+   influenced by the design time(2), and should be changed at some
+   points.  No callers use non-NULL TM anyway.)  */
 
-   If TM is non-NULL, the time_t of the current time will be stored
-   there.  */
 char *
 time_str (time_t *tm)
 {
-  static char tms[15];
+  static char output[15];
   struct tm *ptm;
-  time_t tim;
-
-  *tms = '\0';
-  tim = time (tm);
-  if (tim == -1)
-    return tms;
-  ptm = localtime (&tim);
-  sprintf (tms, "%02d:%02d:%02d", ptm->tm_hour, ptm->tm_min, ptm->tm_sec);
-  return tms;
-}
+  time_t secs = time (tm);
 
-/* Returns an error message for ERRNUM.  #### This requires more work.
-   This function, as well as the whole error system, is very
-   ill-conceived.  */
-const char *
-uerrmsg (uerr_t errnum)
-{
-  switch (errnum)
+  if (secs == -1)
     {
-    case URLUNKNOWN:
-      return _("Unknown/unsupported protocol");
-      break;
-    case URLBADPORT:
-      return _("Invalid port specification");
-      break;
-    case URLBADHOST:
-      return _("Invalid host name");
-      break;
-    default:
-      abort ();
-      /* $@#@#$ compiler.  */
-      return NULL;
+      /* In case of error, return the empty string.  Maybe we should
+        just abort if this happens?  */
+      *output = '\0';
+      return output;
     }
+  ptm = localtime (&secs);
+  sprintf (output, "%02d:%02d:%02d", ptm->tm_hour, ptm->tm_min, ptm->tm_sec);
+  return output;
 }
-\f
-/* The Windows versions of the following two functions are defined in
-   mswindows.c.  */
 
-/* A cuserid() immitation using getpwuid(), to avoid hassling with
-   utmp.  Besides, not all systems have cuesrid().  Under Windows, it
-   is defined in mswindows.c.
+/* Like the above, but include the date: YYYY-MM-DD hh:mm:ss.  */
 
-   If WHERE is non-NULL, the username will be stored there.
-   Otherwise, it will be returned as a static buffer (as returned by
-   getpwuid()).  In the latter case, the buffer should be copied
-   before calling getpwuid() or pwd_cuserid() again.  */
-#ifndef WINDOWS
 char *
-pwd_cuserid (char *where)
+datetime_str (time_t *tm)
 {
-  struct passwd *pwd;
+  static char output[20];      /* "YYYY-MM-DD hh:mm:ss" + \0 */
+  struct tm *ptm;
+  time_t secs = time (tm);
 
-  if (!(pwd = getpwuid (getuid ())) || !pwd->pw_name)
-    return NULL;
-  if (where)
+  if (secs == -1)
     {
-      strcpy (where, pwd->pw_name);
-      return where;
+      /* In case of error, return the empty string.  Maybe we should
+        just abort if this happens?  */
+      *output = '\0';
+      return output;
     }
-  else
-    return pwd->pw_name;
+  ptm = localtime (&secs);
+  sprintf (output, "%04d-%02d-%02d %02d:%02d:%02d",
+          ptm->tm_year + 1900, ptm->tm_mon + 1, ptm->tm_mday,
+          ptm->tm_hour, ptm->tm_min, ptm->tm_sec);
+  return output;
 }
+\f
+/* The Windows versions of the following two functions are defined in
+   mswindows.c.  */
 
+#ifndef WINDOWS
 void
 fork_to_background (void)
 {
@@ -456,6 +440,14 @@ fork_to_background (void)
 }
 #endif /* not WINDOWS */
 \f
+char *
+ps (char *orig)
+{
+  char *r = xstrdup (orig);
+  path_simplify (r);
+  return r;
+}
+
 /* Canonicalize PATH, and return a new path.  The new path differs from PATH
    in that:
        Multple `/'s are collapsed to a single `/'.
@@ -471,7 +463,8 @@ fork_to_background (void)
        Always use '/' as stub_char.
        Don't check for local things using canon_stat.
        Change the original string instead of strdup-ing.
-       React correctly when beginning with `./' and `../'.  */
+       React correctly when beginning with `./' and `../'.
+       Don't zip out trailing slashes.  */
 void
 path_simplify (char *path)
 {
@@ -537,20 +530,15 @@ path_simplify (char *path)
          i = start + 1;
        }
 
-      /* Check for trailing `/'.  */
-      if (start && !path[i])
-       {
-       zero_last:
-         path[--i] = '\0';
-         break;
-       }
-
       /* Check for `../', `./' or trailing `.' by itself.  */
       if (path[i] == '.')
        {
          /* Handle trailing `.' by itself.  */
          if (!path[i + 1])
-           goto zero_last;
+           {
+             path[--i] = '\0';
+             break;
+           }
 
          /* Handle `./'.  */
          if (path[i + 1] == '/')
@@ -571,12 +559,6 @@ path_simplify (char *path)
            }
        }       /* path == '.' */
     } /* while */
-
-  if (!*path)
-    {
-      *path = stub_char;
-      path[1] = '\0';
-    }
 }
 \f
 /* "Touch" FILE, i.e. make its atime and mtime equal to the time
@@ -720,6 +702,30 @@ make_directory (const char *directory)
     }
   return 0;
 }
+
+/* Merge BASE with FILE.  BASE can be a directory or a file name, FILE
+   should be a file name.  For example, file_merge("/foo/bar", "baz")
+   will return "/foo/baz".  file_merge("/foo/bar/", "baz") will return
+   "foo/bar/baz".
+
+   In other words, it's a simpler and gentler version of uri_merge_1.  */
+
+char *
+file_merge (const char *base, const char *file)
+{
+  char *result;
+  const char *cut = (const char *)strrchr (base, '/');
+
+  if (!cut)
+    cut = base + strlen (base);
+
+  result = (char *)xmalloc (cut - base + 1 + strlen (file) + 1);
+  memcpy (result, base, cut - base);
+  result[cut - base] = '/';
+  strcpy (result + (cut - base) + 1, file);
+
+  return result;
+}
 \f
 static int in_acclist PARAMS ((const char *const *, const char *, int));
 
@@ -969,7 +975,7 @@ read_file (const char *file)
        efficiency, but at some cost to generality.  */
     fm->content = mmap (NULL, fm->length, PROT_READ | PROT_WRITE,
                        MAP_PRIVATE, fd, 0);
-    if (fm->content == MAP_FAILED)
+    if (fm->content == (char *)MAP_FAILED)
       goto mmap_lose;
     if (!inhibit_close)
       close (fd);
@@ -1199,7 +1205,7 @@ string_set_add (struct hash_table *ht, const char *s)
   /* First check whether the set element already exists.  If it does,
      do nothing so that we don't have to free() the old element and
      then strdup() a new one.  */
-  if (hash_table_exists (ht, s))
+  if (hash_table_contains (ht, s))
     return;
 
   /* We use "1" as value.  It provides us a useful and clear arbitrary
@@ -1209,12 +1215,12 @@ string_set_add (struct hash_table *ht, const char *s)
   hash_table_put (ht, xstrdup (s), "1");
 }
 
-/* Synonym for hash_table_exists... */
+/* Synonym for hash_table_contains... */
 
 int
-string_set_exists (struct hash_table *ht, const char *s)
+string_set_contains (struct hash_table *ht, const char *s)
 {
-  return hash_table_exists (ht, s);
+  return hash_table_contains (ht, s);
 }
 
 static int
@@ -1248,7 +1254,7 @@ free_keys_and_values (struct hash_table *ht)
 }
 
 \f
-/* Engine for legible and legible_long_long; this function works on
+/* Engine for legible and legible_very_long; this function works on
    strings.  */
 
 static char *
@@ -1296,13 +1302,53 @@ legible (long l)
   return legible_1 (inbuf);
 }
 
+/* Write a string representation of NUMBER into the provided buffer.
+   We cannot use sprintf() because we cannot be sure whether the
+   platform supports printing of what we chose for VERY_LONG_TYPE.
+
+   Example: Gcc supports `long long' under many platforms, but on many
+   of those the native libc knows nothing of it and therefore cannot
+   print it.
+
+   How long BUFFER needs to be depends on the platform and the content
+   of NUMBER.  For 64-bit VERY_LONG_TYPE (the most common case), 24
+   bytes are sufficient.  Using more might be a good idea.
+
+   This function does not go through the hoops that long_to_string
+   goes to because it doesn't aspire to be fast.  (It's called perhaps
+   once in a Wget run.)  */
+
+static void
+very_long_to_string (char *buffer, VERY_LONG_TYPE number)
+{
+  int i = 0;
+  int j;
+
+  /* Print the number backwards... */
+  do
+    {
+      buffer[i++] = '0' + number % 10;
+      number /= 10;
+    }
+  while (number);
+
+  /* ...and reverse the order of the digits. */
+  for (j = 0; j < i / 2; j++)
+    {
+      char c = buffer[j];
+      buffer[j] = buffer[i - 1 - j];
+      buffer[i - 1 - j] = c;
+    }
+  buffer[i] = '\0';
+}
+
 /* The same as legible(), but works on VERY_LONG_TYPE.  See sysdep.h.  */
 char *
 legible_very_long (VERY_LONG_TYPE l)
 {
   char inbuf[128];
   /* Print the number into the buffer.  */
-  sprintf (inbuf, VERY_LONG_FORMAT, l);
+  very_long_to_string (inbuf, l);
   return legible_1 (inbuf);
 }
 
@@ -1311,62 +1357,278 @@ int
 numdigit (long a)
 {
   int res = 1;
+  if (a < 0)
+    {
+      a = -a;
+      ++res;
+    }
   while ((a /= 10) != 0)
     ++res;
   return res;
 }
 
-/* Print NUMBER to BUFFER.  This is equivalent to sprintf(buffer,
-   "%ld", number), only much faster.
+#define ONE_DIGIT(figure) *p++ = n / (figure) + '0'
+#define ONE_DIGIT_ADVANCE(figure) (ONE_DIGIT (figure), n %= (figure))
+
+#define DIGITS_1(figure) ONE_DIGIT (figure)
+#define DIGITS_2(figure) ONE_DIGIT_ADVANCE (figure); DIGITS_1 ((figure) / 10)
+#define DIGITS_3(figure) ONE_DIGIT_ADVANCE (figure); DIGITS_2 ((figure) / 10)
+#define DIGITS_4(figure) ONE_DIGIT_ADVANCE (figure); DIGITS_3 ((figure) / 10)
+#define DIGITS_5(figure) ONE_DIGIT_ADVANCE (figure); DIGITS_4 ((figure) / 10)
+#define DIGITS_6(figure) ONE_DIGIT_ADVANCE (figure); DIGITS_5 ((figure) / 10)
+#define DIGITS_7(figure) ONE_DIGIT_ADVANCE (figure); DIGITS_6 ((figure) / 10)
+#define DIGITS_8(figure) ONE_DIGIT_ADVANCE (figure); DIGITS_7 ((figure) / 10)
+#define DIGITS_9(figure) ONE_DIGIT_ADVANCE (figure); DIGITS_8 ((figure) / 10)
+#define DIGITS_10(figure) ONE_DIGIT_ADVANCE (figure); DIGITS_9 ((figure) / 10)
+
+/* DIGITS_<11-20> are only used on machines with 64-bit longs. */
+
+#define DIGITS_11(figure) ONE_DIGIT_ADVANCE (figure); DIGITS_10 ((figure) / 10)
+#define DIGITS_12(figure) ONE_DIGIT_ADVANCE (figure); DIGITS_11 ((figure) / 10)
+#define DIGITS_13(figure) ONE_DIGIT_ADVANCE (figure); DIGITS_12 ((figure) / 10)
+#define DIGITS_14(figure) ONE_DIGIT_ADVANCE (figure); DIGITS_13 ((figure) / 10)
+#define DIGITS_15(figure) ONE_DIGIT_ADVANCE (figure); DIGITS_14 ((figure) / 10)
+#define DIGITS_16(figure) ONE_DIGIT_ADVANCE (figure); DIGITS_15 ((figure) / 10)
+#define DIGITS_17(figure) ONE_DIGIT_ADVANCE (figure); DIGITS_16 ((figure) / 10)
+#define DIGITS_18(figure) ONE_DIGIT_ADVANCE (figure); DIGITS_17 ((figure) / 10)
+#define DIGITS_19(figure) ONE_DIGIT_ADVANCE (figure); DIGITS_18 ((figure) / 10)
+
+/* Print NUMBER to BUFFER in base 10.  This is completely equivalent
+   to `sprintf(buffer, "%ld", number)', only much faster.
+
+   The speedup may make a difference in programs that frequently
+   convert numbers to strings.  Some implementations of sprintf,
+   particularly the one in GNU libc, have been known to be extremely
+   slow compared to this function.
+
+   BUFFER should accept as many bytes as you expect the number to take
+   up.  On machines with 64-bit longs the maximum needed size is 24
+   bytes.  That includes the worst-case digits, the optional `-' sign,
+   and the trailing \0.  */
 
-   BUFFER should accept 24 bytes.  This should suffice for the longest
-   numbers on 64-bit machines, including the `-' sign and the trailing
-   \0.  */
 void
 long_to_string (char *buffer, long number)
 {
-#if (SIZEOF_LONG != 4) && (SIZEOF_LONG != 8)
-  /* Huh? */
-  sprintf (buffer, "%ld", number);
-#else /* (SIZEOF_LONG == 4) || (SIZEOF_LONG == 8) */
   char *p = buffer;
-  int force = 0;
+  long n = number;
+
+#if (SIZEOF_LONG != 4) && (SIZEOF_LONG != 8)
+  /* We are running in a strange or misconfigured environment.  Let
+     sprintf cope with it.  */
+  sprintf (buffer, "%ld", n);
+#else  /* (SIZEOF_LONG == 4) || (SIZEOF_LONG == 8) */
 
-  if (number < 0)
+  if (n < 0)
     {
       *p++ = '-';
-      number = -number;
+      n = -n;
     }
 
-#define FROB(figure) do {                                              \
-    if (force || number >= figure)                                     \
-      *p++ = number / figure + '0', number %= figure, force = 1;       \
-    } while (0)
-#if SIZEOF_LONG == 8
-  FROB (1000000000000000000L);
-  FROB (100000000000000000L);
-  FROB (10000000000000000L);
-  FROB (1000000000000000L);
-  FROB (100000000000000L);
-  FROB (10000000000000L);
-  FROB (1000000000000L);
-  FROB (100000000000L);
-  FROB (10000000000L);
-#endif /* SIZEOF_LONG == 8 */
-  FROB (1000000000);
-  FROB (100000000);
-  FROB (10000000);
-  FROB (1000000);
-  FROB (100000);
-  FROB (10000);
-  FROB (1000);
-  FROB (100);
-  FROB (10);
-#undef FROB
-  *p++ = number + '0';
+  if      (n < 10)                   { DIGITS_1 (1); }
+  else if (n < 100)                  { DIGITS_2 (10); }
+  else if (n < 1000)                 { DIGITS_3 (100); }
+  else if (n < 10000)                { DIGITS_4 (1000); }
+  else if (n < 100000)               { DIGITS_5 (10000); }
+  else if (n < 1000000)              { DIGITS_6 (100000); }
+  else if (n < 10000000)             { DIGITS_7 (1000000); }
+  else if (n < 100000000)            { DIGITS_8 (10000000); }
+  else if (n < 1000000000)           { DIGITS_9 (100000000); }
+#if SIZEOF_LONG == 4
+  /* ``if (1)'' serves only to preserve editor indentation. */
+  else if (1)                        { DIGITS_10 (1000000000); }
+#else  /* SIZEOF_LONG != 4 */
+  else if (n < 10000000000L)         { DIGITS_10 (1000000000L); }
+  else if (n < 100000000000L)        { DIGITS_11 (10000000000L); }
+  else if (n < 1000000000000L)       { DIGITS_12 (100000000000L); }
+  else if (n < 10000000000000L)      { DIGITS_13 (1000000000000L); }
+  else if (n < 100000000000000L)     { DIGITS_14 (10000000000000L); }
+  else if (n < 1000000000000000L)    { DIGITS_15 (100000000000000L); }
+  else if (n < 10000000000000000L)   { DIGITS_16 (1000000000000000L); }
+  else if (n < 100000000000000000L)  { DIGITS_17 (10000000000000000L); }
+  else if (n < 1000000000000000000L) { DIGITS_18 (100000000000000000L); }
+  else                               { DIGITS_19 (1000000000000000000L); }
+#endif /* SIZEOF_LONG != 4 */
+
   *p = '\0';
 #endif /* (SIZEOF_LONG == 4) || (SIZEOF_LONG == 8) */
 }
+
+#undef ONE_DIGIT
+#undef ONE_DIGIT_ADVANCE
+
+#undef DIGITS_1
+#undef DIGITS_2
+#undef DIGITS_3
+#undef DIGITS_4
+#undef DIGITS_5
+#undef DIGITS_6
+#undef DIGITS_7
+#undef DIGITS_8
+#undef DIGITS_9
+#undef DIGITS_10
+#undef DIGITS_11
+#undef DIGITS_12
+#undef DIGITS_13
+#undef DIGITS_14
+#undef DIGITS_15
+#undef DIGITS_16
+#undef DIGITS_17
+#undef DIGITS_18
+#undef DIGITS_19
+\f
+/* Support for timers. */
+
+#undef TIMER_WINDOWS
+#undef TIMER_GETTIMEOFDAY
+#undef TIMER_TIME
+
+/* Depending on the OS and availability of gettimeofday(), one and
+   only one of the above constants will be defined.  Virtually all
+   modern Unix systems will define TIMER_GETTIMEOFDAY; Windows will
+   use TIMER_WINDOWS.  TIMER_TIME is a catch-all method for
+   non-Windows systems without gettimeofday.
+
+   #### Perhaps we should also support ftime(), which exists on old
+   BSD 4.2-influenced systems?  (It also existed under MS DOS Borland
+   C, if memory serves me.)  */
+
+#ifdef WINDOWS
+# define TIMER_WINDOWS
+#else  /* not WINDOWS */
+# ifdef HAVE_GETTIMEOFDAY
+#  define TIMER_GETTIMEOFDAY
+# else
+#  define TIMER_TIME
+# endif
+#endif /* not WINDOWS */
+
+struct wget_timer {
+#ifdef TIMER_GETTIMEOFDAY
+  long secs;
+  long usecs;
+#endif
+
+#ifdef TIMER_TIME
+  time_t secs;
+#endif
+
+#ifdef TIMER_WINDOWS
+  ULARGE_INTEGER wintime;
+#endif
+};
+
+/* Allocate a timer.  It is not legal to do anything with a freshly
+   allocated timer, except call wtimer_reset() or wtimer_delete().  */
+
+struct wget_timer *
+wtimer_allocate (void)
+{
+  struct wget_timer *wt =
+    (struct wget_timer *)xmalloc (sizeof (struct wget_timer));
+  return wt;
+}
+
+/* Allocate a new timer and reset it.  Return the new timer. */
+
+struct wget_timer *
+wtimer_new (void)
+{
+  struct wget_timer *wt = wtimer_allocate ();
+  wtimer_reset (wt);
+  return wt;
+}
+
+/* Free the resources associated with the timer.  Its further use is
+   prohibited.  */
+
+void
+wtimer_delete (struct wget_timer *wt)
+{
+  xfree (wt);
+}
+
+/* Reset timer WT.  This establishes the starting point from which
+   wtimer_elapsed() will return the number of elapsed
+   milliseconds.  It is allowed to reset a previously used timer.  */
+
+void
+wtimer_reset (struct wget_timer *wt)
+{
+#ifdef TIMER_GETTIMEOFDAY
+  struct timeval t;
+  gettimeofday (&t, NULL);
+  wt->secs  = t.tv_sec;
+  wt->usecs = t.tv_usec;
+#endif
+
+#ifdef TIMER_TIME
+  wt->secs = time (NULL);
+#endif
+
+#ifdef TIMER_WINDOWS
+  FILETIME ft;
+  SYSTEMTIME st;
+  GetSystemTime (&st);
+  SystemTimeToFileTime (&st, &ft);
+  wt->wintime.HighPart = ft.dwHighDateTime;
+  wt->wintime.LowPart  = ft.dwLowDateTime;
+#endif
+}
+
+/* Return the number of milliseconds elapsed since the timer was last
+   reset.  It is allowed to call this function more than once to get
+   increasingly higher elapsed values.  */
+
+long
+wtimer_elapsed (struct wget_timer *wt)
+{
+#ifdef TIMER_GETTIMEOFDAY
+  struct timeval t;
+  gettimeofday (&t, NULL);
+  return (t.tv_sec - wt->secs) * 1000 + (t.tv_usec - wt->usecs) / 1000;
+#endif
+
+#ifdef TIMER_TIME
+  time_t now = time (NULL);
+  return 1000 * (now - wt->secs);
+#endif
+
+#ifdef WINDOWS
+  FILETIME ft;
+  SYSTEMTIME st;
+  ULARGE_INTEGER uli;
+  GetSystemTime (&st);
+  SystemTimeToFileTime (&st, &ft);
+  uli.HighPart = ft.dwHighDateTime;
+  uli.LowPart = ft.dwLowDateTime;
+  return (long)((uli.QuadPart - wt->wintime.QuadPart) / 10000);
+#endif
+}
+
+/* Return the assessed granularity of the timer implementation.  This
+   is important for certain code that tries to deal with "zero" time
+   intervals.  */
+
+long
+wtimer_granularity (void)
+{
+#ifdef TIMER_GETTIMEOFDAY
+  /* Granularity of gettimeofday is hugely architecture-dependent.
+     However, it appears that on modern machines it is better than
+     1ms.  */
+  return 1;
+#endif
+
+#ifdef TIMER_TIME
+  /* This is clear. */
+  return 1000;
+#endif
+
+#ifdef TIMER_WINDOWS
+  /* ? */
+  return 1;
+#endif
+}
 \f
 /* This should probably be at a better place, but it doesn't really
    fit into html-parse.c.  */