- lclint-3.0.0.15 fiddles.
[platform/upstream/rpm.git] / rpmio / rpmrpc.c
1 /** \ingroup rpmio
2  * \file rpmio/rpmrpc.c
3  */
4
5 #include "system.h"
6 #include <rpmio_internal.h>
7 #include <popt.h>
8 #include "ugid.h"
9 #include "debug.h"
10
11 /*@access FD_t@*/
12 /*@access urlinfo@*/
13
14 /*@-redecl@*/
15 extern int _rpmio_debug;
16 /*@=redecl@*/
17
18 /* =============================================================== */
19 static int ftpMkdir(const char * path, /*@unused@*/ mode_t mode)
20         /*@modifies fileSystem @*/
21 {
22     int rc;
23     if ((rc = ftpCmd("MKD", path, NULL)) != 0)
24         return rc;
25 #if NOTYET
26     {   char buf[20];
27         sprintf(buf, " 0%o", mode);
28         (void) ftpCmd("SITE CHMOD", path, buf);
29     }
30 #endif
31     return rc;
32 }
33
34 static int ftpChdir(const char * path)
35         /*@modifies fileSystem @*/
36 {
37     return ftpCmd("CWD", path, NULL);
38 }
39
40 static int ftpRmdir(const char * path)
41         /*@modifies fileSystem @*/
42 {
43     return ftpCmd("RMD", path, NULL);
44 }
45
46 static int ftpRename(const char * oldpath, const char * newpath)
47         /*@modifies fileSystem @*/
48 {
49     int rc;
50     if ((rc = ftpCmd("RNFR", oldpath, NULL)) != 0)
51         return rc;
52     return ftpCmd("RNTO", newpath, NULL);
53 }
54
55 static int ftpUnlink(const char * path)
56         /*@modifies fileSystem @*/
57 {
58     return ftpCmd("DELE", path, NULL);
59 }
60
61 /* =============================================================== */
62 /* XXX rebuilddb.c: analogues to mkdir(2)/rmdir(2). */
63 int Mkdir (const char * path, mode_t mode)
64 {
65     const char * lpath;
66     int ut = urlPath(path, &lpath);
67
68     switch (ut) {
69     case URL_IS_FTP:
70         return ftpMkdir(path, mode);
71         /*@notreached@*/ break;
72     case URL_IS_HTTP:           /* XXX WRONG WRONG WRONG */
73     case URL_IS_PATH:
74         path = lpath;
75         /*@fallthrough@*/
76     case URL_IS_UNKNOWN:
77         break;
78     case URL_IS_DASH:
79     default:
80         return -2;
81         /*@notreached@*/ break;
82     }
83     return mkdir(path, mode);
84 }
85
86 int Chdir (const char * path)
87 {
88     const char * lpath;
89     int ut = urlPath(path, &lpath);
90
91     switch (ut) {
92     case URL_IS_FTP:
93         return ftpChdir(path);
94         /*@notreached@*/ break;
95     case URL_IS_HTTP:           /* XXX WRONG WRONG WRONG */
96     case URL_IS_PATH:
97         path = lpath;
98         /*@fallthrough@*/
99     case URL_IS_UNKNOWN:
100         break;
101     case URL_IS_DASH:
102     default:
103         return -2;
104         /*@notreached@*/ break;
105     }
106     return chdir(path);
107 }
108
109 int Rmdir (const char * path)
110 {
111     const char * lpath;
112     int ut = urlPath(path, &lpath);
113
114     switch (ut) {
115     case URL_IS_FTP:
116         return ftpRmdir(path);
117         /*@notreached@*/ break;
118     case URL_IS_HTTP:           /* XXX WRONG WRONG WRONG */
119     case URL_IS_PATH:
120         path = lpath;
121         /*@fallthrough@*/
122     case URL_IS_UNKNOWN:
123         break;
124     case URL_IS_DASH:
125     default:
126         return -2;
127         /*@notreached@*/ break;
128     }
129     return rmdir(path);
130 }
131
132 /* XXX rpmdb.c: analogue to rename(2). */
133
134 int Rename (const char * oldpath, const char * newpath)
135 {
136     const char *oe = NULL;
137     const char *ne = NULL;
138     int oldut, newut;
139
140     /* XXX lib/install.c used to rely on this behavior. */
141     if (!strcmp(oldpath, newpath)) return 0;
142
143     oldut = urlPath(oldpath, &oe);
144     switch (oldut) {
145     case URL_IS_FTP:            /* XXX WRONG WRONG WRONG */
146     case URL_IS_HTTP:           /* XXX WRONG WRONG WRONG */
147     case URL_IS_PATH:
148     case URL_IS_UNKNOWN:
149         break;
150     case URL_IS_DASH:
151     default:
152         return -2;
153         /*@notreached@*/ break;
154     }
155
156     newut = urlPath(newpath, &ne);
157     switch (newut) {
158     case URL_IS_FTP:
159 if (_rpmio_debug)
160 fprintf(stderr, "*** rename old %*s new %*s\n", (int)(oe - oldpath), oldpath, (int)(ne - newpath), newpath);
161         if (!(oldut == newut && oe && ne && (oe - oldpath) == (ne - newpath) &&
162             !xstrncasecmp(oldpath, newpath, (oe - oldpath))))
163             return -2;
164         return ftpRename(oldpath, newpath);
165         /*@notreached@*/ break;
166     case URL_IS_HTTP:           /* XXX WRONG WRONG WRONG */
167     case URL_IS_PATH:
168         oldpath = oe;
169         newpath = ne;
170         break;
171     case URL_IS_UNKNOWN:
172         break;
173     case URL_IS_DASH:
174     default:
175         return -2;
176         /*@notreached@*/ break;
177     }
178     return rename(oldpath, newpath);
179 }
180
181 int Link (const char * oldpath, const char * newpath)
182 {
183     const char *oe = NULL;
184     const char *ne = NULL;
185     int oldut, newut;
186
187     oldut = urlPath(oldpath, &oe);
188     switch (oldut) {
189     case URL_IS_FTP:            /* XXX WRONG WRONG WRONG */
190     case URL_IS_HTTP:           /* XXX WRONG WRONG WRONG */
191     case URL_IS_PATH:
192     case URL_IS_UNKNOWN:
193         break;
194     case URL_IS_DASH:
195     default:
196         return -2;
197         /*@notreached@*/ break;
198     }
199
200     newut = urlPath(newpath, &ne);
201     switch (newut) {
202     case URL_IS_HTTP:           /* XXX WRONG WRONG WRONG */
203     case URL_IS_FTP:            /* XXX WRONG WRONG WRONG */
204     case URL_IS_PATH:
205 if (_rpmio_debug)
206 fprintf(stderr, "*** link old %*s new %*s\n", (int)(oe - oldpath), oldpath, (int)(ne - newpath), newpath);
207         if (!(oldut == newut && oe && ne && (oe - oldpath) == (ne - newpath) &&
208             !xstrncasecmp(oldpath, newpath, (oe - oldpath))))
209             return -2;
210         oldpath = oe;
211         newpath = ne;
212         break;
213     case URL_IS_UNKNOWN:
214         break;
215     case URL_IS_DASH:
216     default:
217         return -2;
218         /*@notreached@*/ break;
219     }
220     return link(oldpath, newpath);
221 }
222
223 /* XXX build/build.c: analogue to unlink(2). */
224
225 int Unlink(const char * path) {
226     const char * lpath;
227     int ut = urlPath(path, &lpath);
228
229     switch (ut) {
230     case URL_IS_FTP:
231         return ftpUnlink(path);
232         /*@notreached@*/ break;
233     case URL_IS_HTTP:           /* XXX WRONG WRONG WRONG */
234     case URL_IS_PATH:
235         path = lpath;
236         /*@fallthrough@*/
237     case URL_IS_UNKNOWN:
238         break;
239     case URL_IS_DASH:
240     default:
241         return -2;
242         /*@notreached@*/ break;
243     }
244     return unlink(path);
245 }
246
247 /* XXX swiped from mc-4.5.39-pre9 vfs/ftpfs.c */
248
249 #define g_strdup        xstrdup
250 #define g_free          free
251
252 /*
253  * FIXME: this is broken. It depends on mc not crossing border on month!
254  */
255 static int current_mday;
256 static int current_mon;
257 static int current_year;
258
259 /* Following stuff (parse_ls_lga) is used by ftpfs and extfs */
260 #define MAXCOLS         30
261
262 static char *columns [MAXCOLS]; /* Points to the string in column n */
263 static int   column_ptr [MAXCOLS]; /* Index from 0 to the starting positions of the columns */
264
265 static int
266 vfs_split_text (char *p)
267         /*@modifies *p, columns, column_ptr @*/
268 {
269     char *original = p;
270     int  numcols;
271
272
273     for (numcols = 0; *p && numcols < MAXCOLS; numcols++){
274         while (*p == ' ' || *p == '\r' || *p == '\n'){
275             *p = 0;
276             p++;
277         }
278         columns [numcols] = p;
279         column_ptr [numcols] = p - original;
280         while (*p && *p != ' ' && *p != '\r' && *p != '\n')
281             p++;
282     }
283     return numcols;
284 }
285
286 static int
287 is_num (int idx)
288         /*@*/
289 {
290     if (!columns [idx] || columns [idx][0] < '0' || columns [idx][0] > '9')
291         return 0;
292     return 1;
293 }
294
295 static int
296 is_dos_date(/*@null@*/ const char *str)
297         /*@*/
298 {
299     if (str != NULL && strlen(str) == 8 &&
300                 str[2] == str[5] && strchr("\\-/", (int)str[2]) != NULL)
301         return 1;
302     return 0;
303 }
304
305 static int
306 is_week (/*@null@*/ const char * str, /*@out@*/ struct tm * tim)
307         /*@modifies *tim @*/
308 {
309 /*@observer@*/ static const char * week = "SunMonTueWedThuFriSat";
310     const char * pos;
311
312     /*@-observertrans -mayaliasunique@*/
313     if (str != NULL && (pos=strstr(week, str)) != NULL) {
314     /*@=observertrans =mayaliasunique@*/
315         if (tim != NULL)
316             tim->tm_wday = (pos - week)/3;
317         return 1;
318     }
319     return 0;    
320 }
321
322 static int
323 is_month (/*@null@*/ const char * str, /*@out@*/ struct tm * tim)
324         /*@modifies *tim @*/
325 {
326 /*@observer@*/ static const char * month = "JanFebMarAprMayJunJulAugSepOctNovDec";
327     const char * pos;
328     
329     /*@-observertrans -mayaliasunique@*/
330     if (str != NULL && (pos = strstr(month, str)) != NULL) {
331     /*@=observertrans -mayaliasunique@*/
332         if (tim != NULL)
333             tim->tm_mon = (pos - month)/3;
334         return 1;
335     }
336     return 0;
337 }
338
339 static int
340 is_time (/*@null@*/ const char * str, /*@out@*/ struct tm * tim)
341         /*@modifies *tim @*/
342 {
343     const char * p, * p2;
344
345     if (str != NULL && (p = strchr(str, ':')) && (p2 = strrchr(str, ':'))) {
346         if (p != p2) {
347             if (sscanf (str, "%2d:%2d:%2d", &tim->tm_hour, &tim->tm_min, &tim->tm_sec) != 3)
348                 return 0;
349         } else {
350             if (sscanf (str, "%2d:%2d", &tim->tm_hour, &tim->tm_min) != 2)
351                 return 0;
352         }
353     } else 
354         return 0;
355     
356     return 1;
357 }
358
359 static int is_year(/*@null@*/ const char * str, /*@out@*/ struct tm * tim)
360         /*@modifies *tim @*/
361 {
362     long year;
363
364     if (str == NULL)
365         return 0;
366
367     if (strchr(str,':'))
368         return 0;
369
370     if (strlen(str) != 4)
371         return 0;
372
373     if (sscanf(str, "%ld", &year) != 1)
374         return 0;
375
376     if (year < 1900 || year > 3000)
377         return 0;
378
379     tim->tm_year = (int) (year - 1900);
380
381     return 1;
382 }
383
384 /*
385  * FIXME: this is broken. Consider following entry:
386  * -rwx------   1 root     root            1 Aug 31 10:04 2904 1234
387  * where "2904 1234" is filename. Well, this code decodes it as year :-(.
388  */
389
390 static int
391 vfs_parse_filetype (char c)
392         /*@*/
393 {
394     switch (c) {
395         case 'd': return S_IFDIR; 
396         case 'b': return S_IFBLK;
397         case 'c': return S_IFCHR;
398         case 'l': return S_IFLNK;
399         case 's':
400 #ifdef IS_IFSOCK /* And if not, we fall through to IFIFO, which is pretty close */
401                   return S_IFSOCK;
402 #endif
403         case 'p': return S_IFIFO;
404         case 'm': case 'n':             /* Don't know what these are :-) */
405         case '-': case '?': return S_IFREG;
406         default: return -1;
407     }
408 }
409
410 static int vfs_parse_filemode (const char *p)
411         /*@*/
412 {       /* converts rw-rw-rw- into 0666 */
413     int res = 0;
414     switch (*(p++)) {
415         case 'r': res |= 0400; break;
416         case '-': break;
417         default: return -1;
418     }
419     switch (*(p++)) {
420         case 'w': res |= 0200; break;
421         case '-': break;
422         default: return -1;
423     }
424     switch (*(p++)) {
425         case 'x': res |= 0100; break;
426         case 's': res |= 0100 | S_ISUID; break;
427         case 'S': res |= S_ISUID; break;
428         case '-': break;
429         default: return -1;
430     }
431     switch (*(p++)) {
432         case 'r': res |= 0040; break;
433         case '-': break;
434         default: return -1;
435     }
436     switch (*(p++)) {
437         case 'w': res |= 0020; break;
438         case '-': break;
439         default: return -1;
440     }
441     switch (*(p++)) {
442         case 'x': res |= 0010; break;
443         case 's': res |= 0010 | S_ISGID; break;
444         case 'l': /* Solaris produces these */
445         case 'S': res |= S_ISGID; break;
446         case '-': break;
447         default: return -1;
448     }
449     switch (*(p++)) {
450         case 'r': res |= 0004; break;
451         case '-': break;
452         default: return -1;
453     }
454     switch (*(p++)) {
455         case 'w': res |= 0002; break;
456         case '-': break;
457         default: return -1;
458     }
459     switch (*(p++)) {
460         case 'x': res |= 0001; break;
461         case 't': res |= 0001 | S_ISVTX; break;
462         case 'T': res |= S_ISVTX; break;
463         case '-': break;
464         default: return -1;
465     }
466     return res;
467 }
468
469 static int vfs_parse_filedate(int idx, /*@out@*/ time_t *t)
470         /*@modifies *t @*/
471 {       /* This thing parses from idx in columns[] array */
472
473     char *p;
474     struct tm tim;
475     int d[3];
476     int got_year = 0;
477
478     /* Let's setup default time values */
479     tim.tm_year = current_year;
480     tim.tm_mon  = current_mon;
481     tim.tm_mday = current_mday;
482     tim.tm_hour = 0;
483     tim.tm_min  = 0;
484     tim.tm_sec  = 0;
485     tim.tm_isdst = -1; /* Let mktime() try to guess correct dst offset */
486     
487     p = columns [idx++];
488     
489     /* We eat weekday name in case of extfs */
490     if(is_week(p, &tim))
491         p = columns [idx++];
492
493     /* Month name */
494     if(is_month(p, &tim)){
495         /* And we expect, it followed by day number */
496         if (is_num (idx))
497             tim.tm_mday = (int)atol (columns [idx++]);
498         else
499             return 0; /* No day */
500
501     } else {
502         /* We usually expect:
503            Mon DD hh:mm
504            Mon DD  YYYY
505            But in case of extfs we allow these date formats:
506            Mon DD YYYY hh:mm
507            Mon DD hh:mm YYYY
508            Wek Mon DD hh:mm:ss YYYY
509            MM-DD-YY hh:mm
510            where Mon is Jan-Dec, DD, MM, YY two digit day, month, year,
511            YYYY four digit year, hh, mm, ss two digit hour, minute or second. */
512
513         /* Here just this special case with MM-DD-YY */
514         if (is_dos_date(p)){
515             p[2] = p[5] = '-';
516             
517             memset(d, 0, sizeof(d));
518             if (sscanf(p, "%2d-%2d-%2d", &d[0], &d[1], &d[2]) == 3){
519             /*  We expect to get:
520                 1. MM-DD-YY
521                 2. DD-MM-YY
522                 3. YY-MM-DD
523                 4. YY-DD-MM  */
524                 
525                 /* Hmm... maybe, next time :)*/
526                 
527                 /* At last, MM-DD-YY */
528                 d[0]--; /* Months are zerobased */
529                 /* Y2K madness */
530                 if(d[2] < 70)
531                     d[2] += 100;
532
533                 tim.tm_mon  = d[0];
534                 tim.tm_mday = d[1];
535                 tim.tm_year = d[2];
536                 got_year = 1;
537             } else
538                 return 0; /* sscanf failed */
539         } else
540             return 0; /* unsupported format */
541     }
542
543     /* Here we expect to find time and/or year */
544     
545     if (is_num (idx)) {
546         if(is_time(columns[idx], &tim) || (got_year = is_year(columns[idx], &tim))) {
547         idx++;
548
549         /* This is a special case for ctime() or Mon DD YYYY hh:mm */
550         if(is_num (idx) && 
551             ((got_year = is_year(columns[idx], &tim)) || is_time(columns[idx], &tim)))
552                 idx++; /* time & year or reverse */
553         } /* only time or date */
554     }
555     else 
556         return 0; /* Nor time or date */
557
558     /*
559      * If the date is less than 6 months in the past, it is shown without year
560      * other dates in the past or future are shown with year but without time
561      * This does not check for years before 1900 ... I don't know, how
562      * to represent them at all
563      */
564     if (!got_year &&
565         current_mon < 6 && current_mon < tim.tm_mon && 
566         tim.tm_mon - current_mon >= 6)
567
568         tim.tm_year--;
569
570     if ((*t = mktime(&tim)) < 0)
571         *t = 0;
572     return idx;
573 }
574
575 static int
576 vfs_parse_ls_lga (char * p, /*@out@*/ struct stat * st,
577                 /*@out@*/ const char ** filename,
578                 /*@out@*/ const char ** linkname)
579         /*@modifies *st, *filename, *linkname @*/
580 {
581     int idx, idx2, num_cols;
582     int i;
583     char *p_copy;
584     
585     if (strncmp (p, "total", 5) == 0)
586         return 0;
587
588     p_copy = g_strdup(p);
589 /* XXX FIXME: parse out inode number from "NLST -lai ." */
590 /* XXX FIXME: parse out sizein blocks from "NLST -lais ." */
591
592     if ((i = vfs_parse_filetype(*(p++))) == -1)
593         goto error;
594
595     st->st_mode = i;
596     if (*p == ' ')      /* Notwell 4 */
597         p++;
598     if (*p == '['){
599         if (strlen (p) <= 8 || p [8] != ']')
600             goto error;
601         /* Should parse here the Notwell permissions :) */
602         /*@-unrecog@*/
603         if (S_ISDIR (st->st_mode))
604             st->st_mode |= (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IXUSR | S_IXGRP | S_IXOTH);
605         else
606             st->st_mode |= (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR);
607         p += 9;
608         /*@=unrecog@*/
609     } else {
610         if ((i = vfs_parse_filemode(p)) == -1)
611             goto error;
612         st->st_mode |= i;
613         p += 9;
614
615         /* This is for an extra ACL attribute (HP-UX) */
616         if (*p == '+')
617             p++;
618     }
619
620     g_free(p_copy);
621     p_copy = g_strdup(p);
622     num_cols = vfs_split_text (p);
623
624     st->st_nlink = atol (columns [0]);
625     if (st->st_nlink < 0)
626         goto error;
627
628     if (!is_num (1))
629 #ifdef  HACK
630         st->st_uid = finduid (columns [1]);
631 #else
632         (void) unameToUid (columns [1], &st->st_uid);
633 #endif
634     else
635         st->st_uid = (uid_t) atol (columns [1]);
636
637     /* Mhm, the ls -lg did not produce a group field */
638     for (idx = 3; idx <= 5; idx++) 
639         if (is_month(columns [idx], NULL) || is_week(columns [idx], NULL) || is_dos_date(columns[idx]))
640             break;
641
642     if (idx == 6 || (idx == 5 && !S_ISCHR (st->st_mode) && !S_ISBLK (st->st_mode)))
643         goto error;
644
645     /* We don't have gid */     
646     if (idx == 3 || (idx == 4 && (S_ISCHR(st->st_mode) || S_ISBLK (st->st_mode))))
647         idx2 = 2;
648     else { 
649         /* We have gid field */
650         if (is_num (2))
651             st->st_gid = (gid_t) atol (columns [2]);
652         else
653 #ifdef  HACK
654             st->st_gid = findgid (columns [2]);
655 #else
656             (void) gnameToGid (columns [1], &st->st_gid);
657 #endif
658         idx2 = 3;
659     }
660
661     /* This is device */
662     if (S_ISCHR (st->st_mode) || S_ISBLK (st->st_mode)){
663         int maj, min;
664         
665         if (!is_num (idx2) || sscanf(columns [idx2], " %d,", &maj) != 1)
666             goto error;
667         
668         if (!is_num (++idx2) || sscanf(columns [idx2], " %d", &min) != 1)
669             goto error;
670         
671 #ifdef HAVE_ST_RDEV
672         st->st_rdev = ((maj & 0xff) << 8) | (min & 0xffff00ff);
673 #endif
674         st->st_size = 0;
675         
676     } else {
677         /* Common file size */
678         if (!is_num (idx2))
679             goto error;
680         
681         st->st_size = (size_t) atol (columns [idx2]);
682 #ifdef HAVE_ST_RDEV
683         st->st_rdev = 0;
684 #endif
685     }
686
687     idx = vfs_parse_filedate(idx, &st->st_mtime);
688     if (!idx)
689         goto error;
690     /* Use resulting time value */
691     st->st_atime = st->st_ctime = st->st_mtime;
692     st->st_dev = 0;
693     st->st_ino = 0;
694 #ifdef HAVE_ST_BLKSIZE
695     st->st_blksize = 512;
696 #endif
697 #ifdef HAVE_ST_BLOCKS
698     st->st_blocks = (st->st_size + 511) / 512;
699 #endif
700
701     for (i = idx + 1, idx2 = 0; i < num_cols; i++ ) 
702         if (strcmp (columns [i], "->") == 0){
703             idx2 = i;
704             break;
705         }
706     
707     if (((S_ISLNK (st->st_mode) || 
708         (num_cols == idx + 3 && st->st_nlink > 1))) /* Maybe a hardlink? (in extfs) */
709         && idx2){
710         int tlen;
711         char *t;
712             
713         if (filename){
714 #ifdef HACK
715             t = g_strndup (p_copy + column_ptr [idx], column_ptr [idx2] - column_ptr [idx] - 1);
716 #else
717             int nb = column_ptr [idx2] - column_ptr [idx] - 1;
718             t = xmalloc(nb+1);
719             strncpy(t, p_copy + column_ptr [idx], nb);
720 #endif
721             *filename = t;
722         }
723         if (linkname){
724             t = g_strdup (p_copy + column_ptr [idx2+1]);
725             tlen = strlen (t);
726             if (t [tlen-1] == '\r' || t [tlen-1] == '\n')
727                 t [tlen-1] = 0;
728             if (t [tlen-2] == '\r' || t [tlen-2] == '\n')
729                 t [tlen-2] = 0;
730                 
731             *linkname = t;
732         }
733     } else {
734         /* Extract the filename from the string copy, not from the columns
735          * this way we have a chance of entering hidden directories like ". ."
736          */
737         if (filename){
738             /* 
739             *filename = g_strdup (columns [idx++]);
740             */
741             int tlen;
742             char *t;
743             
744             t = g_strdup (p_copy + column_ptr [idx++]);
745             tlen = strlen (t);
746             /* g_strchomp(); */
747             if (t [tlen-1] == '\r' || t [tlen-1] == '\n')
748                 t [tlen-1] = 0;
749             if (t [tlen-2] == '\r' || t [tlen-2] == '\n')
750                 t [tlen-2] = 0;
751             
752             *filename = t;
753         }
754         if (linkname)
755             *linkname = NULL;
756     }
757     g_free (p_copy);
758     return 1;
759
760 error:
761 #ifdef  HACK
762     {
763       static int errorcount = 0;
764
765       if (++errorcount < 5) {
766         message_1s (1, "Could not parse:", p_copy);
767       } else if (errorcount == 5)
768         message_1s (1, "More parsing errors will be ignored.", "(sorry)" );
769     }
770 #endif
771
772     /*@-usereleased@*/
773     if (p_copy != p)            /* Carefull! */
774     /*@=usereleased@*/
775         g_free (p_copy);
776     return 0;
777 }
778
779 typedef enum {
780         DO_FTP_STAT     = 1,
781         DO_FTP_LSTAT    = 2,
782         DO_FTP_READLINK = 3,
783         DO_FTP_ACCESS   = 4,
784         DO_FTP_GLOB     = 5
785 } ftpSysCall_t;
786 static size_t ftpBufAlloced = 0;
787 static /*@only@*/ char * ftpBuf = NULL;
788         
789 #define alloca_strdup(_s)       strcpy(alloca(strlen(_s)+1), (_s))
790
791 static int ftpNLST(const char * url, ftpSysCall_t ftpSysCall,
792                 /*@out@*/ /*@null@*/ struct stat * st,
793                 /*@out@*/ /*@null@*/ char * rlbuf, size_t rlbufsiz)
794         /*@modifies *st, *rlbuf, fileSystem @*/
795 {
796     FD_t fd;
797     const char * path;
798     int bufLength, moretodo;
799     const char *n, *ne, *o, *oe;
800     char * s;
801     char * se;
802     const char * urldn;
803     char * bn = NULL;
804     int nbn = 0;
805     urlinfo u;
806     int rc;
807
808     n = ne = o = oe = NULL;
809     (void) urlPath(url, &path);
810     if (*path == '\0')
811         return -2;
812
813     switch (ftpSysCall) {
814     case DO_FTP_GLOB:
815         fd = ftpOpen(url, 0, 0, &u);
816         if (fd == NULL || u == NULL)
817             return -1;
818
819         u->openError = ftpReq(fd, "NLST", path);
820         break;
821     default:
822         urldn = alloca_strdup(url);
823         if ((bn = strrchr(urldn, '/')) == NULL)
824             return -2;
825         else if (bn == path)
826             bn = ".";
827         else
828             *bn++ = '\0';
829         nbn = strlen(bn);
830
831         rc = ftpChdir(urldn);           /* XXX don't care about CWD */
832         if (rc < 0)
833             return rc;
834
835         fd = ftpOpen(url, 0, 0, &u);
836         if (fd == NULL || u == NULL)
837             return -1;
838
839         /* XXX possibly should do "NLST -lais" to get st_ino/st_blocks also */
840         u->openError = ftpReq(fd, "NLST", "-la");
841
842         if (bn == NULL || nbn <= 0) {
843             rc = -2;
844             goto exit;
845         }
846         break;
847     }
848
849     if (u->openError < 0) {
850         fd = fdLink(fd, "error data (ftpStat)");
851         rc = -2;
852         goto exit;
853     }
854
855     if (ftpBufAlloced == 0 || ftpBuf == NULL) {
856         ftpBufAlloced = url_iobuf_size;
857         ftpBuf = xcalloc(ftpBufAlloced, sizeof(ftpBuf[0]));
858     }
859     *ftpBuf = '\0';
860
861     bufLength = 0;
862     moretodo = 1;
863
864     do {
865
866         /* XXX FIXME: realloc ftpBuf is < ~128 chars remain */
867         if ((ftpBufAlloced - bufLength) < (1024+80)) {
868             ftpBufAlloced <<= 2;
869             ftpBuf = xrealloc(ftpBuf, ftpBufAlloced);
870         }
871         s = se = ftpBuf + bufLength;
872         *se = '\0';
873
874         rc = fdFgets(fd, se, (ftpBufAlloced - bufLength));
875         if (rc <= 0) {
876             moretodo = 0;
877             break;
878         }
879         if (ftpSysCall == DO_FTP_GLOB) {        /* XXX HACK */
880             bufLength += strlen(se);
881             continue;
882         }
883
884         for (s = se; *s != '\0'; s = se) {
885             int bingo;
886
887             while (*se && *se != '\n') se++;
888             if (se > s && se[-1] == '\r') se[-1] = '\0';
889             if (*se == '\0') 
890                 /*@innerbreak@*/ break;
891             *se++ = '\0';
892
893             if (!strncmp(s, "total ", sizeof("total ")-1)) continue;
894
895             o = NULL;
896             for (bingo = 0, n = se; n >= s; n--) {
897                 switch (*n) {
898                 case '\0':
899                     oe = ne = n;
900                     break;
901                 case ' ':
902                     if (o || !(n[-3] == ' ' && n[-2] == '-' && n[-1] == '>')) {
903                         while (*(++n) == ' ')
904                             {};
905                         bingo++;
906                         break;
907                     }
908                     for (o = n + 1; *o == ' '; o++)
909                         {};
910                     n -= 3;
911                     ne = n;
912                     break;
913                 default:
914                     break;
915                 }
916                 if (bingo)
917                     /*@innerbreak@*/ break;
918             }
919
920             if (nbn != (ne - n))        continue;       /* Same name length? */
921             if (strncmp(n, bn, nbn))    continue;       /* Same name? */
922
923             moretodo = 0;
924             /*@innerbreak@*/ break;
925         }
926
927         if (moretodo && se > s) {
928             bufLength = se - s - 1;
929             if (s != ftpBuf)
930                 memmove(ftpBuf, s, bufLength);
931         } else {
932             bufLength = 0;
933         }
934     } while (moretodo);
935
936     switch (ftpSysCall) {
937     case DO_FTP_STAT:
938         if (o && oe) {
939             /* XXX FIXME: symlink, replace urldn/bn from [o,oe) and restart */
940         }
941         /*@fallthrough@*/
942     case DO_FTP_LSTAT:
943         if (st == NULL || !(n && ne)) {
944             rc = -1;
945         } else {
946             rc = ((vfs_parse_ls_lga(s, st, NULL, NULL) > 0) ? 0 : -1);
947         }
948         break;
949     case DO_FTP_READLINK:
950         if (rlbuf == NULL || !(o && oe)) {
951             rc = -1;
952         } else {
953             rc = oe - o;
954             if (rc > rlbufsiz)
955                 rc = rlbufsiz;
956             memcpy(rlbuf, o, rc);
957             if (rc < rlbufsiz)
958                 rlbuf[rc] = '\0';
959         }
960         break;
961     case DO_FTP_ACCESS:
962         rc = 0;         /* XXX WRONG WRONG WRONG */
963         break;
964     case DO_FTP_GLOB:
965         rc = 0;         /* XXX WRONG WRONG WRONG */
966         break;
967     }
968
969 exit:
970     (void) ufdClose(fd);
971     return rc;
972 }
973
974 static int ftpStat(const char * path, /*@out@*/ struct stat *st)
975         /*@modifies *st @*/
976 {
977     return ftpNLST(path, DO_FTP_STAT, st, NULL, 0);
978 }
979
980 static int ftpLstat(const char * path, /*@out@*/ struct stat *st)
981         /*@modifies *st @*/
982 {
983     int rc;
984     rc = ftpNLST(path, DO_FTP_LSTAT, st, NULL, 0);
985 if (_rpmio_debug)
986 fprintf(stderr, "*** ftpLstat(%s) rc %d\n", path, rc);
987     return rc;
988 }
989
990 static int ftpReadlink(const char * path, /*@out@*/ char * buf, size_t bufsiz)
991         /*@modifies *buf @*/
992 {
993     return ftpNLST(path, DO_FTP_READLINK, NULL, buf, bufsiz);
994 }
995
996 static int ftpGlob(const char * path, int flags,
997                 int errfunc(const char * epath, int eerno),
998                 /*@out@*/ glob_t * pglob)
999         /*@modifies *pglob, fileSystem @*/
1000 {
1001     int rc;
1002
1003     if (pglob == NULL)
1004         return -2;
1005     rc = ftpNLST(path, DO_FTP_GLOB, NULL, NULL, 0);
1006 /*@-castfcnptr@*/
1007 if (_rpmio_debug)
1008 fprintf(stderr, "*** ftpGlob(%s,0x%x,%p,%p) ftpNLST rc %d\n", path, (unsigned)flags, (void *)errfunc, pglob, rc);
1009 /*@=castfcnptr@*/
1010     if (rc)
1011         return rc;
1012     rc = poptParseArgvString(ftpBuf, &pglob->gl_pathc, (const char ***)&pglob->gl_pathv);
1013     pglob->gl_offs = -1;        /* XXX HACK HACK HACK */
1014     return rc;
1015 }
1016
1017 static void ftpGlobfree(glob_t * pglob)
1018         /*@modifies *pglob @*/
1019 {
1020 if (_rpmio_debug)
1021 fprintf(stderr, "*** ftpGlobfree(%p)\n", pglob);
1022     if (pglob->gl_offs == -1) { /* XXX HACK HACK HACK */
1023         free((void *)pglob->gl_pathv);
1024         pglob->gl_pathv = NULL;
1025     }
1026 }
1027
1028 int Stat(const char * path, struct stat * st)
1029 {
1030     const char * lpath;
1031     int ut = urlPath(path, &lpath);
1032
1033 if (_rpmio_debug)
1034 fprintf(stderr, "*** Stat(%s,%p)\n", path, st);
1035     switch (ut) {
1036     case URL_IS_FTP:
1037         return ftpStat(path, st);
1038         /*@notreached@*/ break;
1039     case URL_IS_HTTP:           /* XXX WRONG WRONG WRONG */
1040     case URL_IS_PATH:
1041         path = lpath;
1042         /*@fallthrough@*/
1043     case URL_IS_UNKNOWN:
1044         break;
1045     case URL_IS_DASH:
1046     default:
1047         return -2;
1048         /*@notreached@*/ break;
1049     }
1050     return stat(path, st);
1051 }
1052
1053 int Lstat(const char * path, struct stat * st)
1054 {
1055     const char * lpath;
1056     int ut = urlPath(path, &lpath);
1057
1058 if (_rpmio_debug)
1059 fprintf(stderr, "*** Lstat(%s,%p)\n", path, st);
1060     switch (ut) {
1061     case URL_IS_FTP:
1062         return ftpLstat(path, st);
1063         /*@notreached@*/ break;
1064     case URL_IS_HTTP:           /* XXX WRONG WRONG WRONG */
1065     case URL_IS_PATH:
1066         path = lpath;
1067         /*@fallthrough@*/
1068     case URL_IS_UNKNOWN:
1069         break;
1070     case URL_IS_DASH:
1071     default:
1072         return -2;
1073         /*@notreached@*/ break;
1074     }
1075     return lstat(path, st);
1076 }
1077
1078 int Readlink(const char * path, char * buf, size_t bufsiz)
1079 {
1080     const char * lpath;
1081     int ut = urlPath(path, &lpath);
1082
1083     switch (ut) {
1084     case URL_IS_FTP:
1085         return ftpReadlink(path, buf, bufsiz);
1086         /*@notreached@*/ break;
1087     case URL_IS_HTTP:           /* XXX WRONG WRONG WRONG */
1088     case URL_IS_PATH:
1089         path = lpath;
1090         /*@fallthrough@*/
1091     case URL_IS_UNKNOWN:
1092         break;
1093     case URL_IS_DASH:
1094     default:
1095         return -2;
1096         /*@notreached@*/ break;
1097     }
1098     return readlink(path, buf, bufsiz);
1099 }
1100
1101 int Access(const char * path, int amode)
1102 {
1103     const char * lpath;
1104     int ut = urlPath(path, &lpath);
1105
1106 if (_rpmio_debug)
1107 fprintf(stderr, "*** Access(%s,%d)\n", path, amode);
1108     switch (ut) {
1109     case URL_IS_FTP:            /* XXX WRONG WRONG WRONG */
1110     case URL_IS_HTTP:           /* XXX WRONG WRONG WRONG */
1111     case URL_IS_PATH:
1112         path = lpath;
1113         /*@fallthrough@*/
1114     case URL_IS_UNKNOWN:
1115         break;
1116     case URL_IS_DASH:
1117     default:
1118         return -2;
1119         /*@notreached@*/ break;
1120     }
1121     return access(path, amode);
1122 }
1123
1124 int Glob(const char *path, int flags,
1125         int errfunc(const char * epath, int eerrno), glob_t *pglob)
1126 {
1127     const char * lpath;
1128     int ut = urlPath(path, &lpath);
1129
1130 /*@-castfcnptr@*/
1131 if (_rpmio_debug)
1132 fprintf(stderr, "*** Glob(%s,0x%x,%p,%p)\n", path, (unsigned)flags, (void *)errfunc, pglob);
1133 /*@=castfcnptr@*/
1134     switch (ut) {
1135     case URL_IS_FTP:            /* XXX WRONG WRONG WRONG */
1136         return ftpGlob(path, flags, errfunc, pglob);
1137         /*@notreached@*/ break;
1138     case URL_IS_HTTP:           /* XXX WRONG WRONG WRONG */
1139     case URL_IS_PATH:
1140         path = lpath;
1141         /*@fallthrough@*/
1142     case URL_IS_UNKNOWN:
1143         break;
1144     case URL_IS_DASH:
1145     default:
1146         return -2;
1147         /*@notreached@*/ break;
1148     }
1149     return glob(path, flags, errfunc, pglob);
1150 }
1151
1152 void Globfree(glob_t *pglob)
1153 {
1154 if (_rpmio_debug)
1155 fprintf(stderr, "*** Globfree(%p)\n", pglob);
1156     if (pglob->gl_offs == -1) /* XXX HACK HACK HACK */
1157         ftpGlobfree(pglob);
1158     else
1159         globfree(pglob);
1160 }
1161
1162 DIR * Opendir(const char * path)
1163 {
1164     const char * lpath;
1165     int ut = urlPath(path, &lpath);
1166
1167 if (_rpmio_debug)
1168 fprintf(stderr, "*** Opendir(%s)\n", path);
1169     switch (ut) {
1170     case URL_IS_FTP:            /* XXX WRONG WRONG WRONG */
1171     case URL_IS_HTTP:           /* XXX WRONG WRONG WRONG */
1172     case URL_IS_PATH:
1173         path = lpath;
1174         /*@fallthrough@*/
1175     case URL_IS_UNKNOWN:
1176         break;
1177     case URL_IS_DASH:
1178     default:
1179         return NULL;
1180         /*@notreached@*/ break;
1181     }
1182     return opendir(path);
1183 }
1184
1185 /*@+voidabstract@*/
1186 struct dirent * Readdir(DIR * dir)
1187 {
1188 if (_rpmio_debug)
1189 fprintf(stderr, "*** Readdir(%p)\n", (void *)dir);
1190     return readdir(dir);
1191 }
1192
1193 int Closedir(DIR * dir)
1194 {
1195 if (_rpmio_debug)
1196 fprintf(stderr, "*** Closedir(%p)\n", (void *)dir);
1197     return closedir(dir);
1198 }
1199 /*@=voidabstract@*/