+2005-02-20 Hrvoje Niksic <hniksic@xemacs.org>
+
+ * configure.in: Check for LFS. Determine SIZEOF_OFF_T.
+ Check for ftello.
+
2005-02-18 Marco Colombo <m.colombo@ed.ac.uk>
* po/it.po: Updated Italian translation.
GNU Wget NEWS -- history of user-visible changes.
-Copyright (C) 1997, 1998, 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
+Copyright (C) 2005 Free Software Foundation, Inc.
See the end for copying conditions.
Please send GNU Wget bug reports to <bug-wget@gnu.org>.
\f
-* Changes in Wget 1.9+.
+* Changes in Wget 1.10.
+
+** Downloading files greater than 2GB, also known as "large files",
+now works on systems that support them. This includes most modern
+Unix variants, as well as Windows.
** IPv6 is now supported by Wget. Unlike the experimental code in
1.9, this version has no problems with dual-family systems. The new
----------------------------------------------------------------------
Copyright information:
-Copyright (C) 1997, 1998, 2000, 2001, 2003 Free Software Foundation, Inc.
+Copyright (C) 2005 Free Software Foundation, Inc.
Permission is granted to anyone to make or distribute verbatim
copies of this document as received, in any medium, provided that
AC_CHECK_SIZEOF(long)
AC_CHECK_SIZEOF(long long)
+dnl
+dnl Check for large file support. This check needs to come fairly
+dnl early because it could (in principle) affect whether functions and
+dnl headers are available, whether they work, etc.
+dnl
+AC_SYS_LARGEFILE
+AC_CHECK_SIZEOF(off_t)
+
dnl
dnl Checks for non-universal or system-specific types.
dnl
dnl
AC_FUNC_ALLOCA
AC_FUNC_MMAP
+AC_FUNC_FSEEKO
AC_CHECK_FUNCS(strdup strstr strcasecmp strncasecmp strpbrk memmove)
AC_CHECK_FUNCS(gettimeofday mktime strptime strerror snprintf vsnprintf)
-AC_CHECK_FUNCS(usleep select sigblock sigsetjmp signal symlink access isatty)
+AC_CHECK_FUNCS(usleep select ftello sigblock sigsetjmp signal)
+AC_CHECK_FUNCS(symlink access isatty)
dnl
dnl Call Wget's local macros defined in aclocal.
+2005-02-20 Hrvoje Niksic <hniksic@xemacs.org>
+
+ * mswindows.c (wget_ftello): Wget's replacement for ftello.
+
+ * utils.c (file_size): Use ftello where available.
+
+ * ftp-ls.c (ftp_parse_unix_ls): Use str_to_wgint to parse the file
+ size.
+ (ftp_parse_winnt_ls): Ditto.
+
+ * ftp-basic.c (ftp_size): Use str_to_wgint to convert number to
+ wgint; pass 10 instead of 0 as the BASE argument.
+
+ * ftp.c (ftp_expected_bytes): Use str_to_wgint to parse the file
+ size.
+
+ * sysdep.h (LARGE_INT_FMT): Use __int64 as LARGE_INT on
+ MSVC/Windows; print it with "%I64".
+
+ * wget.h: Define a `wgint' type, normally aliased to (the possibly
+ 64-bit variant of) off_t.
+
+ * all: Use `wgint' instead of `long' for numeric variables that
+ can hold file sizes.
+
+ * utils.c (number_to_string): Support printing of `wgint'
+ argument.
+ (number_to_static_string): New function.
+
+ * all: Replace printf("%ld", long_value) with printf("%s",
+ number_to_static_string(wgint_value)).
+
2005-02-18 Mauro Tortonesi <mauro@ferrara.linux.it>
* main.c: Added the --ftp-passwd command line option.
/* Sends REST command to the FTP server. */
uerr_t
-ftp_rest (int csock, long offset)
+ftp_rest (int csock, wgint offset)
{
char *request, *respline;
int nwritten;
uerr_t err;
- static char numbuf[24]; /* Buffer for the number */
- number_to_string (numbuf, offset);
- request = ftp_request ("REST", numbuf);
+ request = ftp_request ("REST", number_to_static_string (offset));
nwritten = fd_write (csock, request, strlen (request), -1);
if (nwritten < 0)
{
/* Sends the SIZE command to the server, and returns the value in 'size'.
* If an error occurs, size is set to zero. */
uerr_t
-ftp_size (int csock, const char *file, long int *size)
+ftp_size (int csock, const char *file, wgint *size)
{
char *request, *respline;
int nwritten;
}
errno = 0;
- *size = strtol (respline + 4, NULL, 0);
- if (errno)
+ *size = str_to_wgint (respline + 4, NULL, 10);
+ if (errno)
{
/*
* Couldn't parse the response for some reason. On the (few)
size, and the filename is three tokens away. */
if (i != 12)
{
- char *t = tok - 2;
- long mul = 1;
+ wgint size;
- for (cur.size = 0; t > line && ISDIGIT (*t); mul *= 10, t--)
- cur.size += mul * (*t - '0');
+ /* Back up to the beginning of the previous token
+ and parse it with str_to_wgint. */
+ char *t = tok - 2;
+ while (t > line && ISDIGIT (*t))
+ --t;
if (t == line)
{
- /* Something is seriously wrong. */
+ /* Something has gone wrong during parsing. */
error = 1;
break;
}
+ errno = 0;
+ size = str_to_wgint (t, NULL, 10);
+ if (size == WGINT_MAX && errno == ERANGE)
+ /* Out of range -- ignore the size. #### Should
+ we refuse to start the download. */
+ cur.size = 0;
+ else
+ cur.size = size;
+
month = i;
next = 5;
DEBUGP (("month: %s; ", months[month]));
if (!dir)
{
- l = dir = (struct fileinfo *)xmalloc (sizeof (struct fileinfo));
+ l = dir = xnew (struct fileinfo);
memcpy (l, &cur, sizeof (cur));
l->prev = l->next = NULL;
}
else
{
cur.prev = l;
- l->next = (struct fileinfo *)xmalloc (sizeof (struct fileinfo));
+ l->next = xnew (struct fileinfo);
l = l->next;
memcpy (l, &cur, sizeof (cur));
l->next = NULL;
}
else
{
+ wgint size;
cur.type = FT_PLAINFILE;
- cur.size = atoi(tok);
+ errno = 0;
+ size = str_to_wgint (tok, NULL, 10);
+ if (size == WGINT_MAX && errno == ERANGE)
+ cur.size = 0; /* overflow */
+ else
+ cur.size = size;
cur.perms = 0644;
- DEBUGP(("File, size %ld bytes\n", cur.size));
+ DEBUGP(("File, size %s bytes\n", number_to_static_string (cur.size)));
}
cur.linkto = NULL;
/* And put everything into the linked list */
if (!dir)
{
- l = dir = (struct fileinfo *)xmalloc (sizeof (struct fileinfo));
+ l = dir = xnew (struct fileinfo);
memcpy (l, &cur, sizeof (cur));
l->prev = l->next = NULL;
}
else
{
cur.prev = l;
- l->next = (struct fileinfo *)xmalloc (sizeof (struct fileinfo));
+ l->next = xnew (struct fileinfo);
l = l->next;
memcpy (l, &cur, sizeof (cur));
l->next = NULL;
/* Look for regexp "( *[0-9]+ *byte" (literal parenthesis) anywhere in
- the string S, and return the number converted to long, if found, 0
+ the string S, and return the number converted to wgint, if found, 0
otherwise. */
-static long
+static wgint
ftp_expected_bytes (const char *s)
{
- long res;
+ wgint res;
while (1)
{
++s;
if (!*s)
return 0;
- for (++s; *s && ISSPACE (*s); s++);
- if (!*s)
- return 0;
- if (!ISDIGIT (*s))
- continue;
- res = 0;
- do
- {
- res = (*s - '0') + 10 * res;
- ++s;
- }
- while (*s && ISDIGIT (*s));
+ res = str_to_wgint (s, (char **) &s, 10);
if (!*s)
return 0;
while (*s && ISSPACE (*s))
connection to the server. It always closes the data connection,
and closes the control connection in case of error. */
static uerr_t
-getftp (struct url *u, long *len, long restval, ccon *con)
+getftp (struct url *u, wgint *len, wgint restval, ccon *con)
{
int csock, dtsock, local_sock, res;
uerr_t err;
char *tms, *tmrate;
int cmd = con->cmd;
int pasv_mode_open = 0;
- long expected_bytes = 0L;
+ wgint expected_bytes = 0L;
int rest_failed = 0;
int flags;
- long rd_size;
+ wgint rd_size;
assert (con != NULL);
assert (con->target != NULL);
if (restval && (cmd & DO_RETR))
{
if (!opt.server_response)
- logprintf (LOG_VERBOSE, "==> REST %ld ... ", restval);
+ logprintf (LOG_VERBOSE, "==> REST %s ... ", number_to_static_string (restval));
err = ftp_rest (csock, restval);
/* FTPRERR, WRITEFAILED, FTPRESTFAIL */
ftp_loop_internal (struct url *u, struct fileinfo *f, ccon *con)
{
int count, orig_lp;
- long restval, len = 0;
+ wgint restval, len = 0;
char *tms, *locf;
char *tmrate = NULL;
uerr_t err;
- struct stat st;
+ struct_stat st;
if (!con->target)
con->target = url_file_name (u);
con->csock = -1;
}
if (!opt.spider)
- logprintf (LOG_VERBOSE, _("%s (%s) - `%s' saved [%ld]\n\n"),
- tms, tmrate, locf, len);
+ logprintf (LOG_VERBOSE, _("%s (%s) - `%s' saved [%s]\n\n"),
+ tms, tmrate, locf, number_to_static_string (len));
if (!opt.verbose && !opt.quiet)
{
/* Need to hide the password from the URL. The `if' is here
so that we don't do the needless allocation every
time. */
char *hurl = url_string (u, 1);
- logprintf (LOG_NONVERBOSE, "%s URL: %s [%ld] -> \"%s\" [%d]\n",
- tms, hurl, len, locf, count);
+ logprintf (LOG_NONVERBOSE, "%s URL: %s [%s] -> \"%s\" [%d]\n",
+ tms, hurl, number_to_static_string (len), locf, count);
xfree (hurl);
}
static int depth = 0;
uerr_t err;
struct fileinfo *orig;
- long local_size;
+ wgint local_size;
time_t tml;
int dlthis;
dlthis = 1;
if (opt.timestamping && f->type == FT_PLAINFILE)
{
- struct stat st;
+ struct_stat st;
/* If conversion of HTML files retrieved via FTP is ever implemented,
we'll need to stat() <file>.orig here when -K has been specified.
I'm not implementing it now since files on an FTP server are much
{
/* Sizes do not match */
logprintf (LOG_VERBOSE, _("\
-The sizes do not match (local %ld) -- retrieving.\n\n"), local_size);
+The sizes do not match (local %s) -- retrieving.\n\n"),
+ number_to_static_string (local_size));
}
}
} /* opt.timestamping && f->type == FT_PLAINFILE */
_("Invalid name of the symlink, skipping.\n"));
else
{
- struct stat st;
+ struct_stat st;
/* Check whether we already have the correct
symbolic link. */
int rc = lstat (con->target, &st);
{
if (!opt.output_document)
{
- struct stat st;
- long sz;
+ struct_stat st;
+ wgint sz;
if (stat (filename, &st) == 0)
sz = st.st_size;
else
sz = -1;
logprintf (LOG_NOTQUIET,
- _("Wrote HTML-ized index to `%s' [%ld].\n"),
- filename, sz);
+ _("Wrote HTML-ized index to `%s' [%s].\n"),
+ filename, number_to_static_string (sz));
}
else
logprintf (LOG_NOTQUIET,
uerr_t ftp_type PARAMS ((int, int));
uerr_t ftp_cwd PARAMS ((int, const char *));
uerr_t ftp_retr PARAMS ((int, const char *));
-uerr_t ftp_rest PARAMS ((int, long));
+uerr_t ftp_rest PARAMS ((int, wgint));
uerr_t ftp_list PARAMS ((int, const char *));
uerr_t ftp_syst PARAMS ((int, enum stype *));
uerr_t ftp_pwd PARAMS ((int, char **));
-uerr_t ftp_size PARAMS ((int, const char *, long int *));
+uerr_t ftp_size PARAMS ((int, const char *, wgint *));
#ifdef USE_OPIE
const char *skey_response PARAMS ((int, const char *, const char *));
{
enum ftype type; /* file type */
char *name; /* file name */
- long size; /* file size */
+ wgint size; /* file size */
long tstamp; /* time-stamp */
int perms; /* file permissions */
char *linkto; /* link to which file points */
logprintf (LOG_NOTQUIET, "%s: %s\n", file, strerror (errno));
return NULL;
}
- DEBUGP (("Loaded %s (size %ld).\n", file, fm->length));
+ DEBUGP (("Loaded %s (size %s).\n", file, number_to_static_string (fm->length)));
ctx.text = fm->content;
ctx.head = ctx.tail = NULL;
logprintf (LOG_NOTQUIET, "%s: %s\n", file, strerror (errno));
return NULL;
}
- DEBUGP (("Loaded %s (size %ld).\n", file, fm->length));
+ DEBUGP (("Loaded %s (size %s).\n", file, number_to_static_string (fm->length)));
head = tail = NULL;
text = fm->content;
request_set_header (req, "Referer", opt.referer, rel_none);
// Value freshly allocated, free it when done.
- request_set_header (req, "Range", aprintf ("bytes=%ld-", hs->restval),
+ request_set_header (req, "Range",
+ aprintf ("bytes=%s-", number_to_static_string (hs->restval)),
rel_value);
*/
longer, read only that much; if the file is shorter, report an error. */
static int
-post_file (int sock, const char *file_name, long promised_size)
+post_file (int sock, const char *file_name, wgint promised_size)
{
static char chunk[8192];
- long written = 0;
+ wgint written = 0;
int write_error;
FILE *fp;
/* Parse the `Content-Range' header and extract the information it
contains. Returns 1 if successful, -1 otherwise. */
static int
-parse_content_range (const char *hdr, long *first_byte_ptr,
- long *last_byte_ptr, long *entity_length_ptr)
+parse_content_range (const char *hdr, wgint *first_byte_ptr,
+ wgint *last_byte_ptr, wgint *entity_length_ptr)
{
- long num;
+ wgint num;
/* Ancient versions of Netscape proxy server, presumably predating
rfc2068, sent out `Content-Range' without the "bytes"
which need to be read anyway. */
static void
-skip_short_body (int fd, long contlen)
+skip_short_body (int fd, wgint contlen)
{
/* Skipping the body doesn't make sense if the content length is
unknown because, in that case, persistent connections cannot be
still be used with the magic of the "chunked" transfer!) */
if (contlen == -1)
return;
- DEBUGP (("Skipping %ld bytes of body data... ", contlen));
+ DEBUGP (("Skipping %s bytes of body data... ", number_to_static_string (contlen)));
while (contlen > 0)
{
\f
struct http_stat
{
- long len; /* received length */
- long contlen; /* expected length */
- long restval; /* the restart value */
+ wgint len; /* received length */
+ wgint contlen; /* expected length */
+ wgint restval; /* the restart value */
int res; /* the result of last read */
char *newloc; /* new location (redirection) */
char *remote_time; /* remote time-stamp string */
char *error; /* textual HTTP error */
int statcode; /* status code */
- long rd_size; /* amount of data read from socket */
+ wgint rd_size; /* amount of data read from socket */
double dltime; /* time it took to download the data */
const char *referer; /* value of the referer header. */
char **local_file; /* local file. */
char *proxyauth;
int statcode;
int write_error;
- long contlen, contrange;
+ wgint contlen, contrange;
struct url *conn;
FILE *fp;
int inhibit_keep_alive = !opt.http_keep_alive || opt.ignore_length;
/* Headers sent when using POST. */
- long post_data_size = 0;
+ wgint post_data_size = 0;
int host_lookup_failed = 0;
request_set_header (req, "Pragma", "no-cache", rel_none);
if (hs->restval)
request_set_header (req, "Range",
- aprintf ("bytes=%ld-", hs->restval), rel_value);
+ aprintf ("bytes=%s-",
+ number_to_static_string (hs->restval)),
+ rel_value);
if (opt.useragent)
request_set_header (req, "User-Agent", opt.useragent, rel_none);
else
}
}
request_set_header (req, "Content-Length",
- aprintf ("%ld", post_data_size), rel_value);
+ xstrdup (number_to_static_string (post_data_size)),
+ rel_value);
}
/* Add the user headers. */
if (!opt.ignore_length
&& response_header_copy (resp, "Content-Length", hdrval, sizeof (hdrval)))
- contlen = strtol (hdrval, NULL, 10);
+ {
+ wgint parsed;
+ errno = 0;
+ parsed = str_to_wgint (hdrval, NULL, 10);
+ if (parsed == WGINT_MAX && errno == ERANGE)
+ /* Out of range.
+ #### If Content-Length is out of range, it most likely
+ means that the file is larger than 2G and that we're
+ compiled without LFS. In that case we should probably
+ refuse to even attempt to download the file. */
+ contlen = -1;
+ else
+ contlen = parsed;
+ }
/* Check for keep-alive related responses. */
if (!inhibit_keep_alive && contlen != -1)
}
if (response_header_copy (resp, "Content-Range", hdrval, sizeof (hdrval)))
{
- long first_byte_pos, last_byte_pos, entity_length;
+ wgint first_byte_pos, last_byte_pos, entity_length;
if (parse_content_range (hdrval, &first_byte_pos, &last_byte_pos,
&entity_length))
contrange = first_byte_pos;
char *tms, *locf, *tmrate;
uerr_t err;
time_t tml = -1, tmr = -1; /* local and remote time-stamps */
- long local_size = 0; /* the size of the local file */
+ wgint local_size = 0; /* the size of the local file */
size_t filename_len;
struct http_stat hstat; /* HTTP status */
- struct stat st;
+ struct_stat st;
char *dummy = NULL;
/* This used to be done in main(), but it's a better idea to do it
point I profiled Wget, and found that a measurable and
non-negligible amount of time was lost calling sprintf()
in url.c. Replacing sprintf with inline calls to
- strcpy() and long_to_string() made a difference.
+ strcpy() and number_to_string() made a difference.
--hniksic */
memcpy (filename_plus_orig_suffix, *hstat.local_file, filename_len);
memcpy (filename_plus_orig_suffix + filename_len,
}
else if (tml >= tmr)
logprintf (LOG_VERBOSE, _("\
-The sizes do not match (local %ld) -- retrieving.\n"), local_size);
+The sizes do not match (local %s) -- retrieving.\n"),
+ number_to_static_string (local_size));
else
logputs (LOG_VERBOSE,
_("Remote file is newer, retrieving.\n"));
if (*dt & RETROKF)
{
logprintf (LOG_VERBOSE,
- _("%s (%s) - `%s' saved [%ld/%ld]\n\n"),
- tms, tmrate, locf, hstat.len, hstat.contlen);
+ _("%s (%s) - `%s' saved [%s/%s]\n\n"),
+ tms, tmrate, locf,
+ number_to_static_string (hstat.len),
+ number_to_static_string (hstat.contlen));
logprintf (LOG_NONVERBOSE,
- "%s URL:%s [%ld/%ld] -> \"%s\" [%d]\n",
- tms, u->url, hstat.len, hstat.contlen, locf, count);
+ "%s URL:%s [%s/%s] -> \"%s\" [%d]\n",
+ tms, u->url,
+ number_to_static_string (hstat.len),
+ number_to_static_string (hstat.contlen),
+ locf, count);
}
++opt.numurls;
total_downloaded_bytes += hstat.len;
if (*dt & RETROKF)
{
logprintf (LOG_VERBOSE,
- _("%s (%s) - `%s' saved [%ld]\n\n"),
- tms, tmrate, locf, hstat.len);
+ _("%s (%s) - `%s' saved [%s]\n\n"),
+ tms, tmrate, locf,
+ number_to_static_string (hstat.len));
logprintf (LOG_NONVERBOSE,
- "%s URL:%s [%ld] -> \"%s\" [%d]\n",
- tms, u->url, hstat.len, locf, count);
+ "%s URL:%s [%s] -> \"%s\" [%d]\n",
+ tms, u->url, number_to_static_string (hstat.len),
+ locf, count);
}
++opt.numurls;
total_downloaded_bytes += hstat.len;
connection too soon */
{
logprintf (LOG_VERBOSE,
- _("%s (%s) - Connection closed at byte %ld. "),
- tms, tmrate, hstat.len);
+ _("%s (%s) - Connection closed at byte %s. "),
+ tms, tmrate, number_to_static_string (hstat.len));
printwhat (count, opt.ntry);
free_hstat (&hstat);
continue;
else if (!opt.kill_longer) /* meaning we got more than expected */
{
logprintf (LOG_VERBOSE,
- _("%s (%s) - `%s' saved [%ld/%ld])\n\n"),
- tms, tmrate, locf, hstat.len, hstat.contlen);
+ _("%s (%s) - `%s' saved [%s/%s])\n\n"),
+ tms, tmrate, locf,
+ number_to_static_string (hstat.len),
+ number_to_static_string (hstat.contlen));
logprintf (LOG_NONVERBOSE,
- "%s URL:%s [%ld/%ld] -> \"%s\" [%d]\n",
- tms, u->url, hstat.len, hstat.contlen, locf, count);
+ "%s URL:%s [%s/%s] -> \"%s\" [%d]\n",
+ tms, u->url,
+ number_to_static_string (hstat.len),
+ number_to_static_string (hstat.contlen),
+ locf, count);
++opt.numurls;
total_downloaded_bytes += hstat.len;
else /* the same, but not accepted */
{
logprintf (LOG_VERBOSE,
- _("%s (%s) - Connection closed at byte %ld/%ld. "),
- tms, tmrate, hstat.len, hstat.contlen);
+ _("%s (%s) - Connection closed at byte %s/%s. "),
+ tms, tmrate,
+ number_to_static_string (hstat.len),
+ number_to_static_string (hstat.contlen));
printwhat (count, opt.ntry);
free_hstat (&hstat);
continue;
if (hstat.contlen == -1)
{
logprintf (LOG_VERBOSE,
- _("%s (%s) - Read error at byte %ld (%s)."),
- tms, tmrate, hstat.len, strerror (errno));
+ _("%s (%s) - Read error at byte %s (%s)."),
+ tms, tmrate, number_to_static_string (hstat.len),
+ strerror (errno));
printwhat (count, opt.ntry);
free_hstat (&hstat);
continue;
else /* hstat.res == -1 and contlen is given */
{
logprintf (LOG_VERBOSE,
- _("%s (%s) - Read error at byte %ld/%ld (%s). "),
- tms, tmrate, hstat.len, hstat.contlen,
+ _("%s (%s) - Read error at byte %s/%s (%s). "),
+ tms, tmrate,
+ number_to_static_string (hstat.len),
+ number_to_static_string (hstat.contlen),
strerror (errno));
printwhat (count, opt.ntry);
free_hstat (&hstat);
static int simple_atof PARAMS ((const char *, const char *, double *));
-/* Enginge for cmd_bytes and cmd_bytes_large: converts a string such
- as "100k" or "2.5G" to a floating point number. */
+/* Engine for cmd_bytes and cmd_bytes_large: converts a string such as
+ "100k" or "2.5G" to a floating point number. */
static int
parse_bytes_helper (const char *val, double *result)
}
/* Parse VAL as a number and set its value to CLOSURE (which should
- point to a long int).
+ point to a wgint).
By default, the value is assumed to be in bytes. If "K", "M", or
"G" are appended, the value is multiplied with 1<<10, 1<<20, or
exec_name, com, val);
return 0;
}
- *(long *)closure = (long)byte_value;
+ *(wgint *)closure = (wgint)byte_value;
return 1;
}
output_stream = stdout;
else
{
- struct stat st;
+ struct_stat st;
output_stream = fopen (opt.output_document,
opt.always_rest ? "ab" : "wb");
if (output_stream == NULL)
#endif /* not HAVE_USLEEP */
}
+#if defined(_MSC_VER) && _MSC_VER < 1300
+
+static inline int
+char_value (char c, int base)
+{
+ int value;
+ if (c < '0')
+ return -1;
+ if ('0' <= c && c <= '9')
+ value = c - '0';
+ else if ('a' <= c && c <= 'z')
+ value = c - 'a' + 10;
+ else if ('A' <= c && c <= 'Z')
+ value = c - 'A' + 10;
+ else
+ return -1;
+ if (value >= base)
+ return -1;
+ return value;
+}
+
+/* A fairly simple strtoll replacement for MS VC versions that don't
+ supply _strtoi64. */
+
+__int64
+str_to_int64 (const char *nptr, char **endptr, int base)
+{
+#define OVERFLOW 9223372036854775807I64
+#define UNDERFLOW (-OVERFLOW - 1)
+
+ __int64 result = 0;
+ int negative;
+
+ if (base != 0 && (base < 2 || base > 36))
+ {
+ errno = EINVAL;
+ return 0;
+ }
+
+ while (*nptr == ' ' || *nptr == '\t')
+ ++nptr;
+ if (*nptr == '-')
+ {
+ negative = 1;
+ ++nptr;
+ }
+ else if (*nptr == '+')
+ {
+ negative = 0;
+ ++nptr;
+ }
+ else
+ negative = 0;
+
+ /* If base is 0, determine the real base based on the beginning on
+ the number; octal numbers begin with "0", hexadecimal with "0x",
+ and the others are considered octal. */
+ if (*nptr == '0')
+ {
+ if ((base == 0 || base == 16)
+ &&
+ (*(nptr + 1) == 'x' || *(nptr + 1) == 'X'))
+ {
+ base = 16;
+ nptr += 2;
+ }
+ else if (base == 0)
+ base = 8;
+ }
+ else if (base == 0)
+ base = 10;
+
+ if (!negative)
+ {
+ /* Parse positive number, checking for overflow. */
+ int val;
+ for (; (val = char_value (*nptr, base)) != -1; ++nptr)
+ {
+ __int64 newresult = base * result + val;
+ if (newresult < result)
+ {
+ result = OVERFLOW;
+ errno = ERANGE;
+ break;
+ }
+ result = newresult;
+ }
+ }
+ else
+ {
+ /* Parse negative number, checking for underflow. */
+ int val;
+ for (; (val = char_value (*nptr, base)) != -1; ++nptr)
+ {
+ __int64 newresult = base * result - val;
+ if (newresult > result)
+ {
+ result = UNDERFLOW;
+ errno = ERANGE;
+ break;
+ }
+ result = newresult;
+ }
+ }
+ if (endptr)
+ *endptr = (char *) nptr;
+ return result;
+}
+
+#else /* !defined(_MSC_VER) || _MSC_VER >= 1300 */
+
+__int64
+str_to_int64 (const char *nptr, char **endptr, int base)
+{
+#ifdef _MSC_VER
+ return _strtoi64 (nptr, endptr, base);
+#else
+ return strtoll (nptr, endptr, base);
+#endif
+}
+
+#endif /* !defined(_MSC_VER) || _MSC_VER >= 1300 */
+
+/* A simple clone of ftello. The normal ftell doesn't work for large
+ files, so this is needed, and used by file_size(), which is itself
+ used for the --post-file option.
+
+ This function uses fgetpos incorrectly and should be considered a
+ hack until a better way to tell the stream position is found. */
+
+__int64
+wget_ftello (FILE *fp)
+{
+ fpos_t pos;
+ if (fgetpos (fp, &pos) != 0)
+ return -1;
+ else
+ return pos;
+}
+
void
windows_main_junk (int *argc, char **argv, char **exec_name)
{
#define snprintf _snprintf
#define vsnprintf _vsnprintf
-/* No stat on Windows. */
+/* Define a wgint type under Windows. */
+typedef __int64 wgint;
+#define SIZEOF_WGINT 8
+
+#if defined(_MSC_VER) || defined (__WATCOMC__)
+# define WGINT_MAX 9223372036854775807I64
+#else
+# define WGINT_MAX 9223372036854775807LL
+#endif
+
+#define str_to_wgint str_to_int64
+__int64 str_to_int64 (const char *, char **, int);
+
+/* No lstat on Windows. */
#define lstat stat
+/* Transparently support large files, in spirit similar to the POSIX
+ LFS API. */
+#define stat(fname, buf) _stati64 (fname, buf)
+#define fstat(fd, buf) _fstati64(fd, buf)
+#define struct_stat struct _stati64
+
#define PATH_SEPARATOR '\\'
/* Microsoft says stat is _stat, Borland doesn't. */
#endif
#endif
-#define REALCLOSE(x) closesocket (x)
+/* If ftello is unavailable, use an approximation. */
+#ifndef HAVE_FTELLO
+__int64 wget_ftello (FILE *);
+# define ftello wget_ftello
+# define HAVE_FTELLO
+#endif
/* #### Do we need this? */
#include <direct.h>
if (home)
{
int err;
- struct stat buf;
+ struct_stat buf;
char *path = (char *)alloca (strlen (home) + 1
+ strlen (NETRC_FILE_NAME) + 1);
sprintf (path, "%s/%s", home, NETRC_FILE_NAME);
int
main (int argc, char **argv)
{
- struct stat sb;
+ struct_stat sb;
char *program_name, *file, *target;
acc_t *head, *a;
struct progress_implementation {
const char *name;
int interactive;
- void *(*create) PARAMS ((long, long));
- void (*update) PARAMS ((void *, long, double));
+ void *(*create) PARAMS ((wgint, wgint));
+ void (*update) PARAMS ((void *, wgint, double));
void (*finish) PARAMS ((void *, double));
void (*set_params) PARAMS ((const char *));
};
/* Necessary forward declarations. */
-static void *dot_create PARAMS ((long, long));
-static void dot_update PARAMS ((void *, long, double));
+static void *dot_create PARAMS ((wgint, wgint));
+static void dot_update PARAMS ((void *, wgint, double));
static void dot_finish PARAMS ((void *, double));
static void dot_set_params PARAMS ((const char *));
-static void *bar_create PARAMS ((long, long));
-static void bar_update PARAMS ((void *, long, double));
+static void *bar_create PARAMS ((wgint, wgint));
+static void bar_update PARAMS ((void *, wgint, double));
static void bar_finish PARAMS ((void *, double));
static void bar_set_params PARAMS ((const char *));
advance. */
void *
-progress_create (long initial, long total)
+progress_create (wgint initial, wgint total)
{
/* Check if the log status has changed under our feet. */
if (output_redirected)
time in milliseconds since the beginning of the download. */
void
-progress_update (void *progress, long howmuch, double dltime)
+progress_update (void *progress, wgint howmuch, double dltime)
{
current_impl->update (progress, howmuch, dltime);
}
/* Dot-printing. */
struct dot_progress {
- long initial_length; /* how many bytes have been downloaded
+ wgint initial_length; /* how many bytes have been downloaded
previously. */
- long total_length; /* expected total byte count when the
+ wgint total_length; /* expected total byte count when the
download finishes */
int accumulated;
/* Dot-progress backend for progress_create. */
static void *
-dot_create (long initial, long total)
+dot_create (wgint initial, wgint total)
{
struct dot_progress *dp = xnew0 (struct dot_progress);
dp->initial_length = initial;
if (dp->initial_length)
{
int dot_bytes = opt.dot_bytes;
- long row_bytes = opt.dot_bytes * opt.dots_in_line;
+ wgint row_bytes = opt.dot_bytes * opt.dots_in_line;
int remainder = (int) (dp->initial_length % row_bytes);
- long skipped = dp->initial_length - remainder;
+ wgint skipped = dp->initial_length - remainder;
if (skipped)
{
2 + skipped_k_len, "", skipped_k);
}
- logprintf (LOG_VERBOSE, "\n%5ldK", skipped / 1024);
+ logprintf (LOG_VERBOSE, "\n%5ldK", (long) (skipped / 1024));
for (; remainder >= dot_bytes; remainder -= dot_bytes)
{
if (dp->dots % opt.dot_spacing == 0)
}
static void
-print_percentage (long bytes, long expected)
+print_percentage (wgint bytes, wgint expected)
{
int percentage = (int)(100.0 * bytes / expected);
logprintf (LOG_VERBOSE, "%3d%%", percentage);
}
static void
-print_download_speed (struct dot_progress *dp, long bytes, double dltime)
+print_download_speed (struct dot_progress *dp, wgint bytes, double dltime)
{
logprintf (LOG_VERBOSE, " %s",
retr_rate (bytes, dltime - dp->last_timer_value, 1));
/* Dot-progress backend for progress_update. */
static void
-dot_update (void *progress, long howmuch, double dltime)
+dot_update (void *progress, wgint howmuch, double dltime)
{
struct dot_progress *dp = progress;
int dot_bytes = opt.dot_bytes;
- long row_bytes = opt.dot_bytes * opt.dots_in_line;
+ wgint row_bytes = opt.dot_bytes * opt.dots_in_line;
log_set_flush (0);
for (; dp->accumulated >= dot_bytes; dp->accumulated -= dot_bytes)
{
if (dp->dots == 0)
- logprintf (LOG_VERBOSE, "\n%5ldK", dp->rows * row_bytes / 1024);
+ logprintf (LOG_VERBOSE, "\n%5ldK", (long) (dp->rows * row_bytes / 1024));
if (dp->dots % opt.dot_spacing == 0)
logputs (LOG_VERBOSE, " ");
++dp->dots;
if (dp->dots >= opt.dots_in_line)
{
- long row_qty = row_bytes;
+ wgint row_qty = row_bytes;
if (dp->rows == dp->initial_length / row_bytes)
row_qty -= dp->initial_length % row_bytes;
{
struct dot_progress *dp = progress;
int dot_bytes = opt.dot_bytes;
- long row_bytes = opt.dot_bytes * opt.dots_in_line;
+ wgint row_bytes = opt.dot_bytes * opt.dots_in_line;
int i;
log_set_flush (0);
if (dp->dots == 0)
- logprintf (LOG_VERBOSE, "\n%5ldK", dp->rows * row_bytes / 1024);
+ logprintf (LOG_VERBOSE, "\n%5ldK", (long) (dp->rows * row_bytes / 1024));
for (i = dp->dots; i < opt.dots_in_line; i++)
{
if (i % opt.dot_spacing == 0)
}
{
- long row_qty = dp->dots * dot_bytes + dp->accumulated;
+ wgint row_qty = dp->dots * dot_bytes + dp->accumulated;
if (dp->rows == dp->initial_length / row_bytes)
row_qty -= dp->initial_length % row_bytes;
print_download_speed (dp, row_qty, dltime);
#define STALL_START_TIME 5000
struct bar_progress {
- long initial_length; /* how many bytes have been downloaded
+ wgint initial_length; /* how many bytes have been downloaded
previously. */
- long total_length; /* expected total byte count when the
+ wgint total_length; /* expected total byte count when the
download finishes */
- long count; /* bytes downloaded so far */
+ wgint count; /* bytes downloaded so far */
double last_screen_update; /* time of the last screen update,
measured since the beginning of
details. */
struct bar_progress_hist {
int pos;
- long times[DLSPEED_HISTORY_SIZE];
- long bytes[DLSPEED_HISTORY_SIZE];
+ wgint times[DLSPEED_HISTORY_SIZE];
+ wgint bytes[DLSPEED_HISTORY_SIZE];
/* The sum of times and bytes respectively, maintained for
efficiency. */
- long total_time;
- long total_bytes;
+ wgint total_time;
+ wgint total_bytes;
} hist;
double recent_start; /* timestamp of beginning of current
position. */
- long recent_bytes; /* bytes downloaded so far. */
+ wgint recent_bytes; /* bytes downloaded so far. */
int stalled; /* set when no data arrives for longer
than STALL_START_TIME, then reset
double last_eta_time; /* time of the last update to download
speed and ETA, measured since the
beginning of download. */
- long last_eta_value;
+ wgint last_eta_value;
};
static void create_image PARAMS ((struct bar_progress *, double));
static void display_image PARAMS ((char *));
static void *
-bar_create (long initial, long total)
+bar_create (wgint initial, wgint total)
{
struct bar_progress *bp = xnew0 (struct bar_progress);
return bp;
}
-static void update_speed_ring PARAMS ((struct bar_progress *, long, double));
+static void update_speed_ring PARAMS ((struct bar_progress *, wgint, double));
static void
-bar_update (void *progress, long howmuch, double dltime)
+bar_update (void *progress, wgint howmuch, double dltime)
{
struct bar_progress *bp = progress;
int force_screen_update = 0;
3-second average would be too erratic. */
static void
-update_speed_ring (struct bar_progress *bp, long howmuch, double dltime)
+update_speed_ring (struct bar_progress *bp, wgint howmuch, double dltime)
{
struct bar_progress_hist *hist = &bp->hist;
double recent_age = dltime - bp->recent_start;
create_image (struct bar_progress *bp, double dl_total_time)
{
char *p = bp->buffer;
- long size = bp->initial_length + bp->count;
+ wgint size = bp->initial_length + bp->count;
char *size_legible = legible (size);
int size_legible_len = strlen (size_legible);
int units = 0;
/* Calculate the download speed using the history ring and
recent data that hasn't made it to the ring yet. */
- long dlquant = hist->total_bytes + bp->recent_bytes;
+ wgint dlquant = hist->total_bytes + bp->recent_bytes;
double dltime = hist->total_time + (dl_total_time - bp->recent_start);
double dlspeed = calc_rate (dlquant, dltime, &units);
sprintf (p, " %7.2f%s", dlspeed, short_units[units]);
reliable. */
if (bp->total_length > 0 && bp->count > 0 && dl_total_time > 3000)
{
- long eta;
+ wgint eta;
int eta_hrs, eta_min, eta_sec;
/* Don't change the value of ETA more than approximately once
I found that doing that results in a very jerky and
ultimately unreliable ETA. */
double time_sofar = (double)dl_total_time / 1000;
- long bytes_remaining = bp->total_length - size;
- eta = (long) (time_sofar * bytes_remaining / bp->count);
+ wgint bytes_remaining = bp->total_length - size;
+ eta = (wgint) (time_sofar * bytes_remaining / bp->count);
bp->last_eta_value = eta;
bp->last_eta_time = dl_total_time;
}
void set_progress_implementation PARAMS ((const char *));
void progress_schedule_redirect PARAMS ((void));
-void *progress_create PARAMS ((long, long));
+void *progress_create PARAMS ((wgint, wgint));
int progress_interactive_p PARAMS ((void *));
-void progress_update PARAMS ((void *, long, double));
+void progress_update PARAMS ((void *, wgint, double));
void progress_finish PARAMS ((void *, double));
RETSIGTYPE progress_handle_sigwinch PARAMS ((int));
int output_stream_regular;
\f
static struct {
- long chunk_bytes;
+ wgint chunk_bytes;
double chunk_start;
double sleep_adjust;
} limit_data;
is the timer that started at the beginning of download. */
static void
-limit_bandwidth (long bytes, struct wget_timer *timer)
+limit_bandwidth (wgint bytes, struct wget_timer *timer)
{
double delta_t = wtimer_read (timer) - limit_data.chunk_start;
double expected;
double t0, t1;
if (slp < 200)
{
- DEBUGP (("deferring a %.2f ms sleep (%ld/%.2f).\n",
- slp, limit_data.chunk_bytes, delta_t));
+ DEBUGP (("deferring a %.2f ms sleep (%s/%.2f).\n",
+ slp, number_to_static_string (limit_data.chunk_bytes),
+ delta_t));
return;
}
- DEBUGP (("\nsleeping %.2f ms for %ld bytes, adjust %.2f ms\n",
- slp, limit_data.chunk_bytes, limit_data.sleep_adjust));
+ DEBUGP (("\nsleeping %.2f ms for %s bytes, adjust %.2f ms\n",
+ slp, number_to_static_string (limit_data.chunk_bytes),
+ limit_data.sleep_adjust));
t0 = wtimer_read (timer);
xsleep (slp / 1000);
of data written. */
static int
-write_data (FILE *out, const char *buf, int bufsize, long *skip,
- long *written)
+write_data (FILE *out, const char *buf, int bufsize, wgint *skip,
+ wgint *written)
{
if (!out)
return 1;
writing data, -2 is returned. */
int
-fd_read_body (int fd, FILE *out, long toread, long startpos,
- long *qtyread, long *qtywritten, double *elapsed, int flags)
+fd_read_body (int fd, FILE *out, wgint toread, wgint startpos,
+ wgint *qtyread, wgint *qtywritten, double *elapsed, int flags)
{
int ret = 0;
int progress_interactive = 0;
int exact = flags & rb_read_exactly;
- long skip = 0;
+ wgint skip = 0;
/* How much data we've read/written. */
- long sum_read = 0;
- long sum_written = 0;
+ wgint sum_read = 0;
+ wgint sum_written = 0;
if (flags & rb_skip_startpos)
skip = startpos;
appropriate for the speed. If PAD is non-zero, strings will be
padded to the width of 7 characters (xxxx.xx). */
char *
-retr_rate (long bytes, double msecs, int pad)
+retr_rate (wgint bytes, double msecs, int pad)
{
static char res[20];
static const char *rate_names[] = {"B/s", "KB/s", "MB/s", "GB/s" };
UNITS is zero for B/s, one for KB/s, two for MB/s, and three for
GB/s. */
double
-calc_rate (long bytes, double msecs, int *units)
+calc_rate (wgint bytes, double msecs, int *units)
{
double dlrate;
int maxlen = strlen (fname) + 1 + numdigit (opt.backups) + 1;
char *from = (char *)alloca (maxlen);
char *to = (char *)alloca (maxlen);
- struct stat sb;
+ struct_stat sb;
int i;
if (stat (fname, &sb) == 0)
rb_skip_startpos = 2
};
-int fd_read_body PARAMS ((int, FILE *, long, long, long *, long *, double *,
+int fd_read_body PARAMS ((int, FILE *, wgint, wgint, wgint *, wgint *, double *,
int));
typedef const char *(*hunk_terminator_t) PARAMS ((const char *, int, int));
const char *, int *));
uerr_t retrieve_from_file PARAMS ((const char *, int, int *));
-char *retr_rate PARAMS ((long, double, int));
-double calc_rate PARAMS ((long, double, int *));
+char *retr_rate PARAMS ((wgint, double, int));
+double calc_rate PARAMS ((wgint, double, int *));
void printwhat PARAMS ((int, int));
void sleep_between_retrievals PARAMS ((int));
/* Define a large integral type useful for storing large sizes that
exceed sizes of one download, such as when printing the sum of all
downloads. Note that this has nothing to do with large file
- support, yet.
+ support, which determines the wgint type.
We use a 64-bit integral type where available, `double' otherwise.
It's hard to print LARGE_INT's portably, but fortunately it's
typedef long long LARGE_INT;
# define LARGE_INT_FMT "%lld"
# else
+# if _MSC_VER
+/* Use __int64 under Windows. */
+typedef __int64 LARGE_INT;
+# define LARGE_INT_FMT "%I64"
+# else
/* Large integer type unavailable; use `double' instead. */
typedef double LARGE_INT;
-# define LARGE_INT_FMT "%.0f"
+# define LARGE_INT_FMT "%.0f"
+# endif
# endif
#endif
+/* Under Windows we #define struct_stat to struct _stati64. */
+#ifndef struct_stat
+# define struct_stat struct stat
+#endif
+
+#ifdef HAVE_LIMITS_H
+# include <limits.h>
+#endif
+
+#ifndef CHAR_BIT
+# define CHAR_BIT 8
+#endif
+
+#ifndef LONG_MAX
+# define LONG_MAX ((long) ~((unsigned long)1 << (CHAR_BIT * sizeof (long) - 1)))
+#endif
+#ifndef LLONG_MAX
+# define LLONG_MAX ((long long) ~((unsigned long long)1 << (CHAR_BIT * sizeof (long long) - 1)))
+#endif
+
/* These are defined in cmpt.c if missing, therefore it's generally
safe to declare their parameters. */
#ifndef HAVE_STRERROR
{
const char *p;
char *t;
- struct stat st;
+ struct_stat st;
int res;
p = path + strlen (path);
remove_link (const char *file)
{
int err = 0;
- struct stat st;
+ struct_stat st;
if (lstat (file, &st) == 0 && S_ISLNK (st.st_mode))
{
#ifdef HAVE_ACCESS
return access (filename, F_OK) >= 0;
#else
- struct stat buf;
+ struct_stat buf;
return stat (filename, &buf) >= 0;
#endif
}
int
file_non_directory_p (const char *path)
{
- struct stat buf;
+ struct_stat buf;
/* Use lstat() rather than stat() so that symbolic links pointing to
directories can be identified correctly. */
if (lstat (path, &buf) != 0)
/* Return the size of file named by FILENAME, or -1 if it cannot be
opened or seeked into. */
-long
+wgint
file_size (const char *filename)
{
- long size;
+#if defined(HAVE_FSEEKO) && defined(HAVE_FTELLO)
+ wgint size;
/* We use fseek rather than stat to determine the file size because
- that way we can also verify whether the file is readable.
- Inspired by the POST patch by Arnaud Wylie. */
+ that way we can also verify that the file is readable without
+ explicitly checking for permissions. Inspired by the POST patch
+ by Arnaud Wylie. */
FILE *fp = fopen (filename, "rb");
if (!fp)
return -1;
- fseek (fp, 0, SEEK_END);
- size = ftell (fp);
+ fseeko (fp, 0, SEEK_END);
+ size = ftello (fp);
fclose (fp);
return size;
+#else
+ struct_stat st;
+ if (stat (filename, &st) < 0)
+ return -1;
+ return st.st_size;
+#endif
}
/* stat file names named PREFIX.1, PREFIX.2, etc., until one that
{
int fd;
struct file_memory *fm;
- long size;
+ wgint size;
int inhibit_close = 0;
/* Some magic in the finest tradition of Perl and its kin: if FILE
#ifdef HAVE_MMAP
{
- struct stat buf;
+ struct_stat buf;
if (fstat (fd, &buf) < 0)
goto mmap_lose;
fm->length = buf.st_size;
fm->content = xmalloc (size);
while (1)
{
- long nread;
+ wgint nread;
if (fm->length > size / 2)
{
/* #### I'm not sure whether the whole exponential-growth
return outbuf;
}
-/* Legible -- return a static pointer to the legibly printed long. */
+/* Legible -- return a static pointer to the legibly printed wgint. */
char *
-legible (long l)
+legible (wgint l)
{
char inbuf[24];
/* Print the number into the buffer. */
return legible_1 (inbuf);
}
-/* Count the digits in a (long) integer. */
+/* Count the digits in an integer number. */
int
-numdigit (long number)
+numdigit (wgint number)
{
int cnt = 1;
if (number < 0)
return cnt;
}
-/* Attempt to calculate INT_MAX on machines that don't bother to
- define it. */
-#ifndef INT_MAX
-# ifndef CHAR_BIT
-# define CHAR_BIT 8
-# endif
-# define INT_MAX ((int) ~((unsigned)1 << CHAR_BIT * sizeof (int) - 1))
-#endif
-
#define ONE_DIGIT(figure) *p++ = n / (figure) + '0'
#define ONE_DIGIT_ADVANCE(figure) (ONE_DIGIT (figure), n %= (figure))
#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. */
+/* DIGITS_<11-20> are only used on machines with 64-bit numbers. */
#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_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 should be completely
- equivalent to `sprintf(buffer, "%ld", number)', only much faster.
+/* It is annoying that we have three different syntaxes for 64-bit constants:
+ - nnnL for 64-bit systems, where they are of type long;
+ - nnnLL for 32-bit systems that support long long;
+ - nnnI64 for MS compiler on Windows, which doesn't support long long. */
+
+#if SIZEOF_LONG > 4
+/* If long is large enough, use long constants. */
+# define C10000000000 10000000000L
+# define C100000000000 100000000000L
+# define C1000000000000 1000000000000L
+# define C10000000000000 10000000000000L
+# define C100000000000000 100000000000000L
+# define C1000000000000000 1000000000000000L
+# define C10000000000000000 10000000000000000L
+# define C100000000000000000 100000000000000000L
+# define C1000000000000000000 1000000000000000000L
+#else
+# if SIZEOF_LONG_LONG != 0
+/* Otherwise, if long long is available, use long long constants. */
+# define C10000000000 10000000000LL
+# define C100000000000 100000000000LL
+# define C1000000000000 1000000000000LL
+# define C10000000000000 10000000000000LL
+# define C100000000000000 100000000000000LL
+# define C1000000000000000 1000000000000000LL
+# define C10000000000000000 10000000000000000LL
+# define C100000000000000000 100000000000000000LL
+# define C1000000000000000000 1000000000000000000LL
+# else
+# if defined(_MSC_VER) || defined(__WATCOM__)
+/* Otherwise, if __int64 is available (under Windows), use __int64
+ constants. */
+# define C10000000000 10000000000I64
+# define C100000000000 100000000000I64
+# define C1000000000000 1000000000000I64
+# define C10000000000000 10000000000000I64
+# define C100000000000000 100000000000000I64
+# define C1000000000000000 1000000000000000I64
+# define C10000000000000000 10000000000000000I64
+# define C100000000000000000 100000000000000000I64
+# define C1000000000000000000 1000000000000000000I64
+# endif
+# endif
+#endif
+
+/* SPRINTF_WGINT is used by number_to_string to handle pathological
+ cases and to portably support strange sizes of wgint. */
+#if SIZEOF_LONG >= SIZEOF_WGINT
+# define SPRINTF_WGINT(buf, n) sprintf(buf, "%ld", (long) (n))
+#else
+# if SIZEOF_LONG_LONG >= SIZEOF_WGINT
+# define SPRINTF_WGINT(buf, n) sprintf(buf, "%lld", (long long) (n))
+# else
+# ifdef _MSC_VER
+# define SPRINTF_WGINT(buf, n) sprintf(buf, "%I64", (__int64) (n))
+# endif
+# endif
+#endif
+
+/* Print NUMBER to BUFFER in base 10. This is equivalent to
+ `sprintf(buffer, "%lld", (long long) number)', only much faster and
+ portable to machines without long long.
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.
+ slow when converting integers to strings.
Return the pointer to the location where the terminating zero was
printed. (Equivalent to calling buffer+strlen(buffer) after the
terminating '\0'. */
char *
-number_to_string (char *buffer, long number)
+number_to_string (char *buffer, wgint number)
{
char *p = buffer;
- long n = number;
+ wgint n = number;
-#if (SIZEOF_LONG != 4) && (SIZEOF_LONG != 8)
+#if (SIZEOF_WGINT != 4) && (SIZEOF_WGINT != 8)
/* We are running in a strange or misconfigured environment. Let
sprintf cope with it. */
- sprintf (buffer, "%ld", n);
+ SPRINTF_WGINT (buffer, n);
p += strlen (buffer);
-#else /* (SIZEOF_LONG == 4) || (SIZEOF_LONG == 8) */
+#else /* (SIZEOF_WGINT == 4) || (SIZEOF_WGINT == 8) */
if (n < 0)
{
- if (n < -INT_MAX)
+ if (n < -WGINT_MAX)
{
/* We cannot print a '-' and assign -n to n because -n would
overflow. Let sprintf deal with this border case. */
- sprintf (buffer, "%ld", n);
+ SPRINTF_WGINT (buffer, n);
p += strlen (buffer);
return p;
}
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 SIZEOF_WGINT == 4
+ /* wgint is four bytes long: we're done. */
/* ``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 */
+#else
+ /* wgint is 64 bits long -- make sure to process all the digits. */
+ else if (n < C10000000000) { DIGITS_10 (1000000000); }
+ else if (n < C100000000000) { DIGITS_11 (C10000000000); }
+ else if (n < C1000000000000) { DIGITS_12 (C100000000000); }
+ else if (n < C10000000000000) { DIGITS_13 (C1000000000000); }
+ else if (n < C100000000000000) { DIGITS_14 (C10000000000000); }
+ else if (n < C1000000000000000) { DIGITS_15 (C100000000000000); }
+ else if (n < C10000000000000000) { DIGITS_16 (C1000000000000000); }
+ else if (n < C100000000000000000) { DIGITS_17 (C10000000000000000); }
+ else if (n < C1000000000000000000) { DIGITS_18 (C100000000000000000); }
+ else { DIGITS_19 (C1000000000000000000); }
+#endif
*p = '\0';
-#endif /* (SIZEOF_LONG == 4) || (SIZEOF_LONG == 8) */
+#endif /* (SIZEOF_WGINT == 4) || (SIZEOF_WGINT == 8) */
return p;
}
#undef DIGITS_17
#undef DIGITS_18
#undef DIGITS_19
+
+#define RING_SIZE 3
+
+/* Print NUMBER to a statically allocated string and return a pointer
+ to the printed representation.
+
+ This function is intended to be used in conjunction with printf.
+ It is hard to portably print wgint values:
+ a) you cannot use printf("%ld", number) because wgint can be long
+ long on 32-bit machines with LFS.
+ b) you cannot use printf("%lld", number) because NUMBER could be
+ long on 32-bit machines without LFS, or on 64-bit machines,
+ which do not require LFS. Also, Windows doesn't support %lld.
+ c) you cannot use printf("%j", (int_max_t) number) because not all
+ versions of printf support "%j", the most notable being the one
+ on Windows.
+ d) you cannot #define WGINT_FMT to the appropriate format and use
+ printf(WGINT_FMT, number) because that would break translations
+ for user-visible messages, such as printf("Downloaded: %d
+ bytes\n", number).
+
+ What you should use instead is printf("%s", number_to_static_string
+ (number)).
+
+ CAVEAT: since the function returns pointers to static data, you
+ must be careful to copy its result before calling it again.
+ However, to make it more useful with printf, the function maintains
+ an internal ring of static buffers to return. That way things like
+ printf("%s %s", number_to_static_string (num1),
+ number_to_static_string (num2)) work as expected. Three buffers
+ are currently used, which means that "%s %s %s" will work, but "%s
+ %s %s %s" won't. If you need to print more than three wgints,
+ bump the RING_SIZE (or rethink your message.) */
+
+char *
+number_to_static_string (wgint number)
+{
+ static char ring[RING_SIZE][24];
+ static int ringpos;
+ char *buf = ring[ringpos];
+ number_to_string (buf, number);
+ ringpos = (ringpos + 1) % RING_SIZE;
+ return buf;
+}
\f
/* Support for timers. */
struct file_memory {
char *content;
- long length;
+ wgint length;
int mmap_p;
};
int remove_link PARAMS ((const char *));
int file_exists_p PARAMS ((const char *));
int file_non_directory_p PARAMS ((const char *));
-long file_size PARAMS ((const char *));
+wgint file_size PARAMS ((const char *));
int make_directory PARAMS ((const char *));
char *unique_name PARAMS ((const char *, int));
char *file_merge PARAMS ((const char *, const char *));
void string_set_free PARAMS ((struct hash_table *));
void free_keys_and_values PARAMS ((struct hash_table *));
-char *legible PARAMS ((long));
+char *legible PARAMS ((wgint));
char *legible_large_int PARAMS ((LARGE_INT));
-int numdigit PARAMS ((long));
-char *number_to_string PARAMS ((char *, long));
+int numdigit PARAMS ((wgint));
+char *number_to_string PARAMS ((char *, wgint));
+char *number_to_static_string PARAMS ((wgint));
struct wget_timer *wtimer_allocate PARAMS ((void));
struct wget_timer *wtimer_new PARAMS ((void));
# define GCC_FORMAT_ATTR(a, b)
#endif /* not __GNUC__ */
+/* Define an integer type that works for LFS. off_t would be perfect
+ for this, but off_t is always 32-bit under Windows. */
+#ifndef WINDOWS
+typedef off_t wgint;
+# define SIZEOF_WGINT SIZEOF_OFF_T
+#endif
+
+/* Define a strtol/strtoll clone that works with wgint. */
+#ifndef str_to_wgint /* mswindows.h defines its own alias */
+# if SIZEOF_WGINT == SIZEOF_LONG
+# define str_to_wgint strtol
+# define WGINT_MAX LONG_MAX
+# else
+# define str_to_wgint strtoll
+# define WGINT_MAX LLONG_MAX
+# endif
+#endif
+
/* Everything uses this, so include them here directly. */
#include "xmalloc.h"