예제 #1
0
파일: rpmds.c 프로젝트: PerilousApricot/rpm
static rpmRC parseSimpleDep(const char **dstrp, char **emsg, rpmrichParseFunction cb, void *cbdata)
{
    const char *p = *dstrp;
    const char *n, *e = 0;
    int nl, el = 0;
    rpmsenseFlags sense = 0;

    n = p;
    SKIPNONWHITEX(p);
    nl = p - n;
    if (nl == 0) {
        if (emsg)
          rasprintf(emsg, _("Name required"));
        return RPMRC_FAIL;
    }   
    SKIPWHITE(p);
    if (*p) {
        const char *pe = p;

        SKIPNONWHITEX(pe);
	sense = rpmParseDSFlags(p, pe - p);
        if (sense) {
            p = pe; 
            SKIPWHITE(p);
            e = p;
            SKIPNONWHITEX(p);
            el = p - e;
        }
    }   
    if (e && el == 0) {
        if (emsg)
          rasprintf(emsg, _("Version required"));
        return RPMRC_FAIL;
    }
    if (cb(cbdata, RPMRICH_PARSE_SIMPLE, n, nl, e, el, sense, RPMRICHOP_SINGLE, emsg) != RPMRC_OK)
	return RPMRC_FAIL;
    *dstrp = p;
    return RPMRC_OK;
}
예제 #2
0
static int parseNoSource(rpmSpec spec, const char * field, rpmTagVal tag)
{
    const char *f, *fe;
    const char *name;
    int flag;
    uint32_t num;

    if (tag == RPMTAG_NOSOURCE) {
	flag = RPMBUILD_ISSOURCE;
	name = "source";
    } else {
	flag = RPMBUILD_ISPATCH;
	name = "patch";
    }
    
    fe = field;
    for (f = fe; *f != '\0'; f = fe) {
        struct Source *p;

	SKIPWHITE(f);
	if (*f == '\0')
	    break;
	fe = f;
	SKIPNONWHITE(fe);
	if (*fe != '\0') fe++;

	if (parseUnsignedNum(f, &num)) {
	    rpmlog(RPMLOG_ERR, _("line %d: Bad number: %s\n"),
		     spec->lineNum, f);
	    return RPMRC_FAIL;
	}

	if (! (p = findSource(spec, num, flag))) {
	    rpmlog(RPMLOG_ERR, _("line %d: Bad no%s number: %u\n"),
		     spec->lineNum, name, num);
	    return RPMRC_FAIL;
	}

	p->flags |= RPMBUILD_ISNO;

    }

    return 0;
}
예제 #3
0
int parseNoSource(Spec spec, const char * field, rpmTag tag)
{
    const char *f, *fe;
    const char *name;
    rpmuint32_t num, flag;

    if (tag == RPMTAG_NOSOURCE) {
	flag = RPMFILE_SOURCE;
	name = "source";
    } else {
	flag = RPMFILE_PATCH;
	name = "patch";
    }
    
    fe = field;
    for (f = fe; *f != '\0'; f = fe) {
	struct Source *p;

	SKIPWHITE(f);
	if (*f == '\0')
	    break;
	fe = f;
	SKIPNONWHITE(fe);
	if (*fe != '\0') fe++;

	if (parseNum(f, &num)) {
	    rpmlog(RPMLOG_ERR, _("line %d: Bad number: %s\n"),
		     spec->lineNum, f);
	    return RPMRC_FAIL;
	}

	if (! (p = findSource(spec, num, flag))) {
	    rpmlog(RPMLOG_ERR, _("line %d: Bad no%s number: %d\n"),
		     spec->lineNum, name, num);
	    return RPMRC_FAIL;
	}

	p->flags |= RPMFILE_GHOST;

    }

    return RPMRC_OK;
}
예제 #4
0
static int ReadTag(GTIF *gt,GTIFReadMethod scan,void *aux)
{
    int i,j,tag;
    char *vptr;
    char tagname[100];
    double *data,*dptr;
    int count,nrows,ncols,num;
    char message[1024];

    scan(message,aux);
    if (!strncmp(message,FMT_TAGEND,8)) return 0;

    num=sscanf(message,"%[^( ] (%d,%d):\n",tagname,&nrows,&ncols);
    if (num!=3) return StringError(message);

    tag = GTIFTagCode(tagname);
    if (tag < 0) return StringError(tagname);

    count = nrows*ncols;

    data = (double *) _GTIFcalloc(count * sizeof(double));
    dptr = data;

    for (i=0; i<nrows; i++)
    {
        scan(message,aux);
        vptr = message;
        for (j=0; j<ncols; j++)
        {
            if (!sscanf(vptr,"%lg",dptr++))
                return StringError(vptr);
            FINDCHAR(vptr,' ');
            SKIPWHITE(vptr);
        }
    }
    (gt->gt_methods.set)(gt->gt_tif, (pinfo_t) tag, count, data );

    _GTIFFree( data );

    return 1;
}
예제 #5
0
static int ReadKey(GTIF *gt, GTIFReadMethod scan, void *aux)
{
    tagtype_t ktype;
    int count,outcount;
    int vals_now,i;
    geokey_t key;
    int icode;
    pinfo_t code;
    short  *sptr;
    char name[1000];
    char type[20];
    double data[100];
    double *dptr;
    char *vptr;
    int num;
    char message[2048];

    scan(message,aux);
    if (!strncmp(message,FMT_KEYEND,8)) return 0;

    num=sscanf(message,"%[^( ] (%[^,],%d):\n",name,type,&count);
    if (num!=3) return StringError(message);

    vptr = message;
    FINDCHAR(vptr,':');
    if (!*vptr) return StringError(message);
    vptr+=2;

    if( GTIFKeyCode(name) < 0 )
        return StringError(name);
    else
        key = (geokey_t) GTIFKeyCode(name);

    if( GTIFTypeCode(type) < 0 )
        return StringError(type);
    else
        ktype = (tagtype_t) GTIFTypeCode(type);

    /* skip white space */
    SKIPWHITE(vptr);
    if (!*vptr) return StringError(message);

    switch (ktype)
    {
    case TYPE_ASCII:
    {
        char *cdata;
        int out_char = 0;

        FINDCHAR(vptr,'"');
        if (!*vptr) return StringError(message);

        cdata = (char *) _GTIFcalloc( count+1 );

        vptr++;
        while( out_char < count-1 )
        {
            if( *vptr == '\0' )
                break;

            else if( vptr[0] == '\\' && vptr[1] == 'n' )
            {
                cdata[out_char++] = '\n';
                vptr += 2;
            }
            else if( vptr[0] == '\\' && vptr[1] == '\\' )
            {
                cdata[out_char++] = '\\';
                vptr += 2;
            }
            else
                cdata[out_char++] = *(vptr++);
        }

        if( out_char < count-1 ) return StringError(message);
        if( *vptr != '"' ) return StringError(message);

        cdata[count-1] = '\0';
        GTIFKeySet(gt,key,ktype,count,cdata);

        _GTIFFree( cdata );
    }
    break;

    case TYPE_DOUBLE:
        outcount = count;
        for (dptr = data; count > 0; count-= vals_now)
        {
            vals_now = count > 3? 3: count;
            for (i=0; i<vals_now; i++,dptr++)
            {
                if (!sscanf(vptr,"%lg" ,dptr))
                    StringError(vptr);
                FINDCHAR(vptr,' ');
                SKIPWHITE(vptr);
            }
            if (vals_now<count)
            {
                scan(message,aux);
                vptr = message;
            }
        }
        if (outcount==1)
            GTIFKeySet(gt,key,ktype,outcount,data[0]);
        else
            GTIFKeySet(gt,key,ktype,outcount,data);
        break;

    case TYPE_SHORT:
        if (count==1)
        {
            icode = GTIFValueCode(key,vptr);
            if (icode < 0) return StringError(vptr);
            code = (pinfo_t) icode;
            GTIFKeySet(gt,key,ktype,count,code);
        }
        else  /* multi-valued short - no such thing yet */
        {
            char* cdata;
            memcpy(&cdata, &data, sizeof(void*));
            sptr = (short *)cdata;
            outcount = count;
            for (; count > 0; count-= vals_now)
            {
                vals_now = count > 3? 3: count;
                for (i=0; i<vals_now; i++,sptr++)
                {
                    int		work_int;

                    /* note: FMT_SHORT (%11hd) not supported on IRIX */
                    sscanf(message,"%11d",&work_int);
                    *sptr = (short) work_int;
                    scan(message,aux);
                }
                if (vals_now<count)
                {
                    scan(message,aux);
                    vptr = message;
                }
            }
            GTIFKeySet(gt,key,ktype,outcount,sptr);
        }
        break;

    default:
        return -1;
    }
    return 1;
}
예제 #6
0
파일: rpmds.c 프로젝트: PerilousApricot/rpm
rpmRC rpmrichParse(const char **dstrp, char **emsg, rpmrichParseFunction cb, void *cbdata)
{
    const char *p = *dstrp, *pe;
    rpmrichOp op = RPMRICHOP_SINGLE, chainop = 0;

    if (cb(cbdata, RPMRICH_PARSE_ENTER, p, 0, 0, 0, 0, op, emsg) != RPMRC_OK)
        return RPMRC_FAIL;
    if (*p++ != '(') {
        if (emsg)
          rasprintf(emsg, _("Rich dependency does not start with '('"));
        return RPMRC_FAIL;
    }
    for (;;) {
        SKIPWHITE(p);
        if (*p == ')') {
            if (emsg) {
                if (chainop)
                    rasprintf(emsg, _("Missing argument to rich dependency op"));
                else
                    rasprintf(emsg, _("Empty rich dependency"));
            }
            return RPMRC_FAIL;
        }
        if (*p == '(') {
            if (rpmrichParse(&p, emsg, cb, cbdata) != RPMRC_OK)
                return RPMRC_FAIL;
        } else {
            if (parseSimpleDep(&p, emsg, cb, cbdata) != RPMRC_OK)
                return RPMRC_FAIL;
        }
        SKIPWHITE(p);
        if (!*p) {
            if (emsg)
                rasprintf(emsg, _("Unterminated rich dependency: %s"), *dstrp);
            return RPMRC_FAIL;
        }
        if (*p == ')')
            break;
        pe = p;
        if (parseRichDepOp(&pe, &op, emsg) != RPMRC_OK)
            return RPMRC_FAIL;
	if (op == RPMRICHOP_ELSE && chainop == RPMRICHOP_THEN)
	    chainop = 0;
        if (chainop && op != chainop) {
            if (emsg)
                rasprintf(emsg, _("Cannot chain different ops"));
            return RPMRC_FAIL;
        }
        if (chainop && op != RPMRICHOP_AND && op != RPMRICHOP_OR) {
            if (emsg)
                rasprintf(emsg, _("Can only chain AND and OR ops"));
            return RPMRC_FAIL;
	}
        if (cb(cbdata, RPMRICH_PARSE_OP, p, pe - p, 0, 0, 0, op, emsg) != RPMRC_OK)
            return RPMRC_FAIL;
        chainop = op;
        p = pe;
    }
    p++;
    if (cb(cbdata, RPMRICH_PARSE_LEAVE, *dstrp, p - *dstrp , 0, 0, 0, op, emsg) != RPMRC_OK)
        return RPMRC_FAIL;
    *dstrp = p;
    return RPMRC_OK;
}
예제 #7
0
파일: parseReqs.c 프로젝트: akozumpl/rpm
rpmRC parseRCPOT(rpmSpec spec, Package pkg, const char *field, rpmTagVal tagN,
	       int index, rpmsenseFlags tagflags)
{
    const char *r, *re, *v, *ve;
    const char *emsg = NULL;
    char * N = NULL, * EVR = NULL;
    rpmTagVal nametag = RPMTAG_NOT_FOUND;
    rpmsenseFlags Flags;
    Header h = pkg->header; /* everything except buildrequires go here */
    rpmRC rc = RPMRC_FAIL; /* assume failure */

    switch (tagN) {
    default:
    case RPMTAG_REQUIREFLAGS:
	nametag = RPMTAG_REQUIRENAME;
	tagflags |= RPMSENSE_ANY;
	break;
    case RPMTAG_PROVIDEFLAGS:
	nametag = RPMTAG_PROVIDENAME;
	break;
    case RPMTAG_OBSOLETEFLAGS:
	nametag = RPMTAG_OBSOLETENAME;
	break;
    case RPMTAG_CONFLICTFLAGS:
	nametag = RPMTAG_CONFLICTNAME;
	break;
    case RPMTAG_ORDERFLAGS:
	nametag = RPMTAG_ORDERNAME;
	break;
    case RPMTAG_PREREQ:
	/* XXX map legacy PreReq into Requires(pre,preun) */
	nametag = RPMTAG_REQUIRENAME;
	tagflags |= (RPMSENSE_SCRIPT_PRE|RPMSENSE_SCRIPT_PREUN);
	break;
    case RPMTAG_TRIGGERPREIN:
	nametag = RPMTAG_TRIGGERNAME;
	tagflags |= RPMSENSE_TRIGGERPREIN;
	break;
    case RPMTAG_TRIGGERIN:
	nametag = RPMTAG_TRIGGERNAME;
	tagflags |= RPMSENSE_TRIGGERIN;
	break;
    case RPMTAG_TRIGGERPOSTUN:
	nametag = RPMTAG_TRIGGERNAME;
	tagflags |= RPMSENSE_TRIGGERPOSTUN;
	break;
    case RPMTAG_TRIGGERUN:
	nametag = RPMTAG_TRIGGERNAME;
	tagflags |= RPMSENSE_TRIGGERUN;
	break;
    case RPMTAG_BUILDPREREQ:
    case RPMTAG_BUILDREQUIRES:
	nametag = RPMTAG_REQUIRENAME;
	tagflags |= RPMSENSE_ANY;
	h = spec->buildRestrictions;
	break;
    case RPMTAG_BUILDCONFLICTS:
	nametag = RPMTAG_CONFLICTNAME;
	h = spec->buildRestrictions;
	break;
    }

    for (r = field; *r != '\0'; r = re) {
	SKIPWHITE(r);
	if (*r == '\0')
	    break;

	Flags = (tagflags & ~RPMSENSE_SENSEMASK);

	/* 
	 * Tokens must begin with alphanumeric, _, or /, but we don't know
	 * the spec's encoding so we only check what we can: plain ascii.
	 */
	if (isascii(r[0]) && !(risalnum(r[0]) || r[0] == '_' || r[0] == '/')) {
	    emsg = _("Dependency tokens must begin with alpha-numeric, '_' or '/'");
	    goto exit;
	}

	re = r;
	SKIPNONWHITE(re);
	N = xmalloc((re-r) + 1);
	rstrlcpy(N, r, (re-r) + 1);

	/* Parse EVR */
	v = re;
	SKIPWHITE(v);
	ve = v;
	SKIPNONWHITE(ve);

	re = v;	/* ==> next token (if no EVR found) starts here */

	/* Check for possible logical operator */
	if (ve > v) {
	  const struct ReqComp *rc;
	  for (rc = ReqComparisons; rc->token != NULL; rc++) {
	    if ((ve-v) != strlen(rc->token) || !rstreqn(v, rc->token, (ve-v)))
		continue;

	    if (r[0] == '/') {
		emsg = _("Versioned file name not permitted");
		goto exit;
	    }

	    Flags |= rc->sense;

	    /* now parse EVR */
	    v = ve;
	    SKIPWHITE(v);
	    ve = v;
	    SKIPNONWHITE(ve);
	    break;
	  }
	}

	if (Flags & RPMSENSE_SENSEMASK) {
	    if (*v == '\0' || ve == v) {
		emsg = _("Version required");
		goto exit;
	    }
	    EVR = xmalloc((ve-v) + 1);
	    rstrlcpy(EVR, v, (ve-v) + 1);
	    if (rpmCharCheck(spec, EVR, ve-v, ".-_+:%{}")) goto exit;
	    re = ve;	/* ==> next token after EVR string starts here */
	} else
	    EVR = NULL;

	if (addReqProv(h, nametag, N, EVR, Flags, index)) {
	    emsg = _("invalid dependency");
	    goto exit;
	}

	N = _free(N);
	EVR = _free(EVR);

    }
    rc = RPMRC_OK;

exit:
    if (emsg) {
	/* Automatic dependencies don't relate to spec lines */
	if (tagflags & (RPMSENSE_FIND_REQUIRES|RPMSENSE_FIND_PROVIDES)) {
	    rpmlog(RPMLOG_ERR, "%s: %s\n", emsg, r);
	} else {
	    rpmlog(RPMLOG_ERR, _("line %d: %s: %s\n"),
		   spec->lineNum, emsg, spec->line);
	}
    }
    free(N);
    free(EVR);

    return rc;
}
예제 #8
0
rpmRC parseRCPOT(rpmSpec spec, Package pkg, const char *field, rpmTagVal tagN,
	       int index, rpmsenseFlags tagflags)
{
    const char *r, *re, *v, *ve;
    char *emsg = NULL;
    char * N = NULL, * EVR = NULL;
    rpmTagVal nametag = RPMTAG_NOT_FOUND;
    rpmsenseFlags Flags;
    rpmds *pdsp = NULL;
    rpmRC rc = RPMRC_FAIL; /* assume failure */

    switch (tagN) {
    default:
    case RPMTAG_REQUIREFLAGS:
	nametag = RPMTAG_REQUIRENAME;
	tagflags |= RPMSENSE_ANY;
	break;
    case RPMTAG_RECOMMENDFLAGS:
	nametag = RPMTAG_RECOMMENDNAME;
	break;
    case RPMTAG_SUGGESTFLAGS:
	nametag = RPMTAG_SUGGESTNAME;
	break;
    case RPMTAG_SUPPLEMENTFLAGS:
	nametag = RPMTAG_SUPPLEMENTNAME;
	break;
    case RPMTAG_ENHANCEFLAGS:
	nametag = RPMTAG_ENHANCENAME;
	break;
    case RPMTAG_PROVIDEFLAGS:
	nametag = RPMTAG_PROVIDENAME;
	break;
    case RPMTAG_OBSOLETEFLAGS:
	nametag = RPMTAG_OBSOLETENAME;
	break;
    case RPMTAG_CONFLICTFLAGS:
	nametag = RPMTAG_CONFLICTNAME;
	break;
    case RPMTAG_ORDERFLAGS:
	nametag = RPMTAG_ORDERNAME;
	break;
    case RPMTAG_PREREQ:
	/* XXX map legacy PreReq into Requires(pre,preun) */
	nametag = RPMTAG_REQUIRENAME;
	tagflags |= (RPMSENSE_SCRIPT_PRE|RPMSENSE_SCRIPT_PREUN);
	break;
    case RPMTAG_TRIGGERPREIN:
	nametag = RPMTAG_TRIGGERNAME;
	tagflags |= RPMSENSE_TRIGGERPREIN;
	break;
    case RPMTAG_TRIGGERIN:
	nametag = RPMTAG_TRIGGERNAME;
	tagflags |= RPMSENSE_TRIGGERIN;
	break;
    case RPMTAG_TRIGGERPOSTUN:
	nametag = RPMTAG_TRIGGERNAME;
	tagflags |= RPMSENSE_TRIGGERPOSTUN;
	break;
    case RPMTAG_TRIGGERUN:
	nametag = RPMTAG_TRIGGERNAME;
	tagflags |= RPMSENSE_TRIGGERUN;
	break;
    case RPMTAG_BUILDPREREQ:
    case RPMTAG_BUILDREQUIRES:
	nametag = RPMTAG_REQUIRENAME;
	tagflags |= RPMSENSE_ANY;
	break;
    case RPMTAG_BUILDCONFLICTS:
	nametag = RPMTAG_CONFLICTNAME;
	break;
    case RPMTAG_FILETRIGGERIN:
	nametag = RPMTAG_FILETRIGGERNAME;
	tagflags |= RPMSENSE_TRIGGERIN;
	break;
    case RPMTAG_FILETRIGGERUN:
	nametag = RPMTAG_FILETRIGGERNAME;
	tagflags |= RPMSENSE_TRIGGERUN;
	break;
    case RPMTAG_FILETRIGGERPOSTUN:
	nametag = RPMTAG_FILETRIGGERNAME;
	tagflags |= RPMSENSE_TRIGGERPOSTUN;
	break;
    case RPMTAG_TRANSFILETRIGGERIN:
	nametag = RPMTAG_TRANSFILETRIGGERNAME;
	tagflags |= RPMSENSE_TRIGGERIN;
	break;
    case RPMTAG_TRANSFILETRIGGERUN:
	nametag = RPMTAG_TRANSFILETRIGGERNAME;
	tagflags |= RPMSENSE_TRIGGERUN;
	break;
    case RPMTAG_TRANSFILETRIGGERPOSTUN:
	nametag = RPMTAG_TRANSFILETRIGGERNAME;
	tagflags |= RPMSENSE_TRIGGERPOSTUN;
	break;
    }

    for (r = field; *r != '\0'; r = re) {
	SKIPWHITE(r);
	if (*r == '\0')
	    break;

	Flags = (tagflags & ~RPMSENSE_SENSEMASK);

	if (r[0] == '(') {
	    struct parseRCPOTRichData data;
	    if (nametag != RPMTAG_REQUIRENAME && nametag != RPMTAG_CONFLICTNAME &&
			nametag != RPMTAG_RECOMMENDNAME && nametag != RPMTAG_SUPPLEMENTNAME &&
			nametag != RPMTAG_SUGGESTNAME && nametag != RPMTAG_ENHANCENAME) {
		rasprintf(&emsg, _("No rich dependencies allowed for this type"));
		goto exit;
	    }
	    data.spec = spec;
	    data.sb = newStringBuf();
	    if (rpmrichParse(&r, &emsg, parseRCPOTRichCB, &data) != RPMRC_OK) {
		freeStringBuf(data.sb);
		goto exit;
	    }
	    if (addReqProv(pkg, nametag, getStringBuf(data.sb), NULL, Flags | RPMSENSE_RICH, index)) {
		rasprintf(&emsg, _("invalid dependency"));
		freeStringBuf(data.sb);
		goto exit;
	    }
	    freeStringBuf(data.sb);
	    re = r;
	    continue;
	}

	re = r;
	SKIPNONWHITE(re);
	N = xmalloc((re-r) + 1);
	rstrlcpy(N, r, (re-r) + 1);

	/* Parse EVR */
	EVR = NULL;
	v = re;
	SKIPWHITE(v);
	ve = v;
	SKIPNONWHITE(ve);

	re = v;	/* ==> next token (if no EVR found) starts here */

	/* Check for possible logical operator */
	if (ve > v) {
	    rpmsenseFlags sense = rpmParseDSFlags(v, ve - v);
	    if (sense) {
		Flags |= sense;

		/* now parse EVR */
		v = ve;
		SKIPWHITE(v);
		ve = v;
		SKIPNONWHITE(ve);
		if (*v == '\0' || ve == v) {
		    rasprintf(&emsg, _("Version required"));
		    goto exit;
		}
		EVR = xmalloc((ve-v) + 1);
		rstrlcpy(EVR, v, (ve-v) + 1);
		re = ve;	/* ==> next token after EVR string starts here */
	    }
	}

	/* check that dependency is well-formed */
	if (checkDep(spec, N, EVR, &emsg))
	    goto exit;

	if (nametag == RPMTAG_FILETRIGGERNAME ||
	    nametag == RPMTAG_TRANSFILETRIGGERNAME) {
	    if (N[0] != '/') {
		rasprintf(&emsg, _("Only absolute paths are allowed in "
				    "file triggers"));
	    }
	}

	/* Deny more "normal" triggers fired by the same pakage. File triggers are ok */
	pdsp = packageDependencies(pkg, nametag);
	rpmdsInit(*pdsp);
	if (nametag == RPMTAG_TRIGGERNAME) {
	    while (rpmdsNext(*pdsp) >= 0) {
		if (rstreq(rpmdsN(*pdsp), N) && ((rpmdsFlags(*pdsp) & tagflags))) {
		    rasprintf(&emsg, _("Trigger fired by the same package "
			"is already defined in spec file"));
		    goto exit;
		}
	    }
	}

	if (addReqProv(pkg, nametag, N, EVR, Flags, index)) {
	    rasprintf(&emsg, _("invalid dependency"));
	    goto exit;
	}

	N = _free(N);
	EVR = _free(EVR);

    }
    rc = RPMRC_OK;

exit:
    if (emsg) {
	int lvl = (rc == RPMRC_OK) ? RPMLOG_WARNING : RPMLOG_ERR;
	/* Automatic dependencies don't relate to spec lines */
	if (tagflags & (RPMSENSE_FIND_REQUIRES|RPMSENSE_FIND_PROVIDES)) {
	    rpmlog(lvl, "%s: %s\n", emsg, r);
	} else {
	    rpmlog(lvl, _("line %d: %s: %s\n"),
		   spec->lineNum, emsg, spec->line);
	}
	free(emsg);
    }
    _free(N);
    _free(EVR);

    return rc;
}