Main Page   Modules   Compound List   File List   Compound Members   File Members   Related Pages  

rpmio/rpmio.c

Go to the documentation of this file.
00001 
00005 #include "system.h"
00006 #include <stdarg.h>
00007 
00008 #ifdef  __LCLINT__
00009 typedef unsigned int            uint32_t;
00010 #define INADDR_ANY              ((uint32_t) 0x00000000)
00011 #define IPPROTO_IP              0
00012 
00013 #else   /* __LCLINT__ */
00014 
00015 #if HAVE_MACHINE_TYPES_H
00016 # include <machine/types.h>
00017 #endif
00018 
00019 #include <netinet/in.h>
00020 #include <arpa/inet.h>          /* XXX for inet_aton and HP-UX */
00021 
00022 #if HAVE_NETINET_IN_SYSTM_H
00023 # include <sys/types.h>
00024 # include <netinet/in_systm.h>
00025 #endif
00026 
00027 #if HAVE_LIBIO_H && defined(_G_IO_IO_FILE_VERSION)
00028 #define _USE_LIBIO      1
00029 #endif
00030 
00031 #endif  /* __LCLINT__ */
00032 
00033 #if !defined(HAVE_HERRNO) && defined(__hpux) /* XXX HP-UX w/o -D_XOPEN_SOURCE needs */
00034 extern int h_errno;
00035 #endif
00036 
00037 #ifndef IPPORT_FTP
00038 #define IPPORT_FTP      21
00039 #endif
00040 #ifndef IPPORT_HTTP
00041 #define IPPORT_HTTP     80
00042 #endif
00043 
00044 #if !defined(HAVE_INET_ATON)
00045 static int inet_aton(const char *cp, struct in_addr *inp)
00046 {
00047     long addr;
00048 
00049     addr = inet_addr(cp);
00050     if (addr == ((long) -1)) return 0;
00051 
00052     memcpy(inp, &addr, sizeof(addr));
00053     return 1;
00054 }
00055 #endif
00056 
00057 #if defined(USE_ALT_DNS) && USE_ALT_DNS
00058 #include "dns.h"
00059 #endif
00060 
00061 #include <rpmio_internal.h>
00062 
00063 #include "ugid.h"
00064 #include "rpmmessages.h"
00065 
00066 #include "debug.h"
00067 
00068 /*@access urlinfo@*/
00069 
00070 #define FDNREFS(fd)     (fd ? ((FD_t)fd)->nrefs : -9)
00071 #define FDTO(fd)        (fd ? ((FD_t)fd)->rd_timeoutsecs : -99)
00072 #define FDCPIOPOS(fd)   (fd ? ((FD_t)fd)->fd_cpioPos : -99)
00073 
00074 #define FDONLY(fd)      assert(fdGetIo(fd) == fdio)
00075 #define GZDONLY(fd)     assert(fdGetIo(fd) == gzdio)
00076 #define BZDONLY(fd)     assert(fdGetIo(fd) == bzdio)
00077 
00078 #define UFDONLY(fd)     /* assert(fdGetIo(fd) == ufdio) */
00079 
00080 #define fdGetFILE(_fd)  ((FILE *)fdGetFp(_fd))
00081 
00082 #if _USE_LIBIO
00083 int noLibio = 0;
00084 #else
00085 int noLibio = 1;
00086 #endif
00087 
00088 #define TIMEOUT_SECS 60
00089 static int ftpTimeoutSecs = TIMEOUT_SECS;
00090 static int httpTimeoutSecs = TIMEOUT_SECS;
00091 
00092 int _ftp_debug = 0;
00093 int _rpmio_debug = 0;
00094 
00095 /* =============================================================== */
00096 
00097 static /*@observer@*/ const char * fdbg(FD_t fd)
00098 {
00099     static char buf[BUFSIZ];
00100     char *be = buf;
00101     int i;
00102 
00103 #if DYING
00104     sprintf(be, "fd %p", fd);   be += strlen(be);
00105     if (fd->rd_timeoutsecs >= 0) {
00106         sprintf(be, " secs %d", fd->rd_timeoutsecs);
00107         be += strlen(be);
00108     }
00109 #endif
00110     if (fd->bytesRemain != -1) {
00111         sprintf(be, " clen %d", (int)fd->bytesRemain);
00112         be += strlen(be);
00113      }
00114     if (fd->wr_chunked) {
00115         strcpy(be, " chunked");
00116         be += strlen(be);
00117      }
00118     *be++ = '\t';
00119     for (i = fd->nfps; i >= 0; i--) {
00120         FDSTACK_t * fps = &fd->fps[i];
00121         if (i != fd->nfps)
00122             *be++ = ' ';
00123         *be++ = '|';
00124         *be++ = ' ';
00125         if (fps->io == fdio) {
00126             sprintf(be, "FD %d fp %p", fps->fdno, fps->fp);
00127         } else if (fps->io == ufdio) {
00128             sprintf(be, "UFD %d fp %p", fps->fdno, fps->fp);
00129         } else if (fps->io == fadio) {
00130             sprintf(be, "FAD %d fp %p", fps->fdno, fps->fp);
00131         } else if (fps->io == gzdio) {
00132             sprintf(be, "GZD %p fdno %d", fps->fp, fps->fdno);
00133 #if HAVE_BZLIB_H
00134         } else if (fps->io == bzdio) {
00135             sprintf(be, "BZD %p fdno %d", fps->fp, fps->fdno);
00136 #endif
00137         } else if (fps->io == fpio) {
00138             /*@+voidabstract@*/
00139             sprintf(be, "%s %p(%d) fdno %d",
00140                 (fps->fdno < 0 ? "LIBIO" : "FP"),
00141                 fps->fp, fileno(((FILE *)fps->fp)), fps->fdno);
00142             /*@=voidabstract@*/
00143         } else {
00144             sprintf(be, "??? io %p fp %p fdno %d ???",
00145                 fps->io, fps->fp, fps->fdno);
00146         }
00147         be += strlen(be);
00148         *be = '\0';
00149     }
00150     return buf;
00151 }
00152 
00153 /* =============================================================== */
00154 off_t fdSize(FD_t fd) {
00155     struct stat sb;
00156     off_t rc = -1; 
00157 
00158 #ifdef  NOISY
00159 DBGIO(0, (stderr, "==>\tfdSize(%p) rc %ld\n", fd, (long)rc));
00160 #endif
00161     FDSANE(fd);
00162     if (fd->contentLength >= 0)
00163         rc = fd->contentLength;
00164     else switch (fd->urlType) {
00165     case URL_IS_PATH:
00166     case URL_IS_UNKNOWN:
00167         if (fstat(Fileno(fd), &sb) == 0)
00168             rc = sb.st_size;
00169         /*@fallthrough@*/
00170     case URL_IS_FTP:
00171     case URL_IS_HTTP:
00172     case URL_IS_DASH:
00173         break;
00174     }
00175     return rc;
00176 }
00177 
00178 FD_t fdDup(int fdno) {
00179     FD_t fd;
00180     int nfdno;
00181 
00182     if ((nfdno = dup(fdno)) < 0)
00183         return NULL;
00184     fd = fdNew("open (fdDup)");
00185     fdSetFdno(fd, nfdno);
00186 DBGIO(fd, (stderr, "==> fdDup(%d) fd %p %s\n", fdno, fd, fdbg(fd)));
00187     /*@-refcounttrans@*/ return fd; /*@=refcounttrans@*/
00188 }
00189 
00190 static inline /*@unused@*/ int fdSeekNot(void * cookie,  /*@unused@*/ _libio_pos_t pos,  /*@unused@*/ int whence) {
00191     FD_t fd = c2f(cookie);
00192     FDSANE(fd);         /* XXX keep gcc quiet */
00193     return -2;
00194 }
00195 
00196 #ifdef UNUSED
00197 FILE *fdFdopen(void * cookie, const char *fmode) {
00198     FD_t fd = c2f(cookie);
00199     int fdno;
00200     FILE * fp;
00201 
00202     if (fmode == NULL) return NULL;
00203     fdno = fdFileno(fd);
00204     if (fdno < 0) return NULL;
00205     fp = fdopen(fdno, fmode);
00206 DBGIO(fd, (stderr, "==> fdFdopen(%p,\"%s\") fdno %d -> fp %p fdno %d\n", cookie, fmode, fdno, fp, fileno(fp)));
00207     fd = fdFree(fd, "open (fdFdopen)");
00208     return fp;
00209 }
00210 #endif
00211 
00212 #if 0
00213 #undef  fdLink
00214 #undef  fdFree
00215 #undef  fdNew
00216 #endif
00217 
00218 /* =============================================================== */
00219 static inline FD_t XfdLink(void * cookie, const char *msg, const char *file, unsigned line) {
00220     FD_t fd;
00221 if (cookie == NULL)
00222 DBGREFS(0, (stderr, "--> fd  %p ++ %d %s at %s:%u\n", cookie, FDNREFS(cookie)+1, msg, file, line));
00223     fd = c2f(cookie);
00224     if (fd) {
00225         fd->nrefs++;
00226 DBGREFS(fd, (stderr, "--> fd  %p ++ %d %s at %s:%u %s\n", fd, fd->nrefs, msg, file, line, fdbg(fd)));
00227     }
00228     return fd;
00229 }
00230 
00231 static inline /*@null@*/ FD_t XfdFree( /*@killref@*/ FD_t fd, const char *msg, const char *file, unsigned line) {
00232 if (fd == NULL)
00233 DBGREFS(0, (stderr, "--> fd  %p -- %d %s at %s:%u\n", fd, FDNREFS(fd), msg, file, line));
00234     FDSANE(fd);
00235     if (fd) {
00236 DBGREFS(fd, (stderr, "--> fd  %p -- %d %s at %s:%u %s\n", fd, fd->nrefs, msg, file, line, fdbg(fd)));
00237         if (--fd->nrefs > 0)
00238             /*@-refcounttrans@*/ return fd; /*@=refcounttrans@*/
00239         if (fd->stats) free(fd->stats);
00240         if (fd->digest) free(fd->digest);
00241         /*@-refcounttrans@*/ free(fd); /*@=refcounttrans@*/
00242     }
00243     return NULL;
00244 }
00245 
00246 static inline /*@null@*/ FD_t XfdNew(const char *msg, const char *file, unsigned line) {
00247     FD_t fd = (FD_t) xmalloc(sizeof(struct _FD_s));
00248     if (fd == NULL)
00249         return NULL;
00250     fd->nrefs = 0;
00251     fd->flags = 0;
00252     fd->magic = FDMAGIC;
00253     fd->urlType = URL_IS_UNKNOWN;
00254 
00255     fd->nfps = 0;
00256     memset(fd->fps, 0, sizeof(fd->fps));
00257 
00258     fd->fps[0].io = fdio;
00259     fd->fps[0].fp = NULL;
00260     fd->fps[0].fdno = -1;
00261 
00262     fd->url = NULL;
00263     fd->rd_timeoutsecs = 1;     /* XXX default value used to be -1 */
00264     fd->contentLength = fd->bytesRemain = -1;
00265     fd->wr_chunked = 0;
00266     fd->syserrno = 0;
00267     fd->errcookie = NULL;
00268     fd->stats = xcalloc(1, sizeof(*fd->stats));
00269     fd->digest = NULL;
00270     gettimeofday(&fd->stats->create, NULL);
00271     fd->stats->begin = fd->stats->create;       /* structure assignment */
00272 
00273     fd->ftpFileDoneNeeded = 0;
00274     fd->firstFree = 0;
00275     fd->fileSize = 0;
00276     fd->fd_cpioPos = 0;
00277 
00278     return XfdLink(fd, msg, file, line);
00279 }
00280 
00281 ssize_t fdRead(void * cookie, /*@out@*/ char * buf, size_t count) {
00282     FD_t fd = c2f(cookie);
00283     ssize_t rc;
00284 
00285     if (fd->bytesRemain == 0) return 0; /* XXX simulate EOF */
00286 
00287     fdstat_enter(fd, FDSTAT_READ);
00288     rc = read(fdFileno(fd), buf, (count > fd->bytesRemain ? fd->bytesRemain : count));
00289     fdstat_exit(fd, FDSTAT_READ, rc);
00290 
00291     if (fd->digest && rc > 0) rpmDigestUpdate(fd->digest, buf, rc);
00292 
00293 DBGIO(fd, (stderr, "==>\tfdRead(%p,%p,%ld) rc %ld %s\n", cookie, buf, (long)count, (long)rc, fdbg(fd)));
00294 
00295     return rc;
00296 }
00297 
00298 ssize_t fdWrite(void * cookie, const char * buf, size_t count) {
00299     FD_t fd = c2f(cookie);
00300     int fdno = fdFileno(fd);
00301     ssize_t rc;
00302 
00303     if (fd->bytesRemain == 0) return 0; /* XXX simulate EOF */
00304 
00305     if (fd->digest && count > 0) rpmDigestUpdate(fd->digest, buf, count);
00306 
00307     if (fd->wr_chunked) {
00308         char chunksize[20];
00309         sprintf(chunksize, "%x\r\n", (unsigned)count);
00310         rc = write(fdno, chunksize, strlen(chunksize));
00311         if (rc == -1)   fd->syserrno = errno;
00312     }
00313     if (count == 0) return 0;
00314 
00315     fdstat_enter(fd, FDSTAT_WRITE);
00316     rc = write(fdno, buf, (count > fd->bytesRemain ? fd->bytesRemain : count));
00317     fdstat_exit(fd, FDSTAT_WRITE, rc);
00318 
00319     if (fd->wr_chunked) {
00320         int ec;
00321         ec = write(fdno, "\r\n", sizeof("\r\n")-1);
00322         if (ec == -1)   fd->syserrno = errno;
00323     }
00324 
00325 DBGIO(fd, (stderr, "==>\tfdWrite(%p,%p,%ld) rc %ld %s\n", cookie, buf, (long)count, (long)rc, fdbg(fd)));
00326 
00327     return rc;
00328 }
00329 
00330 static inline int fdSeek(void * cookie, _libio_pos_t pos, int whence) {
00331 #ifdef USE_COOKIE_SEEK_POINTER
00332     _IO_off64_t p = *pos;
00333 #else
00334     off_t p = pos;
00335 #endif
00336     FD_t fd = c2f(cookie);
00337     off_t rc;
00338 
00339     assert(fd->bytesRemain == -1);      /* XXX FIXME fadio only for now */
00340     fdstat_enter(fd, FDSTAT_SEEK);
00341     rc = lseek(fdFileno(fd), p, whence);
00342     fdstat_exit(fd, FDSTAT_SEEK, rc);
00343 
00344 DBGIO(fd, (stderr, "==>\tfdSeek(%p,%ld,%d) rc %lx %s\n", cookie, (long)p, whence, (long)rc, fdbg(fd)));
00345 
00346     return rc;
00347 }
00348 
00349 int fdClose( /*@only@*/ void * cookie) {
00350     FD_t fd;
00351     int fdno;
00352     int rc;
00353 
00354     if (cookie == NULL) return -2;
00355     fd = c2f(cookie);
00356     fdno = fdFileno(fd);
00357 
00358     fdSetFdno(fd, -1);
00359 
00360     fdstat_enter(fd, FDSTAT_CLOSE);
00361     rc = ((fdno >= 0) ? close(fdno) : -2);
00362     fdstat_exit(fd, FDSTAT_CLOSE, rc);
00363 
00364 DBGIO(fd, (stderr, "==>\tfdClose(%p) rc %lx %s\n", fd, (long)rc, fdbg(fd)));
00365 
00366     fd = fdFree(fd, "open (fdClose)");
00367     return rc;
00368 }
00369 
00370 /*@null@*/ FD_t fdOpen(const char *path, int flags, mode_t mode) {
00371     FD_t fd;
00372     int fdno;
00373 
00374     fdno = open(path, flags, mode);
00375     if (fdno < 0) return NULL;
00376     fd = fdNew("open (fdOpen)");
00377     fdSetFdno(fd, fdno);
00378     fd->flags = flags;
00379 DBGIO(fd, (stderr, "==>\tfdOpen(\"%s\",%x,0%o) %s\n", path, flags, (unsigned)mode, fdbg(fd)));
00380     /*@-refcounttrans@*/ return fd; /*@=refcounttrans@*/
00381 }
00382 
00383 static struct FDIO_s fdio_s = {
00384   fdRead, fdWrite, fdSeek, fdClose, XfdLink, XfdFree, XfdNew, fdFileno,
00385   fdOpen, NULL, fdGetFp, NULL,  mkdir, chdir, rmdir, rename, unlink
00386 };
00387 FDIO_t fdio = /*@-compmempass@*/ &fdio_s /*@=compmempass@*/ ;
00388 
00389 FDIO_t fadio;   /* XXX usually NULL, filled in when linked with rpm */
00390 
00391 int fdWritable(FD_t fd, int secs)
00392 {
00393     int fdno;
00394     fd_set wrfds;
00395     struct timeval timeout, *tvp = (secs >= 0 ? &timeout : NULL);
00396     int rc;
00397         
00398     if ((fdno = fdFileno(fd)) < 0)
00399         return -1;      /* XXX W2DO? */
00400         
00401     FD_ZERO(&wrfds);
00402     do {
00403         FD_SET(fdno, &wrfds);
00404 
00405         if (tvp) {
00406             tvp->tv_sec = secs;
00407             tvp->tv_usec = 0;
00408         }
00409         errno = 0;
00410         rc = select(fdno + 1, NULL, &wrfds, NULL, tvp);
00411 
00412 if (_rpmio_debug && !(rc == 1 && errno == 0))
00413 fprintf(stderr, "*** fdWritable fdno %d rc %d %s\n", fdno, rc, strerror(errno));
00414         if (rc < 0) {
00415             switch (errno) {
00416             case EINTR:
00417                 continue;
00418                 /*@notreached@*/ break;
00419             default:
00420                 return rc;
00421                 /*@notreached@*/ break;
00422             }
00423         }
00424         return rc;
00425     } while (1);
00426     /*@notreached@*/
00427 }
00428 
00429 int fdReadable(FD_t fd, int secs)
00430 {
00431     int fdno;
00432     fd_set rdfds;
00433     struct timeval timeout, *tvp = (secs >= 0 ? &timeout : NULL);
00434     int rc;
00435 
00436     if ((fdno = fdFileno(fd)) < 0)
00437         return -1;      /* XXX W2DO? */
00438         
00439     FD_ZERO(&rdfds);
00440     do {
00441         FD_SET(fdno, &rdfds);
00442 
00443         if (tvp) {
00444             tvp->tv_sec = secs;
00445             tvp->tv_usec = 0;
00446         }
00447         errno = 0;
00448         rc = select(fdno + 1, &rdfds, NULL, NULL, tvp);
00449 
00450         if (rc < 0) {
00451             switch (errno) {
00452             case EINTR:
00453                 continue;
00454                 /*@notreached@*/ break;
00455             default:
00456                 return rc;
00457                 /*@notreached@*/ break;
00458             }
00459         }
00460         return rc;
00461     } while (1);
00462     /*@notreached@*/
00463 }
00464 
00465 int fdFgets(FD_t fd, char * buf, size_t len)
00466 {
00467     int fdno;
00468     int secs = fd->rd_timeoutsecs;
00469     size_t nb = 0;
00470     int ec = 0;
00471     char lastchar = '\0';
00472 
00473     if ((fdno = fdFileno(fd)) < 0)
00474         return 0;       /* XXX W2DO? */
00475         
00476     do {
00477         int rc;
00478 
00479         /* Is there data to read? */
00480         rc = fdReadable(fd, secs);
00481 
00482         switch (rc) {
00483         case -1:        /* error */
00484             ec = -1;
00485             continue;
00486             /*@notreached@*/ break;
00487         case  0:        /* timeout */
00488             ec = -1;
00489             continue;
00490             /*@notreached@*/ break;
00491         default:        /* data to read */
00492             break;
00493         }
00494 
00495         errno = 0;
00496 #ifdef  NOISY
00497         rc = fdRead(fd, buf + nb, 1);
00498 #else
00499         rc = read(fdFileno(fd), buf + nb, 1);
00500 #endif
00501         if (rc < 0) {
00502             fd->syserrno = errno;
00503             switch (errno) {
00504             case EWOULDBLOCK:
00505                 continue;
00506                 /*@notreached@*/ break;
00507             default:
00508                 break;
00509             }
00510 if (_rpmio_debug)
00511 fprintf(stderr, "*** read: fd %p rc %d errno %d %s \"%s\"\n", fd, rc, errno, strerror(errno), buf);
00512             ec = -1;
00513             break;
00514         } else if (rc == 0) {
00515 if (_rpmio_debug)
00516 fprintf(stderr, "*** read: fd %p rc %d EOF errno %d %s \"%s\"\n", fd, rc, errno, strerror(errno), buf);
00517             break;
00518         } else {
00519             nb += rc;
00520             buf[nb] = '\0';
00521             lastchar = buf[nb - 1];
00522         }
00523     } while (ec == 0 && nb < len && lastchar != '\n');
00524 
00525     return (ec >= 0 ? nb : ec);
00526 }
00527 
00528 /* =============================================================== */
00529 /* Support for FTP/HTTP I/O.
00530  */
00531 const char *const ftpStrerror(int errorNumber) {
00532   switch (errorNumber) {
00533     case 0:
00534         return _("Success");
00535 
00536     case FTPERR_BAD_SERVER_RESPONSE:
00537         return _("Bad server response");
00538 
00539     case FTPERR_SERVER_IO_ERROR:
00540         return _("Server I/O error");
00541 
00542     case FTPERR_SERVER_TIMEOUT:
00543         return _("Server timeout");
00544 
00545     case FTPERR_BAD_HOST_ADDR:
00546         return _("Unable to lookup server host address");
00547 
00548     case FTPERR_BAD_HOSTNAME:
00549         return _("Unable to lookup server host name");
00550 
00551     case FTPERR_FAILED_CONNECT:
00552         return _("Failed to connect to server");
00553 
00554     case FTPERR_FAILED_DATA_CONNECT:
00555         return _("Failed to establish data connection to server");
00556 
00557     case FTPERR_FILE_IO_ERROR:
00558         return _("I/O error to local file");
00559 
00560     case FTPERR_PASSIVE_ERROR:
00561         return _("Error setting remote server to passive mode");
00562 
00563     case FTPERR_FILE_NOT_FOUND:
00564         return _("File not found on server");
00565 
00566     case FTPERR_NIC_ABORT_IN_PROGRESS:
00567         return _("Abort in progress");
00568 
00569     case FTPERR_UNKNOWN:
00570     default:
00571         return _("Unknown or unexpected error");
00572   }
00573 }
00574 
00575 const char *urlStrerror(const char *url)
00576 {
00577     const char *retstr;
00578     switch (urlIsURL(url)) {
00579     case URL_IS_FTP:
00580     case URL_IS_HTTP:
00581     {   urlinfo u;
00582 /* XXX This only works for httpReq/ftpLogin/ftpReq failures */
00583         if (urlSplit(url, &u) == 0) {
00584             retstr = ftpStrerror(u->openError);
00585         } else
00586             retstr = "Malformed URL";
00587     }   break;
00588     default:
00589         retstr = strerror(errno);
00590         break;
00591     }
00592     return retstr;
00593 }
00594 
00595 #if !defined(USE_ALT_DNS) || !USE_ALT_DNS 
00596 static int mygethostbyname(const char * host, struct in_addr * address)
00597 {
00598     struct hostent * hostinfo;
00599 
00600     hostinfo = /*@-unrecog@*/ gethostbyname(host) /*@=unrecog@*/;
00601     if (!hostinfo) return 1;
00602 
00603     memcpy(address, hostinfo->h_addr_list[0], sizeof(*address));
00604     return 0;
00605 }
00606 #endif
00607 
00608 static int getHostAddress(const char * host, struct in_addr * address)
00609 {
00610     if (isdigit(host[0])) {
00611       if (! /*@-unrecog@*/ inet_aton(host, address) /*@=unrecog@*/ ) {
00612           return FTPERR_BAD_HOST_ADDR;
00613       }
00614     } else {
00615       if (mygethostbyname(host, address)) {
00616           errno = h_errno;
00617           return FTPERR_BAD_HOSTNAME;
00618       }
00619     }
00620     
00621     return 0;
00622 }
00623 
00624 static int tcpConnect(FD_t ctrl, const char *host, int port)
00625 {
00626     struct sockaddr_in sin;
00627     int fdno = -1;
00628     int rc;
00629 
00630     sin.sin_family = AF_INET;
00631     sin.sin_port = htons(port);
00632     sin.sin_addr.s_addr = INADDR_ANY;
00633     
00634   do {
00635     if ((rc = getHostAddress(host, &sin.sin_addr)) < 0)
00636         break;
00637 
00638     if ((fdno = socket(sin.sin_family, SOCK_STREAM, IPPROTO_IP)) < 0) {
00639         rc = FTPERR_FAILED_CONNECT;
00640         break;
00641     }
00642 
00643     if (connect(fdno, (struct sockaddr *) &sin, sizeof(sin))) {
00644         rc = FTPERR_FAILED_CONNECT;
00645         break;
00646     }
00647   } while (0);
00648 
00649     if (rc < 0)
00650         goto errxit;
00651 
00652 if (_ftp_debug)
00653 fprintf(stderr,"++ connect %s:%d on fdno %d\n",
00654 /*@-unrecog@*/ inet_ntoa(sin.sin_addr) /*@=unrecog@*/ ,
00655 ntohs(sin.sin_port), fdno);
00656 
00657     fdSetFdno(ctrl, (fdno >= 0 ? fdno : -1));
00658     return 0;
00659 
00660 errxit:
00661     fdSetSyserrno(ctrl, errno, ftpStrerror(rc));
00662     if (fdno >= 0)
00663         close(fdno);
00664     return rc;
00665 }
00666 
00667 static int checkResponse(void * uu, FD_t ctrl, /*@out@*/ int *ecp, /*@out@*/ char ** str)
00668 {
00669     urlinfo u = uu;
00670     char *buf;
00671     size_t bufAlloced;
00672     int bufLength = 0; 
00673     const char *s;
00674     char *se;
00675     int ec = 0;
00676     int moretodo = 1;
00677     char errorCode[4];
00678  
00679     URLSANE(u);
00680     if (u->bufAlloced == 0 || u->buf == NULL) {
00681         u->bufAlloced = url_iobuf_size;
00682         u->buf = xcalloc(u->bufAlloced, sizeof(char));
00683     }
00684     buf = u->buf;
00685     bufAlloced = u->bufAlloced;
00686     *buf = '\0';
00687 
00688     errorCode[0] = '\0';
00689     
00690     do {
00691         int rc;
00692 
00693         /*
00694          * Read next line from server.
00695          */
00696         se = buf + bufLength;
00697         *se = '\0';
00698         rc = fdFgets(ctrl, se, (bufAlloced - bufLength));
00699         if (rc < 0) {
00700             ec = FTPERR_BAD_SERVER_RESPONSE;
00701             continue;
00702         } else if (rc == 0 || fdWritable(ctrl, 0) < 1)
00703             moretodo = 0;
00704 
00705         /*
00706          * Process next line from server.
00707          */
00708         for (s = se; *s != '\0'; s = se) {
00709                 const char *e;
00710 
00711                 while (*se && *se != '\n') se++;
00712 
00713                 if (se > s && se[-1] == '\r')
00714                    se[-1] = '\0';
00715                 if (*se == '\0')
00716                     break;
00717 
00718 if (_ftp_debug)
00719 fprintf(stderr, "<- %s\n", s);
00720 
00721                 /* HTTP: header termination on empty line */
00722                 if (*s == '\0') {
00723                     moretodo = 0;
00724                     break;
00725                 }
00726                 *se++ = '\0';
00727 
00728                 /* HTTP: look for "HTTP/1.1 123 ..." */
00729                 if (!strncmp(s, "HTTP", sizeof("HTTP")-1)) {
00730                     ctrl->contentLength = -1;
00731                     if ((e = strchr(s, '.')) != NULL) {
00732                         e++;
00733                         u->httpVersion = *e - '0';
00734                         if (u->httpVersion < 1 || u->httpVersion > 2)
00735                             ctrl->persist = u->httpVersion = 0;
00736                         else
00737                             ctrl->persist = 1;
00738                     }
00739                     if ((e = strchr(s, ' ')) != NULL) {
00740                         e++;
00741                         if (strchr("0123456789", *e))
00742                             strncpy(errorCode, e, 3);
00743                         errorCode[3] = '\0';
00744                     }
00745                     continue;
00746                 }
00747 
00748                 /* HTTP: look for "token: ..." */
00749                 for (e = s; *e && !(*e == ' ' || *e == ':'); e++)
00750                     ;
00751                 if (e > s && *e++ == ':') {
00752                     size_t ne = (e - s);
00753                     while (*e && *e == ' ') e++;
00754 #if 0
00755                     if (!strncmp(s, "Date:", ne)) {
00756                     } else
00757                     if (!strncmp(s, "Server:", ne)) {
00758                     } else
00759                     if (!strncmp(s, "Last-Modified:", ne)) {
00760                     } else
00761                     if (!strncmp(s, "ETag:", ne)) {
00762                     } else
00763 #endif
00764                     if (!strncmp(s, "Accept-Ranges:", ne)) {
00765                         if (!strcmp(e, "bytes"))
00766                             u->httpHasRange = 1;
00767                         if (!strcmp(e, "none"))
00768                             u->httpHasRange = 0;
00769                     } else
00770                     if (!strncmp(s, "Content-Length:", ne)) {
00771                         if (strchr("0123456789", *e))
00772                             ctrl->contentLength = atoi(e);
00773                     } else
00774                     if (!strncmp(s, "Connection:", ne)) {
00775                         if (!strcmp(e, "close"))
00776                             ctrl->persist = 0;
00777                     }
00778 #if 0
00779                     else
00780                     if (!strncmp(s, "Content-Type:", ne)) {
00781                     } else
00782                     if (!strncmp(s, "Transfer-Encoding:", ne)) {
00783                         if (!strcmp(e, "chunked"))
00784                             ctrl->wr_chunked = 1;
00785                         else
00786                             ctrl->wr_chunked = 0;
00787                     } else
00788                     if (!strncmp(s, "Allow:", ne)) {
00789                     }
00790 #endif
00791                     continue;
00792                 }
00793 
00794                 /* HTTP: look for "<TITLE>501 ... </TITLE>" */
00795                 if (!strncmp(s, "<TITLE>", sizeof("<TITLE>")-1))
00796                     s += sizeof("<TITLE>") - 1;
00797 
00798                 /* FTP: look for "123-" and/or "123 " */
00799                 if (strchr("0123456789", *s)) {
00800                     if (errorCode[0]) {
00801                         if (!strncmp(s, errorCode, sizeof("123")-1) && s[3] == ' ')
00802                             moretodo = 0;
00803                     } else {
00804                         strncpy(errorCode, s, sizeof("123")-1);
00805                         errorCode[3] = '\0';
00806                         if (s[3] != '-')
00807                             moretodo = 0;
00808                     }
00809                 }
00810         }
00811 
00812         if (moretodo && se > s) {
00813             bufLength = se - s - 1;
00814             if (s != buf)
00815                 memmove(buf, s, bufLength);
00816         } else {
00817             bufLength = 0;
00818         }
00819     } while (moretodo && ec == 0);
00820 
00821     if (str)    *str = buf;
00822     if (ecp)    *ecp = atoi(errorCode);
00823 
00824     return ec;
00825 }
00826 
00827 static int ftpCheckResponse(urlinfo u, /*@out@*/ char ** str)
00828 {
00829     int ec = 0;
00830     int rc;
00831 
00832     URLSANE(u);
00833     rc = checkResponse(u, u->ctrl, &ec, str);
00834 
00835     switch (ec) {
00836     case 550:
00837         return FTPERR_FILE_NOT_FOUND;
00838         /*@notreached@*/ break;
00839     case 552:
00840         return FTPERR_NIC_ABORT_IN_PROGRESS;
00841         /*@notreached@*/ break;
00842     default:
00843         if (ec >= 400 && ec <= 599) {
00844             return FTPERR_BAD_SERVER_RESPONSE;
00845         }
00846         break;
00847     }
00848     return rc;
00849 }
00850 
00851 static int ftpCommand(urlinfo u, char ** str, ...)
00852 {
00853     va_list ap;
00854     int len = 0;
00855     const char * s, * t;
00856     char * te;
00857     int rc;
00858 
00859     URLSANE(u);
00860     va_start(ap, str);
00861     while ((s = va_arg(ap, const char *)) != NULL) {
00862         if (len) len++;
00863         len += strlen(s);
00864     }
00865     len += sizeof("\r\n")-1;
00866     va_end(ap);
00867 
00868     t = te = alloca(len + 1);
00869 
00870     va_start(ap, str);
00871     while ((s = va_arg(ap, const char *)) != NULL) {
00872         if (te > t) *te++ = ' ';
00873         te = stpcpy(te, s);
00874     }
00875     te = stpcpy(te, "\r\n");
00876     va_end(ap);
00877 
00878 if (_ftp_debug)
00879 fprintf(stderr, "-> %s", t);
00880     if (fdWrite(u->ctrl, t, (te-t)) != (te-t))
00881         return FTPERR_SERVER_IO_ERROR;
00882 
00883     rc = ftpCheckResponse(u, str);
00884     return rc;
00885 }
00886 
00887 static int ftpLogin(urlinfo u)
00888 {
00889     const char * host;
00890     const char * user;
00891     const char * password;
00892     int port;
00893     int rc;
00894 
00895     URLSANE(u);
00896     u->ctrl = fdLink(u->ctrl, "open ctrl");
00897 
00898     if (((host = (u->proxyh ? u->proxyh : u->host)) == NULL)) {
00899         rc = FTPERR_BAD_HOSTNAME;
00900         goto errxit;
00901     }
00902 
00903     if ((port = (u->proxyp > 0 ? u->proxyp : u->port)) < 0) port = IPPORT_FTP;
00904 
00905     if ((user = (u->proxyu ? u->proxyu : u->user)) == NULL)
00906         user = "anonymous";
00907 
00908     if ((password = u->password) == NULL) {
00909         if (getuid()) {
00910             struct passwd * pw = getpwuid(getuid());
00911             char *myp = alloca(strlen(pw->pw_name) + sizeof("@"));
00912             strcpy(myp, pw->pw_name);
00913             strcat(myp, "@");
00914             password = myp;
00915         } else {
00916             password = "root@";
00917         }
00918     }
00919 
00920     if (fdFileno(u->ctrl) >= 0 && fdWritable(u->ctrl, 0) < 1)
00921         fdClose(u->ctrl);
00922 
00923     if (fdFileno(u->ctrl) < 0) {
00924         rc = tcpConnect(u->ctrl, host, port);
00925         if (rc < 0)
00926             goto errxit2;
00927     }
00928 
00929     if ((rc = ftpCheckResponse(u, NULL)))
00930         goto errxit;
00931 
00932     if ((rc = ftpCommand(u, NULL, "USER", user, NULL)))
00933         goto errxit;
00934 
00935     if ((rc = ftpCommand(u, NULL, "PASS", password, NULL)))
00936         goto errxit;
00937 
00938     if ((rc = ftpCommand(u, NULL, "TYPE", "I", NULL)))
00939         goto errxit;
00940 
00941     return 0;
00942 
00943 errxit:
00944     fdSetSyserrno(u->ctrl, errno, ftpStrerror(rc));
00945 errxit2:
00946     if (fdFileno(u->ctrl) >= 0)
00947         fdClose(u->ctrl);
00948     return rc;
00949 }
00950 
00951 int ftpReq(FD_t data, const char * ftpCmd, const char * ftpArg)
00952 {
00953     urlinfo u = data->url;
00954     struct sockaddr_in dataAddress;
00955     char * cmd;
00956     int cmdlen;
00957     char * passReply;
00958     char * chptr;
00959     int rc;
00960 
00961     URLSANE(u);
00962     if (ftpCmd == NULL)
00963         return FTPERR_UNKNOWN;  /* XXX W2DO? */
00964 
00965     cmdlen = strlen(ftpCmd) + (ftpArg ? 1+strlen(ftpArg) : 0) + sizeof("\r\n");
00966     chptr = cmd = alloca(cmdlen);
00967     chptr = stpcpy(chptr, ftpCmd);
00968     if (ftpArg) {
00969         *chptr++ = ' ';
00970         chptr = stpcpy(chptr, ftpArg);
00971     }
00972     chptr = stpcpy(chptr, "\r\n");
00973     cmdlen = chptr - cmd;
00974 
00975 /*
00976  * Get the ftp version of the Content-Length.
00977  */
00978     if (!strncmp(cmd, "RETR", 4)) {
00979         unsigned cl;
00980 
00981         passReply = NULL;
00982         rc = ftpCommand(u, &passReply, "SIZE", ftpArg, NULL);
00983         if (rc)
00984             goto errxit;
00985         if (sscanf(passReply, "%d %u", &rc, &cl) != 2) {
00986             rc = FTPERR_BAD_SERVER_RESPONSE;
00987             goto errxit;
00988         }
00989         rc = 0;
00990         data->contentLength = cl;
00991     }
00992 
00993     passReply = NULL;
00994     rc = ftpCommand(u, &passReply, "PASV", NULL);
00995     if (rc) {
00996         rc = FTPERR_PASSIVE_ERROR;
00997         goto errxit;
00998     }
00999 
01000     chptr = passReply;
01001     while (*chptr && *chptr != '(') chptr++;
01002     if (*chptr != '(') return FTPERR_PASSIVE_ERROR; 
01003     chptr++;
01004     passReply = chptr;
01005     while (*chptr && *chptr != ')') chptr++;
01006     if (*chptr != ')') return FTPERR_PASSIVE_ERROR;
01007     *chptr-- = '\0';
01008 
01009     while (*chptr && *chptr != ',') chptr--;
01010     if (*chptr != ',') return FTPERR_PASSIVE_ERROR;
01011     chptr--;
01012     while (*chptr && *chptr != ',') chptr--;
01013     if (*chptr != ',') return FTPERR_PASSIVE_ERROR;
01014     *chptr++ = '\0';
01015     
01016     /* now passReply points to the IP portion, and chptr points to the
01017        port number portion */
01018 
01019     {   int i, j;
01020         dataAddress.sin_family = AF_INET;
01021         if (sscanf(chptr, "%d,%d", &i, &j) != 2) {
01022             rc = FTPERR_PASSIVE_ERROR;
01023             goto errxit;
01024         }
01025         dataAddress.sin_port = htons((((unsigned)i) << 8) + j);
01026     }
01027 
01028     chptr = passReply;
01029     while (*chptr++) {
01030         if (*chptr == ',') *chptr = '.';
01031     }
01032 
01033     if (!inet_aton(passReply, &dataAddress.sin_addr)) {
01034         rc = FTPERR_PASSIVE_ERROR;
01035         goto errxit;
01036     }
01037 
01038     rc = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
01039     fdSetFdno(data, (rc >= 0 ? rc : -1));
01040     if (rc < 0) {
01041         rc = FTPERR_FAILED_CONNECT;
01042         goto errxit;
01043     }
01044     data = fdLink(data, "open data (ftpReq)");
01045 
01046     /* XXX setsockopt SO_LINGER */
01047     /* XXX setsockopt SO_KEEPALIVE */
01048     /* XXX setsockopt SO_TOS IPTOS_THROUGHPUT */
01049 
01050     while (connect(fdFileno(data), (struct sockaddr *) &dataAddress, 
01051                 sizeof(dataAddress)) < 0) {
01052         if (errno == EINTR)
01053             continue;
01054         rc = FTPERR_FAILED_DATA_CONNECT;
01055         goto errxit;
01056     }
01057 
01058 if (_ftp_debug)
01059 fprintf(stderr, "-> %s", cmd);
01060     if (fdWrite(u->ctrl, cmd, cmdlen) != cmdlen) {
01061         rc = FTPERR_SERVER_IO_ERROR;
01062         goto errxit;
01063     }
01064 
01065     if ((rc = ftpCheckResponse(u, NULL))) {
01066         goto errxit;
01067     }
01068 
01069     data->ftpFileDoneNeeded = 1;
01070     u->ctrl = fdLink(u->ctrl, "grab data (ftpReq)");
01071     u->ctrl = fdLink(u->ctrl, "open data (ftpReq)");
01072     return 0;
01073 
01074 errxit:
01075     fdSetSyserrno(u->ctrl, errno, ftpStrerror(rc));
01076     if (fdFileno(data) >= 0)
01077         fdClose(data);
01078     return rc;
01079 }
01080 
01081 /*@null@*/ static rpmCallbackFunction   urlNotify = NULL;
01082 /*@null@*/ static void *        urlNotifyData = NULL;
01083 static int                      urlNotifyCount = -1;
01084 
01085 void urlSetCallback(rpmCallbackFunction notify, void *notifyData, int notifyCount) {
01086     urlNotify = notify;
01087     urlNotifyData = notifyData;
01088     urlNotifyCount = (notifyCount >= 0) ? notifyCount : 4096;
01089 }
01090 
01091 int ufdCopy(FD_t sfd, FD_t tfd)
01092 {
01093     char buf[BUFSIZ];
01094     int itemsRead;
01095     int itemsCopied = 0;
01096     int rc = 0;
01097     int notifier = -1;
01098 
01099     if (urlNotify) {
01100         (void)(*urlNotify) (NULL, RPMCALLBACK_INST_OPEN_FILE,
01101                 0, 0, NULL, urlNotifyData);
01102     }
01103     
01104     while (1) {
01105         rc = Fread(buf, sizeof(buf[0]), sizeof(buf), sfd);
01106         if (rc < 0)
01107             break;
01108         else if (rc == 0) {
01109             rc = itemsCopied;
01110             break;
01111         }
01112         itemsRead = rc;
01113         rc = Fwrite(buf, sizeof(buf[0]), itemsRead, tfd);
01114         if (rc < 0)
01115             break;
01116         if (rc != itemsRead) {
01117             rc = FTPERR_FILE_IO_ERROR;
01118             break;
01119         }
01120 
01121         itemsCopied += itemsRead;
01122         if (urlNotify && urlNotifyCount > 0) {
01123             int n = itemsCopied/urlNotifyCount;
01124             if (n != notifier) {
01125                 (void)(*urlNotify) (NULL, RPMCALLBACK_INST_PROGRESS,
01126                         itemsCopied, 0, NULL, urlNotifyData);
01127                 notifier = n;
01128             }
01129         }
01130     }
01131 
01132     DBGIO(sfd, (stderr, "++ copied %d bytes: %s\n", itemsCopied,
01133         ftpStrerror(rc)));
01134 
01135     if (urlNotify) {
01136         (void)(*urlNotify) (NULL, RPMCALLBACK_INST_OPEN_FILE,
01137                 itemsCopied, itemsCopied, NULL, urlNotifyData);
01138     }
01139     
01140     return rc;
01141 }
01142 
01143 static int urlConnect(const char * url, /*@out@*/ urlinfo * uret)
01144 {
01145     urlinfo u;
01146     int rc = 0;
01147 
01148     if (urlSplit(url, &u) < 0)
01149         return -1;
01150 
01151     if (u->urltype == URL_IS_FTP) {
01152         FD_t fd;
01153 
01154         if ((fd = u->ctrl) == NULL) {
01155             fd = u->ctrl = fdNew("persist ctrl (urlConnect FTP)");
01156             fdSetIo(u->ctrl, ufdio);
01157         }
01158         
01159         fd->rd_timeoutsecs = ftpTimeoutSecs;
01160         fd->contentLength = fd->bytesRemain = -1;
01161         fd->url = NULL;         /* XXX FTP ctrl has not */
01162         fd->ftpFileDoneNeeded = 0;
01163         fd = fdLink(fd, "grab ctrl (urlConnect FTP)");
01164 
01165         if (fdFileno(u->ctrl) < 0) {
01166             rpmMessage(RPMMESS_DEBUG, _("logging into %s as %s, pw %s\n"),
01167                         u->host,
01168                         u->user ? u->user : "ftp",
01169                         u->password ? u->password : "(username)");
01170 
01171             if ((rc = ftpLogin(u)) < 0) {       /* XXX save ftpLogin error */
01172                 u->ctrl = fdFree(fd, "grab ctrl (urlConnect FTP)");
01173                 u->openError = rc;
01174             }
01175         }
01176     }
01177 
01178     if (uret != NULL)
01179         *uret = urlLink(u, "urlConnect");
01180     u = urlFree(u, "urlSplit (urlConnect)");    
01181 
01182     return rc;
01183 }
01184 
01185 int ufdGetFile(FD_t sfd, FD_t tfd)
01186 {
01187     int rc;
01188 
01189     FDSANE(sfd);
01190     FDSANE(tfd);
01191     rc = ufdCopy(sfd, tfd);
01192     Fclose(sfd);
01193     if (rc > 0)         /* XXX ufdCopy now returns no. bytes copied */
01194         rc = 0;
01195     return rc;
01196 }
01197 
01198 int ftpCmd(const char * cmd, const char * url, const char * arg2) {
01199     urlinfo u;
01200     int rc;
01201     const char * path;
01202 
01203     if (urlConnect(url, &u) < 0)
01204         return -1;
01205 
01206     (void) urlPath(url, &path);
01207 
01208     rc = ftpCommand(u, NULL, cmd, path, arg2, NULL);
01209     u->ctrl = fdFree(u->ctrl, "grab ctrl (ftpCmd)");
01210     return rc;
01211 }
01212 
01213 /* XXX these aren't worth the pain of including correctly */
01214 #if !defined(IAC)
01215 #define IAC     255             /* interpret as command: */
01216 #endif
01217 #if !defined(IP)
01218 #define IP      244             /* interrupt process--permanently */
01219 #endif
01220 #if !defined(DM)
01221 #define DM      242             /* data mark--for connect. cleaning */
01222 #endif
01223 #if !defined(SHUT_RDWR)
01224 #define SHUT_RDWR       1+1
01225 #endif
01226 
01227 static int ftpAbort(urlinfo u, FD_t data) {
01228     static unsigned char ipbuf[3] = { IAC, IP, IAC };
01229     FD_t ctrl;
01230     int rc;
01231     int tosecs;
01232 
01233     URLSANE(u);
01234 
01235     if (data != NULL) {
01236         data->ftpFileDoneNeeded = 0;
01237         if (fdFileno(data) >= 0)
01238             u->ctrl = fdFree(u->ctrl, "open data (ftpAbort)");
01239         u->ctrl = fdFree(u->ctrl, "grab data (ftpAbort)");
01240     }
01241     ctrl = u->ctrl;
01242 
01243     DBGIO(0, (stderr, "-> ABOR\n"));
01244 
01245     if (send(fdFileno(ctrl), ipbuf, sizeof(ipbuf), MSG_OOB) != sizeof(ipbuf)) {
01246         fdClose(ctrl);
01247         return FTPERR_SERVER_IO_ERROR;
01248     }
01249 
01250     sprintf(u->buf, "%cABOR\r\n",(char) DM);
01251     if (fdWrite(ctrl, u->buf, 7) != 7) {
01252         fdClose(ctrl);
01253         return FTPERR_SERVER_IO_ERROR;
01254     }
01255 
01256     if (data && fdFileno(data) >= 0) {
01257         /* XXX shorten data drain time wait */
01258         tosecs = data->rd_timeoutsecs;
01259         data->rd_timeoutsecs = 10;
01260         if (fdReadable(data, data->rd_timeoutsecs) > 0) {
01261             while (timedRead(data, u->buf, u->bufAlloced) > 0)
01262                 ;
01263         }
01264         data->rd_timeoutsecs = tosecs;
01265         /* XXX ftp abort needs to close the data channel to receive status */
01266         shutdown(fdFileno(data), SHUT_RDWR);
01267         close(fdFileno(data));
01268         data->fps[0].fdno = -1; /* XXX WRONG but expedient */
01269     }
01270 
01271     /* XXX shorten ctrl drain time wait */
01272     tosecs = u->ctrl->rd_timeoutsecs;
01273     u->ctrl->rd_timeoutsecs = 10;
01274     if ((rc = ftpCheckResponse(u, NULL)) == FTPERR_NIC_ABORT_IN_PROGRESS) {
01275         rc = ftpCheckResponse(u, NULL);
01276     }
01277     rc = ftpCheckResponse(u, NULL);
01278     u->ctrl->rd_timeoutsecs = tosecs;
01279 
01280     return rc;
01281 }
01282 
01283 static int ftpFileDone(urlinfo u, FD_t data)
01284 {
01285     int rc = 0;
01286 
01287     URLSANE(u);
01288     assert(data->ftpFileDoneNeeded);
01289 
01290     if (data->ftpFileDoneNeeded) {
01291         data->ftpFileDoneNeeded = 0;
01292         u->ctrl = fdFree(u->ctrl, "open data (ftpFileDone)");
01293         u->ctrl = fdFree(u->ctrl, "grab data (ftpFileDone)");
01294         rc = ftpCheckResponse(u, NULL);
01295     }
01296     return rc;
01297 }
01298 
01299 static int httpResp(urlinfo u, FD_t ctrl, /*@out@*/ char ** str)
01300 {
01301     int ec = 0;
01302     int rc;
01303 
01304     URLSANE(u);
01305     rc = checkResponse(u, ctrl, &ec, str);
01306 
01307 if (_ftp_debug && !(rc == 0 && ec == 200))
01308 fprintf(stderr, "*** httpResp: rc %d ec %d\n", rc, ec);
01309 
01310     switch (ec) {
01311     case 200:
01312         break;
01313     default:
01314         rc = FTPERR_FILE_NOT_FOUND;
01315         break;
01316     }
01317 
01318     return rc;
01319 }
01320 
01321 static int httpReq(FD_t ctrl, const char * httpCmd, const char * httpArg)
01322 {
01323     urlinfo u = ctrl->url;
01324     const char * host;
01325     const char * path;
01326     int port;
01327     int rc;
01328     char * req;
01329     size_t len;
01330     int retrying = 0;
01331 
01332     URLSANE(u);
01333     assert(ctrl != NULL);
01334 
01335     if (((host = (u->proxyh ? u->proxyh : u->host)) == NULL))
01336         return FTPERR_BAD_HOSTNAME;
01337 
01338     if ((port = (u->proxyp > 0 ? u->proxyp : u->port)) < 0) port = 80;
01339     path = (u->proxyh || u->proxyp > 0) ? u->url : httpArg;
01340 
01341 reopen:
01342     if (fdFileno(ctrl) >= 0 && (rc = fdWritable(ctrl, 0)) < 1) {
01343         fdClose(ctrl);
01344     }
01345 
01346     if (fdFileno(ctrl) < 0) {
01347         rc = tcpConnect(ctrl, host, port);
01348         if (rc < 0)
01349             goto errxit2;
01350         ctrl = fdLink(ctrl, "open ctrl (httpReq)");
01351     }
01352 
01353     len = sizeof("\
01354 req x HTTP/1.0\r\n\
01355 User-Agent: rpm/3.0.4\r\n\
01356 Host: y:z\r\n\
01357 Accept: text/plain\r\n\
01358 Transfer-Encoding: chunked\r\n\
01359 \r\n\
01360 ") + strlen(httpCmd) + strlen(path) + sizeof(VERSION) + strlen(host) + 20;
01361 
01362     req = alloca(len);
01363     *req = '\0';
01364 
01365   if (!strcmp(httpCmd, "PUT")) {
01366     sprintf(req, "\
01367 %s %s HTTP/1.%d\r\n\
01368 User-Agent: rpm/%s\r\n\
01369 Host: %s:%d\r\n\
01370 Accept: text/plain\r\n\
01371 Transfer-Encoding: chunked\r\n\
01372 \r\n\
01373 ",      httpCmd, path, (u->httpVersion ? 1 : 0), VERSION, host, port);
01374 } else {
01375     sprintf(req, "\
01376 %s %s HTTP/1.%d\r\n\
01377 User-Agent: rpm/%s\r\n\
01378 Host: %s:%d\r\n\
01379 Accept: text/plain\r\n\
01380 \r\n\
01381 ",      httpCmd, path, (u->httpVersion ? 1 : 0), VERSION, host, port);
01382 }
01383 
01384 if (_ftp_debug)
01385 fprintf(stderr, "-> %s", req);
01386 
01387     len = strlen(req);
01388     if (fdWrite(ctrl, req, len) != len) {
01389         rc = FTPERR_SERVER_IO_ERROR;
01390         goto errxit;
01391     }
01392 
01393     if (!strcmp(httpCmd, "PUT")) {
01394         ctrl->wr_chunked = 1;
01395     } else {
01396 
01397         rc = httpResp(u, ctrl, NULL);
01398 
01399         if (rc) {
01400             if (!retrying) {    /* not HTTP_OK */
01401                 retrying = 1;
01402                 fdClose(ctrl);
01403                 goto reopen;
01404             }
01405             goto errxit;
01406         }
01407     }
01408 
01409     ctrl = fdLink(ctrl, "open data (httpReq)");
01410     return 0;
01411 
01412 errxit:
01413     fdSetSyserrno(ctrl, errno, ftpStrerror(rc));
01414 errxit2:
01415     if (fdFileno(ctrl) >= 0)
01416         fdClose(ctrl);
01417     return rc;
01418 }
01419 
01420 /* XXX DYING: unused */
01421 void * ufdGetUrlinfo(FD_t fd) {
01422     FDSANE(fd);
01423     if (fd->url == NULL)
01424         return NULL;
01425     return urlLink(fd->url, "ufdGetUrlinfo");
01426 }
01427 
01428 /* =============================================================== */
01429 static ssize_t ufdRead(void * cookie, /*@out@*/ char * buf, size_t count) {
01430     FD_t fd = c2f(cookie);
01431     int bytesRead;
01432     int total;
01433 
01434     /* XXX preserve timedRead() behavior */
01435     if (fdGetIo(fd) == fdio) {
01436         struct stat sb;
01437         int fdno = fdFileno(fd);
01438         fstat(fdno, &sb);
01439         if (S_ISREG(sb.st_mode))
01440             return fdRead(fd, buf, count);
01441     }
01442 
01443     UFDONLY(fd);
01444     assert(fd->rd_timeoutsecs >= 0);
01445 
01446     for (total = 0; total < count; total += bytesRead) {
01447 
01448         int rc;
01449 
01450         bytesRead = 0;
01451 
01452         /* Is there data to read? */
01453         if (fd->bytesRemain == 0) return total; /* XXX simulate EOF */
01454         rc = fdReadable(fd, fd->rd_timeoutsecs);
01455 
01456         switch (rc) {
01457         case -1:        /* error */
01458         case  0:        /* timeout */
01459             return total;
01460             /*@notreached@*/ break;
01461         default:        /* data to read */
01462             break;
01463         }
01464 
01465         rc = fdRead(fd, buf + total, count - total);
01466 
01467         if (rc < 0) {
01468             switch (errno) {
01469             case EWOULDBLOCK:
01470                 continue;
01471                 /*@notreached@*/ break;
01472             default:
01473                 break;
01474             }
01475 if (_rpmio_debug)
01476 fprintf(stderr, "*** read: rc %d errno %d %s \"%s\"\n", rc, errno, strerror(errno), buf);
01477             return rc;
01478             /*@notreached@*/ break;
01479         } else if (rc == 0) {
01480             return total;
01481             /*@notreached@*/ break;
01482         }
01483         bytesRead = rc;
01484     }
01485 
01486     return count;
01487 }
01488 
01489 static ssize_t ufdWrite(void * cookie, const char * buf, size_t count)
01490 {
01491     FD_t fd = c2f(cookie);
01492     int bytesWritten;
01493     int total = 0;
01494 
01495 #ifdef  NOTYET
01496     if (fdGetIo(fd) == fdio) {
01497         struct stat sb;
01498         fstat(fdGetFdno(fd), &sb);
01499         if (S_ISREG(sb.st_mode))
01500             return fdWrite(fd, buf, count);
01501     }
01502 #endif
01503 
01504     UFDONLY(fd);
01505 
01506     for (total = 0; total < count; total += bytesWritten) {
01507 
01508         int rc;
01509 
01510         bytesWritten = 0;
01511 
01512         /* Is there room to write data? */
01513         if (fd->bytesRemain == 0) {
01514 fprintf(stderr, "*** ufdWrite fd %p WRITE PAST END OF CONTENT\n", fd);
01515             return total;       /* XXX simulate EOF */
01516         }
01517         rc = fdWritable(fd, 2);         /* XXX configurable? */
01518 
01519         switch (rc) {
01520         case -1:        /* error */
01521         case  0:        /* timeout */
01522             return total;
01523             /*@notreached@*/ break;
01524         default:        /* data to write */
01525             break;
01526         }
01527 
01528         rc = fdWrite(fd, buf + total, count - total);
01529 
01530         if (rc < 0) {
01531             switch (errno) {
01532             case EWOULDBLOCK:
01533                 continue;
01534                 /*@notreached@*/ break;
01535             default:
01536                 break;
01537             }
01538 if (_rpmio_debug)
01539 fprintf(stderr, "*** write: rc %d errno %d %s \"%s\"\n", rc, errno, strerror(errno), buf);
01540             return rc;
01541             /*@notreached@*/ break;
01542         } else if (rc == 0) {
01543             return total;
01544             /*@notreached@*/ break;
01545         }
01546         bytesWritten = rc;
01547     }
01548 
01549     return count;
01550 }
01551 
01552 static inline int ufdSeek(void * cookie, _libio_pos_t pos, int whence) {
01553     FD_t fd = c2f(cookie);
01554 
01555     switch (fd->urlType) {
01556     case URL_IS_UNKNOWN:
01557     case URL_IS_PATH:
01558         break;
01559     case URL_IS_DASH:
01560     case URL_IS_FTP:
01561     case URL_IS_HTTP:
01562     default:
01563         return -2;
01564         /*@notreached@*/ break;
01565     }
01566     return fdSeek(cookie, pos, whence);
01567 }
01568 
01569 int ufdClose( /*@only@*/ void * cookie)
01570 {
01571     FD_t fd = c2f(cookie);
01572 
01573     UFDONLY(fd);
01574 
01575     if (fd->url) {
01576         urlinfo u = fd->url;
01577 
01578         if (fd == u->data)
01579                 fd = u->data = fdFree(fd, "grab data (ufdClose persist)");
01580         else
01581                 fd = fdFree(fd, "grab data (ufdClose)");
01582         (void) urlFree(fd->url, "url (ufdClose)");
01583         fd->url = NULL;
01584         u->ctrl = fdFree(u->ctrl, "grab ctrl (ufdClose)");
01585 
01586         if (u->urltype == URL_IS_FTP) {
01587 
01588             /* XXX if not using libio, lose the fp from fpio */
01589             {   FILE * fp;
01590                 /*@+voidabstract@*/
01591                 fp = fdGetFILE(fd);
01592                 if (noLibio && fp)
01593                     fdSetFp(fd, NULL);
01594                 /*@=voidabstract@*/
01595             }
01596 
01597             /*
01598              * Normal FTP has 4 refs on the data fd:
01599              *  "persist data (ufdOpen FTP)"            rpmio.c:888
01600              *  "grab data (ufdOpen FTP)"               rpmio.c:892
01601              *  "open data (ftpReq)"                    ftp.c:633
01602              *  "fopencookie"                           rpmio.c:1507
01603              */
01604 
01605             /*
01606              * Normal FTP has 5 refs on the ctrl fd:
01607              *  "persist ctrl"                          url.c:176
01608              *  "grab ctrl (urlConnect FTP)"            rpmio.c:404
01609              *  "open ctrl"                             ftp.c:504
01610              *  "grab data (ftpReq)"                    ftp.c:661
01611              *  "open data (ftpReq)"                    ftp.c:662
01612              */
01613             if (fd->bytesRemain > 0) {
01614                 if (fd->ftpFileDoneNeeded) {
01615                     if (fdReadable(u->ctrl, 0) > 0)
01616                         ftpFileDone(u, fd);
01617                     else
01618                         ftpAbort(u, fd);
01619                 }
01620             } else {
01621                 int rc;
01622                 /* XXX STOR et al require close before ftpFileDone */
01623                 rc = fdClose(fd);
01624 #if 0   /* XXX error exit from ufdOpen does not have this set */
01625                 assert(fd->ftpFileDoneNeeded != 0);
01626 #endif
01627                 if (fd->ftpFileDoneNeeded)
01628                     ftpFileDone(u, fd);
01629                 return rc;
01630             }
01631         }
01632 
01633         if (!strcmp(u->service, "http")) {
01634             if (fd->wr_chunked) {
01635                 int rc;
01636             /* XXX HTTP PUT requires terminating 0 length chunk. */
01637                 (void) fdWrite(fd, NULL, 0);
01638                 fd->wr_chunked = 0;
01639             /* XXX HTTP PUT requires terminating entity-header. */
01640 if (_ftp_debug)
01641 fprintf(stderr, "-> \r\n");
01642                 (void) fdWrite(fd, "\r\n", sizeof("\r\n")-1);
01643                 rc = httpResp(u, fd, NULL);
01644             }
01645 
01646             if (fd == u->ctrl)
01647                 fd = u->ctrl = fdFree(fd, "open data (ufdClose HTTP persist ctrl)");
01648             else if (fd == u->data)
01649                 fd = u->data = fdFree(fd, "open data (ufdClose HTTP persist data)");
01650             else
01651                 fd = fdFree(fd, "open data (ufdClose HTTP)");
01652 
01653             /*
01654              * HTTP has 4 (or 5 if persistent malloc) refs on the fd:
01655              *  "persist ctrl"                          url.c:177
01656              *  "grab ctrl (ufdOpen HTTP)"              rpmio.c:924
01657              *  "grab data (ufdOpen HTTP)"              rpmio.c:928
01658              *  "open ctrl (httpReq)"                   ftp.c:382
01659              *  "open data (httpReq)"                   ftp.c:435
01660              */
01661 
01662             /* XXX if not using libio, lose the fp from fpio */
01663             {   FILE * fp;
01664                 /*@+voidabstract@*/
01665                 fp = fdGetFILE(fd);
01666                 if (noLibio && fp)
01667                     fdSetFp(fd, NULL);
01668                 /*@=voidabstract@*/
01669             }
01670 
01671             if (fd->persist && u->httpVersion &&
01672                 (fd == u->ctrl || fd == u->data) && fd->bytesRemain == 0) {
01673                 fd->contentLength = fd->bytesRemain = -1;
01674                 return 0;
01675             } else {
01676                 fd->contentLength = fd->bytesRemain = -1;
01677             }
01678         }
01679     }
01680     return fdClose(fd);
01681 }
01682 
01683 /*@null@*/ FD_t ftpOpen(const char *url, /*@unused@*/ int flags,
01684                 /*@unused@*/ mode_t mode, /*@out@*/ urlinfo *uret)
01685 {
01686     urlinfo u = NULL;
01687     FD_t fd = NULL;
01688 
01689 #if 0   /* XXX makeTempFile() heartburn */
01690     assert(!(flags & O_RDWR));
01691 #endif
01692     if (urlConnect(url, &u) < 0)
01693         goto exit;
01694 
01695     if (u->data == NULL)
01696         u->data = fdNew("persist data (ftpOpen)");
01697 
01698     if (u->data->url == NULL)
01699         fd = fdLink(u->data, "grab data (ftpOpen persist data)");
01700     else
01701         fd = fdNew("grab data (ftpOpen)");
01702 
01703     if (fd) {
01704         fdSetIo(fd, ufdio);
01705         fd->ftpFileDoneNeeded = 0;
01706         fd->rd_timeoutsecs = ftpTimeoutSecs;
01707         fd->contentLength = fd->bytesRemain = -1;
01708         fd->url = urlLink(u, "url (ufdOpen FTP)");
01709         fd->urlType = URL_IS_FTP;
01710     }
01711 
01712 exit:
01713     if (uret)
01714         *uret = u;
01715     return fd;
01716 }
01717 
01718 static /*@null@*/ FD_t httpOpen(const char *url, int flags, mode_t mode,
01719                 /*@out@*/ urlinfo *uret)
01720 {
01721     urlinfo u = NULL;
01722     FD_t fd = NULL;
01723 
01724 #if 0   /* XXX makeTempFile() heartburn */
01725     assert(!(flags & O_RDWR));
01726 #endif
01727     if (urlSplit(url, &u))
01728         goto exit;
01729 
01730     if (u->ctrl == NULL)
01731         u->ctrl = fdNew("persist ctrl (httpOpen)");
01732     if (u->ctrl->nrefs > 2 && u->data == NULL)
01733         u->data = fdNew("persist data (httpOpen)");
01734 
01735     if (u->ctrl->url == NULL)
01736         fd = fdLink(u->ctrl, "grab ctrl (httpOpen persist ctrl)");
01737     else if (u->data->url == NULL)
01738         fd = fdLink(u->data, "grab ctrl (httpOpen persist data)");
01739     else
01740         fd = fdNew("grab ctrl (httpOpen)");
01741 
01742     if (fd) {
01743         fdSetIo(fd, ufdio);
01744         fd->ftpFileDoneNeeded = 0;
01745         fd->rd_timeoutsecs = httpTimeoutSecs;
01746         fd->contentLength = fd->bytesRemain = -1;
01747         fd->url = urlLink(u, "url (httpOpen)");
01748         fd = fdLink(fd, "grab data (httpOpen)");
01749         fd->urlType = URL_IS_HTTP;
01750     }
01751 
01752 exit:
01753     if (uret)
01754         *uret = u;
01755     return fd;
01756 }
01757 
01758 static /*@null@*/ FD_t ufdOpen(const char *url, int flags, mode_t mode)
01759 {
01760     FD_t fd = NULL;
01761     const char * cmd;
01762     urlinfo u;
01763     const char * path;
01764     urltype urlType = urlPath(url, &path);
01765 
01766 if (_rpmio_debug)
01767 fprintf(stderr, "*** ufdOpen(%s,0x%x,0%o)\n", url, flags, (unsigned)mode);
01768 
01769     switch (urlType) {
01770     case URL_IS_FTP:
01771         fd = ftpOpen(url, flags, mode, &u);
01772         if (fd == NULL || u == NULL)
01773             break;
01774 
01775         /* XXX W2DO? use STOU rather than STOR to prevent clobbering */
01776         cmd = ((flags & O_WRONLY) 
01777                 ?  ((flags & O_APPEND) ? "APPE" :
01778                    ((flags & O_CREAT) ? "STOR" : "STOR"))
01779                 :  ((flags & O_CREAT) ? "STOR" : "RETR"));
01780         u->openError = ftpReq(fd, cmd, path);
01781         if (u->openError < 0) {
01782             /* XXX make sure that we can exit through ufdClose */
01783             fd = fdLink(fd, "error data (ufdOpen FTP)");
01784         } else {
01785             fd->bytesRemain = ((!strcmp(cmd, "RETR"))
01786                 ?  fd->contentLength : -1);
01787             fd->wr_chunked = 0;
01788         }
01789         break;
01790     case URL_IS_HTTP:
01791         fd = httpOpen(url, flags, mode, &u);
01792         if (fd == NULL || u == NULL)
01793             break;
01794 
01795         cmd = ((flags & O_WRONLY)
01796                 ?  ((flags & O_APPEND) ? "PUT" :
01797                    ((flags & O_CREAT) ? "PUT" : "PUT"))
01798                 : "GET");
01799         u->openError = httpReq(fd, cmd, path);
01800         if (u->openError < 0) {
01801             /* XXX make sure that we can exit through ufdClose */
01802             fd = fdLink(fd, "error ctrl (ufdOpen HTTP)");
01803             fd = fdLink(fd, "error data (ufdOpen HTTP)");
01804         } else {
01805             fd->bytesRemain = ((!strcmp(cmd, "GET"))
01806                 ?  fd->contentLength : -1);
01807             fd->wr_chunked = ((!strcmp(cmd, "PUT"))
01808                 ?  fd->wr_chunked : 0);
01809         }
01810         break;
01811     case URL_IS_DASH:
01812         assert(!(flags & O_RDWR));
01813         fd = fdDup( ((flags & O_WRONLY) ? STDOUT_FILENO : STDIN_FILENO) );
01814         if (fd) {
01815             fdSetIo(fd, ufdio);
01816             fd->rd_timeoutsecs = 600;   /* XXX W2DO? 10 mins? */
01817             fd->contentLength = fd->bytesRemain = -1;
01818         }
01819         break;
01820     case URL_IS_PATH:
01821     case URL_IS_UNKNOWN:
01822     default:
01823         fd = fdOpen(path, flags, mode);
01824         if (fd) {
01825             fdSetIo(fd, ufdio);
01826             fd->rd_timeoutsecs = 1;
01827             fd->contentLength = fd->bytesRemain = -1;
01828         }
01829         break;
01830     }
01831 
01832     if (fd == NULL) return NULL;
01833     fd->urlType = urlType;
01834     if (Fileno(fd) < 0) {
01835         ufdClose(fd);
01836         return NULL;
01837     }
01838 DBGIO(fd, (stderr, "==>\tufdOpen(\"%s\",%x,0%o) %s\n", url, flags, (unsigned)mode, fdbg(fd)));
01839     return fd;
01840 }
01841 
01842 static struct FDIO_s ufdio_s = {
01843   ufdRead, ufdWrite, ufdSeek, ufdClose, XfdLink, XfdFree, XfdNew, fdFileno,
01844   ufdOpen, NULL, fdGetFp, NULL, Mkdir, Chdir, Rmdir, Rename, Unlink
01845 };
01846 FDIO_t ufdio = /*@-compmempass@*/ &ufdio_s /*@=compmempass@*/ ;
01847 
01848 /* =============================================================== */
01849 /* Support for GZIP library.
01850  */
01851 #ifdef  HAVE_ZLIB_H
01852 
01853 #include <zlib.h>
01854 
01855 static inline /*@dependent@*/ /*@null@*/ void * gzdFileno(FD_t fd) {
01856     void * rc = NULL;
01857     int i;
01858 
01859     FDSANE(fd);
01860     for (i = fd->nfps; i >= 0; i--) {
01861         FDSTACK_t * fps = &fd->fps[i];
01862         if (fps->io != gzdio)
01863             continue;
01864         rc = fps->fp;
01865         break;
01866     }
01867     
01868     return rc;
01869 }
01870 
01871 static /*@null@*/ FD_t gzdOpen(const char *path, const char *fmode) {
01872     FD_t fd;
01873     gzFile *gzfile;
01874     if ((gzfile = gzopen(path, fmode)) == NULL)
01875         return NULL;
01876     fd = fdNew("open (gzdOpen)");
01877     fdPop(fd); fdPush(fd, gzdio, gzfile, -1);
01878     
01879 DBGIO(fd, (stderr, "==>\tgzdOpen(\"%s\", \"%s\") fd %p %s\n", path, fmode, fd, fdbg(fd)));
01880     return fdLink(fd, "gzdOpen");
01881 }
01882 
01883 static /*@null@*/ FD_t gzdFdopen(void * cookie, const char *fmode) {
01884     FD_t fd = c2f(cookie);
01885     int fdno;
01886     gzFile *gzfile;
01887 
01888     if (fmode == NULL) return NULL;
01889     fdno = fdFileno(fd);
01890     fdSetFdno(fd, -1);          /* XXX skip the fdio close */
01891     if (fdno < 0) return NULL;
01892     gzfile = gzdopen(fdno, fmode);
01893     if (gzfile == NULL) return NULL;
01894 
01895     fdPush(fd, gzdio, gzfile, fdno);            /* Push gzdio onto stack */
01896 
01897     return fdLink(fd, "gzdFdopen");
01898 }
01899 
01900 static int gzdFlush(FD_t fd) {
01901     return gzflush(gzdFileno(fd), Z_SYNC_FLUSH);        /* XXX W2DO? */
01902 }
01903 
01904 /* =============================================================== */
01905 static ssize_t gzdRead(void * cookie, /*@out@*/ char * buf, size_t count) {
01906     FD_t fd = c2f(cookie);
01907     gzFile *gzfile;
01908     ssize_t rc;
01909 
01910     if (fd->bytesRemain == 0) return 0; /* XXX simulate EOF */
01911     gzfile = gzdFileno(fd);
01912     fdstat_enter(fd, FDSTAT_READ);
01913     rc = gzread(gzfile, buf, count);
01914 DBGIO(fd, (stderr, "==>\tgzdRead(%p,%p,%u) rc %lx %s\n", cookie, buf, (unsigned)count, (long)rc, fdbg(fd)));
01915     if (rc < 0) {
01916         int zerror = 0;
01917         fd->errcookie = gzerror(gzfile, &zerror);
01918         if (zerror == Z_ERRNO) {
01919             fd->syserrno = errno;
01920             fd->errcookie = strerror(fd->syserrno);
01921         }
01922     } else if (rc >= 0) {
01923         fdstat_exit(fd, FDSTAT_READ, rc);
01924         if (fd->digest && rc > 0) rpmDigestUpdate(fd->digest, buf, rc);
01925     }
01926     return rc;
01927 }
01928 
01929 static ssize_t gzdWrite(void * cookie, const char * buf, size_t count) {
01930     FD_t fd = c2f(cookie);
01931     gzFile *gzfile;
01932     ssize_t rc;
01933 
01934     if (fd->bytesRemain == 0) return 0; /* XXX simulate EOF */
01935 
01936     if (fd->digest && count > 0) rpmDigestUpdate(fd->digest, buf, count);
01937 
01938     gzfile = gzdFileno(fd);
01939     fdstat_enter(fd, FDSTAT_WRITE);
01940     rc = gzwrite(gzfile, (void *)buf, count);
01941 DBGIO(fd, (stderr, "==>\tgzdWrite(%p,%p,%u) rc %lx %s\n", cookie, buf, (unsigned)count, (long)rc, fdbg(fd)));
01942     if (rc < 0) {
01943         int zerror = 0;
01944         fd->errcookie = gzerror(gzfile, &zerror);
01945         if (zerror == Z_ERRNO) {
01946             fd->syserrno = errno;
01947             fd->errcookie = strerror(fd->syserrno);
01948         }
01949     } else if (rc > 0) {
01950         fdstat_exit(fd, FDSTAT_WRITE, rc);
01951     }
01952     return rc;
01953 }
01954 
01955 /* XXX zlib-1.0.4 has not */
01956 static inline int gzdSeek(void * cookie, _libio_pos_t pos, int whence) {
01957 #ifdef USE_COOKIE_SEEK_POINTER
01958     _IO_off64_t p = *pos;
01959 #else
01960     off_t p = pos;
01961 #endif
01962     int rc;
01963 #if HAVE_GZSEEK
01964     FD_t fd = c2f(cookie);
01965     gzFile *gzfile;
01966 
01967     assert(fd->bytesRemain == -1);      /* XXX FIXME */
01968     gzfile = gzdFileno(fd);
01969     fdstat_enter(fd, FDSTAT_SEEK);
01970     rc = gzseek(gzfile, p, whence);
01971 DBGIO(fd, (stderr, "==>\tgzdSeek(%p,%ld,%d) rc %lx %s\n", cookie, (long)p, whence, (long)rc, fdbg(fd)));
01972     if (rc < 0) {
01973         int zerror = 0;
01974         fd->errcookie = gzerror(gzfile, &zerror);
01975         if (zerror == Z_ERRNO) {
01976             fd->syserrno = errno;
01977             fd->errcookie = strerror(fd->syserrno);
01978         }
01979     } else if (rc >= 0) {
01980         fdstat_exit(fd, FDSTAT_SEEK, rc);
01981     }
01982 #else
01983     rc = -2;
01984 #endif
01985     return rc;
01986 }
01987 
01988 static int gzdClose( /*@only@*/ void * cookie) {
01989     FD_t fd = c2f(cookie);
01990     gzFile *gzfile;
01991     int rc;
01992 
01993     gzfile = gzdFileno(fd);
01994 
01995     if (gzfile == NULL) return -2;
01996     fdstat_enter(fd, FDSTAT_CLOSE);
01997     rc = gzclose(gzfile);
01998 
01999     /* XXX TODO: preserve fd if errors */
02000 
02001     if (fd) {
02002 DBGIO(fd, (stderr, "==>\tgzdClose(%p) zerror %d %s\n", cookie, rc, fdbg(fd)));
02003         if (rc < 0) {
02004             fd->errcookie = gzerror(gzfile, &rc);
02005             if (rc == Z_ERRNO) {
02006                 fd->syserrno = errno;
02007                 fd->errcookie = strerror(fd->syserrno);
02008             }
02009         } else if (rc >= 0) {
02010             fdstat_exit(fd, FDSTAT_CLOSE, rc);
02011         }
02012     }
02013 
02014 DBGIO(fd, (stderr, "==>\tgzdClose(%p) rc %lx %s\n", cookie, (long)rc, fdbg(fd)));
02015 
02016     if (_rpmio_debug || rpmIsDebug()) fdstat_print(fd, "GZDIO", stderr);
02017     if (rc == 0)
02018         fd = fdFree(fd, "open (gzdClose)");
02019     return rc;
02020 }
02021 
02022 static struct FDIO_s gzdio_s = {
02023   gzdRead, gzdWrite, gzdSeek, gzdClose, XfdLink, XfdFree, XfdNew, fdFileno,
02024   NULL, gzdOpen, gzdFileno, gzdFlush,   NULL, NULL, NULL, NULL, NULL
02025 };
02026 FDIO_t gzdio = /*@-compmempass@*/ &gzdio_s /*@=compmempass@*/ ;
02027 
02028 #endif  /* HAVE_ZLIB_H */
02029 
02030 /* =============================================================== */
02031 /* Support for BZIP2 library.
02032  */
02033 #if HAVE_BZLIB_H
02034 
02035 #include <bzlib.h>
02036 
02037 #ifdef HAVE_BZ2_1_0
02038 # define bzopen  BZ2_bzopen
02039 # define bzclose BZ2_bzclose
02040 # define bzdopen BZ2_bzdopen
02041 # define bzerror BZ2_bzerror
02042 # define bzflush BZ2_bzflush
02043 # define bzread  BZ2_bzread
02044 # define bzwrite BZ2_bzwrite
02045 #endif /* HAVE_BZ2_1_0 */
02046 
02047 static inline /*@dependent@*/ /*@null@*/ void * bzdFileno(FD_t fd) {
02048     void * rc = NULL;
02049     int i;
02050 
02051     FDSANE(fd);
02052     for (i = fd->nfps; i >= 0; i--) {
02053         FDSTACK_t * fps = &fd->fps[i];
02054         if (fps->io != bzdio)
02055             continue;
02056         rc = fps->fp;
02057         break;
02058     }
02059     
02060     return rc;
02061 }
02062 
02063 static /*@null@*/ FD_t bzdOpen(const char *path, const char *mode) {
02064     FD_t fd;
02065     BZFILE *bzfile;;
02066     if ((bzfile = bzopen(path, mode)) == NULL)
02067         return NULL;
02068     fd = fdNew("open (bzdOpen)");
02069     fdPop(fd); fdPush(fd, bzdio, bzfile, -1);
02070     return fdLink(fd, "bzdOpen");
02071 }
02072 
02073 static /*@null@*/ FD_t bzdFdopen(void * cookie, const char * fmode) {
02074     FD_t fd = c2f(cookie);
02075     int fdno;
02076     BZFILE *bzfile;
02077 
02078     if (fmode == NULL) return NULL;
02079     fdno = fdFileno(fd);
02080     fdSetFdno(fd, -1);          /* XXX skip the fdio close */
02081     if (fdno < 0) return NULL;
02082     bzfile = bzdopen(fdno, fmode);
02083     if (bzfile == NULL) return NULL;
02084 
02085     fdPush(fd, bzdio, bzfile, fdno);            /* Push bzdio onto stack */
02086 
02087     return fdLink(fd, "bzdFdopen");
02088 }
02089 
02090 static int bzdFlush(FD_t fd) {
02091     return bzflush(bzdFileno(fd));
02092 }
02093 
02094 /* =============================================================== */
02095 static ssize_t bzdRead(void * cookie, /*@out@*/ char * buf, size_t count) {
02096     FD_t fd = c2f(cookie);
02097     BZFILE *bzfile;
02098     ssize_t rc;
02099 
02100     if (fd->bytesRemain == 0) return 0; /* XXX simulate EOF */
02101     bzfile = bzdFileno(fd);
02102     fdstat_enter(fd, FDSTAT_READ);
02103     rc = bzread(bzfile, buf, count);
02104     if (rc == -1) {
02105         int zerror = 0;
02106         fd->errcookie = bzerror(bzfile, &zerror);
02107     } else if (rc >= 0) {
02108         fdstat_exit(fd, FDSTAT_READ, rc);
02109         if (fd->digest && rc > 0) rpmDigestUpdate(fd->digest, buf, rc);
02110     }
02111     return rc;
02112 }
02113 
02114 static ssize_t bzdWrite(void * cookie, const char * buf, size_t count) {
02115     FD_t fd = c2f(cookie);
02116     BZFILE *bzfile;
02117     ssize_t rc;
02118 
02119     if (fd->bytesRemain == 0) return 0; /* XXX simulate EOF */
02120 
02121     if (fd->digest && count > 0) rpmDigestUpdate(fd->digest, buf, count);
02122 
02123     bzfile = bzdFileno(fd);
02124     fdstat_enter(fd, FDSTAT_WRITE);
02125     rc = bzwrite(bzfile, (void *)buf, count);
02126     if (rc == -1) {
02127         int zerror = 0;
02128         fd->errcookie = bzerror(bzfile, &zerror);
02129     } else if (rc > 0) {
02130         fdstat_exit(fd, FDSTAT_WRITE, rc);
02131     }
02132     return rc;
02133 }
02134 
02135 static inline int bzdSeek(void * cookie, /*@unused@*/ _libio_pos_t pos,
02136                         /*@unused@*/ int whence) {
02137     FD_t fd = c2f(cookie);
02138 
02139     BZDONLY(fd);
02140     return -2;
02141 }
02142 
02143 static int bzdClose( /*@only@*/ void * cookie) {
02144     FD_t fd = c2f(cookie);
02145     BZFILE *bzfile;
02146     int rc;
02147 
02148     bzfile = bzdFileno(fd);
02149 
02150     if (bzfile == NULL) return -2;
02151     fdstat_enter(fd, FDSTAT_CLOSE);
02152     bzclose(bzfile);
02153     rc = 0;     /* XXX FIXME */
02154 
02155     /* XXX TODO: preserve fd if errors */
02156 
02157     if (fd) {
02158         if (rc == -1) {
02159             int zerror = 0;
02160             fd->errcookie = bzerror(bzfile, &zerror);
02161         } else if (rc >= 0) {
02162             fdstat_exit(fd, FDSTAT_CLOSE, rc);
02163         }
02164     }
02165 
02166 DBGIO(fd, (stderr, "==>\tbzdClose(%p) rc %lx %s\n", cookie, (long)rc, fdbg(fd)));
02167 
02168     if (_rpmio_debug || rpmIsDebug()) fdstat_print(fd, "BZDIO", stderr);
02169     if (rc == 0)
02170         fd = fdFree(fd, "open (bzdClose)");
02171     return rc;
02172 }
02173 
02174 static struct FDIO_s bzdio_s = {
02175   bzdRead, bzdWrite, bzdSeek, bzdClose, XfdLink, XfdFree, XfdNew, fdFileno,
02176   NULL, bzdOpen, bzdFileno, bzdFlush,   NULL, NULL, NULL, NULL, NULL
02177 };
02178 FDIO_t bzdio = /*@-compmempass@*/ &bzdio_s /*@=compmempass@*/ ;
02179 
02180 #endif  /* HAVE_BZLIB_H */
02181 
02182 /* =============================================================== */
02183 /*@observer@*/ static const char * getFdErrstr (FD_t fd) {
02184     const char *errstr = NULL;
02185 
02186 #ifdef  HAVE_ZLIB_H
02187     if (fdGetIo(fd) == gzdio) {
02188         errstr = fd->errcookie;
02189     } else
02190 #endif  /* HAVE_ZLIB_H */
02191 
02192 #ifdef  HAVE_BZLIB_H
02193     if (fdGetIo(fd) == bzdio) {
02194         errstr = fd->errcookie;
02195     } else
02196 #endif  /* HAVE_BZLIB_H */
02197 
02198     {
02199         errstr = strerror(fd->syserrno);
02200     }
02201 
02202     return errstr;
02203 }
02204 
02205 /* =============================================================== */
02206 
02207 const char *Fstrerror(FD_t fd) {
02208     if (fd == NULL)
02209         return strerror(errno);
02210     FDSANE(fd);
02211     return getFdErrstr(fd);
02212 }
02213 
02214 #define FDIOVEC(_fd, _vec)      \
02215   ((fdGetIo(_fd) && fdGetIo(_fd)->_vec) ? fdGetIo(_fd)->_vec : NULL)
02216 
02217 size_t Fread(void *buf, size_t size, size_t nmemb, FD_t fd) {
02218     fdio_read_function_t *_read;
02219     int rc;
02220 
02221     FDSANE(fd);
02222 #ifdef __LCLINT__
02223     *(char *)buf = '\0';
02224 #endif
02225 DBGIO(fd, (stderr, "==> Fread(%p,%u,%u,%p) %s\n", buf, (unsigned)size, (unsigned)nmemb, fd, fdbg(fd)));
02226 
02227     if (fdGetIo(fd) == fpio) {
02228         /*@+voidabstract@*/
02229         rc = fread(buf, size, nmemb, fdGetFILE(fd));
02230         /*@=voidabstract@*/
02231         return rc;
02232     }
02233 
02234     _read = FDIOVEC(fd, read);
02235 
02236     rc = (_read ? (*_read) (fd, buf, size * nmemb) : -2);
02237     return rc;
02238 }
02239 
02240 size_t Fwrite(const void *buf, size_t size, size_t nmemb, FD_t fd) {
02241     fdio_write_function_t *_write;
02242     int rc;
02243 
02244     FDSANE(fd);
02245 DBGIO(fd, (stderr, "==> Fwrite(%p,%u,%u,%p) %s\n", buf, (unsigned)size, (unsigned)nmemb, fd, fdbg(fd)));
02246 
02247     if (fdGetIo(fd) == fpio) {
02248         /*@+voidabstract@*/
02249         rc = fwrite(buf, size, nmemb, fdGetFILE(fd));
02250         /*@=voidabstract@*/
02251         return rc;
02252     }
02253 
02254     _write = FDIOVEC(fd, write);
02255 
02256     rc = (_write ? _write(fd, buf, size * nmemb) : -2);
02257     return rc;
02258 }
02259 
02260 int Fseek(FD_t fd, _libio_off_t offset, int whence) {
02261     fdio_seek_function_t *_seek;
02262 #ifdef USE_COOKIE_SEEK_POINTER
02263     _IO_off64_t o64 = offset;
02264     _libio_pos_t pos = &o64;
02265 #else
02266     _libio_pos_t pos = offset;
02267 #endif
02268 
02269     long int rc;
02270 
02271     FDSANE(fd);
02272 DBGIO(fd, (stderr, "==> Fseek(%p,%ld,%d) %s\n", fd, (long)offset, whence, fdbg(fd)));
02273 
02274     if (fdGetIo(fd) == fpio) {
02275         FILE *fp;
02276 
02277         /*@+voidabstract@*/
02278         fp = fdGetFILE(fd);
02279         /*@=voidabstract@*/
02280         rc = fseek(fp, offset, whence);
02281         return rc;
02282     }
02283 
02284     _seek = FDIOVEC(fd, seek);
02285 
02286     rc = (_seek ? _seek(fd, pos, whence) : -2);
02287     return rc;
02288 }
02289 
02290 int Fclose(FD_t fd) {
02291     int rc, ec = 0;
02292 
02293     FDSANE(fd);
02294 DBGIO(fd, (stderr, "==> Fclose(%p) %s\n", fd, fdbg(fd)));
02295 
02296     fd = fdLink(fd, "Fclose");
02297     while (fd->nfps >= 0) {
02298         FDSTACK_t * fps = &fd->fps[fd->nfps];
02299         
02300         if (fps->io == fpio) {
02301             FILE *fp;
02302             int fpno;
02303 
02304             /*@+voidabstract@*/
02305             fp = fdGetFILE(fd);
02306             /*@=voidabstract@*/
02307             fpno = fileno(fp);
02308         /* XXX persistent HTTP/1.1 returns the previously opened fp */
02309             if (fd->nfps > 0 && fpno == -1 &&
02310                 fd->fps[fd->nfps-1].io == ufdio &&
02311                 fd->fps[fd->nfps-1].fp == fp &&
02312                 fd->fps[fd->nfps-1].fdno >= 0)
02313             {
02314                 fflush(fp);
02315                 fd->nfps--;
02316                 rc = ufdClose(fd);
02317                 if (fdGetFdno(fd) >= 0)
02318                     break;
02319                 fdSetFp(fd, NULL);
02320                 fd->nfps++;
02321                 rc = fclose(fp);
02322                 fdPop(fd);
02323                 if (noLibio)
02324                     fdSetFp(fd, NULL);
02325             } else {
02326                 rc = fclose(fp);
02327                 if (fpno == -1) {
02328                     fd = fdFree(fd, "fopencookie (Fclose)");
02329                     fdPop(fd);
02330                 }
02331             }
02332         } else {
02333             fdio_close_function_t * _close = FDIOVEC(fd, close);
02334             rc = _close(fd);
02335         }
02336         if (fd->nfps == 0)
02337             break;
02338         if (ec == 0 && rc)
02339             ec = rc;
02340         fdPop(fd);
02341     }
02342     fd = fdFree(fd, "Fclose");
02343     return ec;
02344 }
02345 
02346 /*
02347  * Convert stdio fmode to open(2) mode, filtering out zlib/bzlib flags.
02348  *      returns stdio[0] = '\0' on error.
02349  *
02350  * gzopen:      [0-9] is compession level
02351  * gzopen:      'f' is filtered (Z_FILTERED)
02352  * gzopen:      'h' is Huffman encoding (Z_HUFFMAN_ONLY)
02353  * bzopen:      [1-9] is block size (modulo 100K)
02354  * bzopen:      's' is smallmode
02355  * HACK:        '.' terminates, rest is type of I/O
02356  */
02357 static inline void cvtfmode (const char *m,
02358                                 /*@out@*/ char *stdio, size_t nstdio,
02359                                 /*@out@*/ char *other, size_t nother,
02360                                 /*@out@*/ const char **end, /*@out@*/ int * f)
02361 {
02362     int flags = 0;
02363     char c;
02364 
02365     switch (*m) {
02366     case 'a':
02367         flags |= O_WRONLY | O_CREAT | O_APPEND;
02368         if (--nstdio > 0) *stdio++ = *m;
02369         break;
02370     case 'w':
02371         flags |= O_WRONLY | O_CREAT | O_TRUNC;
02372         if (--nstdio > 0) *stdio++ = *m;
02373         break;
02374     case 'r':
02375         flags |= O_RDONLY;
02376         if (--nstdio > 0) *stdio++ = *m;
02377         break;
02378     default:
02379         *stdio = '\0';
02380         return;
02381         /*@notreached@*/ break;
02382     }
02383     m++;
02384 
02385     while ((c = *m++) != '\0') {
02386         switch (c) {
02387         case '.':
02388             break;
02389         case '+':
02390             flags &= ~(O_RDONLY|O_WRONLY);
02391             flags |= O_RDWR;
02392             if (--nstdio > 0) *stdio++ = c;
02393             continue;
02394         case 'b':
02395             if (--nstdio > 0) *stdio++ = c;
02396             continue;
02397         case 'x':
02398             flags |= O_EXCL;
02399             if (--nstdio > 0) *stdio++ = c;
02400             continue;
02401         default:
02402             if (--nother > 0) *other++ = c;
02403             continue;
02404         }
02405         break;
02406     }
02407 
02408     *stdio = *other = '\0';
02409     if (end)
02410         *end = (*m ? m : NULL);
02411     if (f)
02412         *f = flags;
02413 }
02414 
02415 #if _USE_LIBIO
02416 #if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ == 0
02417 /* XXX retrofit glibc-2.1.x typedef on glibc-2.0.x systems */
02418 typedef _IO_cookie_io_functions_t cookie_io_functions_t;
02419 #endif
02420 #endif
02421 
02422 FD_t Fdopen(FD_t ofd, const char *fmode)
02423 {
02424     char stdio[20], other[20], zstdio[20];
02425     const char *end = NULL;
02426     FDIO_t iof = NULL;
02427     FD_t fd = ofd;
02428 
02429 if (_rpmio_debug)
02430 fprintf(stderr, "*** Fdopen(%p,%s) %s\n", fd, fmode, fdbg(fd));
02431     FDSANE(fd);
02432 
02433     if (fmode == NULL)
02434         return NULL;
02435 
02436     cvtfmode(fmode, stdio, sizeof(stdio), other, sizeof(other), &end, NULL);
02437     if (stdio[0] == '\0')
02438         return NULL;
02439     zstdio[0] = '\0';
02440     strncat(zstdio, stdio, sizeof(zstdio) - strlen(zstdio));
02441     strncat(zstdio, other, sizeof(zstdio) - strlen(zstdio));
02442 
02443     if (end == NULL && other[0] == '\0')
02444         return fd;
02445 
02446     if (end && *end) {
02447         if (!strcmp(end, "fdio")) {
02448             iof = fdio;
02449         } else if (!strcmp(end, "gzdio")) {
02450             iof = gzdio;
02451             fd = gzdFdopen(fd, zstdio);
02452 #if HAVE_BZLIB_H
02453         } else if (!strcmp(end, "bzdio")) {
02454             iof = bzdio;
02455             fd = bzdFdopen(fd, zstdio);
02456 #endif
02457         } else if (!strcmp(end, "ufdio")) {
02458             iof = ufdio;
02459         } else if (!strcmp(end, "fadio")) {
02460             iof = fadio;
02461         } else if (!strcmp(end, "fpio")) {
02462             iof = fpio;
02463             if (noLibio) {
02464                 int fdno = Fileno(fd);
02465                 FILE * fp = fdopen(fdno, stdio);
02466 if (_rpmio_debug)
02467 fprintf(stderr, "*** Fdopen fpio fp %p\n", fp);
02468                 if (fp == NULL)
02469                     return NULL;
02470                 /* XXX gzdio/bzdio use fp for private data */
02471                 if (fdGetFp(fd) == NULL)
02472                     fdSetFp(fd, fp);
02473                 fdPush(fd, fpio, fp, fdno);     /* Push fpio onto stack */
02474             }
02475         }
02476     } else if (other[0]) {
02477         for (end = other; *end && strchr("0123456789fh", *end); end++)
02478             ;
02479         if (*end == '\0') {
02480             iof = gzdio;
02481             fd = gzdFdopen(fd, zstdio);
02482         }
02483     }
02484     if (iof == NULL)
02485         return fd;
02486 
02487     if (!noLibio) {
02488         FILE * fp = NULL;
02489 
02490 #if _USE_LIBIO
02491         {   cookie_io_functions_t ciof;
02492             ciof.read = iof->read;
02493             ciof.write = iof->write;
02494             ciof.seek = iof->seek;
02495             ciof.close = iof->close;
02496             fp = fopencookie(fd, stdio, ciof);
02497 DBGIO(fd, (stderr, "==> fopencookie(%p,\"%s\",*%p) returns fp %p\n", fd, stdio, iof, fp));
02498         }
02499 #endif
02500 
02501         if (fp) {
02502             /* XXX gzdio/bzdio use fp for private data */
02503             if (fdGetFp(fd) == NULL)
02504                 fdSetFp(fd, fp);
02505             fdPush(fd, fpio, fp, fileno(fp));   /* Push fpio onto stack */
02506             fd = fdLink(fd, "fopencookie");
02507         }
02508     }
02509 
02510 DBGIO(fd, (stderr, "==> Fdopen(%p,\"%s\") returns fd %p %s\n", ofd, fmode, fd, fdbg(fd)));
02511     return fd;
02512 }
02513 
02514 FD_t Fopen(const char *path, const char *fmode)
02515 {
02516     char stdio[20], other[20];
02517     const char *end = NULL;
02518     mode_t perms = 0666;
02519     int flags;
02520     FD_t fd;
02521 
02522     if (path == NULL || fmode == NULL)
02523         return NULL;
02524 
02525     cvtfmode(fmode, stdio, sizeof(stdio), other, sizeof(other), &end, &flags);
02526     if (stdio[0] == '\0')
02527         return NULL;
02528 
02529     if (end == NULL || !strcmp(end, "fdio")) {
02530 if (_rpmio_debug)
02531 fprintf(stderr, "*** Fopen fdio path %s fmode %s\n", path, fmode);
02532         fd = fdOpen(path, flags, perms);
02533         if (fdFileno(fd) < 0) {
02534             fdClose(fd);
02535             return NULL;
02536         }
02537     } else if (!strcmp(end, "fadio")) {
02538 if (_rpmio_debug)
02539 fprintf(stderr, "*** Fopen fadio path %s fmode %s\n", path, fmode);
02540         fd = fadio->_open(path, flags, perms);
02541         if (fdFileno(fd) < 0) {
02542             fdClose(fd);
02543             return NULL;
02544         }
02545     } else {
02546         FILE *fp;
02547         int fdno;
02548         int isHTTP = 0;
02549 
02550         /* XXX gzdio and bzdio here too */
02551 
02552         switch (urlIsURL(path)) {
02553         case URL_IS_HTTP:
02554             isHTTP = 1;
02555             /*@fallthrough@*/
02556         case URL_IS_PATH:
02557         case URL_IS_DASH:
02558         case URL_IS_FTP:
02559         case URL_IS_UNKNOWN:
02560 if (_rpmio_debug)
02561 fprintf(stderr, "*** Fopen ufdio path %s fmode %s\n", path, fmode);
02562             fd = ufdOpen(path, flags, perms);
02563             if (fd == NULL || fdFileno(fd) < 0)
02564                 return fd;
02565             break;
02566         default:
02567 if (_rpmio_debug)
02568 fprintf(stderr, "*** Fopen WTFO path %s fmode %s\n", path, fmode);
02569             return NULL;
02570             /*@notreached@*/ break;
02571         }
02572 
02573         /* XXX persistent HTTP/1.1 returns the previously opened fp */
02574         if (isHTTP && ((fp = fdGetFp(fd)) != NULL) && ((fdno = fdGetFdno(fd)) >= 0)) {
02575             fdPush(fd, fpio, fp, fileno(fp));   /* Push fpio onto stack */
02576             return fd;
02577         }
02578     }
02579 
02580     fd = Fdopen(fd, fmode);
02581     return fd;
02582 }
02583 
02584 int Fflush(FD_t fd)
02585 {
02586     if (fd == NULL) return -1;
02587     if (fdGetIo(fd) == fpio)
02588         /*@+voidabstract@*/
02589         return fflush(fdGetFILE(fd));
02590         /*@=voidabstract@*/
02591     if (fdGetIo(fd) == gzdio)
02592         return gzdFlush(fdGetFp(fd));
02593 #if HAVE_BZLIB_H
02594     if (fdGetIo(fd) == bzdio)
02595         return bzdFlush(fdGetFp(fd));
02596 #endif
02597     return 0;
02598 }
02599 
02600 int Ferror(FD_t fd) {
02601     int i, rc = 0;
02602 
02603     if (fd == NULL) return -1;
02604     for (i = fd->nfps; rc == 0 && i >= 0; i--) {
02605         FDSTACK_t * fps = &fd->fps[i];
02606         int ec;
02607         
02608         if (fps->io == fpio) {
02609             /*@+voidabstract@*/
02610             ec = ferror(fdGetFILE(fd));
02611             /*@=voidabstract@*/
02612         } else if (fps->io == gzdio) {
02613             ec = (fd->syserrno  || fd->errcookie != NULL) ? -1 : 0;
02614 #if HAVE_BZLIB_H
02615         } else if (fps->io == bzdio) {
02616             ec = (fd->syserrno  || fd->errcookie != NULL) ? -1 : 0;
02617 #endif
02618         } else {
02619         /* XXX need to check ufdio/gzdio/bzdio/fdio errors correctly. */
02620             ec = (fdFileno(fd) < 0 ? -1 : 0);
02621         }
02622 
02623         if (rc == 0 && ec)
02624             rc = ec;
02625     }
02626 DBGIO(fd, (stderr, "==> Ferror(%p) rc %d %s\n", fd, rc, fdbg(fd)));
02627     return rc;
02628 }
02629 
02630 int Fileno(FD_t fd) {
02631     int i, rc = -1;
02632 
02633     for (i = fd->nfps ; rc == -1 && i >= 0; i--) {
02634         rc = fd->fps[i].fdno;
02635     }
02636 DBGIO(fd, (stderr, "==> Fileno(%p) rc %d %s\n", fd, rc, fdbg(fd)));
02637     return rc;
02638 }
02639 
02640 /* XXX this is naive */
02641 int Fcntl(FD_t fd, int op, void *lip) {
02642     return fcntl(Fileno(fd), op, lip);
02643 }
02644 
02645 /* =============================================================== */
02646 /* Helper routines that may be generally useful.
02647  */
02648 
02649 /* XXX falloc.c: analogues to pread(3)/pwrite(3). */
02650 ssize_t Pread(FD_t fd, void * buf, size_t count, _libio_off_t offset) {
02651     if (Fseek(fd, offset, SEEK_SET) < 0)
02652         return -1;
02653     return Fread(buf, sizeof(char), count, fd);
02654 }
02655 
02656 ssize_t Pwrite(FD_t fd, const void * buf, size_t count, _libio_off_t offset) {
02657     if (Fseek(fd, offset, SEEK_SET) < 0)
02658         return -1;
02659     return Fwrite(buf, sizeof(char), count, fd);
02660 }
02661 
02662 static struct FDIO_s fpio_s = {
02663   ufdRead, ufdWrite, fdSeek, ufdClose, XfdLink, XfdFree, XfdNew, fdFileno,
02664   ufdOpen, NULL, fdGetFp, NULL, Mkdir, Chdir, Rmdir, Rename, Unlink
02665 };
02666 FDIO_t fpio = /*@-compmempass@*/ &fpio_s /*@=compmempass@*/ ;

Generated at Sun Apr 8 18:43:02 2001 for rpm by doxygen1.2.3 written by Dimitri van Heesch, © 1997-2000