summaryrefslogtreecommitdiffstats
path: root/lib/libfetch
diff options
context:
space:
mode:
authordes <des@FreeBSD.org>1998-11-06 22:14:08 +0000
committerdes <des@FreeBSD.org>1998-11-06 22:14:08 +0000
commit93ce759c73e21f7ac6642b79a8e6146522b452aa (patch)
treef7eca0835b20fb3b31b3e447f024ef540f77afe0 /lib/libfetch
parentf2ceee99496bf97c1d2ffff9c8e7fd6a79474f2e (diff)
downloadFreeBSD-src-93ce759c73e21f7ac6642b79a8e6146522b452aa.zip
FreeBSD-src-93ce759c73e21f7ac6642b79a8e6146522b452aa.tar.gz
Second of a series of cleanups to libfetch.
This commit introduces the following features: a) the fetchStat*() functions, which return meta-information for a document, such as size, modification time, etc. b) the use of the com_err(3) facilities to report errors. It also fixes a bunch of style bugs and a few logic bugs and somewhat improves the man page. Changed files, in alphabetical order: Makefile: Don't generate macros in {ftp,http}err.c. Generate category fields for the error message lists. Compile the error table. Install fetch_err.h along with fetch.h. common.c: Remove the _netdb_errstring() macro, and add FETCH_ERR_NETDB to the error code in the _netdb_seterr() macro. Add categories to the _netdb_errlist table. Report errors through the Common Error library. common.h: Add the DEBUG macros. Add prototype for fetchConnect(). Remove the prototype for _fetch_errstring(), which is local to common.c Add a categroy field to struct fetcherr, and define constants for error categories. Define macros for _{url,netdb,ftp,http}_seterr(). errors.et: (new file) List error categories. fetch.3: Document the fetchStat*() functions. Move the "unimplemented functionality" comments from NOTES to BUGS. Document that applications which use libfetch must also use libcom_err, and list existing error codes. Undocument fetchLastErr{Code,String}. Remove the (empty) DIAGNOSTICS section. Mention Eugene Skepner in the AUTHORS section. fetch.c: Move the DEBUG macros to common.c Add fetchStat() and fetchStatURL(). Generate error messages for URL parser errors, and fix a minor bug in the parser. Use 'struct url' instead of 'url_t'. Remove fetchLastErr{Code,String}. fetch.h: Use 'struct url' instead of 'url_t', and remove the typedef. Define struct url_stat (used by fetchStat()). Add prototypes for fetchStat*(). Remove the declarations for fetchLastErr{Code,String}. Include fetch_err.h. fetch_err.et: (new file) Error table for libfetch. file.c: Add fetchStatFile(). Use 'struct url' instead of 'url_t'. ftp.c: Add fetchStatFTP(). Use 'struct url' instead of 'url_t'. Don't use fetchLastErrCode. ftp.errors: Add categories to all error messages. http.c: Add fetchStatHTTP(). Use 'struct url' instead of 'url_t'. Don't use fetchLastErr{Code,Text}. http.errors: Add categories to all error messages. Prompted by: jkh and Eugene Skepner Numerous sugestions from: Garett Wollman and Eugene Skepner
Diffstat (limited to 'lib/libfetch')
-rw-r--r--lib/libfetch/Makefile37
-rw-r--r--lib/libfetch/common.c107
-rw-r--r--lib/libfetch/common.h20
-rw-r--r--lib/libfetch/fetch.3162
-rw-r--r--lib/libfetch/fetch.c112
-rw-r--r--lib/libfetch/fetch.h58
-rw-r--r--lib/libfetch/fetch_err.et49
-rw-r--r--lib/libfetch/file.c43
-rw-r--r--lib/libfetch/ftp.c33
-rw-r--r--lib/libfetch/ftp.errors80
-rw-r--r--lib/libfetch/http.c23
-rw-r--r--lib/libfetch/http.errors76
12 files changed, 569 insertions, 231 deletions
diff --git a/lib/libfetch/Makefile b/lib/libfetch/Makefile
index ca48d06..14fc44c 100644
--- a/lib/libfetch/Makefile
+++ b/lib/libfetch/Makefile
@@ -1,12 +1,12 @@
-# $Id: Makefile,v 1.5 1998/08/17 20:39:09 bde Exp $
+# $Id: Makefile,v 1.6 1998/11/05 19:48:16 des Exp $
LIB= fetch
CFLAGS+= -I. -Wall -pedantic
.if !defined(DEBUG)
CFLAGS+= -DNDEBUG
.endif
-SRCS= fetch.c common.c ftp.c http.c file.c
-DPSRCS= ftperr.c httperr.c
+SRCS= fetch.c common.c ftp.c http.c file.c fetch_err.c
+DPSRCS= ftperr.inc httperr.inc fetch_err.c fetch_err.h
MAN3= fetch.3
CLEANFILES= ${DPSRCS}
@@ -15,36 +15,37 @@ SHLIB_MINOR= 0
beforedepend: ${DPSRCS}
-beforeinstall:
+beforeinstall: fetch.h fetch_err.h
${INSTALL} -C -o ${BINOWN} -g ${BINGRP} -m 444 ${.CURDIR}/fetch.h \
${DESTDIR}/usr/include
+ ${INSTALL} -C -o ${BINOWN} -g ${BINGRP} -m 444 ${.CURDIR}/fetch_err.h \
+ ${DESTDIR}/usr/include
-ftperr.c: ftp.errors
- @echo "static struct fetcherr _ftp_errlist[] = {" \ >> ${.TARGET}
+ftperr.inc: ftp.errors
+ @echo "static struct fetcherr _ftp_errlist[] = {" > ${.TARGET}
@cat ${.ALLSRC} \
| grep -v ^# \
| sort \
- | while read NUM STRING; do \
- echo " { $${NUM}, \"$${STRING}\" },"; \
+ | while read NUM CAT STRING; do \
+ echo " { $${NUM}, FETCH_$${CAT}, \"$${STRING}\" },"; \
done >> ${.TARGET}
- @echo " { -1, \"Unknown FTP error\" }" >> ${.TARGET}
+ @echo " { -1, FETCH_UNKNOWN, \"Unknown FTP error\" }" >> ${.TARGET}
@echo "};" >> ${.TARGET}
- @echo "#define _ftp_errstring(n) _fetch_errstring(_ftp_errlist, n)" >> ${.TARGET}
- @echo "#define _ftp_seterr(n) _fetch_seterr(_ftp_errlist, n)" >> ${.TARGET}
-httperr.c: http.errors
- @echo "static struct fetcherr _http_errlist[] = {" \ >> ${.TARGET}
+httperr.inc: http.errors
+ @echo "static struct fetcherr _http_errlist[] = {" > ${.TARGET}
@cat ${.ALLSRC} \
| grep -v ^# \
| sort \
- | while read NUM STRING; do \
- echo " { $${NUM}, \"$${STRING}\" },"; \
+ | while read NUM CAT STRING; do \
+ echo " { $${NUM}, FETCH_$${CAT}, \"$${STRING}\" },"; \
done >> ${.TARGET}
- @echo " { -1, \"Unknown HTTP error\" }" >> ${.TARGET}
+ @echo " { -1, FETCH_UNKNOWN, \"Unknown FTP error\" }" >> ${.TARGET}
@echo "};" >> ${.TARGET}
- @echo "#define _http_errstring(n) _fetch_errstring(_http_errlist, n)" >> ${.TARGET}
- @echo "#define _http_seterr(n) _fetch_seterr(_http_errlist, n)" >> ${.TARGET}
+
+fetch_err.c fetch_err.h: fetch_err.et
+ compile_et ${.ALLSRC}
.include <bsd.lib.mk>
diff --git a/lib/libfetch/common.c b/lib/libfetch/common.c
index be0da67..c42f2db 100644
--- a/lib/libfetch/common.c
+++ b/lib/libfetch/common.c
@@ -25,13 +25,14 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
- * $Id$
+ * $Id: common.c,v 1.1 1998/11/05 19:48:16 des Exp $
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
+#include <com_err.h>
#include <errno.h>
#include <netdb.h>
#include <string.h>
@@ -40,34 +41,45 @@
#include "fetch.h"
#include "common.h"
+
/*** Local data **************************************************************/
/*
* Error messages for resolver errors
*/
static struct fetcherr _netdb_errlist[] = {
- { HOST_NOT_FOUND, "Host not found" },
- { TRY_AGAIN, "Transient resolver failure" },
- { NO_RECOVERY, "Non-recoverable resolver failure" },
- { NO_DATA, "No address record" },
- { -1, "Unknown resolver error" }
+ { HOST_NOT_FOUND, FETCH_RESOLV, "Host not found" },
+ { TRY_AGAIN, FETCH_RESOLV, "Transient resolver failure" },
+ { NO_RECOVERY, FETCH_RESOLV, "Non-recoverable resolver failure" },
+ { NO_DATA, FETCH_RESOLV, "No address record" },
+ { -1, FETCH_UNKNOWN, "Unknown resolver error" }
};
-#define _netdb_errstring(n) _fetch_errstring(_netdb_errlist, n)
-#define _netdb_seterr(n) _fetch_seterr(_netdb_errlist, n)
+static int com_err_initialized;
/*** Error-reporting functions ***********************************************/
/*
+ * Initialize the common error library
+ */
+static void
+_fetch_init_com_err(void)
+{
+ initialize_ftch_error_table();
+ com_err_initialized = 1;
+}
+
+/*
* Map error code to string
*/
-const char *
-_fetch_errstring(struct fetcherr *p, int e)
+static int
+_fetch_finderr(struct fetcherr *p, int e)
{
- while ((p->num != -1) && (p->num != e))
- p++;
-
- return p->string;
+ int i;
+ for (i = 0; p[i].num != -1; i++)
+ if (p[i].num == e)
+ break;
+ return i;
}
/*
@@ -76,8 +88,13 @@ _fetch_errstring(struct fetcherr *p, int e)
void
_fetch_seterr(struct fetcherr *p, int e)
{
- fetchLastErrCode = e;
- fetchLastErrText = _fetch_errstring(p, e);
+ int n;
+
+ if (!com_err_initialized)
+ _fetch_init_com_err();
+
+ n = _fetch_finderr(p, e);
+ com_err("libfetch", p[n].cat, "(%d %s)", e, p[n].string);
}
/*
@@ -86,8 +103,62 @@ _fetch_seterr(struct fetcherr *p, int e)
void
_fetch_syserr(void)
{
- fetchLastErrCode = errno;
- fetchLastErrText = strerror(errno);
+ int cat;
+
+ if (!com_err_initialized)
+ _fetch_init_com_err();
+
+ switch (errno) {
+ case 0:
+ cat = FETCH_OK;
+ break;
+ case EPERM:
+ case EACCES:
+ case EROFS:
+ case EAUTH:
+ case ENEEDAUTH:
+ cat = FETCH_AUTH;
+ break;
+ case ENOENT:
+ case EISDIR: /* XXX */
+ cat = FETCH_UNAVAIL;
+ break;
+ case ENOMEM:
+ cat = FETCH_MEMORY;
+ break;
+ case EBUSY:
+ case EAGAIN:
+ cat = FETCH_TEMP;
+ break;
+ case EEXIST:
+ cat = FETCH_EXISTS;
+ break;
+ case ENOSPC:
+ cat = FETCH_FULL;
+ break;
+ case EADDRINUSE:
+ case EADDRNOTAVAIL:
+ case ENETDOWN:
+ case ENETUNREACH:
+ case ENETRESET:
+ case EHOSTUNREACH:
+ cat = FETCH_NETWORK;
+ break;
+ case ECONNABORTED:
+ case ECONNRESET:
+ cat = FETCH_ABORT;
+ break;
+ case ETIMEDOUT:
+ cat = FETCH_TIMEOUT;
+ break;
+ case ECONNREFUSED:
+ case EHOSTDOWN:
+ cat = FETCH_DOWN;
+ break;
+ default:
+ cat = FETCH_UNKNOWN;
+ }
+ com_err("libfetch", cat, "(%02d %s)", errno, strerror(errno));
}
diff --git a/lib/libfetch/common.h b/lib/libfetch/common.h
index f031b91..dc2a42e 100644
--- a/lib/libfetch/common.h
+++ b/lib/libfetch/common.h
@@ -25,20 +25,32 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
- * $Id$
+ * $Id: common.h,v 1.1 1998/11/05 19:48:16 des Exp $
*/
#ifndef _COMMON_H_INCLUDED
#define _COMMON_H_INCLUDED
/* Structure used for error message lists */
+#define ERRCAT_
struct fetcherr {
- const int num;
- const char *string;
+ const int num, cat;
+ const char *string;
};
-const char *_fetch_errstring(struct fetcherr *, int);
void _fetch_seterr(struct fetcherr *, int);
void _fetch_syserr(void);
+int fetchConnect(char *, int);
+#define _ftp_seterr(n) _fetch_seterr(_ftp_errlist, n)
+#define _http_seterr(n) _fetch_seterr(_http_errlist, n)
+#define _netdb_seterr(n) _fetch_seterr(_netdb_errlist, n)
+#define _url_seterr(n) _fetch_seterr(_url_errlist, n)
+
+#ifndef NDEBUG
+#define DEBUG(x) do x; while (0)
+#else
+#define DEBUG(x) do { } while (0)
+#endif
+
#endif
diff --git a/lib/libfetch/fetch.3 b/lib/libfetch/fetch.3
index 2b4c63a..f384fd6 100644
--- a/lib/libfetch/fetch.3
+++ b/lib/libfetch/fetch.3
@@ -22,7 +22,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.\" $Id: fetch.3,v 1.2 1998/09/26 20:42:44 des Exp $
+.\" $Id: fetch.3,v 1.3 1998/11/05 19:48:16 des Exp $
.\"
.Dd July 1, 1998
.Dt FETCH 3
@@ -30,15 +30,20 @@
.Sh NAME
.Nm fetchGetURL ,
.Nm fetchPutURL ,
+.Nm fetchStatURL ,
.Nm fetchParseURL ,
.Nm fetchGet ,
.Nm fetchPut ,
+.Nm fetchStat ,
.Nm fetchGetFile ,
.Nm fetchPutFile ,
+.Nm fetchStatFile ,
.Nm fetchGetHTTP ,
.Nm fetchPutHTTP ,
+.Nm fetchStatHTTP ,
.Nm fetchGetFTP ,
.Nm fetchPutFTP
+.Nm fetchStatFTP
.Nd file transfer library
.Sh SYNOPSIS
.Fd #include <fetch.h>
@@ -46,27 +51,36 @@
.Fn fetchGetURL "char *URL" "char *flags"
.Ft FILE *
.Fn fetchPutURL "char *URL" "char *flags"
-.Ft url_t *
+.Ft int
+.Fn fetchStatURL "char *URL" "struct url_stat *us" "char *flags"
+.Ft struct url *
.Fn fetchParseURL "char *URL" "char *flags"
.Ft FILE *
-.Fn fetchGet "url_t *URL" "char *flags"
+.Fn fetchGet "struct url *URL" "char *flags"
.Ft FILE *
-.Fn fetchPut "url_t *URL" "char *flags"
+.Fn fetchPut "struct url *URL" "char *flags"
+.Ft int
+.Fn fetchStat "struct url *URL" "struct url_stat *us" "char *flags"
.Ft FILE *
-.Fn fetchGetFile "url_t *u" "char *flags"
+.Fn fetchGetFile "struct url *u" "char *flags"
.Ft FILE *
-.Fn fetchPutFile "url_t *u" "char *flags"
+.Fn fetchPutFile "struct url *u" "char *flags"
+.Ft int
+.Fn fetchStatFile "struct url *URL" "struct url_stat *us" "char *flags"
.Ft FILE *
-.Fn fetchGetHTTP "url_t *u" "char *flags"
+.Fn fetchGetHTTP "struct url *u" "char *flags"
.Ft FILE *
-.Fn fetchPutHTTP "url_t *u" "char *flags"
+.Fn fetchPutHTTP "struct url *u" "char *flags"
+.Ft int
+.Fn fetchStatHTTP "struct url *URL" "struct url_stat *us" "char *flags"
.Ft FILE *
-.Fn fetchGetFTP "url_t *u" "char *flags"
+.Fn fetchGetFTP "struct url *u" "char *flags"
.Ft FILE *
-.Fn fetchPutFTP "url_t *u" "char *flags"
-.Vt extern int fetchLastErrCode;
-.Vt extern const char *fetchLastErrText;
+.Fn fetchPutFTP "struct url *u" "char *flags"
+.Ft int
+.Fn fetchStatFTP "struct url *URL" "struct url_stat *us" "char *flags"
.Sh DESCRIPTION
+.Pp
These functions implement a high-level library for retrieving and
uploading files using Uniform Resource Locators (URLs).
.Pp
@@ -83,6 +97,19 @@ argument is a string of characters which specify transfer options. The
meaning of the individual flags is scheme-dependent, and is detailed
in the appropriate section below.
.Pp
+.Fn fetchStatURL
+attempts to obtain the requested document's metadata and fill in the
+structure pointed to by it's second argument. The
+.Fa url_stat
+structure is defined as follows in
+.Aq Pa fetch.h :
+.Bd -literal
+struct url_stat {
+ off_t size;
+ time_t time;
+};
+.Ed
+.Pp
.Fn fetchParseURL
takes a URL in the form of a null-terminated string and splits it into
its components function according to the Common Internet Scheme Syntax
@@ -101,17 +128,19 @@ The pointer returned by
should be freed using
.Fn free .
.Pp
-.Fn fetchGet
-and
+.Fn fetchGet ,
.Fn fetchPut
+and
+.Fn fetchStat
are similar to
-.Fn fetchGetURL
+.Fn fetchGetURL ,
+.Fn fetchPutURL
and
-.Fn fetchPutURL ,
+.Fn fetchStatURL ,
except that they expect a pre-parsed URL in the form of a pointer to
-an
-.Fa url_t
-structure rather than a string.
+a
+.Fa struct url
+rather than a string.
.Pp
All of the
.Fn fetchGetXXX
@@ -175,19 +204,66 @@ is currently unimplemented.
.Sh RETURN VALUES
.Fn fetchParseURL
returns a pointer to a
-.Fa url_t
-structure containing the individual components of the URL. If it is
+.Fa struct url
+containing the individual components of the URL. If it is
unable to allocate memory, or the URL is syntactically incorrect,
.Fn fetchParseURL
returns a NULL pointer.
.Pp
+The
+.Fn fetchStat
+functions return 0 on success and -1 on failure.
+.Pp
All other functions return a stream pointer which may be used to
-access the requested document, or NULL if an error occurred. In the
-latter case, the variables
-.Va fetchLastErrCode
-and
-.Va fetchLastErrText
-are set to appropriately descriptive values.
+access the requested document, or NULL if an error occurred.
+.Pp
+.Nm Libfetch
+uses the Common Error Library
+.Nm ( libcom_err )
+to report errors. The error code passed to
+.Fn com_err
+is one of:
+.Bl -tag -width Er
+.It Bq Er FETCH_ABORT
+Operation aborted
+.It Bq Er FETCH_AUTH
+Authentication failed
+.It Bq Er FETCH_DOWN
+Service unavailable
+.It Bq Er FETCH_EXISTS
+File exists
+.It Bq Er FETCH_FULL
+File system full
+.It Bq Er FETCH_INFO
+Informational response
+.It Bq Er FETCH_MEMORY
+Insufficient memory
+.It Bq Er FETCH_MOVED
+File has moved
+.It Bq Er FETCH_NETWORK
+Network error
+.It Bq Er FETCH_OK
+No error
+.It Bq Er FETCH_PROTO
+Protocol error
+.It Bq Er FETCH_RESOLV
+Resolver error
+.It Bq Er FETCH_SERVER
+Server error
+.It Bq Er FETCH_TEMP
+Temporary error
+.It Bq Er FETCH_TIMEOUT
+Operation timed out
+.It Bq Er FETCH_UNAVAIL
+File is not available
+.It Bq Er FETCH_UNKNOWN
+Unknown error
+.It Bq Er FETCH_URL
+Invalid URL
+.El
+.Pp
+The accompanying error message includes a protocol-specific error code
+and message, e.g. "File is not available (404 Not Found)"
.Sh ENVIRONMENT
The FTP and HTTP related functions use the
.Ev HTTP_PROXY
@@ -196,6 +272,7 @@ and
environment variables, respectively, as the address of a proxy server
to use for transferring files.
.Sh SEE ALSO
+.Xr com_err 3 ,
.Xr fetch 1 ,
.Xr ftpio 3
.Rs
@@ -216,13 +293,14 @@ to use for transferring files.
.%B File Transfer Protocol
.%O RFC959
.Re
-.Sh DIAGNOSTICS
-Add later.
.Sh NOTES
-Some parts of the library are not yet implemented. The most notable
-examples of this are
-.Fn fetchPutHTTP
-and proxy support for the FTP access method.
+The
+.Nm fetch
+library uses the Common Error library, and applications which link
+with
+.Nm libfetch
+must therefore also link with
+.Nm libcom_err .
.Sh HISTORY
The
.Nm fetch
@@ -234,18 +312,26 @@ The
library was mostly written by
.An Dag-Erling Coïdan Smørgrav Aq des@FreeBSD.org
with numerous suggestions from
-.An Jordan K. Hubbard Aq jkh@FreeBSD.org
+.An Jordan K. Hubbard Aq jkh@FreeBSD.org ,
+.An Eugene Skepner Aq eu@qub.com
and other FreeBSD developers.
-It incorporates the older
+It replaces the older
.Nm ftpio
-library, which was originally written by
-.Nm Poul-Henning Kamp Aq pkh@FreeBSD.org
-and later turned inside out by
+library written by
+.An Poul-Henning Kamp Aq pkh@FreeBSD.org
+and
.An Jordan K. Hubbard Aq jkh@FreeBSD.org .
.Pp
This manual page was written by
.An Dag-Erling Coïdan Smørgrav Aq des@FreeBSD.org
.Sh BUGS
+Some parts of the library are not yet implemented. The most notable
+examples of this are
+.Fn fetchPutHTTP ,
+.Fn fetchStatFTP ,
+.Fn fetchStatHTTP
+and FTP proxy support.
+.Pp
There's no way to select a proxy at run-time other than setting the
.Ev HTTP_PROXY
or
diff --git a/lib/libfetch/fetch.c b/lib/libfetch/fetch.c
index 226b628..20cf675 100644
--- a/lib/libfetch/fetch.c
+++ b/lib/libfetch/fetch.c
@@ -25,10 +25,11 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
- * $Id: fetch.c,v 1.4 1998/08/17 09:30:19 des Exp $
+ * $Id: fetch.c,v 1.5 1998/11/05 19:48:17 des Exp $
*/
#include <sys/param.h>
+#include <sys/errno.h>
#include <ctype.h>
#include <stdio.h>
@@ -36,22 +37,33 @@
#include <string.h>
#include "fetch.h"
+#include "common.h"
-#ifndef NDEBUG
-#define DEBUG(x) do x; while (0)
-#else
-#define DEBUG(x) do { } while (0)
-#endif
-int fetchLastErrCode;
-const char *fetchLastErrText;
+/*** Local data **************************************************************/
+
+/*
+ * Error messages for parser errors
+ */
+#define URL_MALFORMED 1
+#define URL_BAD_SCHEME 2
+#define URL_BAD_PORT 3
+static struct fetcherr _url_errlist[] = {
+ { URL_MALFORMED, FETCH_URL, "Malformed URL" },
+ { URL_BAD_SCHEME, FETCH_URL, "Invalid URL scheme" },
+ { URL_BAD_PORT, FETCH_URL, "Invalid server port" },
+ { -1, FETCH_UNKNOWN, "Unknown parser error" }
+};
+
+
+/*** Public API **************************************************************/
/*
* Select the appropriate protocol for the URL scheme, and return a
* read-only stream connected to the document referenced by the URL.
*/
FILE *
-fetchGet(url_t *URL, char *flags)
+fetchGet(struct url *URL, char *flags)
{
if (strcasecmp(URL->scheme, "file") == 0)
return fetchGetFile(URL, flags);
@@ -59,8 +71,10 @@ fetchGet(url_t *URL, char *flags)
return fetchGetHTTP(URL, flags);
else if (strcasecmp(URL->scheme, "ftp") == 0)
return fetchGetFTP(URL, flags);
- else return NULL;
-
+ else {
+ _url_seterr(URL_BAD_SCHEME);
+ return NULL;
+ }
}
/*
@@ -68,7 +82,7 @@ fetchGet(url_t *URL, char *flags)
* write-only stream connected to the document referenced by the URL.
*/
FILE *
-fetchPut(url_t *URL, char *flags)
+fetchPut(struct url *URL, char *flags)
{
if (strcasecmp(URL->scheme, "file") == 0)
return fetchPutFile(URL, flags);
@@ -76,7 +90,29 @@ fetchPut(url_t *URL, char *flags)
return fetchPutHTTP(URL, flags);
else if (strcasecmp(URL->scheme, "ftp") == 0)
return fetchPutFTP(URL, flags);
- else return NULL;
+ else {
+ _url_seterr(URL_BAD_SCHEME);
+ return NULL;
+ }
+}
+
+/*
+ * Select the appropriate protocol for the URL scheme, and return the
+ * size of the document referenced by the URL if it exists.
+ */
+int
+fetchStat(struct url *URL, struct url_stat *us, char *flags)
+{
+ if (strcasecmp(URL->scheme, "file") == 0)
+ return fetchStatFile(URL, us, flags);
+ else if (strcasecmp(URL->scheme, "http") == 0)
+ return fetchStatHTTP(URL, us, flags);
+ else if (strcasecmp(URL->scheme, "ftp") == 0)
+ return fetchStatFTP(URL, us, flags);
+ else {
+ _url_seterr(URL_BAD_SCHEME);
+ return -1;
+ }
}
/*
@@ -85,7 +121,7 @@ fetchPut(url_t *URL, char *flags)
FILE *
fetchGetURL(char *URL, char *flags)
{
- url_t *u;
+ struct url *u;
FILE *f;
if ((u = fetchParseURL(URL)) == NULL)
@@ -104,7 +140,7 @@ fetchGetURL(char *URL, char *flags)
FILE *
fetchPutURL(char *URL, char *flags)
{
- url_t *u;
+ struct url *u;
FILE *f;
if ((u = fetchParseURL(URL)) == NULL)
@@ -117,27 +153,50 @@ fetchPutURL(char *URL, char *flags)
}
/*
+ * Attempt to parse the given URL; if successful, call fetchStat().
+ */
+int
+fetchStatURL(char *URL, struct url_stat *us, char *flags)
+{
+ struct url *u;
+ int s;
+
+ if ((u = fetchParseURL(URL)) == NULL)
+ return -1;
+
+ s = fetchStat(u, us, flags);
+
+ free(u);
+ return s;
+}
+
+/*
* Split an URL into components. URL syntax is:
* method:[//[user[:pwd]@]host[:port]]/[document]
* This almost, but not quite, RFC1738 URL syntax.
*/
-url_t *
+struct url *
fetchParseURL(char *URL)
{
char *p, *q;
- url_t *u;
+ struct url *u;
int i;
- /* allocate url_t */
- if ((u = calloc(1, sizeof(url_t))) == NULL)
+ /* allocate struct url */
+ if ((u = calloc(1, sizeof(struct url))) == NULL) {
+ errno = ENOMEM;
+ _fetch_syserr();
return NULL;
+ }
/* scheme name */
for (i = 0; *URL && (*URL != ':'); URL++)
if (i < URL_SCHEMELEN)
u->scheme[i++] = *URL;
- if (!URL[0] || (URL[1] != '/'))
+ if (!URL[0] || (URL[1] != '/')) {
+ _url_seterr(URL_BAD_SCHEME);
goto ouch;
+ }
else URL++;
if (URL[1] != '/') {
p = URL;
@@ -171,7 +230,11 @@ fetchParseURL(char *URL)
for (q = ++p; *q && (*q != '/'); q++)
if (isdigit(*q))
u->port = u->port * 10 + (*q - '0');
- else return 0; /* invalid port */
+ else {
+ /* invalid port */
+ _url_seterr(URL_BAD_PORT);
+ goto ouch;
+ }
while (*p && (*p != '/'))
p++;
}
@@ -179,10 +242,13 @@ fetchParseURL(char *URL)
nohost:
/* document */
if (*p) {
- url_t *t;
+ struct url *t;
t = realloc(u, sizeof(*u)+strlen(p)-1);
- if (t == NULL)
+ if (t == NULL) {
+ errno = ENOMEM;
+ _fetch_syserr();
goto ouch;
+ }
u = t;
strcpy(u->doc, p);
} else {
diff --git a/lib/libfetch/fetch.h b/lib/libfetch/fetch.h
index cc39e36..286b253 100644
--- a/lib/libfetch/fetch.h
+++ b/lib/libfetch/fetch.h
@@ -25,7 +25,7 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
- * $Id: fetch.h,v 1.4 1998/08/17 09:30:19 des Exp $
+ * $Id: fetch.h,v 1.5 1998/11/05 19:48:17 des Exp $
*/
#ifndef _FETCH_H_INCLUDED
@@ -34,47 +34,51 @@
#include <sys/param.h>
#include <stdio.h>
+#include <fetch_err.h>
+
#define _LIBFETCH_VER "libfetch/1.0"
#define URL_SCHEMELEN 16
#define URL_USERLEN 256
#define URL_PWDLEN 256
-struct url_s {
- char scheme[URL_SCHEMELEN+1];
- char user[URL_USERLEN+1];
- char pwd[URL_PWDLEN+1];
- char host[MAXHOSTNAMELEN+1];
- int port;
- char doc[2];
+struct url {
+ char scheme[URL_SCHEMELEN+1];
+ char user[URL_USERLEN+1];
+ char pwd[URL_PWDLEN+1];
+ char host[MAXHOSTNAMELEN+1];
+ int port;
+ char doc[2];
};
-typedef struct url_s url_t;
+struct url_stat {
+ off_t size;
+ time_t time;
+};
/* FILE-specific functions */
-FILE *fetchGetFile(url_t *, char *);
-FILE *fetchPutFile(url_t *, char *);
+FILE *fetchGetFile(struct url *, char *);
+FILE *fetchPutFile(struct url *, char *);
+int fetchStatFile(struct url *, struct url_stat *, char *);
/* HTTP-specific functions */
-char *fetchContentType(FILE *);
-FILE *fetchGetHTTP(url_t *, char *);
-FILE *fetchPutHTTP(url_t *, char *);
+char *fetchContentType(FILE *);
+FILE *fetchGetHTTP(struct url *, char *);
+FILE *fetchPutHTTP(struct url *, char *);
+int fetchStatHTTP(struct url *, struct url_stat *, char *);
/* FTP-specific functions */
-FILE *fetchGetFTP(url_t *, char *);
-FILE *fetchPutFTP(url_t *, char *);
+FILE *fetchGetFTP(struct url *, char *);
+FILE *fetchPutFTP(struct url *, char *);
+int fetchStatFTP(struct url *, struct url_stat *, char *);
/* Generic functions */
-int fetchConnect(char *, int);
-url_t *fetchParseURL(char *);
-void fetchFreeURL(url_t *);
-FILE *fetchGetURL(char *, char *);
-FILE *fetchPutURL(char *, char *);
-FILE *fetchGet(url_t *, char *);
-FILE *fetchPut(url_t *, char *);
-
-/* Error code and string */
-extern int fetchLastErrCode;
-extern const char *fetchLastErrText;
+struct url *fetchParseURL(char *);
+FILE *fetchGetURL(char *, char *);
+FILE *fetchPutURL(char *, char *);
+int fetchStatURL(char *, struct url_stat *, char *);
+FILE *fetchGet(struct url *, char *);
+FILE *fetchPut(struct url *, char *);
+int fetchStat(struct url *, struct url_stat *, char *);
#endif
diff --git a/lib/libfetch/fetch_err.et b/lib/libfetch/fetch_err.et
new file mode 100644
index 0000000..233d7e7
--- /dev/null
+++ b/lib/libfetch/fetch_err.et
@@ -0,0 +1,49 @@
+#-
+# Copyright (c) 1998 Dag-Erling Coïdan Smørgrav
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer
+# in this position and unchanged.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. The name of the author may not be used to endorse or promote products
+# derived from this software without specific prior written permission
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+# $Id$
+#
+et ftch
+ ec FETCH_ABORT, "Operation aborted"
+ ec FETCH_AUTH, "Authentication failed"
+ ec FETCH_DOWN, "Service unavailable"
+ ec FETCH_EXISTS, "File exists"
+ ec FETCH_FULL, "File system full"
+ ec FETCH_INFO, "Informational response"
+ ec FETCH_MEMORY, "Insufficient memory"
+ ec FETCH_MOVED, "File has moved"
+ ec FETCH_NETWORK, "Network error"
+ ec FETCH_OK, "No error"
+ ec FETCH_PROTO, "Protocol error"
+ ec FETCH_RESOLV, "Resolver error"
+ ec FETCH_SERVER, "Server error"
+ ec FETCH_TEMP, "Temporary error"
+ ec FETCH_TIMEOUT, "Operation timed out"
+ ec FETCH_UNAVAIL, "File is not available"
+ ec FETCH_UNKNOWN, "Unknown error"
+ ec FETCH_URL, "Invalid URL"
+end
diff --git a/lib/libfetch/file.c b/lib/libfetch/file.c
index 0c685ef..66a681f 100644
--- a/lib/libfetch/file.c
+++ b/lib/libfetch/file.c
@@ -25,25 +25,54 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
- * $Id$
+ * $Id: file.c,v 1.1.1.1 1998/07/09 16:52:41 des Exp $
*/
+#include <sys/types.h>
+#include <sys/stat.h>
#include <stdio.h>
#include <string.h>
#include "fetch.h"
+#include "common.h"
FILE *
-fetchGetFile(url_t *u, char *flags)
+fetchGetFile(struct url *u, char *flags)
{
- flags = flags; /* unused */
- return fopen(u->doc, "r");
+ FILE *f;
+
+ f = fopen(u->doc, "r");
+
+ if (f == NULL)
+ _fetch_syserr();
+ return f;
}
FILE *
-fetchPutFile(url_t *u, char *flags)
+fetchPutFile(struct url *u, char *flags)
{
+ FILE *f;
+
if (strchr(flags, 'a'))
- return fopen(u->doc, "a");
- else return fopen(u->doc, "w");
+ f = fopen(u->doc, "a");
+ else
+ f = fopen(u->doc, "w");
+
+ if (f == NULL)
+ _fetch_syserr();
+ return f;
+}
+
+int
+fetchStatFile(struct url *u, struct url_stat *us, char *flags)
+{
+ struct stat sb;
+
+ if (stat(u->doc, &sb) == -1) {
+ _fetch_syserr();
+ return -1;
+ }
+ us->size = sb.st_size;
+ us->time = sb.st_mtime;
+ return 0;
}
diff --git a/lib/libfetch/ftp.c b/lib/libfetch/ftp.c
index 1899d03..81da7f8 100644
--- a/lib/libfetch/ftp.c
+++ b/lib/libfetch/ftp.c
@@ -25,7 +25,7 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
- * $Id: ftp.c,v 1.5 1998/08/17 09:30:19 des Exp $
+ * $Id: ftp.c,v 1.6 1998/11/05 19:48:17 des Exp $
*/
/*
@@ -71,7 +71,7 @@
#include "fetch.h"
#include "common.h"
-#include "ftperr.c"
+#include "ftperr.inc"
#define FTP_DEFAULT_TO_ANONYMOUS
#define FTP_ANONYMOUS_USER "ftp"
@@ -88,7 +88,7 @@
#define ENDL "\r\n"
-static url_t cached_host;
+static struct url cached_host;
static FILE *cached_socket;
static char *_ftp_last_reply;
@@ -101,6 +101,7 @@ _ftp_chkerr(FILE *s, int *e)
{
char *line;
size_t len;
+ int err;
if (e)
*e = 0;
@@ -122,14 +123,15 @@ _ftp_chkerr(FILE *s, int *e)
if (!isdigit(line[1]) || !isdigit(line[1])
|| !isdigit(line[2]) || (line[3] != ' ')) {
- _ftp_seterr(-1);
+ _ftp_seterr(0);
return -1;
}
- _ftp_seterr((line[0] - '0') * 100 + (line[1] - '0') * 10 + (line[2] - '0'));
+ err = (line[0] - '0') * 100 + (line[1] - '0') * 10 + (line[2] - '0');
+ _ftp_seterr(err);
if (e)
- *e = fetchLastErrCode;
+ *e = err;
return (line[0] == '2') - 1;
}
@@ -365,7 +367,7 @@ _ftp_disconnect(FILE *f)
* Check if we're already connected
*/
static int
-_ftp_isconnected(url_t *url)
+_ftp_isconnected(struct url *url)
{
return (cached_socket
&& (strcmp(url->host, cached_host.host) == 0)
@@ -378,7 +380,7 @@ _ftp_isconnected(url_t *url)
* FTP session
*/
static FILE *
-fetchXxxFTP(url_t *url, char *oper, char *mode, char *flags)
+fetchXxxFTP(struct url *url, char *oper, char *mode, char *flags)
{
FILE *cf = NULL;
int e;
@@ -402,7 +404,7 @@ fetchXxxFTP(url_t *url, char *oper, char *mode, char *flags)
if (cached_socket)
_ftp_disconnect(cached_socket);
cached_socket = cf;
- memcpy(&cached_host, url, sizeof(url_t));
+ memcpy(&cached_host, url, sizeof(struct url));
}
/* initiate the transfer */
@@ -413,15 +415,24 @@ fetchXxxFTP(url_t *url, char *oper, char *mode, char *flags)
* Itsy bitsy teeny weenie
*/
FILE *
-fetchGetFTP(url_t *url, char *flags)
+fetchGetFTP(struct url *url, char *flags)
{
return fetchXxxFTP(url, "RETR", "r", flags);
}
FILE *
-fetchPutFTP(url_t *url, char *flags)
+fetchPutFTP(struct url *url, char *flags)
{
if (flags && strchr(flags, 'a'))
return fetchXxxFTP(url, "APPE", "w", flags);
else return fetchXxxFTP(url, "STOR", "w", flags);
}
+
+extern void warnx(char *fmt, ...);
+int
+fetchStatFTP(struct url *url, struct url_stat *us, char *flags)
+{
+ warnx("fetchStatFTP(): not implemented");
+ return -1;
+}
+
diff --git a/lib/libfetch/ftp.errors b/lib/libfetch/ftp.errors
index 53a87f0..d7816d2 100644
--- a/lib/libfetch/ftp.errors
+++ b/lib/libfetch/ftp.errors
@@ -1,44 +1,44 @@
-# $Id: ftp.errors,v 1.3 1997/02/22 15:06:47 peter Exp $
+# $Id: ftp.errors,v 1.1.1.1 1998/07/09 16:52:43 des Exp $
#
# This list is taken from RFC 959.
# It probably needs a going over.
#
-110 Restart marker reply
-120 Service ready in a few minutes
-125 Data connection already open; transfer starting
-150 File status okay; about to open data connection
-200 Command okay
-202 Command not implemented, superfluous at this site
-211 System status, or system help reply
-212 Directory status
-213 File status
-214 Help message
-215 Set system type
-220 Service ready for new user
-221 Service closing control connection
-225 Data connection open; no transfer in progress
-226 Requested file action successful
-227 Entering Passive Mode
-230 User logged in, proceed
-250 Requested file action okay, completed
-257 File/directory created
-331 User name okay, need password
-332 Need account for login
-350 Requested file action pending further information
-421 Service not available, closing control connection
-425 Can't open data connection
-426 Connection closed; transfer aborted
-450 File unavailable (e.g., file busy)
-451 Requested action aborted: local error in processing
-452 Insufficient storage space in system
-500 Syntax error, command unrecognized
-501 Syntax error in parameters or arguments
-502 Command not implemented
-503 Bad sequence of commands
-504 Command not implemented for that parameter
-530 Not logged in
-532 Need account for storing files
-550 File unavailable (e.g., file not found, no access)
-551 Requested action aborted. Page type unknown
-552 Exceeded storage allocation
-553 File name not allowed
+110 OK Restart marker reply
+120 TEMP Service ready in a few minutes
+125 OK Data connection already open; transfer starting
+150 OK File status okay; about to open data connection
+200 OK Command okay
+202 PROTO Command not implemented, superfluous at this site
+211 INFO System status, or system help reply
+212 INFO Directory status
+213 INFO File status
+214 INFO Help message
+215 INFO Set system type
+220 OK Service ready for new user
+221 OK Service closing control connection
+225 OK Data connection open; no transfer in progress
+226 OK Requested file action successful
+227 OK Entering Passive Mode
+230 OK User logged in, proceed
+250 OK Requested file action okay, completed
+257 OK File/directory created
+331 AUTH User name okay, need password
+332 AUTH Need account for login
+350 OK Requested file action pending further information
+421 DOWN Service not available, closing control connection
+425 NETWORK Can't open data connection
+426 ABORT Connection closed; transfer aborted
+450 UNAVAIL File unavailable (e.g., file busy)
+451 SERVER Requested action aborted: local error in processing
+452 FULL Insufficient storage space in system
+500 PROTO Syntax error, command unrecognized
+501 PROTO Syntax error in parameters or arguments
+502 PROTO Command not implemented
+503 PROTO Bad sequence of commands
+504 PROTO Command not implemented for that parameter
+530 AUTH Not logged in
+532 AUTH Need account for storing files
+550 UNAVAIL File unavailable (e.g., file not found, no access)
+551 PROTO Requested action aborted. Page type unknown
+552 FULL Exceeded storage allocation
+553 EXISTS File name not allowed
diff --git a/lib/libfetch/http.c b/lib/libfetch/http.c
index aea3d1d..973584c 100644
--- a/lib/libfetch/http.c
+++ b/lib/libfetch/http.c
@@ -25,7 +25,7 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
- * $Id: http.c,v 1.5 1998/08/17 09:30:19 des Exp $
+ * $Id: http.c,v 1.6 1998/11/05 19:48:17 des Exp $
*/
/*
@@ -78,7 +78,7 @@
#include "fetch.h"
#include "common.h"
-#include "httperr.c"
+#include "httperr.inc"
#ifndef NDEBUG
#define DEBUG(x) do x; while (0)
@@ -300,10 +300,10 @@ _http_auth(char *usr, char *pwd)
}
/*
- * retrieve a file by HTTP
+ * Retrieve a file by HTTP
*/
FILE *
-fetchGetHTTP(url_t *URL, char *flags)
+fetchGetHTTP(struct url *URL, char *flags)
{
int sd = -1, err, i, enc = ENC_NONE;
struct cookie *c;
@@ -389,8 +389,7 @@ fetchGetHTTP(url_t *URL, char *flags)
/* add code to handle redirects later */
if (err != 200) {
- fetchLastErrCode = err;
- fetchLastErrText = _http_errstring(err);
+ _http_seterr(err);
goto fouch;
}
@@ -452,8 +451,18 @@ fouch:
}
FILE *
-fetchPutHTTP(url_t *URL, char *flags)
+fetchPutHTTP(struct url *URL, char *flags)
{
warnx("fetchPutHTTP(): not implemented");
return NULL;
}
+
+/*
+ * Get an HTTP document's metadata
+ */
+int
+fetchStatHTTP(struct url *url, struct url_stat *us, char *flags)
+{
+ warnx("fetchStatHTTP(): not implemented");
+ return -1;
+}
diff --git a/lib/libfetch/http.errors b/lib/libfetch/http.errors
index c0e3539..26907d5 100644
--- a/lib/libfetch/http.errors
+++ b/lib/libfetch/http.errors
@@ -1,41 +1,41 @@
-# $Id$
+# $Id: http.errors,v 1.1.1.1 1998/07/09 16:52:44 des Exp $
#
# This list is taken from RFC 2068.
#
-100 Continue
-101 Switching Protocols
-200 OK
-201 Created
-202 Accepted
-203 Non-Authoritative Information
-204 No Content
-205 Reset Content
-206 Partial Content
-300 Multiple Choices
-301 Moved Permanently
-302 Moved Temporarily
-303 See Other
-304 Not Modified
-305 Use Proxy
-400 Bad Request
-401 Unauthorized
-402 Payment Required
-403 Forbidden
-404 Not Found
-405 Method Not Allowed
-406 Not Acceptable
-407 Proxy Authentication Required
-408 Request Time-out
-409 Conflict
-410 Gone
-411 Length Required
-412 Precondition Failed
-413 Request Entity Too Large
-414 Request-URI Too Large
-415 Unsupported Media Type
-500 Internal Server Error
-501 Not Implemented
-502 Bad Gateway
-503 Service Unavailable
-504 Gateway Time-out
-505 HTTP Version not supported
+100 OK Continue
+101 OK Switching Protocols
+200 OK OK
+201 OK Created
+202 OK Accepted
+203 INFO Non-Authoritative Information
+204 OK No Content
+205 OK Reset Content
+206 OK Partial Content
+300 MOVED Multiple Choices
+301 MOVED Moved Permanently
+302 MOVED Moved Temporarily
+303 MOVED See Other
+304 OK Not Modified
+305 INFO Use Proxy
+400 PROTO Bad Request
+401 AUTH Unauthorized
+402 AUTH Payment Required
+403 AUTH Forbidden
+404 UNAVAIL Not Found
+405 PROTO Method Not Allowed
+406 PROTO Not Acceptable
+407 AUTH Proxy Authentication Required
+408 TIMEOUT Request Time-out
+409 EXISTS Conflict
+410 UNAVAIL Gone
+411 PROTO Length Required
+412 SERVER Precondition Failed
+413 PROTO Request Entity Too Large
+414 PROTO Request-URI Too Large
+415 PROTO Unsupported Media Type
+500 SERVER Internal Server Error
+501 PROTO Not Implemented
+502 SERVER Bad Gateway
+503 TEMP Service Unavailable
+504 TIMEOUT Gateway Time-out
+505 PROTO HTTP Version not supported
OpenPOWER on IntegriCloud