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