common.c revision 106137
140939Sdes/*- 240939Sdes * Copyright (c) 1998 Dag-Erling Co�dan Sm�rgrav 340939Sdes * All rights reserved. 440939Sdes * 540939Sdes * Redistribution and use in source and binary forms, with or without 640939Sdes * modification, are permitted provided that the following conditions 740939Sdes * are met: 840939Sdes * 1. Redistributions of source code must retain the above copyright 940939Sdes * notice, this list of conditions and the following disclaimer 1040939Sdes * in this position and unchanged. 1140939Sdes * 2. Redistributions in binary form must reproduce the above copyright 1240939Sdes * notice, this list of conditions and the following disclaimer in the 1340939Sdes * documentation and/or other materials provided with the distribution. 1440939Sdes * 3. The name of the author may not be used to endorse or promote products 1540939Sdes * derived from this software without specific prior written permission 1640939Sdes * 1740939Sdes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 1840939Sdes * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 1940939Sdes * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 2040939Sdes * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 2140939Sdes * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 2240939Sdes * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2340939Sdes * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2440939Sdes * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2540939Sdes * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 2640939Sdes * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2740939Sdes */ 2840939Sdes 2984203Sdillon#include <sys/cdefs.h> 3084203Sdillon__FBSDID("$FreeBSD: head/lib/libfetch/common.c 106137 2002-10-29 12:17:43Z obrien $"); 3184203Sdillon 3241862Sdes#include <sys/param.h> 3340939Sdes#include <sys/socket.h> 3455557Sdes#include <sys/time.h> 3562981Sdes#include <sys/uio.h> 3640939Sdes#include <netinet/in.h> 3740939Sdes 3840939Sdes#include <errno.h> 3940939Sdes#include <netdb.h> 4060924Sdes#include <stdarg.h> 4141862Sdes#include <stdlib.h> 4241862Sdes#include <stdio.h> 4340939Sdes#include <string.h> 4440939Sdes#include <unistd.h> 4540939Sdes 4640939Sdes#include "fetch.h" 4740939Sdes#include "common.h" 4840939Sdes 4940975Sdes 5040939Sdes/*** Local data **************************************************************/ 5140939Sdes 5240939Sdes/* 5340939Sdes * Error messages for resolver errors 5440939Sdes */ 5540939Sdesstatic struct fetcherr _netdb_errlist[] = { 5690267Sdes { EAI_NODATA, FETCH_RESOLV, "Host not found" }, 5790267Sdes { EAI_AGAIN, FETCH_TEMP, "Transient resolver failure" }, 5890267Sdes { EAI_FAIL, FETCH_RESOLV, "Non-recoverable resolver failure" }, 5990267Sdes { EAI_NONAME, FETCH_RESOLV, "No address record" }, 6090267Sdes { -1, FETCH_UNKNOWN, "Unknown resolver error" } 6140939Sdes}; 6240939Sdes 6362981Sdes/* End-of-Line */ 6475891Sarchiestatic const char ENDL[2] = "\r\n"; 6540939Sdes 6662981Sdes 6740939Sdes/*** Error-reporting functions ***********************************************/ 6840939Sdes 6940939Sdes/* 7040939Sdes * Map error code to string 7140939Sdes */ 7260924Sdesstatic struct fetcherr * 7340975Sdes_fetch_finderr(struct fetcherr *p, int e) 7440939Sdes{ 7590267Sdes while (p->num != -1 && p->num != e) 7690267Sdes p++; 7790267Sdes return (p); 7840939Sdes} 7940939Sdes 8040939Sdes/* 8140939Sdes * Set error code 8240939Sdes */ 8340939Sdesvoid 8440939Sdes_fetch_seterr(struct fetcherr *p, int e) 8540939Sdes{ 8690267Sdes p = _fetch_finderr(p, e); 8790267Sdes fetchLastErrCode = p->cat; 8890267Sdes snprintf(fetchLastErrString, MAXERRSTRING, "%s", p->string); 8940939Sdes} 9040939Sdes 9140939Sdes/* 9240939Sdes * Set error code according to errno 9340939Sdes */ 9440939Sdesvoid 9540939Sdes_fetch_syserr(void) 9640939Sdes{ 9790267Sdes switch (errno) { 9890267Sdes case 0: 9990267Sdes fetchLastErrCode = FETCH_OK; 10090267Sdes break; 10190267Sdes case EPERM: 10290267Sdes case EACCES: 10390267Sdes case EROFS: 10490267Sdes case EAUTH: 10590267Sdes case ENEEDAUTH: 10690267Sdes fetchLastErrCode = FETCH_AUTH; 10790267Sdes break; 10890267Sdes case ENOENT: 10990267Sdes case EISDIR: /* XXX */ 11090267Sdes fetchLastErrCode = FETCH_UNAVAIL; 11190267Sdes break; 11290267Sdes case ENOMEM: 11390267Sdes fetchLastErrCode = FETCH_MEMORY; 11490267Sdes break; 11590267Sdes case EBUSY: 11690267Sdes case EAGAIN: 11790267Sdes fetchLastErrCode = FETCH_TEMP; 11890267Sdes break; 11990267Sdes case EEXIST: 12090267Sdes fetchLastErrCode = FETCH_EXISTS; 12190267Sdes break; 12290267Sdes case ENOSPC: 12390267Sdes fetchLastErrCode = FETCH_FULL; 12490267Sdes break; 12590267Sdes case EADDRINUSE: 12690267Sdes case EADDRNOTAVAIL: 12790267Sdes case ENETDOWN: 12890267Sdes case ENETUNREACH: 12990267Sdes case ENETRESET: 13090267Sdes case EHOSTUNREACH: 13190267Sdes fetchLastErrCode = FETCH_NETWORK; 13290267Sdes break; 13390267Sdes case ECONNABORTED: 13490267Sdes case ECONNRESET: 13590267Sdes fetchLastErrCode = FETCH_ABORT; 13690267Sdes break; 13790267Sdes case ETIMEDOUT: 13890267Sdes fetchLastErrCode = FETCH_TIMEOUT; 13990267Sdes break; 14090267Sdes case ECONNREFUSED: 14190267Sdes case EHOSTDOWN: 14290267Sdes fetchLastErrCode = FETCH_DOWN; 14390267Sdes break; 14490267Sdesdefault: 14590267Sdes fetchLastErrCode = FETCH_UNKNOWN; 14690267Sdes } 14790267Sdes snprintf(fetchLastErrString, MAXERRSTRING, "%s", strerror(errno)); 14840939Sdes} 14940939Sdes 15040939Sdes 15141862Sdes/* 15241862Sdes * Emit status message 15341862Sdes */ 15460924Sdesvoid 15575891Sarchie_fetch_info(const char *fmt, ...) 15641862Sdes{ 15790267Sdes va_list ap; 15890267Sdes 15990267Sdes va_start(ap, fmt); 16090267Sdes vfprintf(stderr, fmt, ap); 16190267Sdes va_end(ap); 16290267Sdes fputc('\n', stderr); 16341862Sdes} 16441862Sdes 16541862Sdes 16640939Sdes/*** Network-related utility functions ***************************************/ 16740939Sdes 16840939Sdes/* 16968551Sdes * Return the default port for a scheme 17068551Sdes */ 17168551Sdesint 17275891Sarchie_fetch_default_port(const char *scheme) 17368551Sdes{ 17490267Sdes struct servent *se; 17568551Sdes 17690267Sdes if ((se = getservbyname(scheme, "tcp")) != NULL) 17790267Sdes return (ntohs(se->s_port)); 17890267Sdes if (strcasecmp(scheme, SCHEME_FTP) == 0) 17990267Sdes return (FTP_DEFAULT_PORT); 18090267Sdes if (strcasecmp(scheme, SCHEME_HTTP) == 0) 18190267Sdes return (HTTP_DEFAULT_PORT); 18290267Sdes return (0); 18368551Sdes} 18468551Sdes 18568551Sdes/* 18668551Sdes * Return the default proxy port for a scheme 18768551Sdes */ 18868551Sdesint 18975891Sarchie_fetch_default_proxy_port(const char *scheme) 19068551Sdes{ 19190267Sdes if (strcasecmp(scheme, SCHEME_FTP) == 0) 19290267Sdes return (FTP_DEFAULT_PROXY_PORT); 19390267Sdes if (strcasecmp(scheme, SCHEME_HTTP) == 0) 19490267Sdes return (HTTP_DEFAULT_PROXY_PORT); 19590267Sdes return (0); 19668551Sdes} 19768551Sdes 19898117Sdes 19968551Sdes/* 20097866Sdes * Create a connection for an existing descriptor. 20197866Sdes */ 20297866Sdesconn_t * 20397866Sdes_fetch_reopen(int sd) 20497866Sdes{ 20597866Sdes conn_t *conn; 20697866Sdes 20797866Sdes /* allocate and fill connection structure */ 20897866Sdes if ((conn = calloc(1, sizeof *conn)) == NULL) 20997866Sdes return (NULL); 21097866Sdes conn->sd = sd; 21198117Sdes ++conn->ref; 21297866Sdes return (conn); 21397866Sdes} 21497866Sdes 21597866Sdes 21697866Sdes/* 21798117Sdes * Bump a connection's reference count. 21898117Sdes */ 21998117Sdesconn_t * 22098117Sdes_fetch_ref(conn_t *conn) 22198117Sdes{ 22298117Sdes 22398117Sdes ++conn->ref; 22498117Sdes return (conn); 22598117Sdes} 22698117Sdes 22798117Sdes 22898117Sdes/* 22940939Sdes * Establish a TCP connection to the specified port on the specified host. 23040939Sdes */ 23197856Sdesconn_t * 23275891Sarchie_fetch_connect(const char *host, int port, int af, int verbose) 23340939Sdes{ 23497856Sdes conn_t *conn; 23590267Sdes char pbuf[10]; 23690267Sdes struct addrinfo hints, *res, *res0; 23790267Sdes int sd, err; 23840939Sdes 23990267Sdes DEBUG(fprintf(stderr, "---> %s:%d\n", host, port)); 24041862Sdes 24190267Sdes if (verbose) 24290267Sdes _fetch_info("looking up %s", host); 24340939Sdes 24490267Sdes /* look up host name and set up socket address structure */ 24590267Sdes snprintf(pbuf, sizeof(pbuf), "%d", port); 24690267Sdes memset(&hints, 0, sizeof(hints)); 24790267Sdes hints.ai_family = af; 24890267Sdes hints.ai_socktype = SOCK_STREAM; 24990267Sdes hints.ai_protocol = 0; 25090267Sdes if ((err = getaddrinfo(host, pbuf, &hints, &res0)) != 0) { 25190267Sdes _netdb_seterr(err); 25297856Sdes return (NULL); 25390267Sdes } 25490267Sdes 25590267Sdes if (verbose) 25690267Sdes _fetch_info("connecting to %s:%d", host, port); 25790267Sdes 25890267Sdes /* try to connect */ 25990267Sdes for (sd = -1, res = res0; res; res = res->ai_next) { 26090267Sdes if ((sd = socket(res->ai_family, res->ai_socktype, 26162981Sdes res->ai_protocol)) == -1) 26290267Sdes continue; 26390267Sdes if (connect(sd, res->ai_addr, res->ai_addrlen) != -1) 26490267Sdes break; 26590267Sdes close(sd); 26690267Sdes sd = -1; 26790267Sdes } 26890267Sdes freeaddrinfo(res0); 26990267Sdes if (sd == -1) { 27090267Sdes _fetch_syserr(); 27197856Sdes return (NULL); 27290267Sdes } 27340939Sdes 274103459Sfenner if ((conn = _fetch_reopen(sd)) == NULL) { 275103459Sfenner _fetch_syserr(); 27697856Sdes close(sd); 277103459Sfenner } 27897856Sdes return (conn); 27940939Sdes} 28041989Sdes 28141989Sdes 28255557Sdes/* 28397868Sdes * Enable SSL on a connection. 28497868Sdes */ 28597868Sdesint 28697868Sdes_fetch_ssl(conn_t *conn, int verbose) 28797868Sdes{ 28897868Sdes 28997891Sdes#ifdef WITH_SSL 29097868Sdes /* Init the SSL library and context */ 29197868Sdes if (!SSL_library_init()){ 29297868Sdes fprintf(stderr, "SSL library init failed\n"); 29397868Sdes return (-1); 29497868Sdes } 29597868Sdes 29697868Sdes SSL_load_error_strings(); 29797868Sdes 29897868Sdes conn->ssl_meth = SSLv23_client_method(); 29997868Sdes conn->ssl_ctx = SSL_CTX_new(conn->ssl_meth); 30097868Sdes 30197868Sdes conn->ssl = SSL_new(conn->ssl_ctx); 30297868Sdes if (conn->ssl == NULL){ 30397868Sdes fprintf(stderr, "SSL context creation failed\n"); 30497868Sdes return (-1); 30597868Sdes } 30697868Sdes SSL_set_fd(conn->ssl, conn->sd); 30797868Sdes if (SSL_connect(conn->ssl) == -1){ 30897868Sdes ERR_print_errors_fp(stderr); 30997868Sdes return (-1); 31097868Sdes } 31197868Sdes 31297868Sdes if (verbose) { 31397868Sdes X509_NAME *name; 31497868Sdes char *str; 31597868Sdes 31697868Sdes fprintf(stderr, "SSL connection established using %s\n", 31797868Sdes SSL_get_cipher(conn->ssl)); 31897868Sdes conn->ssl_cert = SSL_get_peer_certificate(conn->ssl); 31997868Sdes name = X509_get_subject_name(conn->ssl_cert); 32097868Sdes str = X509_NAME_oneline(name, 0, 0); 32197868Sdes printf("Certificate subject: %s\n", str); 32297868Sdes free(str); 32397868Sdes name = X509_get_issuer_name(conn->ssl_cert); 32497868Sdes str = X509_NAME_oneline(name, 0, 0); 32597868Sdes printf("Certificate issuer: %s\n", str); 32697868Sdes free(str); 32797868Sdes } 32897868Sdes 32997868Sdes return (0); 33097891Sdes#else 33197891Sdes (void)conn; 33297891Sdes (void)verbose; 33397891Sdes fprintf(stderr, "SSL support disabled\n"); 33497891Sdes return (-1); 33597891Sdes#endif 33697868Sdes} 33797868Sdes 33898117Sdes 33997868Sdes/* 34097866Sdes * Read a character from a connection w/ timeout 34155557Sdes */ 34297866Sdesssize_t 34397866Sdes_fetch_read(conn_t *conn, char *buf, size_t len) 34455557Sdes{ 34590267Sdes struct timeval now, timeout, wait; 34690267Sdes fd_set readfds; 34797866Sdes ssize_t rlen, total; 34890267Sdes int r; 34990267Sdes 350106137Sobrien rlen = 0; 351106137Sobrien 35255557Sdes if (fetchTimeout) { 35397866Sdes FD_ZERO(&readfds); 35490267Sdes gettimeofday(&timeout, NULL); 35590267Sdes timeout.tv_sec += fetchTimeout; 35655557Sdes } 35790267Sdes 35897866Sdes total = 0; 35997866Sdes while (len > 0) { 36097866Sdes while (fetchTimeout && !FD_ISSET(conn->sd, &readfds)) { 36197856Sdes FD_SET(conn->sd, &readfds); 36290267Sdes gettimeofday(&now, NULL); 36390267Sdes wait.tv_sec = timeout.tv_sec - now.tv_sec; 36490267Sdes wait.tv_usec = timeout.tv_usec - now.tv_usec; 36590267Sdes if (wait.tv_usec < 0) { 36690267Sdes wait.tv_usec += 1000000; 36790267Sdes wait.tv_sec--; 36890267Sdes } 369106137Sobrien if (wait.tv_sec < 0) 370106137Sobrien return (rlen); 37197866Sdes errno = 0; 37297856Sdes r = select(conn->sd + 1, &readfds, NULL, NULL, &wait); 37390267Sdes if (r == -1) { 37490267Sdes if (errno == EINTR && fetchRestartCalls) 37590267Sdes continue; 37690267Sdes return (-1); 37790267Sdes } 37890267Sdes } 37997891Sdes#ifdef WITH_SSL 38097866Sdes if (conn->ssl != NULL) 38197866Sdes rlen = SSL_read(conn->ssl, buf, len); 38297866Sdes else 38397891Sdes#endif 38497866Sdes rlen = read(conn->sd, buf, len); 385106049Sdes if (rlen == 0) 386106049Sdes break; 38797866Sdes if (rlen < 0) { 38890267Sdes if (errno == EINTR && fetchRestartCalls) 38990267Sdes continue; 39090267Sdes return (-1); 39190267Sdes } 39297866Sdes len -= rlen; 39397866Sdes buf += rlen; 39497866Sdes total += rlen; 39597866Sdes } 39697866Sdes return (total); 39797866Sdes} 39897866Sdes 39998117Sdes 40097866Sdes/* 40197866Sdes * Read a line of text from a connection w/ timeout 40297866Sdes */ 40397866Sdes#define MIN_BUF_SIZE 1024 40497866Sdes 40597866Sdesint 40697866Sdes_fetch_getln(conn_t *conn) 40797866Sdes{ 40897866Sdes char *tmp; 40997866Sdes size_t tmpsize; 41097866Sdes char c; 411106137Sobrien int error; 41297866Sdes 41397866Sdes if (conn->buf == NULL) { 41497866Sdes if ((conn->buf = malloc(MIN_BUF_SIZE)) == NULL) { 41597866Sdes errno = ENOMEM; 41697866Sdes return (-1); 41797866Sdes } 41897866Sdes conn->bufsize = MIN_BUF_SIZE; 41997866Sdes } 42097866Sdes 42197866Sdes conn->buf[0] = '\0'; 42297866Sdes conn->buflen = 0; 42397866Sdes 42497866Sdes do { 425106137Sobrien error = _fetch_read(conn, &c, 1); 426106137Sobrien if (error == -1) 42797866Sdes return (-1); 428106137Sobrien else if (error == 0) 429106137Sobrien break; 43097856Sdes conn->buf[conn->buflen++] = c; 43197856Sdes if (conn->buflen == conn->bufsize) { 43297856Sdes tmp = conn->buf; 43397856Sdes tmpsize = conn->bufsize * 2 + 1; 43497856Sdes if ((tmp = realloc(tmp, tmpsize)) == NULL) { 43590267Sdes errno = ENOMEM; 43690267Sdes return (-1); 43790267Sdes } 43897856Sdes conn->buf = tmp; 43997856Sdes conn->bufsize = tmpsize; 44090267Sdes } 44190267Sdes } while (c != '\n'); 44290267Sdes 44397856Sdes conn->buf[conn->buflen] = '\0'; 44497856Sdes DEBUG(fprintf(stderr, "<<< %s", conn->buf)); 44590267Sdes return (0); 44655557Sdes} 44755557Sdes 44855557Sdes 44962981Sdes/* 45097866Sdes * Write to a connection w/ timeout 45162981Sdes */ 45297866Sdesssize_t 45397866Sdes_fetch_write(conn_t *conn, const char *buf, size_t len) 45497866Sdes{ 45597866Sdes struct timeval now, timeout, wait; 45697866Sdes fd_set writefds; 45797866Sdes ssize_t wlen, total; 45897866Sdes int r; 45997866Sdes 460106137Sobrien total = 0; 461106137Sobrien 46297866Sdes if (fetchTimeout) { 46397866Sdes FD_ZERO(&writefds); 46497866Sdes gettimeofday(&timeout, NULL); 46597866Sdes timeout.tv_sec += fetchTimeout; 46697866Sdes } 46797866Sdes 468106137Sobrien while (len > 0) { 46997866Sdes while (fetchTimeout && !FD_ISSET(conn->sd, &writefds)) { 47097866Sdes FD_SET(conn->sd, &writefds); 47197866Sdes gettimeofday(&now, NULL); 47297866Sdes wait.tv_sec = timeout.tv_sec - now.tv_sec; 47397866Sdes wait.tv_usec = timeout.tv_usec - now.tv_usec; 47497866Sdes if (wait.tv_usec < 0) { 47597866Sdes wait.tv_usec += 1000000; 47697866Sdes wait.tv_sec--; 47797866Sdes } 47897866Sdes if (wait.tv_sec < 0) { 47997866Sdes errno = ETIMEDOUT; 48097866Sdes return (-1); 48197866Sdes } 48297866Sdes errno = 0; 48397866Sdes r = select(conn->sd + 1, NULL, &writefds, NULL, &wait); 48497866Sdes if (r == -1) { 48597866Sdes if (errno == EINTR && fetchRestartCalls) 48697866Sdes continue; 48797866Sdes return (-1); 48897866Sdes } 48997866Sdes } 49097866Sdes errno = 0; 49197891Sdes#ifdef WITH_SSL 49297866Sdes if (conn->ssl != NULL) 493106137Sobrien wlen = SSL_write(conn->ssl, buf, len); 49497866Sdes else 49597891Sdes#endif 496106137Sobrien wlen = write(conn->sd, buf, len); 497106137Sobrien if (wlen == 0) 49897866Sdes /* we consider a short write a failure */ 49997866Sdes return (-1); 50097866Sdes if (wlen < 0) { 50197866Sdes if (errno == EINTR && fetchRestartCalls) 50297866Sdes continue; 50397866Sdes return (-1); 50497866Sdes } 505106137Sobrien len -= wlen; 506106137Sobrien buf += wlen; 50797866Sdes total += wlen; 50897866Sdes } 50997866Sdes return (total); 51097866Sdes} 51197866Sdes 51298117Sdes 51397866Sdes/* 51497866Sdes * Write a line of text to a connection w/ timeout 51597866Sdes */ 51662981Sdesint 51797856Sdes_fetch_putln(conn_t *conn, const char *str, size_t len) 51862981Sdes{ 51998748Sdes 52098748Sdes DEBUG(fprintf(stderr, ">>> %s\n", str)); 521106137Sobrien if (_fetch_write(conn, str, len) == -1 || 522106137Sobrien _fetch_write(conn, ENDL, sizeof ENDL) == -1) 52390267Sdes return (-1); 52490267Sdes return (0); 52562981Sdes} 52662981Sdes 52762981Sdes 52897856Sdes/* 52997856Sdes * Close connection 53097856Sdes */ 53197856Sdesint 53297856Sdes_fetch_close(conn_t *conn) 53397856Sdes{ 53497856Sdes int ret; 53597856Sdes 53698117Sdes if (--conn->ref > 0) 53798117Sdes return (0); 53897856Sdes ret = close(conn->sd); 53997856Sdes free(conn); 54097856Sdes return (ret); 54197856Sdes} 54297856Sdes 54397856Sdes 54441989Sdes/*** Directory-related utility functions *************************************/ 54541989Sdes 54641989Sdesint 54741989Sdes_fetch_add_entry(struct url_ent **p, int *size, int *len, 54890267Sdes const char *name, struct url_stat *us) 54941989Sdes{ 55090267Sdes struct url_ent *tmp; 55141989Sdes 55290267Sdes if (*p == NULL) { 55390268Sdes *size = 0; 55490267Sdes *len = 0; 55541989Sdes } 55641989Sdes 55790267Sdes if (*len >= *size - 1) { 55890268Sdes tmp = realloc(*p, (*size * 2 + 1) * sizeof **p); 55990267Sdes if (tmp == NULL) { 56090267Sdes errno = ENOMEM; 56190267Sdes _fetch_syserr(); 56290267Sdes return (-1); 56390267Sdes } 56490268Sdes *size = (*size * 2 + 1); 56590267Sdes *p = tmp; 56690267Sdes } 56741989Sdes 56890267Sdes tmp = *p + *len; 56990267Sdes snprintf(tmp->name, PATH_MAX, "%s", name); 57090267Sdes bcopy(us, &tmp->stat, sizeof *us); 57141989Sdes 57290267Sdes (*len)++; 57390267Sdes (++tmp)->name[0] = 0; 57490267Sdes 57590267Sdes return (0); 57641989Sdes} 577