Remove unnecessary Group tag for localisation packages
[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 #include <errno.h>
8
9 #include <rpm/rpmlog.h>
10 #include <rpm/rpmmacro.h>
11 #include <rpm/rpmfileutil.h>
12 #include <rpm/rpmsw.h>
13 #include <rpm/rpmurl.h>
14
15 #include "rpmio/rpmio_internal.h"
16
17 #include "debug.h"
18
19 typedef struct _FDSTACK_s {
20     FDIO_t              io;
21     void *              fp;
22     int                 fdno;
23 } FDSTACK_t;
24
25 /** \ingroup rpmio
26  * Cumulative statistics for a descriptor.
27  */
28 typedef struct {
29     struct rpmop_s      ops[FDSTAT_MAX];        /*!< Cumulative statistics. */
30 } * FDSTAT_t;
31
32 /** \ingroup rpmio
33  * The FD_t File Handle data structure.
34  */
35 struct _FD_s {
36     int         nrefs;
37     int         flags;
38 #define RPMIO_DEBUG_IO          0x40000000
39     int         magic;
40 #define FDMAGIC                 0x04463138
41     int         nfps;
42     FDSTACK_t   fps[8];
43     int         urlType;        /* ufdio: */
44
45     int         syserrno;       /* last system errno encountered */
46     const char *errcookie;      /* gzdio/bzdio/ufdio/xzdio: */
47
48     char        *descr;         /* file name (or other description) */
49     FDSTAT_t    stats;          /* I/O statistics */
50
51     rpmDigestBundle digests;
52 };
53
54 #define DBG(_f, _m, _x) \
55     \
56     if ((_rpmio_debug | ((_f) ? ((FD_t)(_f))->flags : 0)) & (_m)) fprintf _x \
57
58 #define DBGIO(_f, _x)   DBG((_f), RPMIO_DEBUG_IO, _x)
59
60 static FDIO_t fdGetIo(FD_t fd)
61 {
62     return (fd != NULL) ? fd->fps[fd->nfps].io : NULL;
63 }
64
65 static void fdSetIo(FD_t fd, FDIO_t io)
66 {
67     if (fd)
68         fd->fps[fd->nfps].io = io;
69 }
70
71 static void * fdGetFp(FD_t fd)
72 {
73     return (fd != NULL) ? fd->fps[fd->nfps].fp : NULL;
74 }
75
76 static void fdSetFp(FD_t fd, void * fp)
77 {
78     if (fd)
79         fd->fps[fd->nfps].fp = fp;
80 }
81
82 static void fdSetFdno(FD_t fd, int fdno)
83 {
84     if (fd) 
85         fd->fps[fd->nfps].fdno = fdno;
86 }
87
88 static void fdPush(FD_t fd, FDIO_t io, void * fp, int fdno)
89 {
90     if (fd == NULL || fd->nfps >= (sizeof(fd->fps)/sizeof(fd->fps[0]) - 1))
91         return;
92     fd->nfps++;
93     fdSetIo(fd, io);
94     fdSetFp(fd, fp);
95     fdSetFdno(fd, fdno);
96 }
97
98 static void fdPop(FD_t fd)
99 {
100     if (fd == NULL || fd->nfps < 0) return;
101     fdSetIo(fd, NULL);
102     fdSetFp(fd, NULL);
103     fdSetFdno(fd, -1);
104     fd->nfps--;
105 }
106
107 void fdSetBundle(FD_t fd, rpmDigestBundle bundle)
108 {
109     if (fd)
110         fd->digests = bundle;
111 }
112
113 rpmDigestBundle fdGetBundle(FD_t fd)
114 {
115     return (fd != NULL) ? fd->digests : NULL;
116 }
117
118 static void * iotFileno(FD_t fd, FDIO_t iot)
119 {
120     void * rc = NULL;
121
122     if (fd == NULL)
123         return NULL;
124
125     for (int i = fd->nfps; i >= 0; i--) {
126         FDSTACK_t * fps = &fd->fps[i];
127         if (fps->io != iot)
128             continue;
129         rc = fps->fp;
130         break;
131     }
132     
133     return rc;
134 }
135
136 /** \ingroup rpmio
137  * \name RPMIO Vectors.
138  */
139 typedef ssize_t (*fdio_read_function_t) (FD_t fd, void *buf, size_t nbytes);
140 typedef ssize_t (*fdio_write_function_t) (FD_t fd, const void *buf, size_t nbytes);
141 typedef int (*fdio_seek_function_t) (FD_t fd, off_t pos, int whence);
142 typedef int (*fdio_close_function_t) (FD_t fd);
143 typedef FD_t (*fdio_ref_function_t) (FD_t fd);
144 typedef FD_t (*fdio_deref_function_t) (FD_t fd);
145 typedef FD_t (*fdio_new_function_t) (const char *descr);
146 typedef int (*fdio_fileno_function_t) (FD_t fd);
147 typedef FD_t (*fdio_open_function_t) (const char * path, int flags, mode_t mode);
148 typedef FD_t (*fdio_fopen_function_t) (const char * path, const char * fmode);
149 typedef void * (*fdio_ffileno_function_t) (FD_t fd);
150 typedef int (*fdio_fflush_function_t) (FD_t fd);
151 typedef long (*fdio_ftell_function_t) (FD_t);
152
153 struct FDIO_s {
154   fdio_read_function_t          read;
155   fdio_write_function_t         write;
156   fdio_seek_function_t          seek;
157   fdio_close_function_t         close;
158
159   fdio_ref_function_t           _fdref;
160   fdio_deref_function_t         _fdderef;
161   fdio_new_function_t           _fdnew;
162   fdio_fileno_function_t        _fileno;
163
164   fdio_open_function_t          _open;
165   fdio_fopen_function_t         _fopen;
166   fdio_ffileno_function_t       _ffileno;
167   fdio_fflush_function_t        _fflush;
168   fdio_ftell_function_t         _ftell;
169 };
170
171 /* forward refs */
172 static const FDIO_t fdio;
173 static const FDIO_t ufdio;
174 static const FDIO_t gzdio;
175 static const FDIO_t bzdio;
176 static const FDIO_t xzdio;
177 static const FDIO_t lzdio;
178
179 /** \ingroup rpmio
180  * Update digest(s) attached to fd.
181  */
182 static void fdUpdateDigests(FD_t fd, const void * buf, size_t buflen);
183 static FD_t fdNew(const char *descr);
184 /**
185  */
186 int _rpmio_debug = 0;
187
188 /* =============================================================== */
189
190 static const char * fdbg(FD_t fd)
191 {
192     static char buf[BUFSIZ];
193     char *be = buf;
194     int i;
195
196     buf[0] = '\0';
197     if (fd == NULL)
198         return buf;
199
200     *be++ = '\t';
201     for (i = fd->nfps; i >= 0; i--) {
202         FDSTACK_t * fps = &fd->fps[i];
203         if (i != fd->nfps)
204             *be++ = ' ';
205         *be++ = '|';
206         *be++ = ' ';
207         if (fps->io == fdio) {
208             sprintf(be, "FD %d fp %p", fps->fdno, fps->fp);
209         } else if (fps->io == ufdio) {
210             sprintf(be, "UFD %d fp %p", fps->fdno, fps->fp);
211         } else if (fps->io == gzdio) {
212             sprintf(be, "GZD %p fdno %d", fps->fp, fps->fdno);
213 #if HAVE_BZLIB_H
214         } else if (fps->io == bzdio) {
215             sprintf(be, "BZD %p fdno %d", fps->fp, fps->fdno);
216 #endif
217 #if HAVE_LZMA_H
218         } else if (fps->io == xzdio) {
219             sprintf(be, "XZD %p fdno %d", fps->fp, fps->fdno);
220         } else if (fps->io == lzdio) {
221             sprintf(be, "LZD %p fdno %d", fps->fp, fps->fdno);
222 #endif
223         } else {
224             sprintf(be, "??? io %p fp %p fdno %d ???",
225                 fps->io, fps->fp, fps->fdno);
226         }
227         be += strlen(be);
228         *be = '\0';
229     }
230     return buf;
231 }
232
233 static void fdstat_enter(FD_t fd, fdOpX opx)
234 {
235     if (fd->stats != NULL)
236         (void) rpmswEnter(fdOp(fd, opx), (ssize_t) 0);
237 }
238
239 static void fdstat_exit(FD_t fd, fdOpX opx, ssize_t rc)
240 {
241     if (rc == -1)
242         fd->syserrno = errno;
243     if (fd->stats != NULL)
244         (void) rpmswExit(fdOp(fd, opx), rc);
245 }
246
247 static void fdstat_print(FD_t fd, const char * msg, FILE * fp)
248 {
249     static const int usec_scale = (1000*1000);
250     int opx;
251
252     if (fd == NULL || fd->stats == NULL) return;
253     for (opx = 0; opx < 4; opx++) {
254         rpmop op = &fd->stats->ops[opx];
255         if (op->count <= 0) continue;
256         switch (opx) {
257         case FDSTAT_READ:
258             if (msg) fprintf(fp, "%s:", msg);
259             fprintf(fp, "%8d reads, %8ld total bytes in %d.%06d secs\n",
260                 op->count, (long)op->bytes,
261                 (int)(op->usecs/usec_scale), (int)(op->usecs%usec_scale));
262             break;
263         case FDSTAT_WRITE:
264             if (msg) fprintf(fp, "%s:", msg);
265             fprintf(fp, "%8d writes, %8ld total bytes in %d.%06d secs\n",
266                 op->count, (long)op->bytes,
267                 (int)(op->usecs/usec_scale), (int)(op->usecs%usec_scale));
268             break;
269         case FDSTAT_SEEK:
270             break;
271         case FDSTAT_CLOSE:
272             break;
273         }
274     }
275 }
276
277 off_t fdSize(FD_t fd)
278 {
279     struct stat sb;
280     off_t rc = -1; 
281
282     if (fd != NULL && fstat(Fileno(fd), &sb) == 0)
283         rc = sb.st_size;
284     return rc;
285 }
286
287 FD_t fdDup(int fdno)
288 {
289     FD_t fd;
290     int nfdno;
291
292     if ((nfdno = dup(fdno)) < 0)
293         return NULL;
294     fd = fdNew(NULL);
295     fdSetFdno(fd, nfdno);
296 DBGIO(fd, (stderr, "==> fdDup(%d) fd %p %s\n", fdno, (fd ? fd : NULL), fdbg(fd)));
297     return fd;
298 }
299
300 /* Regular fd doesn't have fflush() equivalent but its not an error either */
301 static int fdFlush(FD_t fd)
302 {
303     return 0;
304 }
305
306 static int fdFileno(FD_t fd)
307 {
308     return (fd != NULL) ? fd->fps[0].fdno : -2;
309 }
310
311 const char * Fdescr(FD_t fd)
312 {
313     if (fd == NULL)
314         return _("[none]");
315
316     /* Lazy lookup if description is not set (eg dupped fd) */
317     if (fd->descr == NULL) {
318         int fdno = fd->fps[fd->nfps].fdno;
319 #if defined(__linux__)
320         /* Grab the path from /proc if we can */
321         char *procpath = NULL;
322         char buf[PATH_MAX];
323         ssize_t llen;
324
325         rasprintf(&procpath, "/proc/self/fd/%d", fdno);
326         llen = readlink(procpath, buf, sizeof(buf)-1);
327         free(procpath);
328
329         if (llen >= 1) {
330             buf[llen] = '\0';
331             /* Real paths in /proc are always absolute */
332             if (buf[0] == '/')
333                 fd->descr = xstrdup(buf);
334             else
335                 fd->descr = rstrscat(NULL, "[", buf, "]", NULL);
336         }
337 #endif
338         /* Still no description, base it on fdno which is always there */
339         if (fd->descr == NULL)
340             rasprintf(&(fd->descr), "[fd %d]", fdno);
341     }
342     return fd->descr;
343 }
344
345 FD_t fdLink(FD_t fd)
346 {
347     if (fd)
348         fd->nrefs++;
349     return fd;
350 }
351
352 FD_t fdFree( FD_t fd)
353 {
354     if (fd) {
355         if (--fd->nrefs > 0)
356             return fd;
357         fd->stats = _free(fd->stats);
358         if (fd->digests) {
359             fd->digests = rpmDigestBundleFree(fd->digests);
360         }
361         free(fd->descr);
362         free(fd);
363     }
364     return NULL;
365 }
366
367 FD_t fdNew(const char *descr)
368 {
369     FD_t fd = xcalloc(1, sizeof(*fd));
370     if (fd == NULL) /* XXX xmalloc never returns NULL */
371         return NULL;
372     fd->nrefs = 0;
373     fd->flags = 0;
374     fd->magic = FDMAGIC;
375     fd->urlType = URL_IS_UNKNOWN;
376
377     fd->nfps = 0;
378     memset(fd->fps, 0, sizeof(fd->fps));
379
380     fd->fps[0].io = fdio;
381     fd->fps[0].fp = NULL;
382     fd->fps[0].fdno = -1;
383
384     fd->syserrno = 0;
385     fd->errcookie = NULL;
386     fd->stats = xcalloc(1, sizeof(*fd->stats));
387     fd->digests = NULL;
388     fd->descr = descr ? xstrdup(descr) : NULL;
389
390     return fdLink(fd);
391 }
392
393 static ssize_t fdRead(FD_t fd, void * buf, size_t count)
394 {
395     return read(fdFileno(fd), buf, count);
396 }
397
398 static ssize_t fdWrite(FD_t fd, const void * buf, size_t count)
399 {
400     if (count == 0)
401         return 0;
402
403     return write(fdFileno(fd), buf, count);
404 }
405
406 static int fdSeek(FD_t fd, off_t pos, int whence)
407 {
408     return lseek(fdFileno(fd), pos, whence);
409 }
410
411 static int fdClose(FD_t fd)
412 {
413     int fdno;
414     int rc;
415
416     if (fd == NULL) return -2;
417     fdno = fdFileno(fd);
418
419     fdSetFdno(fd, -1);
420
421     rc = ((fdno >= 0) ? close(fdno) : -2);
422
423     fdFree(fd);
424     return rc;
425 }
426
427 static FD_t fdOpen(const char *path, int flags, mode_t mode)
428 {
429     FD_t fd;
430     int fdno;
431
432     fdno = open(path, flags, mode);
433     if (fdno < 0) return NULL;
434     if (fcntl(fdno, F_SETFD, FD_CLOEXEC)) {
435         (void) close(fdno);
436         return NULL;
437     }
438     fd = fdNew(path);
439     fdSetFdno(fd, fdno);
440     fd->flags = flags;
441     return fd;
442 }
443
444 static long fdTell(FD_t fd)
445 {
446     return lseek(Fileno(fd), 0, SEEK_CUR);
447 }
448
449 static const struct FDIO_s fdio_s = {
450   fdRead, fdWrite, fdSeek, fdClose, fdLink, fdFree, fdNew, fdFileno,
451   fdOpen, NULL, fdGetFp, fdFlush, fdTell
452 };
453 static const FDIO_t fdio = &fdio_s ;
454
455 off_t ufdCopy(FD_t sfd, FD_t tfd)
456 {
457     char buf[BUFSIZ];
458     ssize_t rdbytes, wrbytes;
459     off_t total = 0;
460
461     while (1) {
462         rdbytes = Fread(buf, sizeof(buf[0]), sizeof(buf), sfd);
463
464         if (rdbytes > 0) {
465             wrbytes = Fwrite(buf, sizeof(buf[0]), rdbytes, tfd);
466             if (wrbytes != rdbytes) {
467                 total = -1;
468                 break;
469             }
470             total += wrbytes;
471         } else {
472             if (rdbytes < 0)
473                 total = -1;
474             break;
475         }
476     }
477
478     return total;
479 }
480
481 /*
482  * Deal with remote url's by fetching them with a helper application
483  * and treat as local file afterwards.
484  * TODO:
485  * - better error checking + reporting 
486  * - curl & friends don't know about hkp://, transform to http?
487  */
488
489 static FD_t urlOpen(const char * url, int flags, mode_t mode)
490 {
491     FD_t fd;
492     char *dest = NULL;
493     int rc = 1; /* assume failure */
494
495     fd = rpmMkTempFile(NULL, &dest);
496     if (fd == NULL) {
497         return NULL;
498     }
499     Fclose(fd);
500
501     rc = urlGetFile(url, dest);
502     if (rc == 0) {
503         fd = fdOpen(dest, flags, mode);
504         unlink(dest);
505     } else {
506         fd = NULL;
507     }
508     dest = _free(dest);
509
510     return fd;
511
512 }
513 static FD_t ufdOpen(const char * url, int flags, mode_t mode)
514 {
515     FD_t fd = NULL;
516     const char * path;
517     urltype urlType = urlPath(url, &path);
518
519 if (_rpmio_debug)
520 fprintf(stderr, "*** ufdOpen(%s,0x%x,0%o)\n", url, (unsigned)flags, (unsigned)mode);
521
522     switch (urlType) {
523     case URL_IS_FTP:
524     case URL_IS_HTTPS:
525     case URL_IS_HTTP:
526     case URL_IS_HKP:
527         fd = urlOpen(url, flags, mode);
528         /* we're dealing with local file when urlOpen() returns */
529         urlType = URL_IS_UNKNOWN;
530         break;
531     case URL_IS_DASH:
532         if ((flags & O_ACCMODE) == O_RDWR) {
533             fd = NULL;
534         } else {
535             fd = fdDup((flags & O_ACCMODE) == O_WRONLY ?
536                         STDOUT_FILENO : STDIN_FILENO);
537         }
538         break;
539     case URL_IS_PATH:
540     case URL_IS_UNKNOWN:
541     default:
542         fd = fdOpen(path, flags, mode);
543         break;
544     }
545
546     if (fd == NULL) return NULL;
547
548     fdSetIo(fd, ufdio);
549     fd->urlType = urlType;
550
551     if (Fileno(fd) < 0) {
552         (void) fdClose(fd);
553         return NULL;
554     }
555     return fd;
556 }
557
558 static const struct FDIO_s ufdio_s = {
559   fdRead, fdWrite, fdSeek, fdClose, fdLink, fdFree, fdNew, fdFileno,
560   ufdOpen, NULL, fdGetFp, fdFlush, fdTell
561 };
562 static const FDIO_t ufdio = &ufdio_s ;
563
564 /* =============================================================== */
565 /* Support for GZIP library.  */
566 #include <zlib.h>
567
568 static void * gzdFileno(FD_t fd)
569 {
570     return iotFileno(fd, gzdio);
571 }
572
573 static
574 FD_t gzdOpen(const char * path, const char * fmode)
575 {
576     FD_t fd;
577     gzFile gzfile;
578     if ((gzfile = gzopen(path, fmode)) == NULL)
579         return NULL;
580     fd = fdNew(path);
581     fdPop(fd); fdPush(fd, gzdio, gzfile, -1);
582     
583     return fdLink(fd);
584 }
585
586 static FD_t gzdFdopen(FD_t fd, const char *fmode)
587 {
588     int fdno;
589     gzFile gzfile;
590
591     if (fd == NULL || fmode == NULL) return NULL;
592     fdno = fdFileno(fd);
593     fdSetFdno(fd, -1);          /* XXX skip the fdio close */
594     if (fdno < 0) return NULL;
595     gzfile = gzdopen(fdno, fmode);
596     if (gzfile == NULL) return NULL;
597
598     fdPush(fd, gzdio, gzfile, fdno);            /* Push gzdio onto stack */
599
600     return fdLink(fd);
601 }
602
603 static int gzdFlush(FD_t fd)
604 {
605     gzFile gzfile;
606     gzfile = gzdFileno(fd);
607     if (gzfile == NULL) return -2;
608     return gzflush(gzfile, Z_SYNC_FLUSH);       /* XXX W2DO? */
609 }
610
611 static ssize_t gzdRead(FD_t fd, void * buf, size_t count)
612 {
613     gzFile gzfile;
614     ssize_t rc;
615
616     gzfile = gzdFileno(fd);
617     if (gzfile == NULL) return -2;      /* XXX can't happen */
618
619     rc = gzread(gzfile, buf, count);
620     if (rc < 0) {
621         int zerror = 0;
622         fd->errcookie = gzerror(gzfile, &zerror);
623         if (zerror == Z_ERRNO) {
624             fd->syserrno = errno;
625             fd->errcookie = strerror(fd->syserrno);
626         }
627     }
628     return rc;
629 }
630
631 static ssize_t gzdWrite(FD_t fd, const void * buf, size_t count)
632 {
633     gzFile gzfile;
634     ssize_t rc;
635
636     gzfile = gzdFileno(fd);
637     if (gzfile == NULL) return -2;      /* XXX can't happen */
638
639     rc = gzwrite(gzfile, (void *)buf, count);
640     if (rc < 0) {
641         int zerror = 0;
642         fd->errcookie = gzerror(gzfile, &zerror);
643         if (zerror == Z_ERRNO) {
644             fd->syserrno = errno;
645             fd->errcookie = strerror(fd->syserrno);
646         }
647     }
648     return rc;
649 }
650
651 /* XXX zlib-1.0.4 has not */
652 static int gzdSeek(FD_t fd, off_t pos, int whence)
653 {
654     off_t p = pos;
655     int rc;
656 #if HAVE_GZSEEK
657     gzFile gzfile;
658
659     if (fd == NULL) return -2;
660
661     gzfile = gzdFileno(fd);
662     if (gzfile == NULL) return -2;      /* XXX can't happen */
663
664     rc = gzseek(gzfile, p, whence);
665     if (rc < 0) {
666         int zerror = 0;
667         fd->errcookie = gzerror(gzfile, &zerror);
668         if (zerror == Z_ERRNO) {
669             fd->syserrno = errno;
670             fd->errcookie = strerror(fd->syserrno);
671         }
672     }
673 #else
674     rc = -2;
675 #endif
676     return rc;
677 }
678
679 static int gzdClose(FD_t fd)
680 {
681     gzFile gzfile;
682     int rc;
683
684     gzfile = gzdFileno(fd);
685     if (gzfile == NULL) return -2;      /* XXX can't happen */
686
687     rc = gzclose(gzfile);
688
689     /* XXX TODO: preserve fd if errors */
690
691     if (fd) {
692         if (rc < 0) {
693             fd->errcookie = "gzclose error";
694             if (rc == Z_ERRNO) {
695                 fd->syserrno = errno;
696                 fd->errcookie = strerror(fd->syserrno);
697             }
698         }
699     }
700
701     if (_rpmio_debug || rpmIsDebug()) fdstat_print(fd, "GZDIO", stderr);
702     if (rc == 0)
703         fdFree(fd);
704     return rc;
705 }
706
707 static long gzdTell(FD_t fd)
708 {
709     off_t pos = -1;
710     gzFile gzfile = gzdFileno(fd);
711
712     if (gzfile != NULL) {
713 #if HAVE_GZSEEK
714         pos = gztell(gzfile);
715         if (pos < 0) {
716             int zerror = 0;
717             fd->errcookie = gzerror(gzfile, &zerror);
718             if (zerror == Z_ERRNO) {
719                 fd->syserrno = errno;
720                 fd->errcookie = strerror(fd->syserrno);
721             }
722         }
723 #else
724         pos = -2;
725 #endif    
726     }
727     return pos;
728 }
729 static const struct FDIO_s gzdio_s = {
730   gzdRead, gzdWrite, gzdSeek, gzdClose, fdLink, fdFree, fdNew, fdFileno,
731   NULL, gzdOpen, gzdFileno, gzdFlush, gzdTell
732 };
733 static const FDIO_t gzdio = &gzdio_s ;
734
735 /* =============================================================== */
736 /* Support for BZIP2 library.  */
737 #if HAVE_BZLIB_H
738
739 #include <bzlib.h>
740
741 static void * bzdFileno(FD_t fd)
742 {
743     return iotFileno(fd, bzdio);
744 }
745
746 static FD_t bzdOpen(const char * path, const char * mode)
747 {
748     FD_t fd;
749     BZFILE *bzfile;;
750     if ((bzfile = BZ2_bzopen(path, mode)) == NULL)
751         return NULL;
752     fd = fdNew(path);
753     fdPop(fd); fdPush(fd, bzdio, bzfile, -1);
754     return fdLink(fd);
755 }
756
757 static FD_t bzdFdopen(FD_t fd, const char * fmode)
758 {
759     int fdno;
760     BZFILE *bzfile;
761
762     if (fd == NULL || fmode == NULL) return NULL;
763     fdno = fdFileno(fd);
764     fdSetFdno(fd, -1);          /* XXX skip the fdio close */
765     if (fdno < 0) return NULL;
766     bzfile = BZ2_bzdopen(fdno, fmode);
767     if (bzfile == NULL) return NULL;
768
769     fdPush(fd, bzdio, bzfile, fdno);            /* Push bzdio onto stack */
770
771     return fdLink(fd);
772 }
773
774 static int bzdFlush(FD_t fd)
775 {
776     return BZ2_bzflush(bzdFileno(fd));
777 }
778
779 static ssize_t bzdRead(FD_t fd, void * buf, size_t count)
780 {
781     BZFILE *bzfile;
782     ssize_t rc = 0;
783
784     bzfile = bzdFileno(fd);
785     if (bzfile)
786         rc = BZ2_bzread(bzfile, buf, count);
787     if (rc == -1) {
788         int zerror = 0;
789         if (bzfile)
790             fd->errcookie = BZ2_bzerror(bzfile, &zerror);
791     }
792     return rc;
793 }
794
795 static ssize_t bzdWrite(FD_t fd, const void * buf, size_t count)
796 {
797     BZFILE *bzfile;
798     ssize_t rc;
799
800     bzfile = bzdFileno(fd);
801     rc = BZ2_bzwrite(bzfile, (void *)buf, count);
802     if (rc == -1) {
803         int zerror = 0;
804         fd->errcookie = BZ2_bzerror(bzfile, &zerror);
805     }
806     return rc;
807 }
808
809 static int bzdClose(FD_t fd)
810 {
811     BZFILE *bzfile;
812     int rc;
813
814     bzfile = bzdFileno(fd);
815
816     if (bzfile == NULL) return -2;
817     /* FIX: check rc */
818     BZ2_bzclose(bzfile);
819     rc = 0;     /* XXX FIXME */
820
821     /* XXX TODO: preserve fd if errors */
822
823     if (fd) {
824         if (rc == -1) {
825             int zerror = 0;
826             fd->errcookie = BZ2_bzerror(bzfile, &zerror);
827         }
828     }
829
830     if (_rpmio_debug || rpmIsDebug()) fdstat_print(fd, "BZDIO", stderr);
831     if (rc == 0)
832         fdFree(fd);
833     return rc;
834 }
835
836 static const struct FDIO_s bzdio_s = {
837   bzdRead, bzdWrite, NULL, bzdClose, fdLink, fdFree, fdNew, fdFileno,
838   NULL, bzdOpen, bzdFileno, bzdFlush, NULL
839 };
840 static const FDIO_t bzdio = &bzdio_s ;
841
842 #endif  /* HAVE_BZLIB_H */
843
844 static const char * getFdErrstr (FD_t fd)
845 {
846     const char *errstr = NULL;
847
848     if (fdGetIo(fd) == gzdio) {
849         errstr = fd->errcookie;
850     } else
851 #ifdef  HAVE_BZLIB_H
852     if (fdGetIo(fd) == bzdio) {
853         errstr = fd->errcookie;
854     } else
855 #endif  /* HAVE_BZLIB_H */
856 #ifdef  HAVE_LZMA_H
857     if (fdGetIo(fd) == xzdio || fdGetIo(fd) == lzdio) {
858         errstr = fd->errcookie;
859     } else
860 #endif  /* HAVE_LZMA_H */
861     {
862         errstr = (fd->syserrno ? strerror(fd->syserrno) : "");
863     }
864
865     return errstr;
866 }
867
868 /* =============================================================== */
869 /* Support for LZMA library.  */
870
871 #ifdef HAVE_LZMA_H
872
873 #include <sys/types.h>
874 #include <inttypes.h>
875 #include <lzma.h>
876
877 #define kBufferSize (1 << 15)
878
879 typedef struct lzfile {
880   /* IO buffer */
881     uint8_t buf[kBufferSize];
882
883     lzma_stream strm;
884
885     FILE *file;
886
887     int encoding;
888     int eof;
889
890 } LZFILE;
891
892 static LZFILE *lzopen_internal(const char *path, const char *mode, int fd, int xz)
893 {
894     int level = 7;      /* Use XZ's default compression level if unspecified */
895     int encoding = 0;
896     FILE *fp;
897     LZFILE *lzfile;
898     lzma_ret ret;
899     lzma_stream init_strm = LZMA_STREAM_INIT;
900
901     for (; *mode; mode++) {
902         if (*mode == 'w')
903             encoding = 1;
904         else if (*mode == 'r')
905             encoding = 0;
906         else if (*mode >= '1' && *mode <= '9')
907             level = *mode - '0';
908     }
909     if (fd != -1)
910         fp = fdopen(fd, encoding ? "w" : "r");
911     else
912         fp = fopen(path, encoding ? "w" : "r");
913     if (!fp)
914         return 0;
915     lzfile = calloc(1, sizeof(*lzfile));
916     if (!lzfile) {
917         fclose(fp);
918         return 0;
919     }
920     
921     lzfile->file = fp;
922     lzfile->encoding = encoding;
923     lzfile->eof = 0;
924     lzfile->strm = init_strm;
925     if (encoding) {
926         if (xz) {
927             ret = lzma_easy_encoder(&lzfile->strm, level, LZMA_CHECK_SHA256);
928         } else {
929             lzma_options_lzma options;
930             lzma_lzma_preset(&options, level);
931             ret = lzma_alone_encoder(&lzfile->strm, &options);
932         }
933     } else {    /* lzma_easy_decoder_memusage(level) is not ready yet, use hardcoded limit for now */
934         ret = lzma_auto_decoder(&lzfile->strm, 100<<20, 0);
935     }
936     if (ret != LZMA_OK) {
937         fclose(fp);
938         free(lzfile);
939         return 0;
940     }
941     return lzfile;
942 }
943
944 static LZFILE *xzopen(const char *path, const char *mode)
945 {
946     return lzopen_internal(path, mode, -1, 1);
947 }
948
949 static LZFILE *xzdopen(int fd, const char *mode)
950 {
951     if (fd < 0)
952         return 0;
953     return lzopen_internal(0, mode, fd, 1);
954 }
955
956 static LZFILE *lzopen(const char *path, const char *mode)
957 {
958     return lzopen_internal(path, mode, -1, 0);
959 }
960
961 static LZFILE *lzdopen(int fd, const char *mode)
962 {
963     if (fd < 0)
964         return 0;
965     return lzopen_internal(0, mode, fd, 0);
966 }
967
968 static int lzflush(LZFILE *lzfile)
969 {
970     return fflush(lzfile->file);
971 }
972
973 static int lzclose(LZFILE *lzfile)
974 {
975     lzma_ret ret;
976     size_t n;
977     int rc;
978
979     if (!lzfile)
980         return -1;
981     if (lzfile->encoding) {
982         for (;;) {
983             lzfile->strm.avail_out = kBufferSize;
984             lzfile->strm.next_out = lzfile->buf;
985             ret = lzma_code(&lzfile->strm, LZMA_FINISH);
986             if (ret != LZMA_OK && ret != LZMA_STREAM_END)
987                 return -1;
988             n = kBufferSize - lzfile->strm.avail_out;
989             if (n && fwrite(lzfile->buf, 1, n, lzfile->file) != n)
990                 return -1;
991             if (ret == LZMA_STREAM_END)
992                 break;
993         }
994     }
995     lzma_end(&lzfile->strm);
996     rc = fclose(lzfile->file);
997     free(lzfile);
998     return rc;
999 }
1000
1001 static ssize_t lzread(LZFILE *lzfile, void *buf, size_t len)
1002 {
1003     lzma_ret ret;
1004     int eof = 0;
1005
1006     if (!lzfile || lzfile->encoding)
1007       return -1;
1008     if (lzfile->eof)
1009       return 0;
1010     lzfile->strm.next_out = buf;
1011     lzfile->strm.avail_out = len;
1012     for (;;) {
1013         if (!lzfile->strm.avail_in) {
1014             lzfile->strm.next_in = lzfile->buf;
1015             lzfile->strm.avail_in = fread(lzfile->buf, 1, kBufferSize, lzfile->file);
1016             if (!lzfile->strm.avail_in)
1017                 eof = 1;
1018         }
1019         ret = lzma_code(&lzfile->strm, LZMA_RUN);
1020         if (ret == LZMA_STREAM_END) {
1021             lzfile->eof = 1;
1022             return len - lzfile->strm.avail_out;
1023         }
1024         if (ret != LZMA_OK)
1025             return -1;
1026         if (!lzfile->strm.avail_out)
1027             return len;
1028         if (eof)
1029             return -1;
1030       }
1031 }
1032
1033 static ssize_t lzwrite(LZFILE *lzfile, void *buf, size_t len)
1034 {
1035     lzma_ret ret;
1036     size_t n;
1037     if (!lzfile || !lzfile->encoding)
1038         return -1;
1039     if (!len)
1040         return 0;
1041     lzfile->strm.next_in = buf;
1042     lzfile->strm.avail_in = len;
1043     for (;;) {
1044         lzfile->strm.next_out = lzfile->buf;
1045         lzfile->strm.avail_out = kBufferSize;
1046         ret = lzma_code(&lzfile->strm, LZMA_RUN);
1047         if (ret != LZMA_OK)
1048             return -1;
1049         n = kBufferSize - lzfile->strm.avail_out;
1050         if (n && fwrite(lzfile->buf, 1, n, lzfile->file) != n)
1051             return -1;
1052         if (!lzfile->strm.avail_in)
1053             return len;
1054     }
1055 }
1056
1057 static void * lzdFileno(FD_t fd)
1058 {
1059     void * rc = NULL;
1060     
1061     if (fd == NULL)
1062         return NULL;
1063
1064     for (int i = fd->nfps; i >= 0; i--) {
1065         FDSTACK_t * fps = &fd->fps[i];
1066         if (fps->io != xzdio && fps->io != lzdio)
1067             continue;
1068         rc = fps->fp;
1069         break;
1070     }
1071     return rc;
1072 }
1073
1074 static FD_t xzdOpen(const char * path, const char * mode)
1075 {
1076     FD_t fd;
1077     LZFILE *lzfile;
1078     if ((lzfile = xzopen(path, mode)) == NULL)
1079         return NULL;
1080     fd = fdNew(path);
1081     fdPop(fd); fdPush(fd, xzdio, lzfile, -1);
1082     return fdLink(fd);
1083 }
1084
1085 static FD_t xzdFdopen(FD_t fd, const char * fmode)
1086 {
1087     int fdno;
1088     LZFILE *lzfile;
1089
1090     if (fd == NULL || fmode == NULL) return NULL;
1091     fdno = fdFileno(fd);
1092     fdSetFdno(fd, -1);          /* XXX skip the fdio close */
1093     if (fdno < 0) return NULL;
1094     lzfile = xzdopen(fdno, fmode);
1095     if (lzfile == NULL) return NULL;
1096     fdPush(fd, xzdio, lzfile, fdno);
1097     return fdLink(fd);
1098 }
1099
1100 static FD_t lzdOpen(const char * path, const char * mode)
1101 {
1102     FD_t fd;
1103     LZFILE *lzfile;
1104     if ((lzfile = lzopen(path, mode)) == NULL)
1105         return NULL;
1106     fd = fdNew(path);
1107     fdPop(fd); fdPush(fd, xzdio, lzfile, -1);
1108     return fdLink(fd);
1109 }
1110
1111 static FD_t lzdFdopen(FD_t fd, const char * fmode)
1112 {
1113     int fdno;
1114     LZFILE *lzfile;
1115
1116     if (fd == NULL || fmode == NULL) return NULL;
1117     fdno = fdFileno(fd);
1118     fdSetFdno(fd, -1);          /* XXX skip the fdio close */
1119     if (fdno < 0) return NULL;
1120     lzfile = lzdopen(fdno, fmode);
1121     if (lzfile == NULL) return NULL;
1122     fdPush(fd, xzdio, lzfile, fdno);
1123     return fdLink(fd);
1124 }
1125
1126 static int lzdFlush(FD_t fd)
1127 {
1128     return lzflush(lzdFileno(fd));
1129 }
1130
1131 static ssize_t lzdRead(FD_t fd, void * buf, size_t count)
1132 {
1133     LZFILE *lzfile;
1134     ssize_t rc = 0;
1135
1136     lzfile = lzdFileno(fd);
1137     if (lzfile)
1138         rc = lzread(lzfile, buf, count);
1139     if (rc == -1) {
1140         fd->errcookie = "Lzma: decoding error";
1141     }
1142     return rc;
1143 }
1144
1145 static ssize_t lzdWrite(FD_t fd, const void * buf, size_t count)
1146 {
1147     LZFILE *lzfile;
1148     ssize_t rc = 0;
1149
1150     lzfile = lzdFileno(fd);
1151
1152     rc = lzwrite(lzfile, (void *)buf, count);
1153     if (rc < 0) {
1154         fd->errcookie = "Lzma: encoding error";
1155     }
1156     return rc;
1157 }
1158
1159 static int lzdClose(FD_t fd)
1160 {
1161     LZFILE *lzfile;
1162     int rc;
1163
1164     lzfile = lzdFileno(fd);
1165
1166     if (lzfile == NULL) return -2;
1167     rc = lzclose(lzfile);
1168
1169     /* XXX TODO: preserve fd if errors */
1170
1171     if (fd) {
1172         if (rc == -1) {
1173             fd->errcookie = "lzclose error";
1174             fd->syserrno = errno;
1175             fd->errcookie = strerror(fd->syserrno);
1176         }
1177     }
1178
1179     if (_rpmio_debug || rpmIsDebug()) fdstat_print(fd, "XZDIO", stderr);
1180     if (rc == 0)
1181         fdFree(fd);
1182     return rc;
1183 }
1184
1185 static struct FDIO_s xzdio_s = {
1186   lzdRead, lzdWrite, NULL, lzdClose, NULL, NULL, NULL, fdFileno,
1187   NULL, xzdOpen, lzdFileno, lzdFlush, NULL
1188 };
1189 static const FDIO_t xzdio = &xzdio_s;
1190
1191 static struct FDIO_s lzdio_s = {
1192   lzdRead, lzdWrite, NULL, lzdClose, NULL, NULL, NULL, fdFileno,
1193   NULL, lzdOpen, lzdFileno, lzdFlush, NULL
1194 };
1195 static const FDIO_t lzdio = &lzdio_s;
1196
1197 #endif  /* HAVE_LZMA_H */
1198
1199 /* =============================================================== */
1200
1201 const char *Fstrerror(FD_t fd)
1202 {
1203     if (fd == NULL)
1204         return (errno ? strerror(errno) : "");
1205     return getFdErrstr(fd);
1206 }
1207
1208 #define FDIOVEC(_fd, _vec)      \
1209   ((fdGetIo(_fd) && fdGetIo(_fd)->_vec) ? fdGetIo(_fd)->_vec : NULL)
1210
1211 ssize_t Fread(void *buf, size_t size, size_t nmemb, FD_t fd)
1212 {
1213     ssize_t rc = -1;
1214
1215     if (fd != NULL) {
1216         fdio_read_function_t _read = FDIOVEC(fd, read);
1217
1218         fdstat_enter(fd, FDSTAT_READ);
1219         do {
1220             rc = (_read ? (*_read) (fd, buf, size * nmemb) : -2);
1221         } while (rc == -1 && errno == EINTR);
1222         fdstat_exit(fd, FDSTAT_READ, rc);
1223
1224         if (fd->digests && rc > 0)
1225             fdUpdateDigests(fd, buf, rc);
1226     }
1227
1228     DBGIO(fd, (stderr, "==>\tFread(%p,%p,%ld) rc %ld %s\n",
1229           fd, buf, (long)size * nmemb, (long)rc, fdbg(fd)));
1230
1231     return rc;
1232 }
1233
1234 ssize_t Fwrite(const void *buf, size_t size, size_t nmemb, FD_t fd)
1235 {
1236     ssize_t rc = -1;
1237
1238     if (fd != NULL) {
1239         fdio_write_function_t _write = FDIOVEC(fd, write);
1240         
1241         fdstat_enter(fd, FDSTAT_WRITE);
1242         do {
1243             rc = (_write ? _write(fd, buf, size * nmemb) : -2);
1244         } while (rc == -1 && errno == EINTR);
1245         fdstat_exit(fd, FDSTAT_WRITE, rc);
1246
1247         if (fd->digests && rc > 0)
1248             fdUpdateDigests(fd, buf, rc);
1249     }
1250
1251     DBGIO(fd, (stderr, "==>\tFwrite(%p,%p,%ld) rc %ld %s\n",
1252           fd, buf, (long)size * nmemb, (long)rc, fdbg(fd)));
1253
1254     return rc;
1255 }
1256
1257 int Fseek(FD_t fd, off_t offset, int whence)
1258 {
1259     int rc = -1;
1260
1261     if (fd != NULL) {
1262         fdio_seek_function_t _seek = FDIOVEC(fd, seek);
1263
1264         fdstat_enter(fd, FDSTAT_SEEK);
1265         rc = (_seek ? _seek(fd, offset, whence) : -2);
1266         fdstat_exit(fd, FDSTAT_SEEK, rc);
1267     }
1268
1269     DBGIO(fd, (stderr, "==>\tFseek(%p,%ld,%d) rc %lx %s\n",
1270           fd, (long)offset, whence, (unsigned long)rc, fdbg(fd)));
1271
1272     return rc;
1273 }
1274
1275 int Fclose(FD_t fd)
1276 {
1277     int rc = 0, ec = 0;
1278
1279     if (fd == NULL)
1280         return -1;
1281
1282     fd = fdLink(fd);
1283     fdstat_enter(fd, FDSTAT_CLOSE);
1284     while (fd->nfps >= 0) {
1285         fdio_close_function_t _close = FDIOVEC(fd, close);
1286         rc = _close ? _close(fd) : -2;
1287
1288         if (fd->nfps == 0)
1289             break;
1290         if (ec == 0 && rc)
1291             ec = rc;
1292         fdPop(fd);
1293     }
1294     fdstat_exit(fd, FDSTAT_CLOSE, rc);
1295     DBGIO(fd, (stderr, "==>\tFclose(%p) rc %lx %s\n",
1296           (fd ? fd : NULL), (unsigned long)rc, fdbg(fd)));
1297
1298     fdFree(fd);
1299     return ec;
1300 }
1301
1302 /**
1303  * Convert stdio fmode to open(2) mode, filtering out zlib/bzlib flags.
1304  *      returns stdio[0] = NUL on error.
1305  *
1306  * - gzopen:    [0-9] is compression level
1307  * - gzopen:    'f' is filtered (Z_FILTERED)
1308  * - gzopen:    'h' is Huffman encoding (Z_HUFFMAN_ONLY)
1309  * - bzopen:    [1-9] is block size (modulo 100K)
1310  * - bzopen:    's' is smallmode
1311  * - HACK:      '.' terminates, rest is type of I/O
1312  */
1313 static void cvtfmode (const char *m,
1314                                 char *stdio, size_t nstdio,
1315                                 char *other, size_t nother,
1316                                 const char **end, int * f)
1317 {
1318     int flags = 0;
1319     char c;
1320
1321     switch (*m) {
1322     case 'a':
1323         flags |= O_WRONLY | O_CREAT | O_APPEND;
1324         if (--nstdio > 0) *stdio++ = *m;
1325         break;
1326     case 'w':
1327         flags |= O_WRONLY | O_CREAT | O_TRUNC;
1328         if (--nstdio > 0) *stdio++ = *m;
1329         break;
1330     case 'r':
1331         flags |= O_RDONLY;
1332         if (--nstdio > 0) *stdio++ = *m;
1333         break;
1334     default:
1335         *stdio = '\0';
1336         return;
1337         break;
1338     }
1339     m++;
1340
1341     while ((c = *m++) != '\0') {
1342         switch (c) {
1343         case '.':
1344             break;
1345         case '+':
1346             flags &= ~(O_RDONLY|O_WRONLY);
1347             flags |= O_RDWR;
1348             if (--nstdio > 0) *stdio++ = c;
1349             continue;
1350             break;
1351         case 'b':
1352             if (--nstdio > 0) *stdio++ = c;
1353             continue;
1354             break;
1355         case 'x':
1356             flags |= O_EXCL;
1357             if (--nstdio > 0) *stdio++ = c;
1358             continue;
1359             break;
1360         default:
1361             if (--nother > 0) *other++ = c;
1362             continue;
1363             break;
1364         }
1365         break;
1366     }
1367
1368     *stdio = *other = '\0';
1369     if (end != NULL)
1370         *end = (*m != '\0' ? m : NULL);
1371     if (f != NULL)
1372         *f = flags;
1373 }
1374
1375 FD_t Fdopen(FD_t ofd, const char *fmode)
1376 {
1377     char stdio[20], other[20], zstdio[40];
1378     const char *end = NULL;
1379     FDIO_t iof = NULL;
1380     FD_t fd = ofd;
1381
1382 if (_rpmio_debug)
1383 fprintf(stderr, "*** Fdopen(%p,%s) %s\n", fd, fmode, fdbg(fd));
1384
1385     if (fd == NULL || fmode == NULL)
1386         return NULL;
1387
1388     cvtfmode(fmode, stdio, sizeof(stdio), other, sizeof(other), &end, NULL);
1389     if (stdio[0] == '\0')
1390         return NULL;
1391     zstdio[0] = '\0';
1392     strncat(zstdio, stdio, sizeof(zstdio) - strlen(zstdio) - 1);
1393     strncat(zstdio, other, sizeof(zstdio) - strlen(zstdio) - 1);
1394
1395     if (end == NULL && other[0] == '\0')
1396         return fd;
1397
1398     if (end && *end) {
1399         if (rstreq(end, "fdio")) {
1400             iof = fdio;
1401         } else if (rstreq(end, "gzdio") || rstreq(end, "gzip")) {
1402             iof = gzdio;
1403             fd = gzdFdopen(fd, zstdio);
1404 #if HAVE_BZLIB_H
1405         } else if (rstreq(end, "bzdio") || rstreq(end, "bzip2")) {
1406             iof = bzdio;
1407             fd = bzdFdopen(fd, zstdio);
1408 #endif
1409 #if HAVE_LZMA_H
1410         } else if (rstreq(end, "xzdio") || rstreq(end, "xz")) {
1411             iof = xzdio;
1412             fd = xzdFdopen(fd, zstdio);
1413         } else if (rstreq(end, "lzdio") || rstreq(end, "lzma")) {
1414             iof = lzdio;
1415             fd = lzdFdopen(fd, zstdio);
1416 #endif
1417         } else if (rstreq(end, "ufdio")) {
1418             iof = ufdio;
1419         }
1420     } else if (other[0] != '\0') {
1421         for (end = other; *end && strchr("0123456789fh", *end); end++)
1422             {};
1423         if (*end == '\0') {
1424             iof = gzdio;
1425             fd = gzdFdopen(fd, zstdio);
1426         }
1427     }
1428     if (iof == NULL)
1429         return fd;
1430
1431 DBGIO(fd, (stderr, "==> Fdopen(%p,\"%s\") returns fd %p %s\n", ofd, fmode, (fd ? fd : NULL), fdbg(fd)));
1432     return fd;
1433 }
1434
1435 FD_t Fopen(const char *path, const char *fmode)
1436 {
1437     char stdio[20], other[20];
1438     const char *end = NULL;
1439     mode_t perms = 0666;
1440     int flags = 0;
1441     FD_t fd;
1442
1443     if (path == NULL || fmode == NULL)
1444         return NULL;
1445
1446     stdio[0] = '\0';
1447     cvtfmode(fmode, stdio, sizeof(stdio), other, sizeof(other), &end, &flags);
1448     if (stdio[0] == '\0')
1449         return NULL;
1450
1451     if (end == NULL || rstreq(end, "fdio")) {
1452 if (_rpmio_debug)
1453 fprintf(stderr, "*** Fopen fdio path %s fmode %s\n", path, fmode);
1454         fd = fdOpen(path, flags, perms);
1455         if (fdFileno(fd) < 0) {
1456             if (fd) (void) fdClose(fd);
1457             return NULL;
1458         }
1459     } else {
1460         /* XXX gzdio and bzdio here too */
1461
1462         switch (urlIsURL(path)) {
1463         case URL_IS_HTTPS:
1464         case URL_IS_HTTP:
1465         case URL_IS_HKP:
1466         case URL_IS_PATH:
1467         case URL_IS_DASH:
1468         case URL_IS_FTP:
1469         case URL_IS_UNKNOWN:
1470 if (_rpmio_debug)
1471 fprintf(stderr, "*** Fopen ufdio path %s fmode %s\n", path, fmode);
1472             fd = ufdOpen(path, flags, perms);
1473             if (fd == NULL || !(fdFileno(fd) >= 0))
1474                 return fd;
1475             break;
1476         default:
1477 if (_rpmio_debug)
1478 fprintf(stderr, "*** Fopen WTFO path %s fmode %s\n", path, fmode);
1479             return NULL;
1480             break;
1481         }
1482
1483     }
1484
1485     if (fd)
1486         fd = Fdopen(fd, fmode);
1487
1488     DBGIO(fd, (stderr, "==>\tFopen(\"%s\",%x,0%o) %s\n",
1489           path, (unsigned)flags, (unsigned)perms, fdbg(fd)));
1490
1491     return fd;
1492 }
1493
1494 int Fflush(FD_t fd)
1495 {
1496     int rc = -1;
1497     if (fd != NULL) {
1498         fdio_fflush_function_t _fflush = FDIOVEC(fd, _fflush);
1499
1500         rc = (_fflush ? _fflush(fd) : -2);
1501     }
1502     return rc;
1503 }
1504
1505 off_t Ftell(FD_t fd)
1506 {
1507     off_t pos = -1;
1508     if (fd != NULL) {
1509         fdio_ftell_function_t _ftell = FDIOVEC(fd, _ftell);
1510
1511         pos = (_ftell ? _ftell(fd) : -2);
1512     }
1513     return pos;
1514 }
1515
1516 int Ferror(FD_t fd)
1517 {
1518     int i, rc = 0;
1519
1520     if (fd == NULL) return -1;
1521     for (i = fd->nfps; rc == 0 && i >= 0; i--) {
1522         FDSTACK_t * fps = &fd->fps[i];
1523         int ec;
1524         
1525         if (fps->io == gzdio) {
1526             ec = (fd->syserrno || fd->errcookie != NULL) ? -1 : 0;
1527             i--;        /* XXX fdio under gzdio always has fdno == -1 */
1528 #if HAVE_BZLIB_H
1529         } else if (fps->io == bzdio) {
1530             ec = (fd->syserrno  || fd->errcookie != NULL) ? -1 : 0;
1531             i--;        /* XXX fdio under bzdio always has fdno == -1 */
1532 #endif
1533 #if HAVE_LZMA_H
1534         } else if (fps->io == xzdio || fps->io == lzdio) {
1535             ec = (fd->syserrno  || fd->errcookie != NULL) ? -1 : 0;
1536             i--;        /* XXX fdio under xzdio/lzdio always has fdno == -1 */
1537 #endif
1538         } else {
1539         /* XXX need to check ufdio/gzdio/bzdio/fdio errors correctly. */
1540             ec = (fdFileno(fd) < 0 ? -1 : 0);
1541         }
1542
1543         if (rc == 0 && ec)
1544             rc = ec;
1545     }
1546 DBGIO(fd, (stderr, "==> Ferror(%p) rc %d %s\n", fd, rc, fdbg(fd)));
1547     return rc;
1548 }
1549
1550 int Fileno(FD_t fd)
1551 {
1552     int i, rc = -1;
1553
1554     if (fd == NULL) return -1;
1555     for (i = fd->nfps ; rc == -1 && i >= 0; i--) {
1556         rc = fd->fps[i].fdno;
1557     }
1558     
1559 DBGIO(fd, (stderr, "==> Fileno(%p) rc %d %s\n", (fd ? fd : NULL), rc, fdbg(fd)));
1560     return rc;
1561 }
1562
1563 /* XXX this is naive */
1564 int Fcntl(FD_t fd, int op, void *lip)
1565 {
1566     return fcntl(Fileno(fd), op, lip);
1567 }
1568
1569 rpmop fdOp(FD_t fd, fdOpX opx)
1570 {
1571     rpmop op = NULL;
1572
1573     if (fd != NULL && fd->stats != NULL && opx >= 0 && opx < FDSTAT_MAX)
1574         op = fd->stats->ops + opx;
1575     return op;
1576 }
1577
1578 int rpmioSlurp(const char * fn, uint8_t ** bp, ssize_t * blenp)
1579 {
1580     static const ssize_t blenmax = (32 * BUFSIZ);
1581     ssize_t blen = 0;
1582     uint8_t * b = NULL;
1583     ssize_t size;
1584     FD_t fd;
1585     int rc = 0;
1586
1587     fd = Fopen(fn, "r.ufdio");
1588     if (fd == NULL || Ferror(fd)) {
1589         rc = 2;
1590         goto exit;
1591     }
1592
1593     size = fdSize(fd);
1594     blen = (size >= 0 ? size : blenmax);
1595     if (blen) {
1596         int nb;
1597         b = xmalloc(blen+1);
1598         b[0] = '\0';
1599         nb = Fread(b, sizeof(*b), blen, fd);
1600         if (Ferror(fd) || (size > 0 && nb != blen)) {
1601             rc = 1;
1602             goto exit;
1603         }
1604         if (blen == blenmax && nb < blen) {
1605             blen = nb;
1606             b = xrealloc(b, blen+1);
1607         }
1608         b[blen] = '\0';
1609     }
1610
1611 exit:
1612     if (fd) (void) Fclose(fd);
1613         
1614     if (rc) {
1615         if (b) free(b);
1616         b = NULL;
1617         blen = 0;
1618     }
1619
1620     if (bp) *bp = b;
1621     else if (b) free(b);
1622
1623     if (blenp) *blenp = blen;
1624
1625     return rc;
1626 }
1627
1628 void fdInitDigest(FD_t fd, int hashalgo, rpmDigestFlags flags)
1629 {
1630     if (fd->digests == NULL) {
1631         fd->digests = rpmDigestBundleNew();
1632     }
1633     fdstat_enter(fd, FDSTAT_DIGEST);
1634     rpmDigestBundleAdd(fd->digests, hashalgo, flags);
1635     fdstat_exit(fd, FDSTAT_DIGEST, (ssize_t) 0);
1636 }
1637
1638 static void fdUpdateDigests(FD_t fd, const void * buf, size_t buflen)
1639 {
1640     if (fd && fd->digests) {
1641         fdstat_enter(fd, FDSTAT_DIGEST);
1642         rpmDigestBundleUpdate(fd->digests, buf, buflen);
1643         fdstat_exit(fd, FDSTAT_DIGEST, (ssize_t) buflen);
1644     }
1645 }
1646
1647 void fdFiniDigest(FD_t fd, int hashalgo,
1648                 void ** datap, size_t * lenp, int asAscii)
1649 {
1650     if (fd && fd->digests) {
1651         fdstat_enter(fd, FDSTAT_DIGEST);
1652         rpmDigestBundleFinal(fd->digests, hashalgo, datap, lenp, asAscii);
1653         fdstat_exit(fd, FDSTAT_DIGEST, (ssize_t) 0);
1654     }
1655 }
1656
1657