build/files.c

Go to the documentation of this file.
00001 
00007 #include "system.h"
00008 
00009 #define MYALLPERMS      07777
00010 
00011 #include <regex.h>
00012 
00013 #include <rpmio_internal.h>
00014 #include <fts.h>
00015 
00016 #include <rpmbuild.h>
00017 
00018 #include "cpio.h"
00019 
00020 #include "argv.h"
00021 #include "rpmfc.h"
00022 
00023 #define _RPMFI_INTERNAL
00024 #include "rpmfi.h"
00025 
00026 #include "rpmsx.h"
00027 
00028 
00029 #define _RPMTE_INTERNAL
00030 #include "rpmte.h"
00031 
00032 #include "buildio.h"
00033 
00034 #include "legacy.h"     /* XXX dodigest */
00035 #include "misc.h"
00036 #include "debug.h"
00037 
00038 /*@access Header @*/
00039 /*@access rpmfi @*/
00040 /*@access rpmte @*/
00041 /*@access FD_t @*/
00042 /*@access StringBuf @*/         /* compared with NULL */
00043 
00044 #define SKIPWHITE(_x)   {while(*(_x) && (xisspace(*_x) || *(_x) == ',')) (_x)++;}
00045 #define SKIPNONWHITE(_x){while(*(_x) &&!(xisspace(*_x) || *(_x) == ',')) (_x)++;}
00046 
00047 #define MAXDOCDIR 1024
00048 
00051 typedef enum specdFlags_e {
00052     SPECD_DEFFILEMODE   = (1 << 0),
00053     SPECD_DEFDIRMODE    = (1 << 1),
00054     SPECD_DEFUID        = (1 << 2),
00055     SPECD_DEFGID        = (1 << 3),
00056     SPECD_DEFVERIFY     = (1 << 4),
00057 
00058     SPECD_FILEMODE      = (1 << 8),
00059     SPECD_DIRMODE       = (1 << 9),
00060     SPECD_UID           = (1 << 10),
00061     SPECD_GID           = (1 << 11),
00062     SPECD_VERIFY        = (1 << 12)
00063 } specdFlags;
00064 
00067 typedef struct FileListRec_s {
00068     struct stat fl_st;
00069 #define fl_dev  fl_st.st_dev
00070 #define fl_ino  fl_st.st_ino
00071 #define fl_mode fl_st.st_mode
00072 #define fl_nlink fl_st.st_nlink
00073 #define fl_uid  fl_st.st_uid
00074 #define fl_gid  fl_st.st_gid
00075 #define fl_rdev fl_st.st_rdev
00076 #define fl_size fl_st.st_size
00077 #define fl_mtime fl_st.st_mtime
00078 
00079 /*@only@*/
00080     const char *diskURL;        /* get file from here       */
00081 /*@only@*/
00082     const char *fileURL;        /* filename in cpio archive */
00083 /*@observer@*/
00084     const char *uname;
00085 /*@observer@*/
00086     const char *gname;
00087     unsigned    flags;
00088     specdFlags  specdFlags;     /* which attributes have been explicitly specified. */
00089     unsigned    verifyFlags;
00090 /*@only@*/
00091     const char *langs;          /* XXX locales separated with | */
00092 } * FileListRec;
00093 
00096 typedef struct AttrRec_s {
00097 /*@null@*/
00098     const char *ar_fmodestr;
00099 /*@null@*/
00100     const char *ar_dmodestr;
00101 /*@null@*/
00102     const char *ar_user;
00103 /*@null@*/
00104     const char *ar_group;
00105     mode_t      ar_fmode;
00106     mode_t      ar_dmode;
00107 } * AttrRec;
00108 
00109 /*@-readonlytrans@*/
00110 /*@unchecked@*/ /*@observer@*/
00111 static struct AttrRec_s root_ar = { NULL, NULL, "root", "root", 0, 0 };
00112 /*@=readonlytrans@*/
00113 
00114 /* list of files */
00115 /*@unchecked@*/ /*@only@*/ /*@null@*/
00116 static StringBuf check_fileList = NULL;
00117 
00121 typedef struct FileList_s {
00122 /*@only@*/
00123     const char * buildRootURL;
00124 /*@only@*/
00125     const char * prefix;
00126 
00127     int fileCount;
00128     int totalFileSize;
00129     int processingFailed;
00130 
00131     int passedSpecialDoc;
00132     int isSpecialDoc;
00133 
00134     int noGlob;
00135     unsigned devtype;
00136     unsigned devmajor;
00137     int devminor;
00138     
00139     int isDir;
00140     int inFtw;
00141     int currentFlags;
00142     specdFlags currentSpecdFlags;
00143     int currentVerifyFlags;
00144     struct AttrRec_s cur_ar;
00145     struct AttrRec_s def_ar;
00146     specdFlags defSpecdFlags;
00147     int defVerifyFlags;
00148     int nLangs;
00149 /*@only@*/ /*@null@*/
00150     const char ** currentLangs;
00151 
00152     /* Hard coded limit of MAXDOCDIR docdirs.         */
00153     /* If you break it you are doing something wrong. */
00154     const char * docDirs[MAXDOCDIR];
00155     int docDirCount;
00156     
00157 /*@only@*/
00158     FileListRec fileList;
00159     int fileListRecsAlloced;
00160     int fileListRecsUsed;
00161 } * FileList;
00162 
00165 static void nullAttrRec(/*@out@*/ AttrRec ar)   /*@modifies ar @*/
00166 {
00167     ar->ar_fmodestr = NULL;
00168     ar->ar_dmodestr = NULL;
00169     ar->ar_user = NULL;
00170     ar->ar_group = NULL;
00171     ar->ar_fmode = 0;
00172     ar->ar_dmode = 0;
00173 }
00174 
00177 static void freeAttrRec(AttrRec ar)     /*@modifies ar @*/
00178 {
00179     ar->ar_fmodestr = _free(ar->ar_fmodestr);
00180     ar->ar_dmodestr = _free(ar->ar_dmodestr);
00181     ar->ar_user = _free(ar->ar_user);
00182     ar->ar_group = _free(ar->ar_group);
00183     /* XXX doesn't free ar (yet) */
00184     /*@-nullstate@*/
00185     return;
00186     /*@=nullstate@*/
00187 }
00188 
00191 static void dupAttrRec(const AttrRec oar, /*@in@*/ /*@out@*/ AttrRec nar)
00192         /*@modifies nar @*/
00193 {
00194     if (oar == nar)
00195         return;
00196     freeAttrRec(nar);
00197     nar->ar_fmodestr = (oar->ar_fmodestr ? xstrdup(oar->ar_fmodestr) : NULL);
00198     nar->ar_dmodestr = (oar->ar_dmodestr ? xstrdup(oar->ar_dmodestr) : NULL);
00199     nar->ar_user = (oar->ar_user ? xstrdup(oar->ar_user) : NULL);
00200     nar->ar_group = (oar->ar_group ? xstrdup(oar->ar_group) : NULL);
00201     nar->ar_fmode = oar->ar_fmode;
00202     nar->ar_dmode = oar->ar_dmode;
00203 }
00204 
00205 #if 0
00206 
00208 static void dumpAttrRec(const char * msg, AttrRec ar)
00209         /*@globals fileSystem@*/
00210         /*@modifies fileSystem @*/
00211 {
00212     if (msg)
00213         fprintf(stderr, "%s:\t", msg);
00214     fprintf(stderr, "(%s, %s, %s, %s)\n",
00215         ar->ar_fmodestr,
00216         ar->ar_user,
00217         ar->ar_group,
00218         ar->ar_dmodestr);
00219 }
00220 #endif
00221 
00226 /*@-boundswrite@*/
00227 /*@null@*/
00228 static char *strtokWithQuotes(/*@null@*/ char *s, char *delim)
00229         /*@modifies *s @*/
00230 {
00231     static char *olds = NULL;
00232     char *token;
00233 
00234     if (s == NULL)
00235         s = olds;
00236     if (s == NULL)
00237         return NULL;
00238 
00239     /* Skip leading delimiters */
00240     s += strspn(s, delim);
00241     if (*s == '\0')
00242         return NULL;
00243 
00244     /* Find the end of the token.  */
00245     token = s;
00246     if (*token == '"') {
00247         token++;
00248         /* Find next " char */
00249         s = strchr(token, '"');
00250     } else {
00251         s = strpbrk(token, delim);
00252     }
00253 
00254     /* Terminate it */
00255     if (s == NULL) {
00256         /* This token finishes the string */
00257         olds = strchr(token, '\0');
00258     } else {
00259         /* Terminate the token and make olds point past it */
00260         *s = '\0';
00261         olds = s+1;
00262     }
00263 
00264     /*@-retalias -temptrans @*/
00265     return token;
00266     /*@=retalias =temptrans @*/
00267 }
00268 /*@=boundswrite@*/
00269 
00272 static void timeCheck(int tc, Header h)
00273         /*@globals internalState @*/
00274         /*@modifies internalState @*/
00275 {
00276     HGE_t hge = (HGE_t)headerGetEntryMinMemory;
00277     HFD_t hfd = headerFreeData;
00278     int * mtime;
00279     const char ** files;
00280     rpmTagType fnt;
00281     int count, x;
00282     time_t currentTime = time(NULL);
00283 
00284     x = hge(h, RPMTAG_OLDFILENAMES, &fnt, (void **) &files, &count);
00285     x = hge(h, RPMTAG_FILEMTIMES, NULL, (void **) &mtime, NULL);
00286     
00287 /*@-boundsread@*/
00288     for (x = 0; x < count; x++) {
00289         if ((currentTime - mtime[x]) > tc)
00290             rpmMessage(RPMMESS_WARNING, _("TIMECHECK failure: %s\n"), files[x]);
00291     }
00292     files = hfd(files, fnt);
00293 /*@=boundsread@*/
00294 }
00295 
00298 typedef struct VFA {
00299 /*@observer@*/ /*@null@*/ const char * attribute;
00300     int not;
00301     int flag;
00302 } VFA_t;
00303 
00306 /*@-exportlocal -exportheadervar@*/
00307 /*@unchecked@*/
00308 VFA_t verifyAttrs[] = {
00309     { "md5",    0,      RPMVERIFY_MD5 },
00310     { "size",   0,      RPMVERIFY_FILESIZE },
00311     { "link",   0,      RPMVERIFY_LINKTO },
00312     { "user",   0,      RPMVERIFY_USER },
00313     { "group",  0,      RPMVERIFY_GROUP },
00314     { "mtime",  0,      RPMVERIFY_MTIME },
00315     { "mode",   0,      RPMVERIFY_MODE },
00316     { "rdev",   0,      RPMVERIFY_RDEV },
00317     { NULL, 0,  0 }
00318 };
00319 /*@=exportlocal =exportheadervar@*/
00320 
00327 /*@-boundswrite@*/
00328 static int parseForVerify(char * buf, FileList fl)
00329         /*@modifies buf, fl->processingFailed,
00330                 fl->currentVerifyFlags, fl->defVerifyFlags,
00331                 fl->currentSpecdFlags, fl->defSpecdFlags @*/
00332 {
00333     char *p, *pe, *q;
00334     const char *name;
00335     int *resultVerify;
00336     int negated;
00337     int verifyFlags;
00338     specdFlags * specdFlags;
00339 
00340     if ((p = strstr(buf, (name = "%verify"))) != NULL) {
00341         resultVerify = &(fl->currentVerifyFlags);
00342         specdFlags = &fl->currentSpecdFlags;
00343     } else if ((p = strstr(buf, (name = "%defverify"))) != NULL) {
00344         resultVerify = &(fl->defVerifyFlags);
00345         specdFlags = &fl->defSpecdFlags;
00346     } else
00347         return 0;
00348 
00349     for (pe = p; (pe-p) < strlen(name); pe++)
00350         *pe = ' ';
00351 
00352     SKIPSPACE(pe);
00353 
00354     if (*pe != '(') {
00355         rpmError(RPMERR_BADSPEC, _("Missing '(' in %s %s\n"), name, pe);
00356         fl->processingFailed = 1;
00357         return RPMERR_BADSPEC;
00358     }
00359 
00360     /* Bracket %*verify args */
00361     *pe++ = ' ';
00362     for (p = pe; *pe && *pe != ')'; pe++)
00363         {};
00364 
00365     if (*pe == '\0') {
00366         rpmError(RPMERR_BADSPEC, _("Missing ')' in %s(%s\n"), name, p);
00367         fl->processingFailed = 1;
00368         return RPMERR_BADSPEC;
00369     }
00370 
00371     /* Localize. Erase parsed string */
00372     q = alloca((pe-p) + 1);
00373     strncpy(q, p, pe-p);
00374     q[pe-p] = '\0';
00375     while (p <= pe)
00376         *p++ = ' ';
00377 
00378     negated = 0;
00379     verifyFlags = RPMVERIFY_NONE;
00380 
00381     for (p = q; *p != '\0'; p = pe) {
00382         SKIPWHITE(p);
00383         if (*p == '\0')
00384             break;
00385         pe = p;
00386         SKIPNONWHITE(pe);
00387         if (*pe != '\0')
00388             *pe++ = '\0';
00389 
00390         {   VFA_t *vfa;
00391             for (vfa = verifyAttrs; vfa->attribute != NULL; vfa++) {
00392                 if (strcmp(p, vfa->attribute))
00393                     /*@innercontinue@*/ continue;
00394                 verifyFlags |= vfa->flag;
00395                 /*@innerbreak@*/ break;
00396             }
00397             if (vfa->attribute)
00398                 continue;
00399         }
00400 
00401         if (!strcmp(p, "not")) {
00402             negated ^= 1;
00403         } else {
00404             rpmError(RPMERR_BADSPEC, _("Invalid %s token: %s\n"), name, p);
00405             fl->processingFailed = 1;
00406             return RPMERR_BADSPEC;
00407         }
00408     }
00409 
00410     *resultVerify = negated ? ~(verifyFlags) : verifyFlags;
00411     *specdFlags |= SPECD_VERIFY;
00412 
00413     return 0;
00414 }
00415 /*@=boundswrite@*/
00416 
00417 #define isAttrDefault(_ars)     ((_ars)[0] == '-' && (_ars)[1] == '\0')
00418 
00425 /*@-boundswrite@*/
00426 static int parseForDev(char * buf, FileList fl)
00427         /*@modifies buf, fl->processingFailed,
00428                 fl->noGlob, fl->devtype, fl->devmajor, fl->devminor @*/
00429 {
00430     const char * name;
00431     const char * errstr = NULL;
00432     char *p, *pe, *q;
00433     int rc = RPMERR_BADSPEC;    /* assume error */
00434 
00435     if ((p = strstr(buf, (name = "%dev"))) == NULL)
00436         return 0;
00437 
00438     for (pe = p; (pe-p) < strlen(name); pe++)
00439         *pe = ' ';
00440     SKIPSPACE(pe);
00441 
00442     if (*pe != '(') {
00443         errstr = "'('";
00444         goto exit;
00445     }
00446 
00447     /* Bracket %dev args */
00448     *pe++ = ' ';
00449     for (p = pe; *pe && *pe != ')'; pe++)
00450         {};
00451     if (*pe != ')') {
00452         errstr = "')'";
00453         goto exit;
00454     }
00455 
00456     /* Localize. Erase parsed string */
00457     q = alloca((pe-p) + 1);
00458     strncpy(q, p, pe-p);
00459     q[pe-p] = '\0';
00460     while (p <= pe)
00461         *p++ = ' ';
00462 
00463     p = q; SKIPWHITE(p);
00464     pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0';
00465     if (*p == 'b')
00466         fl->devtype = 'b';
00467     else if (*p == 'c')
00468         fl->devtype = 'c';
00469     else {
00470         errstr = "devtype";
00471         goto exit;
00472     }
00473 
00474     p = pe; SKIPWHITE(p);
00475     pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0';
00476     for (pe = p; *pe && xisdigit(*pe); pe++)
00477         {} ;
00478     if (*pe == '\0') {
00479         fl->devmajor = atoi(p);
00480         /*@-unsignedcompare @*/ /* LCL: ge is ok */
00481         if (!(fl->devmajor >= 0 && fl->devmajor < 256)) {
00482             errstr = "devmajor";
00483             goto exit;
00484         }
00485         /*@=unsignedcompare @*/
00486         pe++;
00487     } else {
00488         errstr = "devmajor";
00489         goto exit;
00490     }
00491 
00492     p = pe; SKIPWHITE(p);
00493     pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0';
00494     for (pe = p; *pe && xisdigit(*pe); pe++)
00495         {} ;
00496     if (*pe == '\0') {
00497         fl->devminor = atoi(p);
00498         if (!(fl->devminor >= 0 && fl->devminor < 256)) {
00499             errstr = "devminor";
00500             goto exit;
00501         }
00502         pe++;
00503     } else {
00504         errstr = "devminor";
00505         goto exit;
00506     }
00507 
00508     fl->noGlob = 1;
00509 
00510     rc = 0;
00511 
00512 exit:
00513     if (rc) {
00514         rpmError(RPMERR_BADSPEC, _("Missing %s in %s %s\n"), errstr, name, p);
00515         fl->processingFailed = 1;
00516     }
00517     return rc;
00518 }
00519 /*@=boundswrite@*/
00520 
00527 /*@-boundswrite@*/
00528 static int parseForAttr(char * buf, FileList fl)
00529         /*@modifies buf, fl->processingFailed,
00530                 fl->cur_ar, fl->def_ar,
00531                 fl->currentSpecdFlags, fl->defSpecdFlags @*/
00532 {
00533     const char *name;
00534     char *p, *pe, *q;
00535     int x;
00536     struct AttrRec_s arbuf;
00537     AttrRec ar = &arbuf, ret_ar;
00538     specdFlags * specdFlags;
00539 
00540     if ((p = strstr(buf, (name = "%attr"))) != NULL) {
00541         ret_ar = &(fl->cur_ar);
00542         specdFlags = &fl->currentSpecdFlags;
00543     } else if ((p = strstr(buf, (name = "%defattr"))) != NULL) {
00544         ret_ar = &(fl->def_ar);
00545         specdFlags = &fl->defSpecdFlags;
00546     } else
00547         return 0;
00548 
00549     for (pe = p; (pe-p) < strlen(name); pe++)
00550         *pe = ' ';
00551 
00552     SKIPSPACE(pe);
00553 
00554     if (*pe != '(') {
00555         rpmError(RPMERR_BADSPEC, _("Missing '(' in %s %s\n"), name, pe);
00556         fl->processingFailed = 1;
00557         return RPMERR_BADSPEC;
00558     }
00559 
00560     /* Bracket %*attr args */
00561     *pe++ = ' ';
00562     for (p = pe; *pe && *pe != ')'; pe++)
00563         {};
00564 
00565     if (ret_ar == &(fl->def_ar)) {      /* %defattr */
00566         q = pe;
00567         q++;
00568         SKIPSPACE(q);
00569         if (*q != '\0') {
00570             rpmError(RPMERR_BADSPEC,
00571                      _("Non-white space follows %s(): %s\n"), name, q);
00572             fl->processingFailed = 1;
00573             return RPMERR_BADSPEC;
00574         }
00575     }
00576 
00577     /* Localize. Erase parsed string */
00578     q = alloca((pe-p) + 1);
00579     strncpy(q, p, pe-p);
00580     q[pe-p] = '\0';
00581     while (p <= pe)
00582         *p++ = ' ';
00583 
00584     nullAttrRec(ar);
00585 
00586     p = q; SKIPWHITE(p);
00587     if (*p != '\0') {
00588         pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0';
00589         ar->ar_fmodestr = p;
00590         p = pe; SKIPWHITE(p);
00591     }
00592     if (*p != '\0') {
00593         pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0';
00594         ar->ar_user = p;
00595         p = pe; SKIPWHITE(p);
00596     }
00597     if (*p != '\0') {
00598         pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0';
00599         ar->ar_group = p;
00600         p = pe; SKIPWHITE(p);
00601     }
00602     if (*p != '\0' && ret_ar == &(fl->def_ar)) {        /* %defattr */
00603         pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0';
00604         ar->ar_dmodestr = p;
00605         p = pe; SKIPWHITE(p);
00606     }
00607 
00608     if (!(ar->ar_fmodestr && ar->ar_user && ar->ar_group) || *p != '\0') {
00609         rpmError(RPMERR_BADSPEC, _("Bad syntax: %s(%s)\n"), name, q);
00610         fl->processingFailed = 1;
00611         return RPMERR_BADSPEC;
00612     }
00613 
00614     /* Do a quick test on the mode argument and adjust for "-" */
00615     if (ar->ar_fmodestr && !isAttrDefault(ar->ar_fmodestr)) {
00616         unsigned int ui;
00617         x = sscanf(ar->ar_fmodestr, "%o", &ui);
00618         if ((x == 0) || (ar->ar_fmode & ~MYALLPERMS)) {
00619             rpmError(RPMERR_BADSPEC, _("Bad mode spec: %s(%s)\n"), name, q);
00620             fl->processingFailed = 1;
00621             return RPMERR_BADSPEC;
00622         }
00623         ar->ar_fmode = ui;
00624     } else
00625         ar->ar_fmodestr = NULL;
00626 
00627     if (ar->ar_dmodestr && !isAttrDefault(ar->ar_dmodestr)) {
00628         unsigned int ui;
00629         x = sscanf(ar->ar_dmodestr, "%o", &ui);
00630         if ((x == 0) || (ar->ar_dmode & ~MYALLPERMS)) {
00631             rpmError(RPMERR_BADSPEC, _("Bad dirmode spec: %s(%s)\n"), name, q);
00632             fl->processingFailed = 1;
00633             return RPMERR_BADSPEC;
00634         }
00635         ar->ar_dmode = ui;
00636     } else
00637         ar->ar_dmodestr = NULL;
00638 
00639     if (!(ar->ar_user && !isAttrDefault(ar->ar_user)))
00640         ar->ar_user = NULL;
00641 
00642     if (!(ar->ar_group && !isAttrDefault(ar->ar_group)))
00643         ar->ar_group = NULL;
00644 
00645     dupAttrRec(ar, ret_ar);
00646 
00647     /* XXX fix all this */
00648     *specdFlags |= SPECD_UID | SPECD_GID | SPECD_FILEMODE | SPECD_DIRMODE;
00649     
00650     return 0;
00651 }
00652 /*@=boundswrite@*/
00653 
00660 /*@-boundswrite@*/
00661 static int parseForConfig(char * buf, FileList fl)
00662         /*@modifies buf, fl->processingFailed, fl->currentFlags @*/
00663 {
00664     char *p, *pe, *q;
00665     const char *name;
00666 
00667     if ((p = strstr(buf, (name = "%config"))) == NULL)
00668         return 0;
00669 
00670     fl->currentFlags |= RPMFILE_CONFIG;
00671 
00672     /* Erase "%config" token. */
00673     for (pe = p; (pe-p) < strlen(name); pe++)
00674         *pe = ' ';
00675     SKIPSPACE(pe);
00676     if (*pe != '(')
00677         return 0;
00678 
00679     /* Bracket %config args */
00680     *pe++ = ' ';
00681     for (p = pe; *pe && *pe != ')'; pe++)
00682         {};
00683 
00684     if (*pe == '\0') {
00685         rpmError(RPMERR_BADSPEC, _("Missing ')' in %s(%s\n"), name, p);
00686         fl->processingFailed = 1;
00687         return RPMERR_BADSPEC;
00688     }
00689 
00690     /* Localize. Erase parsed string. */
00691     q = alloca((pe-p) + 1);
00692     strncpy(q, p, pe-p);
00693     q[pe-p] = '\0';
00694     while (p <= pe)
00695         *p++ = ' ';
00696 
00697     for (p = q; *p != '\0'; p = pe) {
00698         SKIPWHITE(p);
00699         if (*p == '\0')
00700             break;
00701         pe = p;
00702         SKIPNONWHITE(pe);
00703         if (*pe != '\0')
00704             *pe++ = '\0';
00705         if (!strcmp(p, "missingok")) {
00706             fl->currentFlags |= RPMFILE_MISSINGOK;
00707         } else if (!strcmp(p, "noreplace")) {
00708             fl->currentFlags |= RPMFILE_NOREPLACE;
00709         } else {
00710             rpmError(RPMERR_BADSPEC, _("Invalid %s token: %s\n"), name, p);
00711             fl->processingFailed = 1;
00712             return RPMERR_BADSPEC;
00713         }
00714     }
00715 
00716     return 0;
00717 }
00718 /*@=boundswrite@*/
00719 
00722 static int langCmp(const void * ap, const void * bp)
00723         /*@*/
00724 {
00725 /*@-boundsread@*/
00726     return strcmp(*(const char **)ap, *(const char **)bp);
00727 /*@=boundsread@*/
00728 }
00729 
00736 /*@-bounds@*/
00737 static int parseForLang(char * buf, FileList fl)
00738         /*@modifies buf, fl->processingFailed,
00739                 fl->currentLangs, fl->nLangs @*/
00740 {
00741     char *p, *pe, *q;
00742     const char *name;
00743 
00744   while ((p = strstr(buf, (name = "%lang"))) != NULL) {
00745 
00746     for (pe = p; (pe-p) < strlen(name); pe++)
00747         *pe = ' ';
00748     SKIPSPACE(pe);
00749 
00750     if (*pe != '(') {
00751         rpmError(RPMERR_BADSPEC, _("Missing '(' in %s %s\n"), name, pe);
00752         fl->processingFailed = 1;
00753         return RPMERR_BADSPEC;
00754     }
00755 
00756     /* Bracket %lang args */
00757     *pe++ = ' ';
00758     for (pe = p; *pe && *pe != ')'; pe++)
00759         {};
00760 
00761     if (*pe == '\0') {
00762         rpmError(RPMERR_BADSPEC, _("Missing ')' in %s(%s\n"), name, p);
00763         fl->processingFailed = 1;
00764         return RPMERR_BADSPEC;
00765     }
00766 
00767     /* Localize. Erase parsed string. */
00768     q = alloca((pe-p) + 1);
00769     strncpy(q, p, pe-p);
00770     q[pe-p] = '\0';
00771     while (p <= pe)
00772         *p++ = ' ';
00773 
00774     /* Parse multiple arguments from %lang */
00775     for (p = q; *p != '\0'; p = pe) {
00776         char *newp;
00777         size_t np;
00778         int i;
00779 
00780         SKIPWHITE(p);
00781         pe = p;
00782         SKIPNONWHITE(pe);
00783 
00784         np = pe - p;
00785         
00786         /* Sanity check on locale lengths */
00787         if (np < 1 || (np == 1 && *p != 'C') || np >= 32) {
00788             rpmError(RPMERR_BADSPEC,
00789                 _("Unusual locale length: \"%.*s\" in %%lang(%s)\n"),
00790                 (int)np, p, q);
00791             fl->processingFailed = 1;
00792             return RPMERR_BADSPEC;
00793         }
00794 
00795         /* Check for duplicate locales */
00796         if (fl->currentLangs != NULL)
00797         for (i = 0; i < fl->nLangs; i++) {
00798             if (strncmp(fl->currentLangs[i], p, np))
00799                 /*@innercontinue@*/ continue;
00800             rpmError(RPMERR_BADSPEC, _("Duplicate locale %.*s in %%lang(%s)\n"),
00801                 (int)np, p, q);
00802             fl->processingFailed = 1;
00803             return RPMERR_BADSPEC;
00804         }
00805 
00806         /* Add new locale */
00807         fl->currentLangs = xrealloc(fl->currentLangs,
00808                                 (fl->nLangs + 1) * sizeof(*fl->currentLangs));
00809         newp = xmalloc( np+1 );
00810         strncpy(newp, p, np);
00811         newp[np] = '\0';
00812         fl->currentLangs[fl->nLangs++] = newp;
00813         if (*pe == ',') pe++;   /* skip , if present */
00814     }
00815   }
00816 
00817     /* Insure that locales are sorted. */
00818     if (fl->currentLangs)
00819         qsort(fl->currentLangs, fl->nLangs, sizeof(*fl->currentLangs), langCmp);
00820 
00821     return 0;
00822 }
00823 /*@=bounds@*/
00824 
00827 /*@-boundswrite@*/
00828 static int parseForRegexLang(const char * fileName, /*@out@*/ char ** lang)
00829         /*@globals rpmGlobalMacroContext, h_errno @*/
00830         /*@modifies *lang, rpmGlobalMacroContext @*/
00831 {
00832     static int initialized = 0;
00833     static int hasRegex = 0;
00834     static regex_t compiledPatt;
00835     static char buf[BUFSIZ];
00836     int x;
00837     regmatch_t matches[2];
00838     const char *s;
00839 
00840     if (! initialized) {
00841         const char *patt = rpmExpand("%{?_langpatt}", NULL);
00842         int rc = 0;
00843         if (!(patt && *patt != '\0'))
00844             rc = 1;
00845         else if (regcomp(&compiledPatt, patt, REG_EXTENDED))
00846             rc = -1;
00847         patt = _free(patt);
00848         if (rc)
00849             return rc;
00850         hasRegex = 1;
00851         initialized = 1;
00852     }
00853     
00854     memset(matches, 0, sizeof(matches));
00855     if (! hasRegex || regexec(&compiledPatt, fileName, 2, matches, REG_NOTEOL))
00856         return 1;
00857 
00858     /* Got match */
00859     s = fileName + matches[1].rm_eo - 1;
00860     x = matches[1].rm_eo - matches[1].rm_so;
00861     buf[x] = '\0';
00862     while (x) {
00863         buf[--x] = *s--;
00864     }
00865     if (lang)
00866         *lang = buf;
00867     return 0;
00868 }
00869 /*@=boundswrite@*/
00870 
00873 /*@-exportlocal -exportheadervar@*/
00874 /*@unchecked@*/
00875 VFA_t virtualFileAttributes[] = {
00876         { "%dir",       0,      0 },    /* XXX why not RPMFILE_DIR? */
00877         { "%doc",       0,      RPMFILE_DOC },
00878         { "%ghost",     0,      RPMFILE_GHOST },
00879         { "%exclude",   0,      RPMFILE_EXCLUDE },
00880         { "%readme",    0,      RPMFILE_README },
00881         { "%license",   0,      RPMFILE_LICENSE },
00882         { "%pubkey",    0,      RPMFILE_PUBKEY },
00883         { "%policy",    0,      RPMFILE_POLICY },
00884 
00885 #if WHY_NOT
00886         { "%icon",      0,      RPMFILE_ICON },
00887         { "%spec",      0,      RPMFILE_SPEC },
00888         { "%config",    0,      RPMFILE_CONFIG },
00889         { "%missingok", 0,      RPMFILE_CONFIG|RPMFILE_MISSINGOK },
00890         { "%noreplace", 0,      RPMFILE_CONFIG|RPMFILE_NOREPLACE },
00891 #endif
00892 
00893         { NULL, 0, 0 }
00894 };
00895 /*@=exportlocal =exportheadervar@*/
00896 
00906 /*@-boundswrite@*/
00907 static int parseForSimple(/*@unused@*/Spec spec, Package pkg, char * buf,
00908                           FileList fl, /*@out@*/ const char ** fileName)
00909         /*@globals rpmGlobalMacroContext, h_errno @*/
00910         /*@modifies buf, fl->processingFailed, *fileName,
00911                 fl->currentFlags,
00912                 fl->docDirs, fl->docDirCount, fl->isDir,
00913                 fl->passedSpecialDoc, fl->isSpecialDoc,
00914                 pkg->specialDoc, rpmGlobalMacroContext @*/
00915 {
00916     char *s, *t;
00917     int res, specialDoc = 0;
00918     char specialDocBuf[BUFSIZ];
00919 
00920     specialDocBuf[0] = '\0';
00921     *fileName = NULL;
00922     res = 0;
00923 
00924     t = buf;
00925     while ((s = strtokWithQuotes(t, " \t\n")) != NULL) {
00926         t = NULL;
00927         if (!strcmp(s, "%docdir")) {
00928             s = strtokWithQuotes(NULL, " \t\n");
00929             if (fl->docDirCount == MAXDOCDIR) {
00930                 rpmError(RPMERR_INTERNAL, _("Hit limit for %%docdir\n"));
00931                 fl->processingFailed = 1;
00932                 res = 1;
00933             }
00934         
00935             if (s != NULL)
00936                 fl->docDirs[fl->docDirCount++] = xstrdup(s);
00937             if (s == NULL || strtokWithQuotes(NULL, " \t\n")) {
00938                 rpmError(RPMERR_INTERNAL, _("Only one arg for %%docdir\n"));
00939                 fl->processingFailed = 1;
00940                 res = 1;
00941             }
00942             break;
00943         }
00944 #if defined(__LCLINT__)
00945         assert(s != NULL);
00946 #endif
00947 
00948     /* Set flags for virtual file attributes */
00949     {   VFA_t *vfa;
00950         for (vfa = virtualFileAttributes; vfa->attribute != NULL; vfa++) {
00951             if (strcmp(s, vfa->attribute))
00952                 /*@innercontinue@*/ continue;
00953             if (!vfa->flag) {
00954                 if (!strcmp(s, "%dir"))
00955                     fl->isDir = 1;      /* XXX why not RPMFILE_DIR? */
00956             } else {
00957                 if (vfa->not)
00958                     fl->currentFlags &= ~vfa->flag;
00959                 else
00960                     fl->currentFlags |= vfa->flag;
00961             }
00962 
00963             /*@innerbreak@*/ break;
00964         }
00965         /* if we got an attribute, continue with next token */
00966         if (vfa->attribute != NULL)
00967             continue;
00968     }
00969 
00970         if (*fileName) {
00971             /* We already got a file -- error */
00972             rpmError(RPMERR_BADSPEC, _("Two files on one line: %s\n"),
00973                 *fileName);
00974             fl->processingFailed = 1;
00975             res = 1;
00976         }
00977 
00978         /*@-branchstate@*/
00979         if (*s != '/') {
00980             if (fl->currentFlags & RPMFILE_DOC) {
00981                 specialDoc = 1;
00982                 strcat(specialDocBuf, " ");
00983                 strcat(specialDocBuf, s);
00984             } else
00985             if (fl->currentFlags & (RPMFILE_POLICY|RPMFILE_PUBKEY|RPMFILE_ICON))
00986             {
00987                 *fileName = s;
00988             } else {
00989                 const char * sfn = NULL;
00990                 int urltype = urlPath(s, &sfn);
00991                 switch (urltype) {
00992                 default: /* relative path, not in %doc and not a URL */
00993                     rpmError(RPMERR_BADSPEC,
00994                         _("File must begin with \"/\": %s\n"), s);
00995                     fl->processingFailed = 1;
00996                     res = 1;
00997                     /*@switchbreak@*/ break;
00998                 case URL_IS_PATH:
00999                     *fileName = s;
01000                     /*@switchbreak@*/ break;
01001                 }
01002             }
01003         } else {
01004             *fileName = s;
01005         }
01006         /*@=branchstate@*/
01007     }
01008 
01009     if (specialDoc) {
01010         if (*fileName || (fl->currentFlags & ~(RPMFILE_DOC))) {
01011             rpmError(RPMERR_BADSPEC,
01012                      _("Can't mix special %%doc with other forms: %s\n"),
01013                      (*fileName ? *fileName : ""));
01014             fl->processingFailed = 1;
01015             res = 1;
01016         } else {
01017         /* XXX WATCHOUT: buf is an arg */
01018            {    static char *_docdir_fmt= 0;
01019                 static int oneshot = 0;
01020                 const char *ddir, *fmt, *errstr;
01021                 if (!oneshot) {
01022                     _docdir_fmt = rpmExpand("%{?_docdir_fmt}", NULL);
01023                     if (!_docdir_fmt || !*_docdir_fmt)
01024                         _docdir_fmt = "%{NAME}-%{VERSION}";
01025                     oneshot = 1;
01026                 }
01027                 fmt = headerSprintf(pkg->header, _docdir_fmt, rpmTagTable, rpmHeaderFormats, &errstr);
01028                 if (!fmt) {
01029                     rpmError(RPMERR_BADSPEC, _("illegal _docdir_fmt: %s\n"), errstr);
01030                     fl->processingFailed = 1;
01031                     res = 1;
01032                 }
01033                 ddir = rpmGetPath("%{_docdir}/", fmt, NULL);
01034                 strcpy(buf, ddir);
01035                 ddir = _free(ddir);
01036             }
01037 
01038         /* XXX FIXME: this is easy to do as macro expansion */
01039 
01040             if (! fl->passedSpecialDoc) {
01041                 char *compress_doc;
01042 
01043                 pkg->specialDoc = newStringBuf();
01044                 appendStringBuf(pkg->specialDoc, "DOCDIR=\"$RPM_BUILD_ROOT\"");
01045                 appendLineStringBuf(pkg->specialDoc, buf);
01046                 appendLineStringBuf(pkg->specialDoc, "export DOCDIR");
01047                 appendLineStringBuf(pkg->specialDoc, "rm -rf \"$DOCDIR\"");
01048                 appendLineStringBuf(pkg->specialDoc, MKDIR_P " \"$DOCDIR\"");
01049 
01050                 compress_doc = rpmExpand("%{__compress_doc}", NULL);
01051                 if (compress_doc && *compress_doc != '%')
01052                     appendLineStringBuf(pkg->specialDoc, compress_doc);
01053                 compress_doc = _free(compress_doc);
01054 
01055                 /*@-temptrans@*/
01056                 *fileName = buf;
01057                 /*@=temptrans@*/
01058                 fl->passedSpecialDoc = 1;
01059                 fl->isSpecialDoc = 1;
01060             }
01061 
01062             appendStringBuf(pkg->specialDoc, "cp -pr ");
01063             appendStringBuf(pkg->specialDoc, specialDocBuf);
01064             appendLineStringBuf(pkg->specialDoc, " \"$DOCDIR\"");
01065         }
01066     }
01067 
01068     return res;
01069 }
01070 /*@=boundswrite@*/
01071 
01074 static int compareFileListRecs(const void * ap, const void * bp)        /*@*/
01075 {
01076     const char *aurl = ((FileListRec)ap)->fileURL;
01077     const char *a = NULL;
01078     const char *burl = ((FileListRec)bp)->fileURL;
01079     const char *b = NULL;
01080     (void) urlPath(aurl, &a);
01081     (void) urlPath(burl, &b);
01082     return strcmp(a, b);
01083 }
01084 
01092 static int isDoc(FileList fl, const char * fileName)    /*@*/
01093 {
01094     int x = fl->docDirCount;
01095 
01096     while (x--) {
01097         if (strstr(fileName, fl->docDirs[x]) == fileName)
01098             return 1;
01099     }
01100     return 0;
01101 }
01102 
01109 static int