Lose unused leftover definitions
[platform/upstream/rpm.git] / rpmio / rpmio.c
1 /** \ingroup rpmio
2  * \file rpmio/rpmio.c
3  */
4
5 #include "system.h"
6 #include <stdarg.h>
7
8 #if HAVE_LIBIO_H && defined(_G_IO_IO_FILE_VERSION)
9 #define _USE_LIBIO      1
10 #endif
11
12 #include <rpm/rpmlog.h>
13 #include <rpm/rpmmacro.h>
14 #include <rpm/rpmfileutil.h>
15 #include <rpm/rpmsw.h>
16 #include <rpm/rpmurl.h>
17
18 #include "rpmio/rpmio_internal.h"
19
20 #include "debug.h"
21
22 typedef struct _FDSTACK_s {
23     FDIO_t              io;
24     void *              fp;
25     int                 fdno;
26 } FDSTACK_t;
27
28 /** \ingroup rpmio
29  * Cumulative statistics for a descriptor.
30  */
31 typedef struct {
32     struct rpmop_s      ops[FDSTAT_MAX];        /*!< Cumulative statistics. */
33 } * FDSTAT_t;
34
35 /** \ingroup rpmio
36  * The FD_t File Handle data structure.
37  */
38 struct _FD_s {
39     int         nrefs;
40     int         flags;
41 #define RPMIO_DEBUG_IO          0x40000000
42 #define RPMIO_DEBUG_REFS        0x20000000
43     int         magic;
44 #define FDMAGIC                 0x04463138
45     int         nfps;
46     FDSTACK_t   fps[8];
47     int         urlType;        /* ufdio: */
48
49     ssize_t     bytesRemain;    /* ufdio: */
50
51     int         syserrno;       /* last system errno encountered */
52     const void *errcookie;      /* gzdio/bzdio/ufdio/xzdio: */
53
54     FDSTAT_t    stats;          /* I/O statistics */
55
56     rpmDigestBundle digests;
57 };
58
59 #define FDSANE(fd)      assert(fd && fd->magic == FDMAGIC)
60
61 #define DBG(_f, _m, _x) \
62     \
63     if ((_rpmio_debug | ((_f) ? ((FD_t)(_f))->flags : 0)) & (_m)) fprintf _x \
64
65 #define DBGIO(_f, _x)   DBG((_f), RPMIO_DEBUG_IO, _x)
66 #define DBGREFS(_f, _x) DBG((_f), RPMIO_DEBUG_REFS, _x)
67
68 static FDIO_t fdGetIo(FD_t fd)
69 {
70     FDSANE(fd);
71     return fd->fps[fd->nfps].io;
72 }
73
74 static void fdSetIo(FD_t fd, FDIO_t io)
75 {
76     FDSANE(fd);
77     fd->fps[fd->nfps].io = io;
78 }
79
80 static void * fdGetFp(FD_t fd)
81 {
82     FDSANE(fd);
83     return fd->fps[fd->nfps].fp;
84 }
85
86 static void fdSetFp(FD_t fd, void * fp)
87 {
88     FDSANE(fd);
89     fd->fps[fd->nfps].fp = fp;
90 }
91
92 static void fdSetFdno(FD_t fd, int fdno)
93 {
94     FDSANE(fd);
95     fd->fps[fd->nfps].fdno = fdno;
96 }
97
98 static void fdPush(FD_t fd, FDIO_t io, void * fp, int fdno)
99 {
100     FDSANE(fd);
101     if (fd->nfps >= (sizeof(fd->fps)/sizeof(fd->fps[0]) - 1))
102         return;
103     fd->nfps++;
104     fdSetIo(fd, io);
105     fdSetFp(fd, fp);
106     fdSetFdno(fd, fdno);
107 }
108
109 static void fdPop(FD_t fd)
110 {
111     FDSANE(fd);
112     if (fd->nfps < 0) return;
113     fdSetIo(fd, NULL);
114     fdSetFp(fd, NULL);
115     fdSetFdno(fd, -1);
116     fd->nfps--;
117 }
118
119 static FD_t c2f(void * cookie)
120 {
121     FD_t fd = (FD_t) cookie;
122     FDSANE(fd);
123     return fd;
124 }
125
126 void fdSetBundle(FD_t fd, rpmDigestBundle bundle)
127 {
128     FDSANE(fd);
129     fd->digests = bundle;
130 }
131
132 rpmDigestBundle fdGetBundle(FD_t fd)
133 {
134     FDSANE(fd);
135     return fd->digests;
136 }
137
138 #define FDNREFS(fd)     (fd ? ((FD_t)fd)->nrefs : -9)
139
140 #define FDONLY(fd)      assert(fdGetIo(fd) == fdio)
141 #define GZDONLY(fd)     assert(fdGetIo(fd) == gzdio)
142 #define BZDONLY(fd)     assert(fdGetIo(fd) == bzdio)
143 #define LZDONLY(fd)     assert(fdGetIo(fd) == xzdio || fdGetIo(fd) == lzdio)
144
145 #define UFDONLY(fd)     /* assert(fdGetIo(fd) == ufdio) */
146
147 /**
148  */
149 #if _USE_LIBIO
150 static int noLibio = 0;
151 #else
152 static int noLibio = 1;
153 #endif
154
155 /** \ingroup rpmio
156  * \name RPMIO Vectors.
157  */
158 typedef ssize_t (*fdio_read_function_t) (void *cookie, char *buf, size_t nbytes);
159 typedef ssize_t (*fdio_write_function_t) (void *cookie, const char *buf, size_t nbytes);
160 typedef int (*fdio_seek_function_t) (void *cookie, _libio_pos_t pos, int whence);
161 typedef int (*fdio_close_function_t) (void *cookie);
162 typedef FD_t (*fdio_ref_function_t) ( void * cookie, const char * msg);
163 typedef FD_t (*fdio_deref_function_t) ( FD_t fd, const char * msg);
164 typedef FD_t (*fdio_new_function_t) (const char * msg);
165 typedef int (*fdio_fileno_function_t) (void * cookie);
166 typedef FD_t (*fdio_open_function_t) (const char * path, int flags, mode_t mode);
167 typedef FD_t (*fdio_fopen_function_t) (const char * path, const char * fmode);
168 typedef void * (*fdio_ffileno_function_t) (FD_t fd);
169 typedef int (*fdio_fflush_function_t) (FD_t fd);
170
171 /** \ingroup rpmio
172  */
173 struct FDIO_s {
174   fdio_read_function_t          read;
175   fdio_write_function_t         write;
176   fdio_seek_function_t          seek;
177   fdio_close_function_t         close;
178
179   fdio_ref_function_t           _fdref;
180   fdio_deref_function_t         _fdderef;
181   fdio_new_function_t           _fdnew;
182   fdio_fileno_function_t        _fileno;
183
184   fdio_open_function_t          _open;
185   fdio_fopen_function_t         _fopen;
186   fdio_ffileno_function_t       _ffileno;
187   fdio_fflush_function_t        _fflush;
188 };
189
190 /* forward refs */
191 static const FDIO_t fdio;
192 static const FDIO_t fpio;
193 static const FDIO_t ufdio;
194 static const FDIO_t gzdio;
195 static const FDIO_t bzdio;
196 static const FDIO_t xzdio;
197 static const FDIO_t lzdio;
198
199 /** \ingroup rpmio
200  * Update digest(s) attached to fd.
201  */
202 static void fdUpdateDigests(FD_t fd, const unsigned char * buf, size_t buflen);
203
204 /**
205  */
206 int _rpmio_debug = 0;
207
208 /* =============================================================== */
209
210 static const char * fdbg(FD_t fd)
211 {
212     static char buf[BUFSIZ];
213     char *be = buf;
214     int i;
215
216     buf[0] = '\0';
217     if (fd == NULL)
218         return buf;
219
220     if (fd->bytesRemain != -1) {
221         sprintf(be, " clen %d", (int)fd->bytesRemain);
222         be += strlen(be);
223      }
224     *be++ = '\t';
225     for (i = fd->nfps; i >= 0; i--) {
226         FDSTACK_t * fps = &fd->fps[i];
227         if (i != fd->nfps)
228             *be++ = ' ';
229         *be++ = '|';
230         *be++ = ' ';
231         if (fps->io == fdio) {
232             sprintf(be, "FD %d fp %p", fps->fdno, fps->fp);
233         } else if (fps->io == ufdio) {
234             sprintf(be, "UFD %d fp %p", fps->fdno, fps->fp);
235 #if HAVE_ZLIB_H
236         } else if (fps->io == gzdio) {
237             sprintf(be, "GZD %p fdno %d", fps->fp, fps->fdno);
238 #endif
239 #if HAVE_BZLIB_H
240         } else if (fps->io == bzdio) {
241             sprintf(be, "BZD %p fdno %d", fps->fp, fps->fdno);
242 #endif
243 #if HAVE_LZMA_H
244         } else if (fps->io == xzdio) {
245             sprintf(be, "XZD %p fdno %d", fps->fp, fps->fdno);
246         } else if (fps->io == lzdio) {
247             sprintf(be, "LZD %p fdno %d", fps->fp, fps->fdno);
248 #endif
249         } else if (fps->io == fpio) {
250             sprintf(be, "%s %p(%d) fdno %d",
251                 (fps->fdno < 0 ? "LIBIO" : "FP"),
252                 fps->fp, fileno(((FILE *)fps->fp)), fps->fdno);
253         } else {
254             sprintf(be, "??? io %p fp %p fdno %d ???",
255                 fps->io, fps->fp, fps->fdno);
256         }
257         be += strlen(be);
258         *be = '\0';
259     }
260     return buf;
261 }
262
263 static void fdstat_enter(FD_t fd, int opx)
264 {
265     if (fd == NULL) return;
266     if (fd->stats != NULL)
267         (void) rpmswEnter(fdOp(fd, opx), (ssize_t) 0);
268 }
269
270 static void fdstat_exit(FD_t fd, int opx, ssize_t rc)
271 {
272     if (fd == NULL) return;
273     if (rc == -1)
274         fd->syserrno = errno;
275     else if (rc > 0 && fd->bytesRemain > 0)
276         switch (opx) {
277         case FDSTAT_READ:
278         case FDSTAT_WRITE:
279         fd->bytesRemain -= rc;
280             break;
281         default:
282             break;
283         }
284     if (fd->stats != NULL)
285         (void) rpmswExit(fdOp(fd, opx), rc);
286 }
287
288 static void fdstat_print(FD_t fd, const char * msg, FILE * fp)
289 {
290     static const int usec_scale = (1000*1000);
291     int opx;
292
293     if (fd == NULL || fd->stats == NULL) return;
294     for (opx = 0; opx < 4; opx++) {
295         rpmop op = &fd->stats->ops[opx];
296         if (op->count <= 0) continue;
297         switch (opx) {
298         case FDSTAT_READ:
299             if (msg) fprintf(fp, "%s:", msg);
300             fprintf(fp, "%8d reads, %8ld total bytes in %d.%06d secs\n",
301                 op->count, (long)op->bytes,
302                 (int)(op->usecs/usec_scale), (int)(op->usecs%usec_scale));
303             break;
304         case FDSTAT_WRITE:
305             if (msg) fprintf(fp, "%s:", msg);
306             fprintf(fp, "%8d writes, %8ld total bytes in %d.%06d secs\n",
307                 op->count, (long)op->bytes,
308                 (int)(op->usecs/usec_scale), (int)(op->usecs%usec_scale));
309             break;
310         case FDSTAT_SEEK:
311             break;
312         case FDSTAT_CLOSE:
313             break;
314         }
315     }
316 }
317
318 /* =============================================================== */
319 off_t fdSize(FD_t fd)
320 {
321     struct stat sb;
322     off_t rc = -1; 
323
324 #ifdef  NOISY
325 DBGIO(0, (stderr, "==>\tfdSize(%p) rc %ld\n", fd, (long)rc));
326 #endif
327     FDSANE(fd);
328     switch (fd->urlType) {
329     case URL_IS_PATH:
330     case URL_IS_UNKNOWN:
331         if (fstat(Fileno(fd), &sb) == 0)
332             rc = sb.st_size;
333     case URL_IS_HTTPS:
334     case URL_IS_HTTP:
335     case URL_IS_HKP:
336     case URL_IS_FTP:
337     case URL_IS_DASH:
338         break;
339     }
340     return rc;
341 }
342
343 FD_t fdDup(int fdno)
344 {
345     FD_t fd;
346     int nfdno;
347
348     if ((nfdno = dup(fdno)) < 0)
349         return NULL;
350     fd = fdNew(RPMDBG_M("open (fdDup)"));
351     fdSetFdno(fd, nfdno);
352 DBGIO(fd, (stderr, "==> fdDup(%d) fd %p %s\n", fdno, (fd ? fd : NULL), fdbg(fd)));
353     return fd;
354 }
355
356 static int fdSeekNot(void * cookie, _libio_pos_t pos,  int whence)
357 {
358     FD_t fd = c2f(cookie);
359     FDSANE(fd);         /* XXX keep gcc quiet */
360     return -2;
361 }
362
363 /** \ingroup rpmio
364  */
365 static int fdFileno(void * cookie)
366 {
367     FD_t fd;
368     if (cookie == NULL) return -2;
369     fd = c2f(cookie);
370     return fd->fps[0].fdno;
371 }
372
373 FILE * fdGetFILE(FD_t fd)
374 {
375     return ((FILE *)fdGetFp(fd));
376 }
377
378 /* =============================================================== */
379
380 FD_t fdLink(void * cookie, const char * msg)
381 {
382     FD_t fd;
383 if (cookie == NULL)
384 DBGREFS(0, (stderr, "--> fd  %p ++ %d %s\n", cookie, FDNREFS(cookie)+1, msg));
385     fd = c2f(cookie);
386     if (fd) {
387         fd->nrefs++;
388 DBGREFS(fd, (stderr, "--> fd  %p ++ %d %s  %s\n", fd, fd->nrefs, msg, fdbg(fd)));
389     }
390     return fd;
391 }
392
393 /**
394  */
395 FD_t fdFree( FD_t fd, const char *msg)
396 {
397 if (fd == NULL)
398 DBGREFS(0, (stderr, "--> fd  %p -- %d %s\n", fd, FDNREFS(fd), msg));
399     FDSANE(fd);
400     if (fd) {
401 DBGREFS(fd, (stderr, "--> fd  %p -- %d %s %s\n", fd, fd->nrefs, msg, fdbg(fd)));
402         if (--fd->nrefs > 0)
403             return fd;
404         fd->stats = _free(fd->stats);
405         if (fd->digests) {
406             fd->digests = rpmDigestBundleFree(fd->digests);
407         }
408         free(fd);
409     }
410     return NULL;
411 }
412
413 /**
414  */
415 FD_t fdNew(const char * msg)
416 {
417     FD_t fd = xcalloc(1, sizeof(*fd));
418     if (fd == NULL) /* XXX xmalloc never returns NULL */
419         return NULL;
420     fd->nrefs = 0;
421     fd->flags = 0;
422     fd->magic = FDMAGIC;
423     fd->urlType = URL_IS_UNKNOWN;
424
425     fd->nfps = 0;
426     memset(fd->fps, 0, sizeof(fd->fps));
427
428     fd->fps[0].io = fdio;
429     fd->fps[0].fp = NULL;
430     fd->fps[0].fdno = -1;
431
432     fd->bytesRemain = -1;
433     fd->syserrno = 0;
434     fd->errcookie = NULL;
435     fd->stats = xcalloc(1, sizeof(*fd->stats));
436     fd->digests = NULL;
437
438     return fdLink(fd, msg);
439 }
440
441 /**
442  */
443 static ssize_t fdRead(void * cookie, char * buf, size_t count)
444 {
445     FD_t fd = c2f(cookie);
446     ssize_t rc;
447
448     if (fd->bytesRemain == 0) return 0; /* XXX simulate EOF */
449
450     fdstat_enter(fd, FDSTAT_READ);
451     rc = read(fdFileno(fd), buf, (count > fd->bytesRemain ? fd->bytesRemain : count));
452     fdstat_exit(fd, FDSTAT_READ, rc);
453
454     if (fd->digests && rc > 0) fdUpdateDigests(fd, (void *)buf, rc);
455
456 DBGIO(fd, (stderr, "==>\tfdRead(%p,%p,%ld) rc %ld %s\n", cookie, buf, (long)count, (long)rc, fdbg(fd)));
457
458     return rc;
459 }
460
461 /**
462  */
463 static ssize_t fdWrite(void * cookie, const char * buf, size_t count)
464 {
465     FD_t fd = c2f(cookie);
466     int fdno = fdFileno(fd);
467     ssize_t rc;
468
469     if (fd->bytesRemain == 0) return 0; /* XXX simulate EOF */
470
471     if (fd->digests && count > 0) fdUpdateDigests(fd, (void *)buf, count);
472
473     if (count == 0) return 0;
474
475     fdstat_enter(fd, FDSTAT_WRITE);
476     rc = write(fdno, buf, (count > fd->bytesRemain ? fd->bytesRemain : count));
477     fdstat_exit(fd, FDSTAT_WRITE, rc);
478
479 DBGIO(fd, (stderr, "==>\tfdWrite(%p,%p,%ld) rc %ld %s\n", cookie, buf, (long)count, (long)rc, fdbg(fd)));
480
481     return rc;
482 }
483
484 static int fdSeek(void * cookie, _libio_pos_t pos, int whence)
485 {
486 #ifdef USE_COOKIE_SEEK_POINTER
487     _IO_off64_t p = *pos;
488 #else
489     off_t p = pos;
490 #endif
491     FD_t fd = c2f(cookie);
492     off_t rc;
493
494     assert(fd->bytesRemain == -1);      /* XXX FIXME fadio only for now */
495     fdstat_enter(fd, FDSTAT_SEEK);
496     rc = lseek(fdFileno(fd), p, whence);
497     fdstat_exit(fd, FDSTAT_SEEK, rc);
498
499 DBGIO(fd, (stderr, "==>\tfdSeek(%p,%ld,%d) rc %lx %s\n", cookie, (long)p, whence, (unsigned long)rc, fdbg(fd)));
500
501     return rc;
502 }
503
504 /**
505  */
506 static int fdClose( void * cookie)
507 {
508     FD_t fd;
509     int fdno;
510     int rc;
511
512     if (cookie == NULL) return -2;
513     fd = c2f(cookie);
514     fdno = fdFileno(fd);
515
516     fdSetFdno(fd, -1);
517
518     fdstat_enter(fd, FDSTAT_CLOSE);
519     rc = ((fdno >= 0) ? close(fdno) : -2);
520     fdstat_exit(fd, FDSTAT_CLOSE, rc);
521
522 DBGIO(fd, (stderr, "==>\tfdClose(%p) rc %lx %s\n", (fd ? fd : NULL), (unsigned long)rc, fdbg(fd)));
523
524     fd = fdFree(fd, "open (fdClose)");
525     return rc;
526 }
527
528 /**
529  */
530 static FD_t fdOpen(const char *path, int flags, mode_t mode)
531 {
532     FD_t fd;
533     int fdno;
534
535     fdno = open(path, flags, mode);
536     if (fdno < 0) return NULL;
537     if (fcntl(fdno, F_SETFD, FD_CLOEXEC)) {
538         (void) close(fdno);
539         return NULL;
540     }
541     fd = fdNew(RPMDBG_M("open (fdOpen)"));
542     fdSetFdno(fd, fdno);
543     fd->flags = flags;
544 DBGIO(fd, (stderr, "==>\tfdOpen(\"%s\",%x,0%o) %s\n", path, (unsigned)flags, (unsigned)mode, fdbg(fd)));
545     return fd;
546 }
547
548 static const struct FDIO_s fdio_s = {
549   fdRead, fdWrite, fdSeek, fdClose, fdLink, fdFree, fdNew, fdFileno,
550   fdOpen, NULL, fdGetFp, NULL
551 };
552 static const FDIO_t fdio = &fdio_s ;
553
554 int fdWritable(FD_t fd, int secs)
555 {
556     int fdno;
557     int rc;
558 #if HAVE_POLL_H
559     int msecs = (secs >= 0 ? (1000 * secs) : -1);
560     struct pollfd wrfds;
561 #else
562     struct timeval timeout, *tvp = (secs >= 0 ? &timeout : NULL);
563     fd_set wrfds;
564     FD_ZERO(&wrfds);
565 #endif
566         
567     if ((fdno = fdFileno(fd)) < 0)
568         return -1;      /* XXX W2DO? */
569         
570     do {
571 #if HAVE_POLL_H
572         wrfds.fd = fdno;
573         wrfds.events = POLLOUT;
574         wrfds.revents = 0;
575         rc = poll(&wrfds, 1, msecs);
576 #else
577         if (tvp) {
578             tvp->tv_sec = secs;
579             tvp->tv_usec = 0;
580         }
581         FD_SET(fdno, &wrfds);
582         rc = select(fdno + 1, NULL, &wrfds, NULL, tvp);
583 #endif
584
585 if (_rpmio_debug && !(rc == 1 && errno == 0))
586 fprintf(stderr, "*** fdWritable fdno %d rc %d %s\n", fdno, rc, strerror(errno));
587         if (rc < 0) {
588             switch (errno) {
589             case EINTR:
590                 continue;
591                 break;
592             default:
593                 return rc;
594                 break;
595             }
596         }
597         return rc;
598     } while (1);
599 }
600
601 int fdReadable(FD_t fd, int secs)
602 {
603     int fdno;
604     int rc;
605 #if HAVE_POLL_H
606     int msecs = (secs >= 0 ? (1000 * secs) : -1);
607     struct pollfd rdfds;
608 #else
609     struct timeval timeout, *tvp = (secs >= 0 ? &timeout : NULL);
610     fd_set rdfds;
611     FD_ZERO(&rdfds);
612 #endif
613
614     if ((fdno = fdFileno(fd)) < 0)
615         return -1;      /* XXX W2DO? */
616         
617     do {
618 #if HAVE_POLL_H
619         rdfds.fd = fdno;
620         rdfds.events = POLLIN;
621         rdfds.revents = 0;
622         rc = poll(&rdfds, 1, msecs);
623 #else
624         if (tvp) {
625             tvp->tv_sec = secs;
626             tvp->tv_usec = 0;
627         }
628         FD_SET(fdno, &rdfds);
629         rc = select(fdno + 1, &rdfds, NULL, NULL, tvp);
630 #endif
631
632         if (rc < 0) {
633             switch (errno) {
634             case EINTR:
635                 continue;
636                 break;
637             default:
638                 return rc;
639                 break;
640             }
641         }
642         return rc;
643     } while (1);
644 }
645
646 int ufdCopy(FD_t sfd, FD_t tfd)
647 {
648     char buf[BUFSIZ];
649     int itemsRead;
650     int itemsCopied = 0;
651     int rc = 0;
652
653     while (1) {
654         rc = Fread(buf, sizeof(buf[0]), sizeof(buf), sfd);
655         if (rc < 0)
656             break;
657         else if (rc == 0) {
658             rc = itemsCopied;
659             break;
660         }
661         itemsRead = rc;
662         rc = Fwrite(buf, sizeof(buf[0]), itemsRead, tfd);
663         if (rc < 0)
664             break;
665         if (rc != itemsRead) {
666             rc = -1;
667             break;
668         }
669
670         itemsCopied += itemsRead;
671     }
672
673     DBGIO(sfd, (stderr, "++ copied %d bytes\n", itemsCopied));
674
675     return rc;
676 }
677
678 /*
679  * Deal with remote url's by fetching them with a helper application
680  * and treat as local file afterwards.
681  * TODO:
682  * - better error checking + reporting 
683  * - curl & friends don't know about hkp://, transform to http?
684  */
685
686 static FD_t urlOpen(const char * url, int flags, mode_t mode)
687 {
688     FD_t fd;
689     char *dest = NULL;
690     int rc = 1; /* assume failure */
691
692     fd = rpmMkTempFile(NULL, &dest);
693     if (fd == NULL) {
694         return NULL;
695     }
696     Fclose(fd);
697
698     rc = urlGetFile(url, dest);
699     if (rc == 0) {
700         fd = fdOpen(dest, flags, mode);
701         unlink(dest);
702     } else {
703         fd = NULL;
704     }
705     dest = _free(dest);
706
707     return fd;
708
709 }
710 static FD_t ufdOpen(const char * url, int flags, mode_t mode)
711 {
712     FD_t fd = NULL;
713     const char * path;
714     urltype urlType = urlPath(url, &path);
715
716 if (_rpmio_debug)
717 fprintf(stderr, "*** ufdOpen(%s,0x%x,0%o)\n", url, (unsigned)flags, (unsigned)mode);
718
719     switch (urlType) {
720     case URL_IS_FTP:
721     case URL_IS_HTTPS:
722     case URL_IS_HTTP:
723     case URL_IS_HKP:
724         fd = urlOpen(url, flags, mode);
725         /* we're dealing with local file when urlOpen() returns */
726         urlType = URL_IS_UNKNOWN;
727         break;
728     case URL_IS_DASH:
729         if ((flags & O_ACCMODE) == O_RDWR) {
730             fd = NULL;
731         } else {
732             fd = fdDup((flags & O_ACCMODE) == O_WRONLY ?
733                         STDOUT_FILENO : STDIN_FILENO);
734         }
735         break;
736     case URL_IS_PATH:
737     case URL_IS_UNKNOWN:
738     default:
739         fd = fdOpen(path, flags, mode);
740         break;
741     }
742
743     if (fd == NULL) return NULL;
744
745     fdSetIo(fd, ufdio);
746     fd->bytesRemain = -1;
747     fd->urlType = urlType;
748
749     if (Fileno(fd) < 0) {
750         (void) fdClose(fd);
751         return NULL;
752     }
753 DBGIO(fd, (stderr, "==>\tufdOpen(\"%s\",%x,0%o) %s\n", url, (unsigned)flags, (unsigned)mode, fdbg(fd)));
754     return fd;
755 }
756
757 static const struct FDIO_s ufdio_s = {
758   fdRead, fdWrite, fdSeek, fdClose, fdLink, fdFree, fdNew, fdFileno,
759   ufdOpen, NULL, fdGetFp, NULL
760 };
761 static const FDIO_t ufdio = &ufdio_s ;
762
763 ssize_t timedRead(FD_t fd, void * bufptr, size_t length)
764 {
765     return ufdio->read(fd, bufptr, length);
766 }
767
768 /* =============================================================== */
769 /* Support for GZIP library.
770  */
771 #ifdef  HAVE_ZLIB_H
772
773 #include <zlib.h>
774
775 static void * gzdFileno(FD_t fd)
776 {
777     void * rc = NULL;
778     int i;
779
780     FDSANE(fd);
781     for (i = fd->nfps; i >= 0; i--) {
782         FDSTACK_t * fps = &fd->fps[i];
783         if (fps->io != gzdio)
784             continue;
785         rc = fps->fp;
786         break;
787     }
788     
789     return rc;
790 }
791
792 static
793 FD_t gzdOpen(const char * path, const char * fmode)
794 {
795     FD_t fd;
796     gzFile gzfile;
797     if ((gzfile = gzopen(path, fmode)) == NULL)
798         return NULL;
799     fd = fdNew(RPMDBG_M("open (gzdOpen)"));
800     fdPop(fd); fdPush(fd, gzdio, gzfile, -1);
801     
802 DBGIO(fd, (stderr, "==>\tgzdOpen(\"%s\", \"%s\") fd %p %s\n", path, fmode, (fd ? fd : NULL), fdbg(fd)));
803     return fdLink(fd, RPMDBG_M("gzdOpen"));
804 }
805
806 static FD_t gzdFdopen(void * cookie, const char *fmode)
807 {
808     FD_t fd = c2f(cookie);
809     int fdno;
810     gzFile gzfile;
811
812     if (fmode == NULL) return NULL;
813     fdno = fdFileno(fd);
814     fdSetFdno(fd, -1);          /* XXX skip the fdio close */
815     if (fdno < 0) return NULL;
816     gzfile = gzdopen(fdno, fmode);
817     if (gzfile == NULL) return NULL;
818
819     fdPush(fd, gzdio, gzfile, fdno);            /* Push gzdio onto stack */
820
821     return fdLink(fd, RPMDBG_M("gzdFdopen"));
822 }
823
824 static int gzdFlush(FD_t fd)
825 {
826     gzFile gzfile;
827     gzfile = gzdFileno(fd);
828     if (gzfile == NULL) return -2;
829     return gzflush(gzfile, Z_SYNC_FLUSH);       /* XXX W2DO? */
830 }
831
832 /* =============================================================== */
833 static ssize_t gzdRead(void * cookie, char * buf, size_t count)
834 {
835     FD_t fd = c2f(cookie);
836     gzFile gzfile;
837     ssize_t rc;
838
839     if (fd == NULL || fd->bytesRemain == 0) return 0;   /* XXX simulate EOF */
840
841     gzfile = gzdFileno(fd);
842     if (gzfile == NULL) return -2;      /* XXX can't happen */
843
844     fdstat_enter(fd, FDSTAT_READ);
845     rc = gzread(gzfile, buf, count);
846 DBGIO(fd, (stderr, "==>\tgzdRead(%p,%p,%u) rc %lx %s\n", cookie, buf, (unsigned)count, (unsigned long)rc, fdbg(fd)));
847     if (rc < 0) {
848         int zerror = 0;
849         fd->errcookie = gzerror(gzfile, &zerror);
850         if (zerror == Z_ERRNO) {
851             fd->syserrno = errno;
852             fd->errcookie = strerror(fd->syserrno);
853         }
854     } else if (rc >= 0) {
855         fdstat_exit(fd, FDSTAT_READ, rc);
856         if (fd->digests && rc > 0) fdUpdateDigests(fd, (void *)buf, rc);
857     }
858     return rc;
859 }
860
861 static ssize_t gzdWrite(void * cookie, const char * buf, size_t count)
862 {
863     FD_t fd = c2f(cookie);
864     gzFile gzfile;
865     ssize_t rc;
866
867     if (fd == NULL || fd->bytesRemain == 0) return 0;   /* XXX simulate EOF */
868
869     if (fd->digests && count > 0) fdUpdateDigests(fd, (void *)buf, count);
870
871     gzfile = gzdFileno(fd);
872     if (gzfile == NULL) return -2;      /* XXX can't happen */
873
874     fdstat_enter(fd, FDSTAT_WRITE);
875     rc = gzwrite(gzfile, (void *)buf, count);
876 DBGIO(fd, (stderr, "==>\tgzdWrite(%p,%p,%u) rc %lx %s\n", cookie, buf, (unsigned)count, (unsigned long)rc, fdbg(fd)));
877     if (rc < 0) {
878         int zerror = 0;
879         fd->errcookie = gzerror(gzfile, &zerror);
880         if (zerror == Z_ERRNO) {
881             fd->syserrno = errno;
882             fd->errcookie = strerror(fd->syserrno);
883         }
884     } else if (rc > 0) {
885         fdstat_exit(fd, FDSTAT_WRITE, rc);
886     }
887     return rc;
888 }
889
890 /* XXX zlib-1.0.4 has not */
891 static int gzdSeek(void * cookie, _libio_pos_t pos, int whence)
892 {
893 #ifdef USE_COOKIE_SEEK_POINTER
894     _IO_off64_t p = *pos;
895 #else
896     off_t p = pos;
897 #endif
898     int rc;
899 #if HAVE_GZSEEK
900     FD_t fd = c2f(cookie);
901     gzFile gzfile;
902
903     if (fd == NULL) return -2;
904     assert(fd->bytesRemain == -1);      /* XXX FIXME */
905
906     gzfile = gzdFileno(fd);
907     if (gzfile == NULL) return -2;      /* XXX can't happen */
908
909     fdstat_enter(fd, FDSTAT_SEEK);
910     rc = gzseek(gzfile, p, whence);
911 DBGIO(fd, (stderr, "==>\tgzdSeek(%p,%ld,%d) rc %lx %s\n", cookie, (long)p, whence, (unsigned long)rc, fdbg(fd)));
912     if (rc < 0) {
913         int zerror = 0;
914         fd->errcookie = gzerror(gzfile, &zerror);
915         if (zerror == Z_ERRNO) {
916             fd->syserrno = errno;
917             fd->errcookie = strerror(fd->syserrno);
918         }
919     } else if (rc >= 0) {
920         fdstat_exit(fd, FDSTAT_SEEK, rc);
921     }
922 #else
923     rc = -2;
924 #endif
925     return rc;
926 }
927
928 static int gzdClose( void * cookie)
929 {
930     FD_t fd = c2f(cookie);
931     gzFile gzfile;
932     int rc;
933
934     gzfile = gzdFileno(fd);
935     if (gzfile == NULL) return -2;      /* XXX can't happen */
936
937     fdstat_enter(fd, FDSTAT_CLOSE);
938     rc = gzclose(gzfile);
939
940     /* XXX TODO: preserve fd if errors */
941
942     if (fd) {
943 DBGIO(fd, (stderr, "==>\tgzdClose(%p) zerror %d %s\n", cookie, rc, fdbg(fd)));
944         if (rc < 0) {
945             fd->errcookie = "gzclose error";
946             if (rc == Z_ERRNO) {
947                 fd->syserrno = errno;
948                 fd->errcookie = strerror(fd->syserrno);
949             }
950         } else if (rc >= 0) {
951             fdstat_exit(fd, FDSTAT_CLOSE, rc);
952         }
953     }
954
955 DBGIO(fd, (stderr, "==>\tgzdClose(%p) rc %lx %s\n", cookie, (unsigned long)rc, fdbg(fd)));
956
957     if (_rpmio_debug || rpmIsDebug()) fdstat_print(fd, "GZDIO", stderr);
958     if (rc == 0)
959         fd = fdFree(fd, RPMDBG_M("open (gzdClose)"));
960     return rc;
961 }
962
963 static const struct FDIO_s gzdio_s = {
964   gzdRead, gzdWrite, gzdSeek, gzdClose, fdLink, fdFree, fdNew, fdFileno,
965   NULL, gzdOpen, gzdFileno, gzdFlush
966 };
967 static const FDIO_t gzdio = &gzdio_s ;
968
969 #endif  /* HAVE_ZLIB_H */
970
971 /* =============================================================== */
972 /* Support for BZIP2 library.
973  */
974 #if HAVE_BZLIB_H
975
976 #include <bzlib.h>
977
978 #ifdef HAVE_BZ2_1_0
979 # define bzopen  BZ2_bzopen
980 # define bzclose BZ2_bzclose
981 # define bzdopen BZ2_bzdopen
982 # define bzerror BZ2_bzerror
983 # define bzflush BZ2_bzflush
984 # define bzread  BZ2_bzread
985 # define bzwrite BZ2_bzwrite
986 #endif /* HAVE_BZ2_1_0 */
987
988 static void * bzdFileno(FD_t fd)
989 {
990     void * rc = NULL;
991     int i;
992
993     FDSANE(fd);
994     for (i = fd->nfps; i >= 0; i--) {
995         FDSTACK_t * fps = &fd->fps[i];
996         if (fps->io != bzdio)
997             continue;
998         rc = fps->fp;
999         break;
1000     }
1001     
1002     return rc;
1003 }
1004
1005 static FD_t bzdOpen(const char * path, const char * mode)
1006 {
1007     FD_t fd;
1008     BZFILE *bzfile;;
1009     if ((bzfile = bzopen(path, mode)) == NULL)
1010         return NULL;
1011     fd = fdNew(RPMDBG_M("open (bzdOpen)"));
1012     fdPop(fd); fdPush(fd, bzdio, bzfile, -1);
1013     return fdLink(fd, RPMDBG_M("bzdOpen"));
1014 }
1015
1016 static FD_t bzdFdopen(void * cookie, const char * fmode)
1017 {
1018     FD_t fd = c2f(cookie);
1019     int fdno;
1020     BZFILE *bzfile;
1021
1022     if (fmode == NULL) return NULL;
1023     fdno = fdFileno(fd);
1024     fdSetFdno(fd, -1);          /* XXX skip the fdio close */
1025     if (fdno < 0) return NULL;
1026     bzfile = bzdopen(fdno, fmode);
1027     if (bzfile == NULL) return NULL;
1028
1029     fdPush(fd, bzdio, bzfile, fdno);            /* Push bzdio onto stack */
1030
1031     return fdLink(fd, RPMDBG_M("bzdFdopen"));
1032 }
1033
1034 static int bzdFlush(FD_t fd)
1035 {
1036     return bzflush(bzdFileno(fd));
1037 }
1038
1039 /* =============================================================== */
1040 static ssize_t bzdRead(void * cookie, char * buf, size_t count)
1041 {
1042     FD_t fd = c2f(cookie);
1043     BZFILE *bzfile;
1044     ssize_t rc = 0;
1045
1046     if (fd->bytesRemain == 0) return 0; /* XXX simulate EOF */
1047     bzfile = bzdFileno(fd);
1048     fdstat_enter(fd, FDSTAT_READ);
1049     if (bzfile)
1050         rc = bzread(bzfile, buf, count);
1051     if (rc == -1) {
1052         int zerror = 0;
1053         if (bzfile)
1054             fd->errcookie = bzerror(bzfile, &zerror);
1055     } else if (rc >= 0) {
1056         fdstat_exit(fd, FDSTAT_READ, rc);
1057         if (fd->digests && rc > 0) fdUpdateDigests(fd, (void *)buf, rc);
1058     }
1059     return rc;
1060 }
1061
1062 static ssize_t bzdWrite(void * cookie, const char * buf, size_t count)
1063 {
1064     FD_t fd = c2f(cookie);
1065     BZFILE *bzfile;
1066     ssize_t rc;
1067
1068     if (fd->bytesRemain == 0) return 0; /* XXX simulate EOF */
1069
1070     if (fd->digests && count > 0) fdUpdateDigests(fd, (void *)buf, count);
1071
1072     bzfile = bzdFileno(fd);
1073     fdstat_enter(fd, FDSTAT_WRITE);
1074     rc = bzwrite(bzfile, (void *)buf, count);
1075     if (rc == -1) {
1076         int zerror = 0;
1077         fd->errcookie = bzerror(bzfile, &zerror);
1078     } else if (rc > 0) {
1079         fdstat_exit(fd, FDSTAT_WRITE, rc);
1080     }
1081     return rc;
1082 }
1083
1084 static int bzdClose( void * cookie)
1085 {
1086     FD_t fd = c2f(cookie);
1087     BZFILE *bzfile;
1088     int rc;
1089
1090     bzfile = bzdFileno(fd);
1091
1092     if (bzfile == NULL) return -2;
1093     fdstat_enter(fd, FDSTAT_CLOSE);
1094     /* FIX: check rc */
1095     bzclose(bzfile);
1096     rc = 0;     /* XXX FIXME */
1097
1098     /* XXX TODO: preserve fd if errors */
1099
1100     if (fd) {
1101         if (rc == -1) {
1102             int zerror = 0;
1103             fd->errcookie = bzerror(bzfile, &zerror);
1104         } else if (rc >= 0) {
1105             fdstat_exit(fd, FDSTAT_CLOSE, rc);
1106         }
1107     }
1108
1109 DBGIO(fd, (stderr, "==>\tbzdClose(%p) rc %lx %s\n", cookie, (unsigned long)rc, fdbg(fd)));
1110
1111     if (_rpmio_debug || rpmIsDebug()) fdstat_print(fd, "BZDIO", stderr);
1112     if (rc == 0)
1113         fd = fdFree(fd, RPMDBG_M("open (bzdClose)"));
1114     return rc;
1115 }
1116
1117 static const struct FDIO_s bzdio_s = {
1118   bzdRead, bzdWrite, fdSeekNot, bzdClose, fdLink, fdFree, fdNew, fdFileno,
1119   NULL, bzdOpen, bzdFileno, bzdFlush
1120 };
1121 static const FDIO_t bzdio = &bzdio_s ;
1122
1123 #endif  /* HAVE_BZLIB_H */
1124
1125 /* =============================================================== */
1126 static const char * getFdErrstr (FD_t fd)
1127 {
1128     const char *errstr = NULL;
1129
1130 #ifdef  HAVE_ZLIB_H
1131     if (fdGetIo(fd) == gzdio) {
1132         errstr = fd->errcookie;
1133     } else
1134 #endif  /* HAVE_ZLIB_H */
1135
1136 #ifdef  HAVE_BZLIB_H
1137     if (fdGetIo(fd) == bzdio) {
1138         errstr = fd->errcookie;
1139     } else
1140 #endif  /* HAVE_BZLIB_H */
1141 #ifdef  HAVE_LZMA_H
1142     if (fdGetIo(fd) == xzdio || fdGetIo(fd) == lzdio) {
1143         errstr = fd->errcookie;
1144     } else
1145 #endif  /* HAVE_LZMA_H */
1146     {
1147         errstr = (fd->syserrno ? strerror(fd->syserrno) : "");
1148     }
1149
1150     return errstr;
1151 }
1152
1153 /* =============================================================== */
1154 /* Support for LZMA library.
1155  */
1156
1157 #ifdef HAVE_LZMA_H
1158
1159 #include <sys/types.h>
1160 #include <inttypes.h>
1161 #include <lzma.h>
1162
1163 #define kBufferSize (1 << 15)
1164
1165 typedef struct lzfile {
1166   /* IO buffer */
1167     uint8_t buf[kBufferSize];
1168
1169     lzma_stream strm;
1170
1171     FILE *file;
1172
1173     int encoding;
1174     int eof;
1175
1176 } LZFILE;
1177
1178 static LZFILE *lzopen_internal(const char *path, const char *mode, int fd, int xz)
1179 {
1180     int level = 7;      /* Use XZ's default compression level if unspecified */
1181     int encoding = 0;
1182     FILE *fp;
1183     LZFILE *lzfile;
1184     lzma_ret ret;
1185     lzma_stream init_strm = LZMA_STREAM_INIT;
1186
1187     for (; *mode; mode++) {
1188         if (*mode == 'w')
1189             encoding = 1;
1190         else if (*mode == 'r')
1191             encoding = 0;
1192         else if (*mode >= '1' && *mode <= '9')
1193             level = *mode - '0';
1194     }
1195     if (fd != -1)
1196         fp = fdopen(fd, encoding ? "w" : "r");
1197     else
1198         fp = fopen(path, encoding ? "w" : "r");
1199     if (!fp)
1200         return 0;
1201     lzfile = calloc(1, sizeof(*lzfile));
1202     if (!lzfile) {
1203         fclose(fp);
1204         return 0;
1205     }
1206     
1207     lzfile->file = fp;
1208     lzfile->encoding = encoding;
1209     lzfile->eof = 0;
1210     lzfile->strm = init_strm;
1211     if (encoding) {
1212         if (xz) {
1213             ret = lzma_easy_encoder(&lzfile->strm, level, LZMA_CHECK_SHA256);
1214         } else {
1215             lzma_options_lzma options;
1216             lzma_lzma_preset(&options, level);
1217             ret = lzma_alone_encoder(&lzfile->strm, &options);
1218         }
1219     } else {    /* lzma_easy_decoder_memusage(level) is not ready yet, use hardcoded limit for now */
1220         ret = lzma_auto_decoder(&lzfile->strm, 100<<20, 0);
1221     }
1222     if (ret != LZMA_OK) {
1223         fclose(fp);
1224         free(lzfile);
1225         return 0;
1226     }
1227     return lzfile;
1228 }
1229
1230 static LZFILE *xzopen(const char *path, const char *mode)
1231 {
1232     return lzopen_internal(path, mode, -1, 1);
1233 }
1234
1235 static LZFILE *xzdopen(int fd, const char *mode)
1236 {
1237     if (fd < 0)
1238         return 0;
1239     return lzopen_internal(0, mode, fd, 1);
1240 }
1241
1242 static LZFILE *lzopen(const char *path, const char *mode)
1243 {
1244     return lzopen_internal(path, mode, -1, 0);
1245 }
1246
1247 static LZFILE *lzdopen(int fd, const char *mode)
1248 {
1249     if (fd < 0)
1250         return 0;
1251     return lzopen_internal(0, mode, fd, 0);
1252 }
1253
1254 static int lzflush(LZFILE *lzfile)
1255 {
1256     return fflush(lzfile->file);
1257 }
1258
1259 static int lzclose(LZFILE *lzfile)
1260 {
1261     lzma_ret ret;
1262     size_t n;
1263     int rc;
1264
1265     if (!lzfile)
1266         return -1;
1267     if (lzfile->encoding) {
1268         for (;;) {
1269             lzfile->strm.avail_out = kBufferSize;
1270             lzfile->strm.next_out = lzfile->buf;
1271             ret = lzma_code(&lzfile->strm, LZMA_FINISH);
1272             if (ret != LZMA_OK && ret != LZMA_STREAM_END)
1273                 return -1;
1274             n = kBufferSize - lzfile->strm.avail_out;
1275             if (n && fwrite(lzfile->buf, 1, n, lzfile->file) != n)
1276                 return -1;
1277             if (ret == LZMA_STREAM_END)
1278                 break;
1279         }
1280     }
1281     lzma_end(&lzfile->strm);
1282     rc = fclose(lzfile->file);
1283     free(lzfile);
1284     return rc;
1285 }
1286
1287 static ssize_t lzread(LZFILE *lzfile, void *buf, size_t len)
1288 {
1289     lzma_ret ret;
1290     int eof = 0;
1291
1292     if (!lzfile || lzfile->encoding)
1293       return -1;
1294     if (lzfile->eof)
1295       return 0;
1296     lzfile->strm.next_out = buf;
1297     lzfile->strm.avail_out = len;
1298     for (;;) {
1299         if (!lzfile->strm.avail_in) {
1300             lzfile->strm.next_in = lzfile->buf;
1301             lzfile->strm.avail_in = fread(lzfile->buf, 1, kBufferSize, lzfile->file);
1302             if (!lzfile->strm.avail_in)
1303                 eof = 1;
1304         }
1305         ret = lzma_code(&lzfile->strm, LZMA_RUN);
1306         if (ret == LZMA_STREAM_END) {
1307             lzfile->eof = 1;
1308             return len - lzfile->strm.avail_out;
1309         }
1310         if (ret != LZMA_OK)
1311             return -1;
1312         if (!lzfile->strm.avail_out)
1313             return len;
1314         if (eof)
1315             return -1;
1316       }
1317 }
1318
1319 static ssize_t lzwrite(LZFILE *lzfile, void *buf, size_t len)
1320 {
1321     lzma_ret ret;
1322     size_t n;
1323     if (!lzfile || !lzfile->encoding)
1324         return -1;
1325     if (!len)
1326         return 0;
1327     lzfile->strm.next_in = buf;
1328     lzfile->strm.avail_in = len;
1329     for (;;) {
1330         lzfile->strm.next_out = lzfile->buf;
1331         lzfile->strm.avail_out = kBufferSize;
1332         ret = lzma_code(&lzfile->strm, LZMA_RUN);
1333         if (ret != LZMA_OK)
1334             return -1;
1335         n = kBufferSize - lzfile->strm.avail_out;
1336         if (n && fwrite(lzfile->buf, 1, n, lzfile->file) != n)
1337             return -1;
1338         if (!lzfile->strm.avail_in)
1339             return len;
1340     }
1341 }
1342
1343 /* =============================================================== */
1344
1345 static void * lzdFileno(FD_t fd)
1346 {
1347     void * rc = NULL;
1348     int i;
1349
1350     FDSANE(fd);
1351     for (i = fd->nfps; i >= 0; i--) {
1352             FDSTACK_t * fps = &fd->fps[i];
1353             if (fps->io != xzdio && fps->io != lzdio)
1354                 continue;
1355             rc = fps->fp;
1356         break;
1357     }
1358     
1359     return rc;
1360 }
1361
1362 static FD_t xzdOpen(const char * path, const char * mode)
1363 {
1364     FD_t fd;
1365     LZFILE *lzfile;
1366     if ((lzfile = xzopen(path, mode)) == NULL)
1367         return NULL;
1368     fd = fdNew("open (xzdOpen)");
1369     fdPop(fd); fdPush(fd, xzdio, lzfile, -1);
1370     return fdLink(fd, "xzdOpen");
1371 }
1372
1373 static FD_t xzdFdopen(void * cookie, const char * fmode)
1374 {
1375     FD_t fd = c2f(cookie);
1376     int fdno;
1377     LZFILE *lzfile;
1378
1379     if (fmode == NULL) return NULL;
1380     fdno = fdFileno(fd);
1381     fdSetFdno(fd, -1);          /* XXX skip the fdio close */
1382     if (fdno < 0) return NULL;
1383     lzfile = xzdopen(fdno, fmode);
1384     if (lzfile == NULL) return NULL;
1385     fdPush(fd, xzdio, lzfile, fdno);
1386     return fdLink(fd, "xzdFdopen");
1387 }
1388
1389 static FD_t lzdOpen(const char * path, const char * mode)
1390 {
1391     FD_t fd;
1392     LZFILE *lzfile;
1393     if ((lzfile = lzopen(path, mode)) == NULL)
1394         return NULL;
1395     fd = fdNew("open (lzdOpen)");
1396     fdPop(fd); fdPush(fd, xzdio, lzfile, -1);
1397     return fdLink(fd, "lzdOpen");
1398 }
1399
1400 static FD_t lzdFdopen(void * cookie, const char * fmode)
1401 {
1402     FD_t fd = c2f(cookie);
1403     int fdno;
1404     LZFILE *lzfile;
1405
1406     if (fmode == NULL) return NULL;
1407     fdno = fdFileno(fd);
1408     fdSetFdno(fd, -1);          /* XXX skip the fdio close */
1409     if (fdno < 0) return NULL;
1410     lzfile = lzdopen(fdno, fmode);
1411     if (lzfile == NULL) return NULL;
1412     fdPush(fd, xzdio, lzfile, fdno);
1413     return fdLink(fd, "lzdFdopen");
1414 }
1415
1416 static int lzdFlush(FD_t fd)
1417 {
1418     return lzflush(lzdFileno(fd));
1419 }
1420
1421 /* =============================================================== */
1422 static ssize_t lzdRead(void * cookie, char * buf, size_t count)
1423 {
1424     FD_t fd = c2f(cookie);
1425     LZFILE *lzfile;
1426     ssize_t rc = 0;
1427
1428     if (fd->bytesRemain == 0) return 0; /* XXX simulate EOF */
1429     lzfile = lzdFileno(fd);
1430     fdstat_enter(fd, FDSTAT_READ);
1431     if (lzfile)
1432         rc = lzread(lzfile, buf, count);
1433     if (rc == -1) {
1434         fd->errcookie = "Lzma: decoding error";
1435     } else if (rc >= 0) {
1436         fdstat_exit(fd, FDSTAT_READ, rc);
1437         if (fd->digests && rc > 0) fdUpdateDigests(fd, (void *)buf, rc);
1438     }
1439     return rc;
1440 }
1441
1442 static ssize_t lzdWrite(void * cookie, const char * buf, size_t count)
1443 {
1444     FD_t fd = c2f(cookie);
1445     LZFILE *lzfile;
1446     ssize_t rc = 0;
1447
1448     if (fd == NULL || fd->bytesRemain == 0) return 0;   /* XXX simulate EOF */
1449
1450     if (fd->digests && count > 0) fdUpdateDigests(fd, (void *)buf, count);
1451
1452     lzfile = lzdFileno(fd);
1453
1454     fdstat_enter(fd, FDSTAT_WRITE);
1455     rc = lzwrite(lzfile, (void *)buf, count);
1456     if (rc < 0) {
1457         fd->errcookie = "Lzma: encoding error";
1458     } else if (rc > 0) {
1459         fdstat_exit(fd, FDSTAT_WRITE, rc);
1460     }
1461     return rc;
1462 }
1463
1464 static int lzdClose(void * cookie)
1465 {
1466     FD_t fd = c2f(cookie);
1467     LZFILE *lzfile;
1468     int rc;
1469
1470     lzfile = lzdFileno(fd);
1471
1472     if (lzfile == NULL) return -2;
1473     fdstat_enter(fd, FDSTAT_CLOSE);
1474     rc = lzclose(lzfile);
1475
1476     /* XXX TODO: preserve fd if errors */
1477
1478     if (fd) {
1479         if (rc == -1) {
1480             fd->errcookie = strerror(ferror(lzfile->file));
1481         } else if (rc >= 0) {
1482             fdstat_exit(fd, FDSTAT_CLOSE, rc);
1483         }
1484     }
1485
1486 DBGIO(fd, (stderr, "==>\tlzdClose(%p) rc %lx %s\n", cookie, (unsigned long)rc, fdbg(fd)));
1487
1488     if (_rpmio_debug || rpmIsDebug()) fdstat_print(fd, "XZDIO", stderr);
1489     if (rc == 0)
1490         fd = fdFree(fd, "open (lzdClose)");
1491     return rc;
1492 }
1493
1494 static struct FDIO_s xzdio_s = {
1495   lzdRead, lzdWrite, fdSeekNot, lzdClose, NULL, NULL, NULL, fdFileno,
1496   NULL, xzdOpen, lzdFileno, lzdFlush
1497 };
1498 static const FDIO_t xzdio = &xzdio_s;
1499
1500 static struct FDIO_s lzdio_s = {
1501   lzdRead, lzdWrite, fdSeekNot, lzdClose, NULL, NULL, NULL, fdFileno,
1502   NULL, lzdOpen, lzdFileno, lzdFlush
1503 };
1504 static const FDIO_t lzdio = &lzdio_s;
1505
1506 #endif  /* HAVE_LZMA_H */
1507
1508 /* =============================================================== */
1509
1510 const char *Fstrerror(FD_t fd)
1511 {
1512     if (fd == NULL)
1513         return (errno ? strerror(errno) : "");
1514     FDSANE(fd);
1515     return getFdErrstr(fd);
1516 }
1517
1518 #define FDIOVEC(_fd, _vec)      \
1519   ((fdGetIo(_fd) && fdGetIo(_fd)->_vec) ? fdGetIo(_fd)->_vec : NULL)
1520
1521 ssize_t Fread(void *buf, size_t size, size_t nmemb, FD_t fd) {
1522     fdio_read_function_t _read;
1523     int rc;
1524
1525     FDSANE(fd);
1526 DBGIO(fd, (stderr, "==> Fread(%p,%u,%u,%p) %s\n", buf, (unsigned)size, (unsigned)nmemb, (fd ? fd : NULL), fdbg(fd)));
1527
1528     if (fdGetIo(fd) == fpio) {
1529         rc = fread(buf, size, nmemb, fdGetFILE(fd));
1530         return rc;
1531     }
1532
1533     _read = FDIOVEC(fd, read);
1534
1535     rc = (_read ? (*_read) (fd, buf, size * nmemb) : -2);
1536     return rc;
1537 }
1538
1539 ssize_t Fwrite(const void *buf, size_t size, size_t nmemb, FD_t fd)
1540 {
1541     fdio_write_function_t _write;
1542     int rc;
1543
1544     FDSANE(fd);
1545 DBGIO(fd, (stderr, "==> Fwrite(%p,%u,%u,%p) %s\n", buf, (unsigned)size, (unsigned)nmemb, (fd ? fd : NULL), fdbg(fd)));
1546
1547     if (fdGetIo(fd) == fpio) {
1548         rc = fwrite(buf, size, nmemb, fdGetFILE(fd));
1549         return rc;
1550     }
1551
1552     _write = FDIOVEC(fd, write);
1553
1554     rc = (_write ? _write(fd, buf, size * nmemb) : -2);
1555     return rc;
1556 }
1557
1558 int Fseek(FD_t fd, _libio_off_t offset, int whence) {
1559     fdio_seek_function_t _seek;
1560 #ifdef USE_COOKIE_SEEK_POINTER
1561     _IO_off64_t o64 = offset;
1562     _libio_pos_t pos = &o64;
1563 #else
1564     _libio_pos_t pos = offset;
1565 #endif
1566
1567     long int rc;
1568
1569     FDSANE(fd);
1570 DBGIO(fd, (stderr, "==> Fseek(%p,%ld,%d) %s\n", fd, (long)offset, whence, fdbg(fd)));
1571
1572     if (fdGetIo(fd) == fpio) {
1573         FILE *fp;
1574
1575         fp = fdGetFILE(fd);
1576         rc = fseek(fp, offset, whence);
1577         return rc;
1578     }
1579
1580     _seek = FDIOVEC(fd, seek);
1581
1582     rc = (_seek ? _seek(fd, pos, whence) : -2);
1583     return rc;
1584 }
1585
1586 int Fclose(FD_t fd)
1587 {
1588     int rc = 0, ec = 0;
1589
1590     FDSANE(fd);
1591 DBGIO(fd, (stderr, "==> Fclose(%p) %s\n", (fd ? fd : NULL), fdbg(fd)));
1592
1593     fd = fdLink(fd, RPMDBG_M("Fclose"));
1594     while (fd->nfps >= 0) {
1595         FDSTACK_t * fps = &fd->fps[fd->nfps];
1596         
1597         if (fps->io == fpio) {
1598             FILE *fp;
1599             int fpno;
1600
1601             fp = fdGetFILE(fd);
1602             fpno = fileno(fp);
1603             if (fp)
1604                 rc = fclose(fp);
1605             if (fpno == -1) {
1606                 fd = fdFree(fd, RPMDBG_M("fopencookie (Fclose)"));
1607                 fdPop(fd);
1608             }
1609         } else {
1610             fdio_close_function_t _close = FDIOVEC(fd, close);
1611             rc = _close ? _close(fd) : -2;
1612         }
1613         if (fd->nfps == 0)
1614             break;
1615         if (ec == 0 && rc)
1616             ec = rc;
1617         fdPop(fd);
1618     }
1619     fd = fdFree(fd, RPMDBG_M("Fclose"));
1620     return ec;
1621 }
1622
1623 /**
1624  * Convert stdio fmode to open(2) mode, filtering out zlib/bzlib flags.
1625  *      returns stdio[0] = NUL on error.
1626  *
1627  * - gzopen:    [0-9] is compression level
1628  * - gzopen:    'f' is filtered (Z_FILTERED)
1629  * - gzopen:    'h' is Huffman encoding (Z_HUFFMAN_ONLY)
1630  * - bzopen:    [1-9] is block size (modulo 100K)
1631  * - bzopen:    's' is smallmode
1632  * - HACK:      '.' terminates, rest is type of I/O
1633  */
1634 static void cvtfmode (const char *m,
1635                                 char *stdio, size_t nstdio,
1636                                 char *other, size_t nother,
1637                                 const char **end, int * f)
1638 {
1639     int flags = 0;
1640     char c;
1641
1642     switch (*m) {
1643     case 'a':
1644         flags |= O_WRONLY | O_CREAT | O_APPEND;
1645         if (--nstdio > 0) *stdio++ = *m;
1646         break;
1647     case 'w':
1648         flags |= O_WRONLY | O_CREAT | O_TRUNC;
1649         if (--nstdio > 0) *stdio++ = *m;
1650         break;
1651     case 'r':
1652         flags |= O_RDONLY;
1653         if (--nstdio > 0) *stdio++ = *m;
1654         break;
1655     default:
1656         *stdio = '\0';
1657         return;
1658         break;
1659     }
1660     m++;
1661
1662     while ((c = *m++) != '\0') {
1663         switch (c) {
1664         case '.':
1665             break;
1666         case '+':
1667             flags &= ~(O_RDONLY|O_WRONLY);
1668             flags |= O_RDWR;
1669             if (--nstdio > 0) *stdio++ = c;
1670             continue;
1671             break;
1672         case 'b':
1673             if (--nstdio > 0) *stdio++ = c;
1674             continue;
1675             break;
1676         case 'x':
1677             flags |= O_EXCL;
1678             if (--nstdio > 0) *stdio++ = c;
1679             continue;
1680             break;
1681         default:
1682             if (--nother > 0) *other++ = c;
1683             continue;
1684             break;
1685         }
1686         break;
1687     }
1688
1689     *stdio = *other = '\0';
1690     if (end != NULL)
1691         *end = (*m != '\0' ? m : NULL);
1692     if (f != NULL)
1693         *f = flags;
1694 }
1695
1696 #if _USE_LIBIO
1697 #if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ == 0
1698 /* XXX retrofit glibc-2.1.x typedef on glibc-2.0.x systems */
1699 typedef _IO_cookie_io_functions_t cookie_io_functions_t;
1700 #endif
1701 #endif
1702
1703 FD_t Fdopen(FD_t ofd, const char *fmode)
1704 {
1705     char stdio[20], other[20], zstdio[20];
1706     const char *end = NULL;
1707     FDIO_t iof = NULL;
1708     FD_t fd = ofd;
1709
1710 if (_rpmio_debug)
1711 fprintf(stderr, "*** Fdopen(%p,%s) %s\n", fd, fmode, fdbg(fd));
1712     FDSANE(fd);
1713
1714     if (fmode == NULL)
1715         return NULL;
1716
1717     cvtfmode(fmode, stdio, sizeof(stdio), other, sizeof(other), &end, NULL);
1718     if (stdio[0] == '\0')
1719         return NULL;
1720     zstdio[0] = '\0';
1721     strncat(zstdio, stdio, sizeof(zstdio) - strlen(zstdio));
1722     strncat(zstdio, other, sizeof(zstdio) - strlen(zstdio));
1723
1724     if (end == NULL && other[0] == '\0')
1725         return fd;
1726
1727     if (end && *end) {
1728         if (rstreq(end, "fdio")) {
1729             iof = fdio;
1730 #if HAVE_ZLIB_H
1731         } else if (rstreq(end, "gzdio") || rstreq(end, "gzip")) {
1732             iof = gzdio;
1733             fd = gzdFdopen(fd, zstdio);
1734 #endif
1735 #if HAVE_BZLIB_H
1736         } else if (rstreq(end, "bzdio") || rstreq(end, "bzip2")) {
1737             iof = bzdio;
1738             fd = bzdFdopen(fd, zstdio);
1739 #endif
1740 #if HAVE_LZMA_H
1741         } else if (rstreq(end, "xzdio") || rstreq(end, "xz")) {
1742             iof = xzdio;
1743             fd = xzdFdopen(fd, zstdio);
1744         } else if (rstreq(end, "lzdio") || rstreq(end, "lzma")) {
1745             iof = lzdio;
1746             fd = lzdFdopen(fd, zstdio);
1747 #endif
1748         } else if (rstreq(end, "ufdio")) {
1749             iof = ufdio;
1750         } else if (rstreq(end, "fpio")) {
1751             iof = fpio;
1752             if (noLibio) {
1753                 int fdno = Fileno(fd);
1754                 FILE * fp = fdopen(fdno, stdio);
1755 if (_rpmio_debug)
1756 fprintf(stderr, "*** Fdopen fpio fp %p\n", (void *)fp);
1757                 if (fp == NULL)
1758                     return NULL;
1759                 /* XXX gzdio/bzdio use fp for private data */
1760                 if (fdGetFp(fd) == NULL)
1761                     fdSetFp(fd, fp);
1762                 fdPush(fd, fpio, fp, fdno);     /* Push fpio onto stack */
1763             }
1764         }
1765     } else if (other[0] != '\0') {
1766         for (end = other; *end && strchr("0123456789fh", *end); end++)
1767             {};
1768         if (*end == '\0') {
1769             iof = gzdio;
1770             fd = gzdFdopen(fd, zstdio);
1771         }
1772     }
1773     if (iof == NULL)
1774         return fd;
1775
1776     if (!noLibio) {
1777         FILE * fp = NULL;
1778
1779 #if _USE_LIBIO
1780         {   cookie_io_functions_t ciof;
1781             ciof.read = iof->read;
1782             ciof.write = iof->write;
1783             ciof.seek = iof->seek;
1784             ciof.close = iof->close;
1785             fp = fopencookie(fd, stdio, ciof);
1786 DBGIO(fd, (stderr, "==> fopencookie(%p,\"%s\",*%p) returns fp %p\n", fd, stdio, iof, fp));
1787         }
1788 #endif
1789
1790         if (fp) {
1791             /* XXX gzdio/bzdio use fp for private data */
1792             if (fdGetFp(fd) == NULL)
1793                 fdSetFp(fd, fp);
1794             fdPush(fd, fpio, fp, fileno(fp));   /* Push fpio onto stack */
1795             fd = fdLink(fd, RPMDBG_M("fopencookie"));
1796         }
1797     }
1798
1799 DBGIO(fd, (stderr, "==> Fdopen(%p,\"%s\") returns fd %p %s\n", ofd, fmode, (fd ? fd : NULL), fdbg(fd)));
1800     return fd;
1801 }
1802
1803 FD_t Fopen(const char *path, const char *fmode)
1804 {
1805     char stdio[20], other[20];
1806     const char *end = NULL;
1807     mode_t perms = 0666;
1808     int flags = 0;
1809     FD_t fd;
1810
1811     if (path == NULL || fmode == NULL)
1812         return NULL;
1813
1814     stdio[0] = '\0';
1815     cvtfmode(fmode, stdio, sizeof(stdio), other, sizeof(other), &end, &flags);
1816     if (stdio[0] == '\0')
1817         return NULL;
1818
1819     if (end == NULL || rstreq(end, "fdio")) {
1820 if (_rpmio_debug)
1821 fprintf(stderr, "*** Fopen fdio path %s fmode %s\n", path, fmode);
1822         fd = fdOpen(path, flags, perms);
1823         if (fdFileno(fd) < 0) {
1824             if (fd) (void) fdClose(fd);
1825             return NULL;
1826         }
1827     } else {
1828         /* XXX gzdio and bzdio here too */
1829
1830         switch (urlIsURL(path)) {
1831         case URL_IS_HTTPS:
1832         case URL_IS_HTTP:
1833         case URL_IS_HKP:
1834         case URL_IS_PATH:
1835         case URL_IS_DASH:
1836         case URL_IS_FTP:
1837         case URL_IS_UNKNOWN:
1838 if (_rpmio_debug)
1839 fprintf(stderr, "*** Fopen ufdio path %s fmode %s\n", path, fmode);
1840             fd = ufdOpen(path, flags, perms);
1841             if (fd == NULL || !(fdFileno(fd) >= 0))
1842                 return fd;
1843             break;
1844         default:
1845 if (_rpmio_debug)
1846 fprintf(stderr, "*** Fopen WTFO path %s fmode %s\n", path, fmode);
1847             return NULL;
1848             break;
1849         }
1850
1851     }
1852
1853     if (fd)
1854         fd = Fdopen(fd, fmode);
1855     return fd;
1856 }
1857
1858 int Fflush(FD_t fd)
1859 {
1860     void * vh;
1861     if (fd == NULL) return -1;
1862     if (fdGetIo(fd) == fpio)
1863         return fflush(fdGetFILE(fd));
1864
1865     vh = fdGetFp(fd);
1866 #if HAVE_ZLIB_H
1867     if (vh && fdGetIo(fd) == gzdio)
1868         return gzdFlush(vh);
1869 #endif
1870 #if HAVE_BZLIB_H
1871     if (vh && fdGetIo(fd) == bzdio)
1872         return bzdFlush(vh);
1873 #endif
1874 #if HAVE_LZMA_H
1875     if (vh && (fdGetIo(fd) == xzdio || fdGetIo(fd) == lzdio))
1876         return lzdFlush(vh);
1877 #endif
1878 /* FIXME: If we get here, something went wrong above */
1879     return 0;
1880 }
1881
1882 off_t Ftell(FD_t fd)
1883 {
1884     FDIO_t iot;
1885     off_t pos = -2; /* assume not implemented */
1886
1887     if (fd == NULL) return -1;
1888     iot = fdGetIo(fd);
1889     /* this wont work correctly for compressed types */
1890     if (iot == fpio || iot == fdio || iot == ufdio) {
1891         pos = lseek(Fileno(fd), 0, SEEK_CUR);
1892     }
1893
1894     return pos;
1895 }
1896
1897 int Ferror(FD_t fd)
1898 {
1899     int i, rc = 0;
1900
1901     if (fd == NULL) return -1;
1902     for (i = fd->nfps; rc == 0 && i >= 0; i--) {
1903         FDSTACK_t * fps = &fd->fps[i];
1904         int ec;
1905         
1906         if (fps->io == fpio) {
1907             ec = ferror(fdGetFILE(fd));
1908 #if HAVE_ZLIB_H
1909         } else if (fps->io == gzdio) {
1910             ec = (fd->syserrno || fd->errcookie != NULL) ? -1 : 0;
1911             i--;        /* XXX fdio under gzdio always has fdno == -1 */
1912 #endif
1913 #if HAVE_BZLIB_H
1914         } else if (fps->io == bzdio) {
1915             ec = (fd->syserrno  || fd->errcookie != NULL) ? -1 : 0;
1916             i--;        /* XXX fdio under bzdio always has fdno == -1 */
1917 #endif
1918 #if HAVE_LZMA_H
1919         } else if (fps->io == xzdio || fps->io == lzdio) {
1920             ec = (fd->syserrno  || fd->errcookie != NULL) ? -1 : 0;
1921             i--;        /* XXX fdio under xzdio/lzdio always has fdno == -1 */
1922 #endif
1923         } else {
1924         /* XXX need to check ufdio/gzdio/bzdio/fdio errors correctly. */
1925             ec = (fdFileno(fd) < 0 ? -1 : 0);
1926         }
1927
1928         if (rc == 0 && ec)
1929             rc = ec;
1930     }
1931 DBGIO(fd, (stderr, "==> Ferror(%p) rc %d %s\n", fd, rc, fdbg(fd)));
1932     return rc;
1933 }
1934
1935 int Fileno(FD_t fd)
1936 {
1937     int i, rc = -1;
1938
1939     if (fd == NULL) return -1;
1940     for (i = fd->nfps ; rc == -1 && i >= 0; i--) {
1941         rc = fd->fps[i].fdno;
1942     }
1943     
1944 DBGIO(fd, (stderr, "==> Fileno(%p) rc %d %s\n", (fd ? fd : NULL), rc, fdbg(fd)));
1945     return rc;
1946 }
1947
1948 /* XXX this is naive */
1949 int Fcntl(FD_t fd, int op, void *lip)
1950 {
1951     return fcntl(Fileno(fd), op, lip);
1952 }
1953
1954 rpmop fdOp(FD_t fd, fdOpX opx)
1955 {
1956     rpmop op = NULL;
1957
1958     if (fd != NULL && fd->stats != NULL && opx >= 0 && opx < FDSTAT_MAX)
1959         op = fd->stats->ops + opx;
1960     return op;
1961 }
1962
1963 int rpmioSlurp(const char * fn, uint8_t ** bp, ssize_t * blenp)
1964 {
1965     static const ssize_t blenmax = (32 * BUFSIZ);
1966     ssize_t blen = 0;
1967     uint8_t * b = NULL;
1968     ssize_t size;
1969     FD_t fd;
1970     int rc = 0;
1971
1972     fd = Fopen(fn, "r.ufdio");
1973     if (fd == NULL || Ferror(fd)) {
1974         rc = 2;
1975         goto exit;
1976     }
1977
1978     size = fdSize(fd);
1979     blen = (size >= 0 ? size : blenmax);
1980     if (blen) {
1981         int nb;
1982         b = xmalloc(blen+1);
1983         b[0] = '\0';
1984         nb = Fread(b, sizeof(*b), blen, fd);
1985         if (Ferror(fd) || (size > 0 && nb != blen)) {
1986             rc = 1;
1987             goto exit;
1988         }
1989         if (blen == blenmax && nb < blen) {
1990             blen = nb;
1991             b = xrealloc(b, blen+1);
1992         }
1993         b[blen] = '\0';
1994     }
1995
1996 exit:
1997     if (fd) (void) Fclose(fd);
1998         
1999     if (rc) {
2000         if (b) free(b);
2001         b = NULL;
2002         blen = 0;
2003     }
2004
2005     if (bp) *bp = b;
2006     else if (b) free(b);
2007
2008     if (blenp) *blenp = blen;
2009
2010     return rc;
2011 }
2012
2013 static const struct FDIO_s fpio_s = {
2014   fdRead, fdWrite, fdSeek, fdClose, fdLink, fdFree, fdNew, fdFileno,
2015   ufdOpen, NULL, fdGetFp, NULL
2016 };
2017 static const FDIO_t fpio = &fpio_s ;
2018
2019 void fdInitDigest(FD_t fd, pgpHashAlgo hashalgo, int flags)
2020 {
2021     if (fd->digests == NULL) {
2022         fd->digests = rpmDigestBundleNew();
2023     }
2024     fdstat_enter(fd, FDSTAT_DIGEST);
2025     rpmDigestBundleAdd(fd->digests, hashalgo, flags);
2026     fdstat_exit(fd, FDSTAT_DIGEST, (ssize_t) 0);
2027 }
2028
2029 static void fdUpdateDigests(FD_t fd, const unsigned char * buf, size_t buflen)
2030 {
2031     if (fd && fd->digests) {
2032         fdstat_enter(fd, FDSTAT_DIGEST);
2033         rpmDigestBundleUpdate(fd->digests, buf, buflen);
2034         fdstat_exit(fd, FDSTAT_DIGEST, (ssize_t) buflen);
2035     }
2036 }
2037
2038 void fdFiniDigest(FD_t fd, pgpHashAlgo hashalgo,
2039                 void ** datap,
2040                 size_t * lenp,
2041                 int asAscii)
2042 {
2043     if (fd && fd->digests) {
2044         fdstat_enter(fd, FDSTAT_DIGEST);
2045         rpmDigestBundleFinal(fd->digests, hashalgo, datap, lenp, asAscii);
2046         fdstat_exit(fd, FDSTAT_DIGEST, (ssize_t) 0);
2047     }
2048 }
2049
2050