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

Generated at Thu Apr 19 15:29:45 2001 for rpm by doxygen1.2.6-20010408 written by Dimitri van Heesch, © 1997-2001