/* 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>
#include <fcntl.h>
#include <assert.h>
+/* For TIOCGWINSZ and friends: */
+#ifdef HAVE_SYS_IOCTL_H
+# include <sys/ioctl.h>
+#endif
+#ifdef HAVE_TERMIOS_H
+# include <termios.h>
+#endif
+
#include "wget.h"
#include "utils.h"
#include "fnmatch.h"
#endif /* DEBUG_MALLOC */
\f
+/* Utility function: like xstrdup(), but also lowercases S. */
+
+char *
+xstrdup_lower (const char *s)
+{
+ char *copy = xstrdup (s);
+ char *p = copy;
+ for (; *p; p++)
+ *p = TOLOWER (*p);
+ return copy;
+}
+
+/* Return a count of how many times CHR occurs in STRING. */
+
+int
+count_char (const char *string, char chr)
+{
+ const char *p;
+ int count = 0;
+ for (p = string; *p; p++)
+ if (*p == chr)
+ ++count;
+ return count;
+}
+
/* Copy the string formed by two pointers (one on the beginning, other
on the char after the last char) to a new, malloc-ed location.
0-terminate it. */
}
\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;
+ time_t secs = time (tm);
- *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;
-}
-
-/* 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)
{
}
#endif /* not WINDOWS */
\f
+#if 0
+/* debug */
+char *
+ps (char *orig)
+{
+ char *r = xstrdup (orig);
+ path_simplify (r);
+ return r;
+}
+#endif
+
/* Canonicalize PATH, and return a new path. The new path differs from PATH
in that:
Multple `/'s are collapsed to a single `/'.
Non-leading `../'s and trailing `..'s are handled by removing
portions of the path.
- E.g. "a/b/c/./../d/.." will yield "a/b". This function originates
- from GNU Bash.
+ E.g. "a/b/c/./../d/.." will yield "a/b/". This function originates
+ from GNU Bash and has been mutilated to unrecognition for use in
+ Wget.
Changes for Wget:
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 `../'. */
-void
+ React correctly when beginning with `./' and `../'.
+ Don't zip out trailing slashes.
+ Return a value indicating whether any modifications took place.
+
+ If you dare change this function, take a careful look at the test
+ cases below, and make sure that they pass. */
+
+int
path_simplify (char *path)
{
- register int i, start, ddot;
- char stub_char;
+ register int i, start;
+ int changes = 0;
if (!*path)
- return;
-
- /*stub_char = (*path == '/') ? '/' : '.';*/
- stub_char = '/';
+ return 0;
- /* Addition: Remove all `./'-s preceding the string. If `../'-s
- precede, put `/' in front and remove them too. */
- i = 0;
- ddot = 0;
- while (1)
- {
- if (path[i] == '.' && path[i + 1] == '/')
- i += 2;
- else if (path[i] == '.' && path[i + 1] == '.' && path[i + 2] == '/')
- {
- i += 3;
- ddot = 1;
- }
- else
- break;
- }
- if (i)
- strcpy (path, path + i - ddot);
+ if (path[0] == '/')
+ /* Preserve initial '/'. */
+ ++path;
- /* Replace single `.' or `..' with `/'. */
+ /* Nix out leading `.' or `..' with. */
if ((path[0] == '.' && path[1] == '\0')
|| (path[0] == '.' && path[1] == '.' && path[2] == '\0'))
{
- path[0] = stub_char;
- path[1] = '\0';
- return;
+ path[0] = '\0';
+ changes = 1;
+ return changes;
}
+
/* Walk along PATH looking for things to compact. */
i = 0;
while (1)
{
strcpy (path + start + 1, path + i);
i = start + 1;
- }
-
- /* Check for trailing `/'. */
- if (start && !path[i])
- {
- zero_last:
- path[--i] = '\0';
- break;
+ changes = 1;
}
/* Check for `../', `./' or trailing `.' by itself. */
{
/* Handle trailing `.' by itself. */
if (!path[i + 1])
- goto zero_last;
+ {
+ path[--i] = '\0';
+ changes = 1;
+ break;
+ }
/* Handle `./'. */
if (path[i + 1] == '/')
{
strcpy (path + i, path + i + 1);
i = (start < 0) ? 0 : start;
+ changes = 1;
continue;
}
(path[i + 2] == '/' || !path[i + 2]))
{
while (--start > -1 && path[start] != '/');
- strcpy (path + start + 1, path + i + 2);
+ strcpy (path + start + 1, path + i + 2 + (start == -1 && path[i + 2]));
i = (start < 0) ? 0 : start;
+ changes = 1;
continue;
}
} /* path == '.' */
} /* while */
- if (!*path)
+ /* Addition: Remove all `./'-s and `../'-s preceding the string. */
+ i = 0;
+ while (1)
+ {
+ if (path[i] == '.' && path[i + 1] == '/')
+ i += 2;
+ else if (path[i] == '.' && path[i + 1] == '.' && path[i + 2] == '/')
+ i += 3;
+ else
+ break;
+ }
+ if (i)
{
- *path = stub_char;
- path[1] = '\0';
+ strcpy (path, path + i - 0);
+ changes = 1;
}
+
+ return changes;
}
+
+/* Test cases:
+ ps("") -> ""
+ ps("/") -> "/"
+ ps(".") -> ""
+ ps("..") -> ""
+ ps("/.") -> "/"
+ ps("/..") -> "/"
+ ps("foo") -> "foo"
+ ps("foo/bar") -> "foo/bar"
+ ps("foo//bar") -> "foo/bar" (possibly a bug)
+ ps("foo/../bar") -> "bar"
+ ps("foo/bar/..") -> "foo/"
+ ps("foo/bar/../x") -> "foo/x"
+ ps("foo/bar/../x/") -> "foo/x/"
+ ps("foo/..") -> ""
+ ps("/foo/..") -> "/"
+ ps("a/b/../../c") -> "c"
+ ps("/a/b/../../c") -> "/c"
+ ps("./a/../b") -> "b"
+ ps("/./a/../b") -> "/b"
+*/
\f
/* "Touch" FILE, i.e. make its atime and mtime equal to the time
specified with TM. */
}
return 0;
}
+
+/* Merge BASE with FILE. BASE can be a directory or a file name, FILE
+ should be a file name.
+
+ file_merge("/foo/bar", "baz") => "/foo/baz"
+ file_merge("/foo/bar/", "baz") => "/foo/bar/baz"
+ file_merge("foo", "bar") => "bar"
+
+ 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)
+ return xstrdup (file);
+
+ 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));
match_backwards ("abc", "bc") -> 1
match_backwards ("abc", "ab") -> 0
match_backwards ("abc", "abc") -> 1 */
-static int
-match_backwards (const char *string, const char *pattern)
+int
+match_tail (const char *string, const char *pattern)
{
int i, j;
}
/* Checks whether string S matches each element of ACCEPTS. A list
- element are matched either with fnmatch() or match_backwards(),
+ element are matched either with fnmatch() or match_tail(),
according to whether the element contains wildcards or not.
If the BACKWARD is 0, don't do backward comparison -- just compare
{
if (backward)
{
- if (match_backwards (s, *accepts))
+ if (match_tail (s, *accepts))
return 1;
}
else
return 0;
}
-/* Return the malloc-ed suffix of STR. For instance:
+/* Return the location of STR's suffix (file extension). Examples:
suffix ("foo.bar") -> "bar"
suffix ("foo.bar.baz") -> "baz"
suffix ("/foo/bar") -> NULL
{
int i;
- for (i = strlen (str); i && str[i] != '/' && str[i] != '.'; i--);
+ for (i = strlen (str); i && str[i] != '/' && str[i] != '.'; i--)
+ ;
+
if (str[i++] == '.')
- return xstrdup (str + i);
+ return (char *)str + i;
else
return NULL;
}
/* Read a line from FP. The function reallocs the storage as needed
to accomodate for any length of the line. Reallocs are done
- storage exponentially, doubling the storage after each overflow to
- minimize the number of calls to realloc() and fgets(). The newline
+ exponentially, doubling the storage after each overflow to minimize
+ the number of calls to realloc() and fgets(). The newline
character at the end of line is retained.
After end-of-file is encountered without anything being read, NULL
is returned. NULL is also returned on error. To distinguish
- between these two cases, use the stdio function ferror(). */
+ between these two cases, use the stdio function ferror().
+
+ A future version of this function will be rewritten to use fread()
+ instead of fgets(), and to return the length of the line, which
+ will make the function usable on files with binary content. */
char *
read_whole_line (FILE *fp)
while (fgets (line + length, bufsize - length, fp))
{
length += strlen (line + length);
- assert (length > 0);
+ if (length == 0)
+ /* Possible for example when reading from a binary file where
+ a line begins with \0. */
+ continue;
+
if (line[length - 1] == '\n')
break;
+
/* fgets() guarantees to read the whole line, or to use up the
space we've given it. We can double the buffer
unconditionally. */
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);
/* 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
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
}
\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 *
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);
}
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 (number < 0)
+#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 (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. */
*p = '\0';
return res;
}
+
+/* Determine the width of the terminal we're running on. If that's
+ not possible, return 0. */
+
+int
+determine_screen_width (void)
+{
+ /* If there's a way to get the terminal size using POSIX
+ tcgetattr(), somebody please tell me. */
+#ifndef TIOCGWINSZ
+ return 0;
+#else /* TIOCGWINSZ */
+ int fd;
+ struct winsize wsz;
+
+ if (opt.lfilename != NULL)
+ return 0;
+
+ fd = fileno (stderr);
+ if (ioctl (fd, TIOCGWINSZ, &wsz) < 0)
+ return 0; /* most likely ENOTTY */
+
+ return wsz.ws_col;
+#endif /* TIOCGWINSZ */
+}
+
+#if 0
+/* A debugging function for checking whether an MD5 library works. */
+
+#include "gen-md5.h"
+
+char *
+debug_test_md5 (char *buf)
+{
+ unsigned char raw[16];
+ static char res[33];
+ unsigned char *p1;
+ char *p2;
+ int cnt;
+ ALLOCA_MD5_CONTEXT (ctx);
+
+ gen_md5_init (ctx);
+ gen_md5_update ((unsigned char *)buf, strlen (buf), ctx);
+ gen_md5_finish (ctx, raw);
+
+ p1 = raw;
+ p2 = res;
+ cnt = 16;
+ while (cnt--)
+ {
+ *p2++ = XDIGIT_TO_xchar (*p1 >> 4);
+ *p2++ = XDIGIT_TO_xchar (*p1 & 0xf);
+ ++p1;
+ }
+ *p2 = '\0';
+
+ return res;
+}
+#endif