2002-03-25 23:16:26 +03:00
/** \ingroup header
2005-10-15 18:12:36 +04:00
* \ file rpmdb / header . c
2002-03-25 23:16:26 +03:00
*/
/* RPM - Copyright (C) 1995-2000 Red Hat Software */
/* Data written to file descriptors is in network byte order. */
/* Data read from file descriptors is expected to be in */
/* network byte order and is converted on the fly to host order. */
# include "system.h"
2004-06-26 20:46:55 +04:00
# include <locale.h>
2002-03-26 03:33:14 +03:00
# include <langinfo.h>
# include <iconv.h>
2002-03-25 23:16:26 +03:00
# define __HEADER_PROTOTYPES__
# include <header_internal.h>
2002-07-20 14:39:40 +04:00
/* We get parseEVR() from there used in parsing the >-test in conditionals: */
2006-05-15 02:57:16 +04:00
# include "rpmlib.h"
2002-07-20 14:39:40 +04:00
2002-03-25 23:16:26 +03:00
# include "debug.h"
/*@-redecl@*/ /* FIX: avoid rpmlib.h, need for debugging. */
2006-05-15 02:48:59 +04:00
/*@observer@*/ const char * tagName ( int tag ) /*@*/ ;
2002-03-25 23:16:26 +03:00
/*@=redecl@*/
/*@access entryInfo @*/
/*@access indexEntry @*/
/*@access extensionCache @*/
/*@access sprintfTag @*/
/*@access sprintfToken @*/
/*@access HV_t @*/
# define PARSER_BEGIN 0
# define PARSER_IN_ARRAY 1
# define PARSER_IN_EXPR 2
/** \ingroup header
*/
/*@observer@*/ /*@unchecked@*/
static unsigned char header_magic [ 8 ] = {
0x8e , 0xad , 0xe8 , 0x01 , 0x00 , 0x00 , 0x00 , 0x00
} ;
/** \ingroup header
* Maximum no . of bytes permitted in a header .
*/
/*@unchecked@*/
static size_t headerMaxbytes = ( 32 * 1024 * 1024 ) ;
/**
* Sanity check on no . of tags .
* This check imposes a limit of 65 K tags , more than enough .
*/
# define hdrchkTags(_ntags) ((_ntags) & 0xffff0000)
/**
* Sanity check on data size and / or offset .
* This check imposes a limit of 16 Mb , more than enough .
*/
# define hdrchkData(_nbytes) ((_nbytes) & 0xff000000)
2011-09-29 14:22:32 +04:00
/**
* Sanity check on range of data offset .
*/
# define hdrchkRange(_dl, _off) ((_off) < 0 || (_off) > (_dl))
2002-03-25 23:16:26 +03:00
/** \ingroup header
* Alignment needs ( and sizeof scalars types ) for internal rpm data types .
*/
/*@observer@*/ /*@unchecked@*/
static int typeSizes [ ] = {
0 , /*!< RPM_NULL_TYPE */
1 , /*!< RPM_CHAR_TYPE */
1 , /*!< RPM_INT8_TYPE */
2 , /*!< RPM_INT16_TYPE */
4 , /*!< RPM_INT32_TYPE */
- 1 , /*!< RPM_INT64_TYPE */
- 1 , /*!< RPM_STRING_TYPE */
1 , /*!< RPM_BIN_TYPE */
- 1 , /*!< RPM_STRING_ARRAY_TYPE */
- 1 /*!< RPM_I18NSTRING_TYPE */
} ;
/*@observer@*/ /*@unchecked@*/
HV_t hdrVec ; /* forward reference */
2002-07-20 14:39:40 +04:00
/* Already defined in <rpmlib.h> */
#if 0
2002-03-25 23:16:26 +03:00
/**
* Wrapper to free ( 3 ) , hides const compilation noise , permit NULL , return NULL .
* @ param p memory to free
* @ return NULL always
*/
/*@unused@*/ static inline /*@null@*/ void *
_free ( /*@only@*/ /*@null@*/ /*@out@*/ const void * p ) /*@modifies *p @*/
{
if ( p ! = NULL ) free ( ( void * ) p ) ;
return NULL ;
}
2002-07-20 14:39:40 +04:00
# endif
2002-03-25 23:16:26 +03:00
/** \ingroup header
* Reference a header instance .
* @ param h header
* @ return referenced header instance
*/
static
Header headerLink ( Header h )
/*@modifies h @*/
{
if ( h ! = NULL ) h - > nrefs + + ;
/*@-refcounttrans -nullret @*/
return h ;
/*@=refcounttrans =nullret @*/
}
/** \ingroup header
* Dereference a header instance .
* @ param h header
* @ return NULL always
*/
static /*@null@*/
Header headerUnlink ( /*@killref@*/ /*@null@*/ Header h )
/*@modifies h @*/
{
if ( h ! = NULL ) h - > nrefs - - ;
return NULL ;
}
/** \ingroup header
* Dereference a header instance .
* @ param h header
* @ return NULL always
*/
static /*@null@*/
Header headerFree ( /*@killref@*/ /*@null@*/ Header h )
/*@modifies h @*/
{
( void ) headerUnlink ( h ) ;
/*@-usereleased@*/
if ( h = = NULL | | h - > nrefs > 0 )
return NULL ; /* XXX return previous header? */
if ( h - > index ) {
indexEntry entry = h - > index ;
int i ;
for ( i = 0 ; i < h - > indexUsed ; i + + , entry + + ) {
if ( ( h - > flags & HEADERFLAG_ALLOCATED ) & & ENTRY_IS_REGION ( entry ) ) {
if ( entry - > length > 0 ) {
int_32 * ei = entry - > data ;
if ( ( ei - 2 ) = = h - > blob ) h - > blob = _free ( h - > blob ) ;
entry - > data = NULL ;
}
} else if ( ! ENTRY_IN_REGION ( entry ) ) {
entry - > data = _free ( entry - > data ) ;
}
entry - > data = NULL ;
}
h - > index = _free ( h - > index ) ;
}
/*@-refcounttrans@*/ h = _free ( h ) ; /*@=refcounttrans@*/
return h ;
/*@=usereleased@*/
}
/** \ingroup header
* Create new ( empty ) header instance .
* @ return header
*/
static
Header headerNew ( void )
/*@*/
{
Header h = xcalloc ( 1 , sizeof ( * h ) ) ;
/*@-assignexpose@*/
h - > hv = * hdrVec ; /* structure assignment */
/*@=assignexpose@*/
h - > blob = NULL ;
h - > indexAlloced = INDEX_MALLOC_SIZE ;
h - > indexUsed = 0 ;
2010-08-19 02:03:03 +04:00
h - > instance = 0 ;
2002-03-25 23:16:26 +03:00
h - > flags = HEADERFLAG_SORTED ;
h - > index = ( h - > indexAlloced
? xcalloc ( h - > indexAlloced , sizeof ( * h - > index ) )
: NULL ) ;
/*@-globstate -observertrans @*/
h - > nrefs = 0 ;
return headerLink ( h ) ;
/*@=globstate =observertrans @*/
}
/**
*/
static int indexCmp ( const void * avp , const void * bvp ) /*@*/
{
/*@-castexpose@*/
indexEntry ap = ( indexEntry ) avp , bp = ( indexEntry ) bvp ;
/*@=castexpose@*/
return ( ap - > info . tag - bp - > info . tag ) ;
}
/** \ingroup header
* Sort tags in header .
* @ param h header
*/
static
void headerSort ( Header h )
/*@modifies h @*/
{
if ( ! ( h - > flags & HEADERFLAG_SORTED ) ) {
qsort ( h - > index , h - > indexUsed , sizeof ( * h - > index ) , indexCmp ) ;
h - > flags | = HEADERFLAG_SORTED ;
}
}
/**
*/
static int offsetCmp ( const void * avp , const void * bvp ) /*@*/
{
/*@-castexpose@*/
indexEntry ap = ( indexEntry ) avp , bp = ( indexEntry ) bvp ;
/*@=castexpose@*/
int rc = ( ap - > info . offset - bp - > info . offset ) ;
if ( rc = = 0 ) {
/* Within a region, entries sort by address. Added drips sort by tag. */
if ( ap - > info . offset < 0 )
rc = ( ( ( char * ) ap - > data ) - ( ( char * ) bp - > data ) ) ;
else
rc = ( ap - > info . tag - bp - > info . tag ) ;
}
return rc ;
}
/** \ingroup header
* Restore tags in header to original ordering .
* @ param h header
*/
static
void headerUnsort ( Header h )
/*@modifies h @*/
{
qsort ( h - > index , h - > indexUsed , sizeof ( * h - > index ) , offsetCmp ) ;
}
/** \ingroup header
* Return size of on - disk header representation in bytes .
* @ param h header
* @ param magicp include size of 8 bytes for ( magic , 0 ) ?
* @ return size of on - disk header
*/
static
unsigned int headerSizeof ( /*@null@*/ Header h , enum hMagic magicp )
/*@modifies h @*/
{
indexEntry entry ;
unsigned int size = 0 ;
unsigned int pad = 0 ;
int i ;
if ( h = = NULL )
return size ;
headerSort ( h ) ;
switch ( magicp ) {
case HEADER_MAGIC_YES :
size + = sizeof ( header_magic ) ;
break ;
case HEADER_MAGIC_NO :
break ;
}
/*@-sizeoftype@*/
size + = 2 * sizeof ( int_32 ) ; /* count of index entries */
/*@=sizeoftype@*/
for ( i = 0 , entry = h - > index ; i < h - > indexUsed ; i + + , entry + + ) {
unsigned diff ;
int_32 type ;
/* Regions go in as is ... */
if ( ENTRY_IS_REGION ( entry ) ) {
size + = entry - > length ;
/* XXX Legacy regions do not include the region tag and data. */
/*@-sizeoftype@*/
if ( i = = 0 & & ( h - > flags & HEADERFLAG_LEGACY ) )
size + = sizeof ( struct entryInfo ) + entry - > info . count ;
/*@=sizeoftype@*/
continue ;
}
/* ... and region elements are skipped. */
if ( entry - > info . offset < 0 )
continue ;
/* Alignment */
type = entry - > info . type ;
if ( typeSizes [ type ] > 1 ) {
diff = typeSizes [ type ] - ( size % typeSizes [ type ] ) ;
if ( diff ! = typeSizes [ type ] ) {
size + = diff ;
pad + = diff ;
}
}
/*@-sizeoftype@*/
size + = sizeof ( struct entryInfo ) + entry - > length ;
/*@=sizeoftype@*/
}
return size ;
}
/**
* Return length of entry data .
* @ todo Remove sanity check exit ' s .
* @ param type entry data type
* @ param p entry data
* @ param count entry item count
* @ param onDisk data is concatenated strings ( with NUL ' s ) ) ?
* @ return no . bytes in data
*/
/*@mayexit@*/
static int dataLength ( int_32 type , hPTR_t p , int_32 count , int onDisk )
/*@*/
{
int length = 0 ;
switch ( type ) {
case RPM_STRING_TYPE :
if ( count = = 1 ) { /* Special case -- p is just the string */
length = strlen ( p ) + 1 ;
break ;
}
/* This should not be allowed */
/*@-modfilesys@*/
fprintf ( stderr , _ ( " dataLength() RPM_STRING_TYPE count must be 1. \n " ) ) ;
/*@=modfilesys@*/
exit ( EXIT_FAILURE ) ;
/*@notreached@*/ break ;
case RPM_STRING_ARRAY_TYPE :
case RPM_I18NSTRING_TYPE :
/* This is like RPM_STRING_TYPE, except it's *always* an array */
/* Compute sum of length of all strings, including null terminators */
if ( onDisk ) {
2011-01-21 02:02:57 +03:00
const char * end = p ;
while ( count - - )
end + = strlen ( end ) + 1 ;
length = end - ( const char * ) p ;
2002-03-25 23:16:26 +03:00
} else {
2011-01-21 02:02:57 +03:00
const char * * src = ( const char * * ) p ;
const char * * end = src + count ;
while ( src < end )
2002-03-25 23:16:26 +03:00
length + = strlen ( * src + + ) + 1 ;
}
2011-01-21 02:02:57 +03:00
break ;
2002-03-25 23:16:26 +03:00
default :
if ( typeSizes [ type ] ! = - 1 ) {
length = typeSizes [ type ] * count ;
break ;
}
/*@-modfilesys@*/
fprintf ( stderr , _ ( " Data type %d not supported \n " ) , ( int ) type ) ;
/*@=modfilesys@*/
exit ( EXIT_FAILURE ) ;
/*@notreached@*/ break ;
}
return length ;
}
/** \ingroup header
* Swap int_32 and int_16 arrays within header region .
*
* This code is way more twisty than I would like .
*
* A bug with RPM_I18NSTRING_TYPE in rpm - 2.5 . x ( fixed in August 1998 )
* causes the offset and length of elements in a header region to disagree
* regarding the total length of the region data .
*
* The " fix " is to compute the size using both offset and length and
* return the larger of the two numbers as the size of the region .
* Kinda like computing left and right Riemann sums of the data elements
* to determine the size of a data structure , go figger : - ) .
*
* There ' s one other twist if a header region tag is in the set to be swabbed ,
* as the data for a header region is located after all other tag data .
*
* @ param entry header entry
* @ param il no . of entries
* @ param dl start no . bytes of data
* @ param pe header physical entry pointer ( swapped )
* @ param dataStart header data
* @ param regionid region offset
* @ return no . bytes of data in region , - 1 on error
*/
static int regionSwab ( /*@null@*/ indexEntry entry , int il , int dl ,
entryInfo pe , char * dataStart , int regionid )
/*@modifies *entry, *dataStart @*/
{
char * tprev = NULL ;
char * t = NULL ;
int tdel , tl = dl ;
struct indexEntry ieprev ;
2011-09-29 13:50:57 +04:00
if ( ( entry ! = NULL & & regionid > = 0 ) | | ( entry = = NULL & & regionid ! = 0 ) )
return - 1 ;
2002-03-25 23:16:26 +03:00
memset ( & ieprev , 0 , sizeof ( ieprev ) ) ;
for ( ; il > 0 ; il - - , pe + + ) {
struct indexEntry ie ;
int_32 type ;
ie . info . tag = ntohl ( pe - > tag ) ;
ie . info . type = ntohl ( pe - > type ) ;
if ( ie . info . type < RPM_MIN_TYPE | | ie . info . type > RPM_MAX_TYPE )
return - 1 ;
ie . info . count = ntohl ( pe - > count ) ;
ie . info . offset = ntohl ( pe - > offset ) ;
ie . data = t = dataStart + ie . info . offset ;
ie . length = dataLength ( ie . info . type , ie . data , ie . info . count , 1 ) ;
ie . rdlen = 0 ;
if ( entry ) {
ie . info . offset = regionid ;
* entry = ie ; /* structure assignment */
entry + + ;
}
/* Alignment */
type = ie . info . type ;
if ( typeSizes [ type ] > 1 ) {
unsigned diff ;
diff = typeSizes [ type ] - ( dl % typeSizes [ type ] ) ;
if ( diff ! = typeSizes [ type ] ) {
dl + = diff ;
if ( ieprev . info . type = = RPM_I18NSTRING_TYPE )
ieprev . length + = diff ;
}
}
tdel = ( tprev ? ( t - tprev ) : 0 ) ;
if ( ieprev . info . type = = RPM_I18NSTRING_TYPE )
tdel = ieprev . length ;
if ( ie . info . tag > = HEADER_I18NTABLE ) {
tprev = t ;
} else {
tprev = dataStart ;
/* XXX HEADER_IMAGE tags don't include region sub-tag. */
/*@-sizeoftype@*/
if ( ie . info . tag = = HEADER_IMAGE )
tprev - = REGION_TAG_COUNT ;
/*@=sizeoftype@*/
}
/* Perform endian conversions */
2011-01-21 01:07:44 +03:00
switch ( type ) {
2002-03-25 23:16:26 +03:00
case RPM_INT32_TYPE :
2011-01-21 01:07:44 +03:00
{ int_32 * it = ( int_32 * ) t ;
t = ( char * ) ( it + ie . info . count ) ;
2014-02-16 02:42:58 +04:00
for ( ; it < ( int_32 * ) t ; + + it )
* it = htonl ( * it ) ;
2002-03-25 23:16:26 +03:00
} /*@switchbreak@*/ break ;
case RPM_INT16_TYPE :
2011-01-21 01:07:44 +03:00
{ int_16 * it = ( int_16 * ) t ;
t = ( char * ) ( it + ie . info . count ) ;
2014-02-16 02:42:58 +04:00
for ( ; it < ( int_16 * ) t ; + + it )
* it = htons ( * it ) ;
2002-03-25 23:16:26 +03:00
} /*@switchbreak@*/ break ;
default :
t + = ie . length ;
/*@switchbreak@*/ break ;
}
dl + = ie . length ;
tl + = tdel ;
ieprev = ie ; /* structure assignment */
}
tdel = ( tprev ? ( t - tprev ) : 0 ) ;
tl + = tdel ;
/* XXX
* There are two hacks here :
* 1 ) tl is 16 b ( i . e . REGION_TAG_COUNT ) short while doing headerReload ( ) .
* 2 ) the 8 / 98 rpm bug with inserting i18n tags needs to use tl , not dl .
*/
/*@-sizeoftype@*/
if ( tl + REGION_TAG_COUNT = = dl )
tl + = REGION_TAG_COUNT ;
/*@=sizeoftype@*/
return dl ;
}
/** \ingroup header
*/
static /*@only@*/ /*@null@*/ void * doHeaderUnload ( Header h ,
/*@out@*/ int * lengthPtr )
/*@modifies h, *lengthPtr @*/
{
int_32 * ei = NULL ;
entryInfo pe ;
char * dataStart ;
char * te ;
unsigned pad ;
unsigned len ;
int_32 il = 0 ;
int_32 dl = 0 ;
indexEntry entry ;
int_32 type ;
int i ;
int drlen , ndribbles ;
int driplen , ndrips ;
/* Sort entries by (offset,tag). */
headerUnsort ( h ) ;
/* Compute (il,dl) for all tags, including those deleted in region. */
pad = 0 ;
drlen = ndribbles = driplen = ndrips = 0 ;
for ( i = 0 , entry = h - > index ; i < h - > indexUsed ; i + + , entry + + ) {
if ( ENTRY_IS_REGION ( entry ) ) {
int_32 rdl = - entry - > info . offset ; /* negative offset */
int_32 ril = rdl / sizeof ( * pe ) ;
int rid = entry - > info . offset ;
il + = ril ;
dl + = entry - > rdlen + entry - > info . count ;
/* XXX Legacy regions do not include the region tag and data. */
if ( i = = 0 & & ( h - > flags & HEADERFLAG_LEGACY ) )
il + = 1 ;
/* Skip rest of entries in region, but account for dribbles. */
for ( ; i < h - > indexUsed & & entry - > info . offset < = rid + 1 ; i + + , entry + + ) {
if ( entry - > info . offset < = rid )
/*@innercontinue@*/ continue ;
/* Alignment */
type = entry - > info . type ;
if ( typeSizes [ type ] > 1 ) {
unsigned diff ;
diff = typeSizes [ type ] - ( dl % typeSizes [ type ] ) ;
if ( diff ! = typeSizes [ type ] ) {
drlen + = diff ;
pad + = diff ;
dl + = diff ;
}
}
ndribbles + + ;
il + + ;
drlen + = entry - > length ;
dl + = entry - > length ;
}
i - - ;
entry - - ;
continue ;
}
/* Ignore deleted drips. */
if ( entry - > data = = NULL | | entry - > length < = 0 )
continue ;
/* Alignment */
type = entry - > info . type ;
if ( typeSizes [ type ] > 1 ) {
unsigned diff ;
diff = typeSizes [ type ] - ( dl % typeSizes [ type ] ) ;
if ( diff ! = typeSizes [ type ] ) {
driplen + = diff ;
pad + = diff ;
dl + = diff ;
} else
diff = 0 ;
}
ndrips + + ;
il + + ;
driplen + = entry - > length ;
dl + = entry - > length ;
}
/* Sanity checks on header intro. */
if ( hdrchkTags ( il ) | | hdrchkData ( dl ) )
goto errxit ;
len = sizeof ( il ) + sizeof ( dl ) + ( il * sizeof ( * pe ) ) + dl ;
ei = xmalloc ( len ) ;
ei [ 0 ] = htonl ( il ) ;
ei [ 1 ] = htonl ( dl ) ;
pe = ( entryInfo ) & ei [ 2 ] ;
dataStart = te = ( char * ) ( pe + il ) ;
pad = 0 ;
for ( i = 0 , entry = h - > index ; i < h - > indexUsed ; i + + , entry + + ) {
const char * src ;
char * t ;
int count ;
int rdlen ;
if ( entry - > data = = NULL | | entry - > length < = 0 )
continue ;
t = te ;
pe - > tag = htonl ( entry - > info . tag ) ;
pe - > type = htonl ( entry - > info . type ) ;
pe - > count = htonl ( entry - > info . count ) ;
if ( ENTRY_IS_REGION ( entry ) ) {
int_32 rdl = - entry - > info . offset ; /* negative offset */
int_32 ril = rdl / sizeof ( * pe ) + ndribbles ;
int rid = entry - > info . offset ;
src = ( char * ) entry - > data ;
rdlen = entry - > rdlen ;
/* XXX Legacy regions do not include the region tag and data. */
if ( i = = 0 & & ( h - > flags & HEADERFLAG_LEGACY ) ) {
int_32 stei [ 4 ] ;
memcpy ( pe + 1 , src , rdl ) ;
memcpy ( te , src + rdl , rdlen ) ;
te + = rdlen ;
pe - > offset = htonl ( te - dataStart ) ;
stei [ 0 ] = pe - > tag ;
stei [ 1 ] = pe - > type ;
stei [ 2 ] = htonl ( - rdl - entry - > info . count ) ;
stei [ 3 ] = pe - > count ;
memcpy ( te , stei , entry - > info . count ) ;
te + = entry - > info . count ;
ril + + ;
rdlen + = entry - > info . count ;
count = regionSwab ( NULL , ril , 0 , pe , t , 0 ) ;
if ( count ! = rdlen )
goto errxit ;
} else {
memcpy ( pe + 1 , src + sizeof ( * pe ) , ( ( ril - 1 ) * sizeof ( * pe ) ) ) ;
memcpy ( te , src + ( ril * sizeof ( * pe ) ) , rdlen + entry - > info . count + drlen ) ;
te + = rdlen ;
{ /*@-castexpose@*/
entryInfo se = ( entryInfo ) src ;
/*@=castexpose@*/
int off = ntohl ( se - > offset ) ;
pe - > offset = ( off ) ? htonl ( te - dataStart ) : htonl ( off ) ;
}
te + = entry - > info . count + drlen ;
count = regionSwab ( NULL , ril , 0 , pe , t , 0 ) ;
if ( count ! = ( rdlen + entry - > info . count + drlen ) )
goto errxit ;
}
/* Skip rest of entries in region. */
while ( i < h - > indexUsed & & entry - > info . offset < = rid + 1 ) {
i + + ;
entry + + ;
}
i - - ;
entry - - ;
pe + = ril ;
continue ;
}
/* Ignore deleted drips. */
if ( entry - > data = = NULL | | entry - > length < = 0 )
continue ;
/* Alignment */
type = entry - > info . type ;
if ( typeSizes [ type ] > 1 ) {
unsigned diff ;
diff = typeSizes [ type ] - ( ( te - dataStart ) % typeSizes [ type ] ) ;
if ( diff ! = typeSizes [ type ] ) {
memset ( te , 0 , diff ) ;
te + = diff ;
pad + = diff ;
}
}
pe - > offset = htonl ( te - dataStart ) ;
/* copy data w/ endian conversions */
switch ( entry - > info . type ) {
case RPM_INT32_TYPE :
count = entry - > info . count ;
src = entry - > data ;
while ( count - - ) {
* ( ( int_32 * ) te ) = htonl ( * ( ( int_32 * ) src ) ) ;
/*@-sizeoftype@*/
te + = sizeof ( int_32 ) ;
src + = sizeof ( int_32 ) ;
/*@=sizeoftype@*/
}
/*@switchbreak@*/ break ;
case RPM_INT16_TYPE :
count = entry - > info . count ;
src = entry - > data ;
while ( count - - ) {
* ( ( int_16 * ) te ) = htons ( * ( ( int_16 * ) src ) ) ;
/*@-sizeoftype@*/
te + = sizeof ( int_16 ) ;
src + = sizeof ( int_16 ) ;
/*@=sizeoftype@*/
}
/*@switchbreak@*/ break ;
default :
memcpy ( te , entry - > data , entry - > length ) ;
te + = entry - > length ;
/*@switchbreak@*/ break ;
}
pe + + ;
}
/* Insure that there are no memcpy underruns/overruns. */
if ( ( ( char * ) pe ) ! = dataStart )
goto errxit ;
if ( ( ( ( char * ) ei ) + len ) ! = te )
goto errxit ;
if ( lengthPtr )
* lengthPtr = len ;
h - > flags & = ~ HEADERFLAG_SORTED ;
headerSort ( h ) ;
return ( void * ) ei ;
errxit :
/*@-usereleased@*/
ei = _free ( ei ) ;
/*@=usereleased@*/
return ( void * ) ei ;
}
/** \ingroup header
* Convert header to on - disk representation .
* @ param h header ( with pointers )
* @ return on - disk header blob ( i . e . with offsets )
*/
static /*@only@*/ /*@null@*/
void * headerUnload ( Header h )
/*@modifies h @*/
{
int length ;
void * uh = doHeaderUnload ( h , & length ) ;
return uh ;
}
/**
* Find matching ( tag , type ) entry in header .
* @ param h header
* @ param tag entry tag
* @ param type entry type
* @ return header entry
*/
static /*@null@*/
indexEntry findEntry ( /*@null@*/ Header h , int_32 tag , int_32 type )
/*@modifies h @*/
{
if ( h = = NULL ) return NULL ;
if ( ! ( h - > flags & HEADERFLAG_SORTED ) ) headerSort ( h ) ;
header.c (findEntry): replaced bsearch with custom routine
Headers normally have around 64 tags, which means bsearch will issue
6 callbacks, only to perform a single instruction (a->tag - b->tag).
This also removes a bogus piece of code.
Here is how these optimizations will impact apt. I remove
/var/cache/apt/*.bin and run "apt-cache show rpm".
4.0.4-alt100.11:
3,630,479,716 PROGRAM TOTALS
399,285,303 ???:0x0000000000033150'2 [libc-2.11.3.so]
319,106,185 ???:strlen [ld-2.11.3.so]
242,215,488 ???:pkgCache::FindPackage(char const*) [libapt-pkg-libc6.9-6.so.2.1.0]
198,711,478 ???:memcpy [ld-2.11.3.so]
198,594,999 ???:0x0000000000071970 [libc-2.11.3.so]
195,473,223 header.c:regionSwab.clone.2 [librpmdb-4.0.4.so]
135,707,034 ???:strcmp [ld-2.11.3.so]
117,134,836 header.c:dataLength [librpmdb-4.0.4.so]
102,919,244 ???:0x00000000000710f0 [libc-2.11.3.so]
102,291,091 ???:stpcpy [libc-2.11.3.so]
75,555,843 ???:bsearch [libc-2.11.3.so]
68,014,424 ???:getenv [libc-2.11.3.so]
this commit:
3,093,300,624 PROGRAM TOTALS
319,106,185 ???:strlen [ld-2.11.3.so]
242,215,488 ???:pkgCache::FindPackage(char const*) [libapt-pkg-libc6.9-6.so.2.1.0]
197,663,758 ???:0x0000000000071970 [libc-2.11.3.so]
185,771,577 header.c:regionSwab.clone.2 [librpmdb-4.0.4.so]
136,263,419 ???:strcmp [ld-2.11.3.so]
111,934,199 ???:memcpy [ld-2.11.3.so]
102,399,027 ???:0x00000000000710f0 [libc-2.11.3.so]
102,291,091 ???:stpcpy [libc-2.11.3.so]
95,231,406 header.c:dataLength [librpmdb-4.0.4.so]
76,706,744 ???:0x0000000000033150'2 [libc-2.11.3.so]
68,359,227 header.c:findEntry [librpmdb-4.0.4.so]
68,014,424 ???:getenv [libc-2.11.3.so]
2011-01-21 14:28:30 +03:00
int found = 0 ;
indexEntry entry = NULL ;
int l = 0 ;
int u = h - > indexUsed ;
while ( l < u ) {
int i = ( l + u ) / 2 ;
entry = h - > index + i ;
if ( tag < entry - > info . tag )
u = i ;
else if ( tag > entry - > info . tag )
l = i + 1 ;
else {
found = 1 ;
break ;
}
}
2002-03-25 23:16:26 +03:00
header.c (findEntry): replaced bsearch with custom routine
Headers normally have around 64 tags, which means bsearch will issue
6 callbacks, only to perform a single instruction (a->tag - b->tag).
This also removes a bogus piece of code.
Here is how these optimizations will impact apt. I remove
/var/cache/apt/*.bin and run "apt-cache show rpm".
4.0.4-alt100.11:
3,630,479,716 PROGRAM TOTALS
399,285,303 ???:0x0000000000033150'2 [libc-2.11.3.so]
319,106,185 ???:strlen [ld-2.11.3.so]
242,215,488 ???:pkgCache::FindPackage(char const*) [libapt-pkg-libc6.9-6.so.2.1.0]
198,711,478 ???:memcpy [ld-2.11.3.so]
198,594,999 ???:0x0000000000071970 [libc-2.11.3.so]
195,473,223 header.c:regionSwab.clone.2 [librpmdb-4.0.4.so]
135,707,034 ???:strcmp [ld-2.11.3.so]
117,134,836 header.c:dataLength [librpmdb-4.0.4.so]
102,919,244 ???:0x00000000000710f0 [libc-2.11.3.so]
102,291,091 ???:stpcpy [libc-2.11.3.so]
75,555,843 ???:bsearch [libc-2.11.3.so]
68,014,424 ???:getenv [libc-2.11.3.so]
this commit:
3,093,300,624 PROGRAM TOTALS
319,106,185 ???:strlen [ld-2.11.3.so]
242,215,488 ???:pkgCache::FindPackage(char const*) [libapt-pkg-libc6.9-6.so.2.1.0]
197,663,758 ???:0x0000000000071970 [libc-2.11.3.so]
185,771,577 header.c:regionSwab.clone.2 [librpmdb-4.0.4.so]
136,263,419 ???:strcmp [ld-2.11.3.so]
111,934,199 ???:memcpy [ld-2.11.3.so]
102,399,027 ???:0x00000000000710f0 [libc-2.11.3.so]
102,291,091 ???:stpcpy [libc-2.11.3.so]
95,231,406 header.c:dataLength [librpmdb-4.0.4.so]
76,706,744 ???:0x0000000000033150'2 [libc-2.11.3.so]
68,359,227 header.c:findEntry [librpmdb-4.0.4.so]
68,014,424 ???:getenv [libc-2.11.3.so]
2011-01-21 14:28:30 +03:00
if ( ! found )
2002-03-25 23:16:26 +03:00
return NULL ;
if ( type = = RPM_NULL_TYPE )
return entry ;
/* look backwards */
while ( entry - > info . tag = = tag & & entry - > info . type ! = type & &
entry > h - > index ) entry - - ;
if ( entry - > info . tag = = tag & & entry - > info . type = = type )
return entry ;
return NULL ;
}
/** \ingroup header
* Delete tag in header .
* Removes all entries of type tag from the header , returns 1 if none were
* found .
*
* @ param h header
* @ param tag tag
* @ return 0 on success , 1 on failure ( INCONSISTENT )
*/
static
int headerRemoveEntry ( Header h , int_32 tag )
/*@modifies h @*/
{
indexEntry last = h - > index + h - > indexUsed ;
indexEntry entry , first ;
int ne ;
entry = findEntry ( h , tag , RPM_NULL_TYPE ) ;
if ( ! entry ) return 1 ;
/* Make sure entry points to the first occurence of this tag. */
while ( entry > h - > index & & ( entry - 1 ) - > info . tag = = tag )
entry - - ;
/* Free data for tags being removed. */
for ( first = entry ; first < last ; first + + ) {
void * data ;
if ( first - > info . tag ! = tag )
break ;
data = first - > data ;
first - > data = NULL ;
first - > length = 0 ;
if ( ENTRY_IN_REGION ( first ) )
continue ;
data = _free ( data ) ;
}
ne = ( first - entry ) ;
if ( ne > 0 ) {
h - > indexUsed - = ne ;
ne = last - first ;
if ( ne > 0 )
memmove ( entry , first , ( ne * sizeof ( * entry ) ) ) ;
}
return 0 ;
}
/** \ingroup header
* Convert header to in - memory representation .
* @ param uh on - disk header blob ( i . e . with offsets )
* @ return header
*/
static /*@null@*/
Header headerLoad ( /*@kept@*/ void * uh )
/*@modifies uh @*/
{
int_32 * ei = ( int_32 * ) uh ;
int_32 il = ntohl ( ei [ 0 ] ) ; /* index length */
int_32 dl = ntohl ( ei [ 1 ] ) ; /* data length */
/*@-sizeoftype@*/
size_t pvlen = sizeof ( il ) + sizeof ( dl ) +
( il * sizeof ( struct entryInfo ) ) + dl ;
/*@=sizeoftype@*/
void * pv = uh ;
Header h = NULL ;
entryInfo pe ;
char * dataStart ;
indexEntry entry ;
int rdlen ;
/* Sanity checks on header intro. */
if ( hdrchkTags ( il ) | | hdrchkData ( dl ) )
goto errxit ;
ei = ( int_32 * ) pv ;
/*@-castexpose@*/
pe = ( entryInfo ) & ei [ 2 ] ;
/*@=castexpose@*/
dataStart = ( char * ) ( pe + il ) ;
h = xcalloc ( 1 , sizeof ( * h ) ) ;
/*@-assignexpose@*/
h - > hv = * hdrVec ; /* structure assignment */
/*@=assignexpose@*/
/*@-assignexpose -kepttrans@*/
h - > blob = uh ;
/*@=assignexpose =kepttrans@*/
h - > indexAlloced = il + 1 ;
h - > indexUsed = il ;
2010-08-19 02:03:03 +04:00
h - > instance = 0 ;
2002-03-25 23:16:26 +03:00
h - > index = xcalloc ( h - > indexAlloced , sizeof ( * h - > index ) ) ;
h - > flags = HEADERFLAG_SORTED ;
h - > nrefs = 0 ;
h = headerLink ( h ) ;
/*
* XXX XFree86 - libs , ash , and pdksh from Red Hat 5.2 have bogus
* % verifyscript tag that needs to be diddled .
*/
if ( ntohl ( pe - > tag ) = = 15 & &
ntohl ( pe - > type ) = = RPM_STRING_TYPE & &
ntohl ( pe - > count ) = = 1 )
{
pe - > tag = htonl ( 1079 ) ;
}
entry = h - > index ;
if ( ! ( htonl ( pe - > tag ) < HEADER_I18NTABLE ) ) {
h - > flags | = HEADERFLAG_LEGACY ;
entry - > info . type = REGION_TAG_TYPE ;
entry - > info . tag = HEADER_IMAGE ;
/*@-sizeoftype@*/
entry - > info . count = REGION_TAG_COUNT ;
/*@=sizeoftype@*/
entry - > info . offset = ( ( char * ) pe - dataStart ) ; /* negative offset */
/*@-assignexpose@*/
entry - > data = pe ;
/*@=assignexpose@*/
entry - > length = pvlen - sizeof ( il ) - sizeof ( dl ) ;
rdlen = regionSwab ( entry + 1 , il , 0 , pe , dataStart , entry - > info . offset ) ;
#if 0 /* XXX don't check, the 8/98 i18n bug fails here. */
if ( rdlen ! = dl )
goto errxit ;
# endif
entry - > rdlen = rdlen ;
entry + + ;
h - > indexUsed + + ;
} else {
int nb = ntohl ( pe - > count ) ;
int_32 rdl ;
int_32 ril ;
h - > flags & = ~ HEADERFLAG_LEGACY ;
entry - > info . type = htonl ( pe - > type ) ;
if ( entry - > info . type < RPM_MIN_TYPE | | entry - > info . type > RPM_MAX_TYPE )
goto errxit ;
entry - > info . count = htonl ( pe - > count ) ;
if ( hdrchkTags ( entry - > info . count ) )
goto errxit ;
{ int off = ntohl ( pe - > offset ) ;
2011-09-29 14:22:32 +04:00
if ( hdrchkData ( off ) | | hdrchkRange ( dl , off ) )
2002-03-25 23:16:26 +03:00
goto errxit ;
if ( off ) {
int_32 * stei = memcpy ( alloca ( nb ) , dataStart + off , nb ) ;
rdl = - ntohl ( stei [ 2 ] ) ; /* negative offset */
ril = rdl / sizeof ( * pe ) ;
if ( hdrchkTags ( ril ) | | hdrchkData ( rdl ) )
goto errxit ;
entry - > info . tag = htonl ( pe - > tag ) ;
} else {
ril = il ;
/*@-sizeoftype@*/
rdl = ( ril * sizeof ( struct entryInfo ) ) ;
/*@=sizeoftype@*/
entry - > info . tag = HEADER_IMAGE ;
}
}
entry - > info . offset = - rdl ; /* negative offset */
/*@-assignexpose@*/
entry - > data = pe ;
/*@=assignexpose@*/
entry - > length = pvlen - sizeof ( il ) - sizeof ( dl ) ;
rdlen = regionSwab ( entry + 1 , ril - 1 , 0 , pe + 1 , dataStart , entry - > info . offset ) ;
if ( rdlen < 0 )
goto errxit ;
entry - > rdlen = rdlen ;
if ( ril < h - > indexUsed ) {
indexEntry newEntry = entry + ril ;
int ne = ( h - > indexUsed - ril ) ;
int rid = entry - > info . offset + 1 ;
int rc ;
/* Load dribble entries from region. */
rc = regionSwab ( newEntry , ne , 0 , pe + ril , dataStart , rid ) ;
if ( rc < 0 )
goto errxit ;
rdlen + = rc ;
{ indexEntry firstEntry = newEntry ;
int save = h - > indexUsed ;
int j ;
/* Dribble entries replace duplicate region entries. */
h - > indexUsed - = ne ;
for ( j = 0 ; j < ne ; j + + , newEntry + + ) {
( void ) headerRemoveEntry ( h , newEntry - > info . tag ) ;
if ( newEntry - > info . tag = = HEADER_BASENAMES )
( void ) headerRemoveEntry ( h , HEADER_OLDFILENAMES ) ;
}
/* If any duplicate entries were replaced, move new entries down. */
if ( h - > indexUsed < ( save - ne ) ) {
memmove ( h - > index + h - > indexUsed , firstEntry ,
( ne * sizeof ( * entry ) ) ) ;
}
h - > indexUsed + = ne ;
}
}
}
2011-01-21 03:26:30 +03:00
/* See if the header needs sorting. */
entry = h - > index ;
indexEntry end = entry + h - > indexUsed ;
int prevtag = - 1 ;
while ( entry < end ) {
int tag = entry - > info . tag ;
if ( prevtag > tag ) {
h - > flags & = ~ HEADERFLAG_SORTED ;
headerSort ( h ) ;
break ;
}
prevtag = tag ;
entry + + ;
}
2002-03-25 23:16:26 +03:00
/*@-globstate -observertrans @*/
return h ;
/*@=globstate =observertrans @*/
errxit :
/*@-usereleased@*/
if ( h ) {
h - > index = _free ( h - > index ) ;
/*@-refcounttrans@*/
h = _free ( h ) ;
/*@=refcounttrans@*/
}
/*@=usereleased@*/
/*@-refcounttrans -globstate@*/
return h ;
/*@=refcounttrans =globstate@*/
}
/** \ingroup header
* Convert header to on - disk representation , and then reload .
* This is used to insure that all header data is in one chunk .
* @ param h header ( with pointers )
* @ param tag region tag
* @ return on - disk header ( with offsets )
*/
static /*@null@*/
Header headerReload ( /*@only@*/ Header h , int tag )
/*@modifies h @*/
{
Header nh ;
int length ;
/*@-onlytrans@*/
void * uh = doHeaderUnload ( h , & length ) ;
h = headerFree ( h ) ;
/*@=onlytrans@*/
if ( uh = = NULL )
return NULL ;
nh = headerLoad ( uh ) ;
if ( nh = = NULL ) {
uh = _free ( uh ) ;
return NULL ;
}
if ( nh - > flags & HEADERFLAG_ALLOCATED )
uh = _free ( uh ) ;
nh - > flags | = HEADERFLAG_ALLOCATED ;
if ( ENTRY_IS_REGION ( nh - > index ) ) {
if ( tag = = HEADER_SIGNATURES | | tag = = HEADER_IMMUTABLE )
nh - > index [ 0 ] . info . tag = tag ;
}
return nh ;
}
/** \ingroup header
* Make a copy and convert header to in - memory representation .
* @ param uh on - disk header blob ( i . e . with offsets )
* @ return header
*/
static /*@null@*/
Header headerCopyLoad ( const void * uh )
/*@*/
{
int_32 * ei = ( int_32 * ) uh ;
int_32 il = ntohl ( ei [ 0 ] ) ; /* index length */
int_32 dl = ntohl ( ei [ 1 ] ) ; /* data length */
/*@-sizeoftype@*/
size_t pvlen = sizeof ( il ) + sizeof ( dl ) +
( il * sizeof ( struct entryInfo ) ) + dl ;
/*@=sizeoftype@*/
void * nuh = NULL ;
Header h = NULL ;
/* Sanity checks on header intro. */
/*@-branchstate@*/
if ( ! ( hdrchkTags ( il ) | | hdrchkData ( dl ) ) & & pvlen < headerMaxbytes ) {
nuh = memcpy ( xmalloc ( pvlen ) , uh , pvlen ) ;
if ( ( h = headerLoad ( nuh ) ) ! = NULL )
h - > flags | = HEADERFLAG_ALLOCATED ;
}
/*@=branchstate@*/
/*@-branchstate@*/
if ( h = = NULL )
nuh = _free ( nuh ) ;
/*@=branchstate@*/
return h ;
}
/** \ingroup header
* Read ( and load ) header from file handle .
* @ param fd file handle
* @ param magicp read ( and verify ) 8 bytes of ( magic , 0 ) ?
* @ return header ( or NULL on error )
*/
static /*@null@*/
Header headerRead ( FD_t fd , enum hMagic magicp )
/*@modifies fd @*/
{
int_32 block [ 4 ] ;
int_32 * ei = NULL ;
int_32 il ;
int_32 dl ;
Header h = NULL ;
size_t len ;
int i ;
memset ( block , 0 , sizeof ( block ) ) ;
i = 2 ;
if ( magicp = = HEADER_MAGIC_YES )
i + = 2 ;
/*@-type@*/ /* FIX: cast? */
if ( timedRead ( fd , ( char * ) block , i * sizeof ( * block ) ) ! = ( i * sizeof ( * block ) ) )
goto exit ;
/*@=type@*/
i = 0 ;
if ( magicp = = HEADER_MAGIC_YES ) {
2014-02-16 02:44:35 +04:00
int_32 magic = block [ i + + ] ;
2002-03-25 23:16:26 +03:00
if ( memcmp ( & magic , header_magic , sizeof ( magic ) ) )
goto exit ;
2014-02-16 02:44:35 +04:00
i + + ;
2002-03-25 23:16:26 +03:00
}
il = ntohl ( block [ i ] ) ; i + + ;
dl = ntohl ( block [ i ] ) ; i + + ;
/*@-sizeoftype@*/
len = sizeof ( il ) + sizeof ( dl ) + ( il * sizeof ( struct entryInfo ) ) + dl ;
/*@=sizeoftype@*/
/* Sanity checks on header intro. */
if ( hdrchkTags ( il ) | | hdrchkData ( dl ) | | len > headerMaxbytes )
goto exit ;
ei = xmalloc ( len ) ;
ei [ 0 ] = htonl ( il ) ;
ei [ 1 ] = htonl ( dl ) ;
len - = sizeof ( il ) + sizeof ( dl ) ;
/*@-type@*/ /* FIX: cast? */
if ( timedRead ( fd , ( char * ) & ei [ 2 ] , len ) ! = len )
goto exit ;
/*@=type@*/
h = headerLoad ( ei ) ;
exit :
if ( h ) {
if ( h - > flags & HEADERFLAG_ALLOCATED )
ei = _free ( ei ) ;
h - > flags | = HEADERFLAG_ALLOCATED ;
} else if ( ei )
ei = _free ( ei ) ;
/*@-mustmod@*/ /* FIX: timedRead macro obscures annotation */
return h ;
/*@-mustmod@*/
}
/** \ingroup header
* Write ( with unload ) header to file handle .
* @ param fd file handle
* @ param h header
* @ param magicp prefix write with 8 bytes of ( magic , 0 ) ?
* @ return 0 on success , 1 on error
*/
static
int headerWrite ( FD_t fd , /*@null@*/ Header h , enum hMagic magicp )
/*@globals fileSystem @*/
/*@modifies fd, h, fileSystem @*/
{
ssize_t nb ;
int length ;
const void * uh ;
if ( h = = NULL )
return 1 ;
uh = doHeaderUnload ( h , & length ) ;
if ( uh = = NULL )
return 1 ;
switch ( magicp ) {
case HEADER_MAGIC_YES :
/*@-sizeoftype@*/
nb = Fwrite ( header_magic , sizeof ( char ) , sizeof ( header_magic ) , fd ) ;
/*@=sizeoftype@*/
if ( nb ! = sizeof ( header_magic ) )
goto exit ;
break ;
case HEADER_MAGIC_NO :
break ;
}
/*@-sizeoftype@*/
nb = Fwrite ( uh , sizeof ( char ) , length , fd ) ;
/*@=sizeoftype@*/
exit :
uh = _free ( uh ) ;
return ( nb = = length ? 0 : 1 ) ;
}
/** \ingroup header
* Check if tag is in header .
* @ param h header
* @ param tag tag
* @ return 1 on success , 0 on failure
*/
static
int headerIsEntry ( /*@null@*/ Header h , int_32 tag )
/*@*/
{
/*@-mods@*/ /*@ FIX: h modified by sort. */
return ( findEntry ( h , tag , RPM_NULL_TYPE ) ? 1 : 0 ) ;
/*@=mods@*/
}
/** \ingroup header
* Retrieve data from header entry .
* @ todo Permit retrieval of regions other than HEADER_IMUTABLE .
* @ param entry header entry
* @ retval type address of type ( or NULL )
* @ retval p address of data ( or NULL )
* @ retval c address of count ( or NULL )
* @ param minMem string pointers refer to header memory ?
* @ return 1 on success , otherwise error .
*/
static int copyEntry ( const indexEntry entry ,
/*@null@*/ /*@out@*/ hTYP_t type ,
/*@null@*/ /*@out@*/ hPTR_t * p ,
/*@null@*/ /*@out@*/ hCNT_t c ,
int minMem )
/*@modifies *type, *p, *c @*/
{
int_32 count = entry - > info . count ;
int rc = 1 ; /* XXX 1 on success. */
if ( p )
switch ( entry - > info . type ) {
case RPM_BIN_TYPE :
/*
* XXX This only works for
* XXX " sealed " HEADER_IMMUTABLE / HEADER_SIGNATURES / HEADER_IMAGE .
* XXX This will * not * work for unsealed legacy HEADER_IMAGE ( i . e .
* XXX a legacy header freshly read , but not yet unloaded to the rpmdb ) .
*/
if ( ENTRY_IS_REGION ( entry ) ) {
int_32 * ei = ( ( int_32 * ) entry - > data ) - 2 ;
/*@-castexpose@*/
entryInfo pe = ( entryInfo ) ( ei + 2 ) ;
/*@=castexpose@*/
char * dataStart = ( char * ) ( pe + ntohl ( ei [ 0 ] ) ) ;
int_32 rdl = - entry - > info . offset ; /* negative offset */
int_32 ril = rdl / sizeof ( * pe ) ;
/*@-sizeoftype@*/
rdl = entry - > rdlen ;
count = 2 * sizeof ( * ei ) + ( ril * sizeof ( * pe ) ) + rdl ;
if ( entry - > info . tag = = HEADER_IMAGE ) {
ril - = 1 ;
pe + = 1 ;
} else {
count + = REGION_TAG_COUNT ;
rdl + = REGION_TAG_COUNT ;
}
* p = xmalloc ( count ) ;
ei = ( int_32 * ) * p ;
ei [ 0 ] = htonl ( ril ) ;
ei [ 1 ] = htonl ( rdl ) ;
/*@-castexpose@*/
pe = ( entryInfo ) memcpy ( ei + 2 , pe , ( ril * sizeof ( * pe ) ) ) ;
/*@=castexpose@*/
dataStart = ( char * ) memcpy ( pe + ril , dataStart , rdl ) ;
/*@=sizeoftype@*/
rc = regionSwab ( NULL , ril , 0 , pe , dataStart , 0 ) ;
/* XXX 1 on success. */
rc = ( rc < 0 ) ? 0 : 1 ;
} else {
count = entry - > length ;
* p = ( ! minMem
? memcpy ( xmalloc ( count ) , entry - > data , count )
: entry - > data ) ;
}
break ;
case RPM_STRING_TYPE :
if ( count = = 1 ) {
* p = entry - > data ;
break ;
}
/*@fallthrough@*/
case RPM_STRING_ARRAY_TYPE :
case RPM_I18NSTRING_TYPE :
{ const char * * ptrEntry ;
/*@-sizeoftype@*/
int tableSize = count * sizeof ( char * ) ;
/*@=sizeoftype@*/
char * t ;
int i ;
/*@-mods@*/
if ( minMem ) {
* p = xmalloc ( tableSize ) ;
ptrEntry = ( const char * * ) * p ;
t = entry - > data ;
} else {
t = xmalloc ( tableSize + entry - > length ) ;
* p = ( void * ) t ;
ptrEntry = ( const char * * ) * p ;
t + = tableSize ;
memcpy ( t , entry - > data , entry - > length ) ;
}
/*@=mods@*/
for ( i = 0 ; i < count ; i + + ) {
* ptrEntry + + = t ;
t = strchr ( t , 0 ) ;
t + + ;
}
} break ;
default :
* p = entry - > data ;
break ;
}
if ( type ) * type = entry - > info . type ;
if ( c ) * c = count ;
return rc ;
}
2004-06-26 20:46:55 +04:00
/* Guess value of current locale from value of the environment variables. */
static const char *
guess_category_value ( int category )
{
const char * language ;
const char * retval ;
2007-06-22 15:12:25 +04:00
language = getenv ( " RPM_LANGUAGE_I18NSTRING " ) ;
if ( language & & * language )
return language ;
2004-06-26 20:46:55 +04:00
/* The highest priority value is the `LANGUAGE' environment
variable . But we don ' t use the value if the currently selected
locale is the C locale . This is a GNU extension . */
language = getenv ( " LANGUAGE " ) ;
if ( language ! = NULL & & language [ 0 ] = = ' \0 ' )
language = NULL ;
/* We have to proceed with the POSIX methods of looking to `LC_ALL',
` LC_xxx ' , and ` LANG ' . On some systems this can be done by the
` setlocale ' function itself . */
retval = setlocale ( category , NULL ) ;
/* Ignore LANGUAGE if the locale is set to "C" because
1. " C " locale usually uses the ASCII encoding , and most international
messages use non - ASCII characters . These characters get displayed
as question marks ( if using glibc ' s iconv ( ) ) or as invalid 8 - bit
characters ( because other iconv ( ) s refuse to convert most non - ASCII
characters to ASCII ) . In any case , the output is ugly .
2. The precise output of some programs in the " C " locale is specified
by POSIX and should not depend on environment variables like
" LANGUAGE " . We allow such programs to use gettext ( ) . */
return language ! = NULL & & strcmp ( retval , " C " ) ! = 0 ? language : retval ;
}
2002-06-13 13:37:35 +04:00
static int
locale_match ( const char * sample , const char * l_b , const char * l_e ,
char delim )
2002-03-26 03:33:14 +03:00
{
2002-06-13 13:37:35 +04:00
const char * p = l_b ;
2002-03-26 03:33:14 +03:00
2002-06-13 13:37:35 +04:00
for ( ; p < l_e & & * p ! = delim ; + + p )
;
if ( p < l_e & & ! strncmp ( sample , l_b , ( p - l_b ) ) )
return 1 ;
2002-03-26 03:33:14 +03:00
2002-06-13 13:37:35 +04:00
return 0 ;
2002-03-26 03:33:14 +03:00
}
2002-03-25 23:16:26 +03:00
/**
* Does locale match entry in header i18n table ?
*
* \ verbatim
* The range [ l , le ) contains the next locale to match :
* ll [ _CC ] [ . EEEEE ] [ @ dddd ]
* where
* ll ISO language code ( in lowercase ) .
* CC ( optional ) ISO coutnry code ( in uppercase ) .
* EEEEE ( optional ) encoding ( not really standardized ) .
* dddd ( optional ) dialect .
* \ endverbatim
*
* @ param td header i18n table data , NUL terminated
* @ param l start of locale to match
* @ param le end of locale to match
* @ return 1 on match , 0 on no match
*/
2002-06-13 13:37:35 +04:00
static int
headerMatchLocale ( const char * td , const char * l , const char * le ,
int strip_lang )
/*@ */
2002-03-25 23:16:26 +03:00
{
2002-06-13 13:37:35 +04:00
switch ( strip_lang )
{
case 0 :
/* First try a complete match. */
if ( strlen ( td ) = = ( le - l )
& & ! memcmp ( td , l , ( le - l ) ) )
return 1 ;
return 0 ;
case 1 :
/* Next, try stripping optional dialect and matching. */
return locale_match ( td , l , le , ' @ ' ) ;
case 2 :
/* Next, try stripping optional codeset and matching. */
return locale_match ( td , l , le , ' . ' ) ;
default :
/* Finally, try stripping optional country code and matching. */
return locale_match ( td , l , le , ' _ ' ) ;
}
2002-03-26 03:33:14 +03:00
}
2002-03-25 23:16:26 +03:00
2002-03-26 03:33:14 +03:00
static char *
2004-06-26 23:51:13 +04:00
convert ( char * ed , const char * td )
2002-03-26 03:33:14 +03:00
{
2002-06-13 13:37:35 +04:00
char * saved_ctype = 0 , * from_codeset = 0 , * to_codeset = 0 ;
2002-06-13 14:15:57 +04:00
char * saved_ctype1 , * from_codeset1 , * to_codeset1 , * result = 0 ;
2002-03-26 03:33:14 +03:00
iconv_t icd ;
2002-06-13 14:22:49 +04:00
if ( ! * ed )
return ed ;
2002-06-13 14:15:57 +04:00
2002-06-13 13:37:35 +04:00
if ( ( saved_ctype1 = setlocale ( LC_CTYPE , 0 ) ) & &
( saved_ctype = strdup ( saved_ctype1 ) ) & &
( to_codeset1 = nl_langinfo ( CODESET ) ) & &
( to_codeset = strdup ( to_codeset1 ) ) & &
setlocale ( LC_CTYPE , td ) & &
( from_codeset1 = nl_langinfo ( CODESET ) ) & &
( from_codeset = strdup ( from_codeset1 ) ) & &
strcmp ( from_codeset , to_codeset ) & &
( ( icd = iconv_open ( to_codeset , from_codeset ) ) ! = ( iconv_t ) - 1 ) )
2002-03-26 03:33:14 +03:00
{
2002-06-13 13:37:35 +04:00
size_t insize = strlen ( ed ) ;
2002-06-13 18:39:30 +04:00
size_t inbufleft = insize , outbufleft = insize * 4 + 1 ;
2002-06-13 14:15:57 +04:00
char buf [ outbufleft ] ;
2002-06-13 13:37:35 +04:00
char * inbuf = ed , * outbuf = buf ;
2002-06-13 18:39:30 +04:00
if ( iconv ( icd , & inbuf , & inbufleft , & outbuf , & outbufleft ) > = 0 )
{
* outbuf = ' \0 ' ;
if ( strcmp ( ed , buf ) )
/* XXX memory leak */
result = strdup ( buf ) ;
}
2002-06-13 13:37:35 +04:00
iconv_close ( icd ) ;
2002-03-26 03:33:14 +03:00
}
2002-03-25 23:16:26 +03:00
2002-06-13 13:37:35 +04:00
if ( saved_ctype )
setlocale ( LC_CTYPE , saved_ctype ) ;
from_codeset = _free ( from_codeset ) ;
to_codeset = _free ( to_codeset ) ;
saved_ctype = _free ( saved_ctype ) ;
2002-06-13 14:15:57 +04:00
2002-06-13 14:22:49 +04:00
return result ? : ed ;
2002-03-25 23:16:26 +03:00
}
/**
* Return i18n string from header that matches locale .
* @ param h header
* @ param entry i18n string data
* @ return matching i18n string ( or 1 st string if no match )
*/
/*@dependent@*/ /*@exposed@*/ static char *
2004-06-26 23:51:13 +04:00
headerFindI18NString ( Header h , indexEntry entry )
2002-03-25 23:16:26 +03:00
{
2011-01-21 15:25:56 +03:00
const char * l , * le ;
2004-06-26 23:51:13 +04:00
indexEntry table ;
int strip_lang ;
2011-01-21 15:25:56 +03:00
static int guessed ;
static const char * lang ;
if ( ! guessed ) {
lang = guess_category_value ( LC_MESSAGES ) ;
2011-01-21 15:41:20 +03:00
if ( lang ) {
if ( strcmp ( lang , " C " ) = = 0 )
lang = NULL ;
else
lang = xstrdup ( lang ) ;
}
2011-01-21 15:25:56 +03:00
guessed = 1 ;
}
2004-06-26 23:51:13 +04:00
if ( ! entry - > data
| | ! * ( const char * ) entry - > data
2011-01-21 15:25:56 +03:00
| | ! lang )
2004-06-26 23:51:13 +04:00
return entry - > data ;
/*@-mods@ */
if ( ( table =
findEntry ( h , HEADER_I18NTABLE , RPM_STRING_ARRAY_TYPE ) ) = = NULL )
return entry - > data ;
/*@=mods@ */
for ( strip_lang = 0 ; strip_lang < 4 ; strip_lang + + )
{
for ( l = lang ; * l ; l = le )
{
const char * td ;
char * ed ;
int langNum ;
while ( * l & & * l = = ' : ' ) /* skip leading colons */
l + + ;
if ( * l = = ' \0 ' )
break ;
for ( le = l ; * le & & * le ! = ' : ' ; le + + ) /* find end of this locale */
;
/* For each entry in the header ... */
for ( langNum = 0 , td = table - > data , ed = entry - > data ;
langNum < entry - > info . count ;
langNum + + , td + = strlen ( td ) + 1 , ed + =
strlen ( ed ) + 1 )
{
if ( headerMatchLocale ( td , l , le , strip_lang ) )
return convert ( ed , td ) ;
}
}
}
2002-03-25 23:16:26 +03:00
2004-06-26 23:51:13 +04:00
return gettext ( entry - > data ) ;
2002-03-25 23:16:26 +03:00
}
/**
* Retrieve tag data from header .
* @ param h header
* @ param tag tag to retrieve
* @ retval type address of type ( or NULL )
* @ retval p address of data ( or NULL )
* @ retval c address of count ( or NULL )
* @ param minMem string pointers reference header memory ?
* @ return 1 on success , 0 on not found
*/
static int intGetEntry ( Header h , int_32 tag ,
/*@null@*/ /*@out@*/ hTAG_t type ,
/*@null@*/ /*@out@*/ hPTR_t * p ,
/*@null@*/ /*@out@*/ hCNT_t c ,
int minMem )
/*@modifies *type, *p, *c @*/
{
indexEntry entry ;
int rc ;
/* First find the tag */
/*@-mods@*/ /*@ FIX: h modified by sort. */
entry = findEntry ( h , tag , RPM_NULL_TYPE ) ;
/*@mods@*/
if ( entry = = NULL ) {
if ( type ) type = 0 ;
if ( p ) * p = NULL ;
if ( c ) * c = 0 ;
return 0 ;
}
switch ( entry - > info . type ) {
case RPM_I18NSTRING_TYPE :
rc = 1 ;
if ( type ) * type = RPM_STRING_TYPE ;
if ( c ) * c = 1 ;
/*@-dependenttrans@*/
if ( p ) * p = headerFindI18NString ( h , entry ) ;
/*@=dependenttrans@*/
break ;
default :
rc = copyEntry ( entry , type , p , c , minMem ) ;
break ;
}
/* XXX 1 on success */
return ( ( rc = = 1 ) ? 1 : 0 ) ;
}
/** \ingroup header
* Free data allocated when retrieved from header .
* @ param h header
* @ param data address of data ( or NULL )
* @ param type type of data ( or - 1 to force free )
* @ return NULL always
*/
static /*@null@*/ void * headerFreeTag ( /*@unused@*/ Header h ,
/*@only@*/ /*@null@*/ const void * data , rpmTagType type )
/*@modifies data @*/
{
if ( data ) {
/*@-branchstate@*/
if ( type = = - 1 | |
type = = RPM_STRING_ARRAY_TYPE | |
type = = RPM_I18NSTRING_TYPE | |
type = = RPM_BIN_TYPE )
data = _free ( data ) ;
/*@=branchstate@*/
}
return NULL ;
}
/** \ingroup header
* Retrieve tag value .
* Will never return RPM_I18NSTRING_TYPE ! RPM_STRING_TYPE elements with
* RPM_I18NSTRING_TYPE equivalent entries are translated ( if HEADER_I18NTABLE
* entry is present ) .
*
* @ param h header
* @ param tag tag
* @ retval type address of tag value data type ( or NULL )
* @ retval p address of pointer to tag value ( s ) ( or NULL )
* @ retval c address of number of values ( or NULL )
* @ return 1 on success , 0 on failure
*/
static
int headerGetEntry ( Header h , int_32 tag ,
/*@null@*/ /*@out@*/ hTYP_t type ,
/*@null@*/ /*@out@*/ void * * p ,
/*@null@*/ /*@out@*/ hCNT_t c )
/*@modifies *type, *p, *c @*/
{
return intGetEntry ( h , tag , type , ( hPTR_t * ) p , c , 0 ) ;
}
/** \ingroup header
* Retrieve tag value using header internal array .
* Get an entry using as little extra RAM as possible to return the tag value .
* This is only an issue for RPM_STRING_ARRAY_TYPE .
*
* @ param h header
* @ param tag tag
* @ retval type address of tag value data type ( or NULL )
* @ retval p address of pointer to tag value ( s ) ( or NULL )
* @ retval c address of number of values ( or NULL )
* @ return 1 on success , 0 on failure
*/
static
int headerGetEntryMinMemory ( Header h , int_32 tag ,
/*@null@*/ /*@out@*/ hTYP_t type ,
/*@null@*/ /*@out@*/ hPTR_t * p ,
/*@null@*/ /*@out@*/ hCNT_t c )
/*@modifies *type, *p, *c @*/
{
return intGetEntry ( h , tag , type , p , c , 1 ) ;
}
int headerGetRawEntry ( Header h , int_32 tag , int_32 * type , hPTR_t * p ,
int_32 * c )
{
indexEntry entry ;
int rc ;
if ( p = = NULL ) return headerIsEntry ( h , tag ) ;
/* First find the tag */
/*@-mods@*/ /*@ FIX: h modified by sort. */
entry = findEntry ( h , tag , RPM_NULL_TYPE ) ;
/*@=mods@*/
if ( ! entry ) {
if ( p ) * p = NULL ;
if ( c ) * c = 0 ;
return 0 ;
}
rc = copyEntry ( entry , type , p , c , 0 ) ;
/* XXX 1 on success */
return ( ( rc = = 1 ) ? 1 : 0 ) ;
}
/**
*/
static void copyData ( int_32 type , /*@out@*/ void * dstPtr , const void * srcPtr ,
int_32 c , int dataLength )
/*@modifies *dstPtr @*/
{
const char * * src ;
char * dst ;
int i ;
switch ( type ) {
case RPM_STRING_ARRAY_TYPE :
case RPM_I18NSTRING_TYPE :
/* Otherwise, p is char** */
i = c ;
src = ( const char * * ) srcPtr ;
dst = dstPtr ;
while ( i - - ) {
if ( * src ) {
int len = strlen ( * src ) + 1 ;
memcpy ( dst , * src , len ) ;
dst + = len ;
}
src + + ;
}
break ;
default :
memmove ( dstPtr , srcPtr , dataLength ) ;
break ;
}
}
/**
* Return ( malloc ' ed ) copy of entry data .
* @ param type entry data type
* @ param p entry data
* @ param c entry item count
* @ retval lengthPtr no . bytes in returned data
* @ return ( malloc ' ed ) copy of entry data
*/
static void * grabData ( int_32 type , hPTR_t p , int_32 c ,
/*@out@*/ int * lengthPtr )
/*@modifies *lengthPtr @*/
{
int length = dataLength ( type , p , c , 0 ) ;
void * data = xmalloc ( length ) ;
copyData ( type , data , p , c , length ) ;
if ( lengthPtr )
* lengthPtr = length ;
return data ;
}
/** \ingroup header
* Add tag to header .
* Duplicate tags are okay , but only defined for iteration ( with the
* exceptions noted below ) . While you are allowed to add i18n string
* arrays through this function , you probably don ' t mean to . See
* headerAddI18NString ( ) instead .
*
* @ param h header
* @ param tag tag
* @ param type tag value data type
* @ param p pointer to tag value ( s )
* @ param c number of values
* @ return 1 on success , 0 on failure
*/
static
int headerAddEntry ( Header h , int_32 tag , int_32 type , const void * p , int_32 c )
/*@modifies h @*/
{
indexEntry entry ;
/* Count must always be >= 1 for headerAddEntry. */
if ( c < = 0 )
return 0 ;
/* Allocate more index space if necessary */
if ( h - > indexUsed = = h - > indexAlloced ) {
h - > indexAlloced + = INDEX_MALLOC_SIZE ;
h - > index = xrealloc ( h - > index , h - > indexAlloced * sizeof ( * h - > index ) ) ;
}
/* Fill in the index */
entry = h - > index + h - > indexUsed ;
entry - > info . tag = tag ;
entry - > info . type = type ;
entry - > info . count = c ;
entry - > info . offset = 0 ;
entry - > data = grabData ( type , p , c , & entry - > length ) ;
if ( h - > indexUsed > 0 & & tag < h - > index [ h - > indexUsed - 1 ] . info . tag )
h - > flags & = ~ HEADERFLAG_SORTED ;
h - > indexUsed + + ;
return 1 ;
}
/** \ingroup header
* Append element to tag array in header .
* Appends item p to entry w / tag and type as passed . Won ' t work on
* RPM_STRING_TYPE . Any pointers into header memory returned from
* headerGetEntryMinMemory ( ) for this entry are invalid after this
* call has been made !
*
* @ param h header
* @ param tag tag
* @ param type tag value data type
* @ param p pointer to tag value ( s )
* @ param c number of values
* @ return 1 on success , 0 on failure
*/
static
int headerAppendEntry ( Header h , int_32 tag , int_32 type ,
const void * p , int_32 c )
/*@modifies h @*/
{
indexEntry entry ;
int length ;
/* First find the tag */
entry = findEntry ( h , tag , type ) ;
if ( ! entry )
return 0 ;
if ( type = = RPM_STRING_TYPE | | type = = RPM_I18NSTRING_TYPE ) {
/* we can't do this */
return 0 ;
}
length = dataLength ( type , p , c , 0 ) ;
if ( ENTRY_IN_REGION ( entry ) ) {
char * t = xmalloc ( entry - > length + length ) ;
memcpy ( t , entry - > data , entry - > length ) ;
entry - > data = t ;
entry - > info . offset = 0 ;
} else
entry - > data = xrealloc ( entry - > data , entry - > length + length ) ;
copyData ( type , ( ( char * ) entry - > data ) + entry - > length , p , c , length ) ;
entry - > length + = length ;
entry - > info . count + = c ;
return 1 ;
}
/** \ingroup header
* Add or append element to tag array in header .
* @ todo Arg " p " should have const .
* @ param h header
* @ param tag tag
* @ param type tag value data type
* @ param p pointer to tag value ( s )
* @ param c number of values
* @ return 1 on success , 0 on failure
*/
static
int headerAddOrAppendEntry ( Header h , int_32 tag , int_32 type ,
const void * p , int_32 c )
/*@modifies h @*/
{
return ( findEntry ( h , tag , type )
? headerAppendEntry ( h , tag , type , p , c )
: headerAddEntry ( h , tag , type , p , c ) ) ;
}
/** \ingroup header
* Add locale specific tag to header .
* A NULL lang is interpreted as the C locale . Here are the rules :
* \ verbatim
* - If the tag isn ' t in the header , it ' s added with the passed string
* as new value .
* - If the tag occurs multiple times in entry , which tag is affected
* by the operation is undefined .
* - If the tag is in the header w / this language , the entry is
* * replaced * ( like headerModifyEntry ( ) ) .
* \ endverbatim
* This function is intended to just " do the right thing " . If you need
* more fine grained control use headerAddEntry ( ) and headerModifyEntry ( ) .
*
* @ param h header
* @ param tag tag
* @ param string tag value
* @ param lang locale
* @ return 1 on success , 0 on failure
*/
static
int headerAddI18NString ( Header h , int_32 tag , const char * string ,
const char * lang )
/*@modifies h @*/
{
indexEntry table , entry ;
const char * * strArray ;
int length ;
int ghosts ;
int i , langNum ;
char * buf ;
table = findEntry ( h , HEADER_I18NTABLE , RPM_STRING_ARRAY_TYPE ) ;
entry = findEntry ( h , tag , RPM_I18NSTRING_TYPE ) ;
if ( ! table & & entry )
return 0 ; /* this shouldn't ever happen!! */
if ( ! table & & ! entry ) {
const char * charArray [ 2 ] ;
int count = 0 ;
if ( ! lang | | ( lang [ 0 ] = = ' C ' & & lang [ 1 ] = = ' \0 ' ) ) {
/*@-observertrans -readonlytrans@*/
charArray [ count + + ] = " C " ;
/*@=observertrans =readonlytrans@*/
} else {
/*@-observertrans -readonlytrans@*/
charArray [ count + + ] = " C " ;
/*@=observertrans =readonlytrans@*/
charArray [ count + + ] = lang ;
}
if ( ! headerAddEntry ( h , HEADER_I18NTABLE , RPM_STRING_ARRAY_TYPE ,
& charArray , count ) )
return 0 ;
table = findEntry ( h , HEADER_I18NTABLE , RPM_STRING_ARRAY_TYPE ) ;
}
if ( ! table )
return 0 ;
/*@-branchstate@*/
if ( ! lang ) lang = " C " ;
/*@=branchstate@*/
{ const char * l = table - > data ;
for ( langNum = 0 ; langNum < table - > info . count ; langNum + + ) {
if ( ! strcmp ( l , lang ) ) break ;
l + = strlen ( l ) + 1 ;
}
}
if ( langNum > = table - > info . count ) {
length = strlen ( lang ) + 1 ;
if ( ENTRY_IN_REGION ( table ) ) {
char * t = xmalloc ( table - > length + length ) ;
memcpy ( t , table - > data , table - > length ) ;
table - > data = t ;
table - > info . offset = 0 ;
} else
table - > data = xrealloc ( table - > data , table - > length + length ) ;
memmove ( ( ( char * ) table - > data ) + table - > length , lang , length ) ;
table - > length + = length ;
table - > info . count + + ;
}
if ( ! entry ) {
strArray = alloca ( sizeof ( * strArray ) * ( langNum + 1 ) ) ;
for ( i = 0 ; i < langNum ; i + + )
strArray [ i ] = " " ;
strArray [ langNum ] = string ;
return headerAddEntry ( h , tag , RPM_I18NSTRING_TYPE , strArray ,
langNum + 1 ) ;
} else if ( langNum > = entry - > info . count ) {
ghosts = langNum - entry - > info . count ;
length = strlen ( string ) + 1 + ghosts ;
if ( ENTRY_IN_REGION ( entry ) ) {
char * t = xmalloc ( entry - > length + length ) ;
memcpy ( t , entry - > data , entry - > length ) ;
entry - > data = t ;
entry - > info . offset = 0 ;
} else
entry - > data = xrealloc ( entry - > data , entry - > length + length ) ;
memset ( ( ( char * ) entry - > data ) + entry - > length , ' \0 ' , ghosts ) ;
memmove ( ( ( char * ) entry - > data ) + entry - > length + ghosts , string , strlen ( string ) + 1 ) ;
entry - > length + = length ;
entry - > info . count = langNum + 1 ;
} else {
char * b , * be , * e , * ee , * t ;
size_t bn , sn , en ;
/* Set beginning/end pointers to previous data */
b = be = e = ee = entry - > data ;
for ( i = 0 ; i < table - > info . count ; i + + ) {
if ( i = = langNum )
be = ee ;
ee + = strlen ( ee ) + 1 ;
if ( i = = langNum )
e = ee ;
}
/* Get storage for new buffer */
bn = ( be - b ) ;
sn = strlen ( string ) + 1 ;
en = ( ee - e ) ;
length = bn + sn + en ;
t = buf = xmalloc ( length ) ;
/* Copy values into new storage */
memcpy ( t , b , bn ) ;
t + = bn ;
/*@-mayaliasunique@*/
memcpy ( t , string , sn ) ;
t + = sn ;
memcpy ( t , e , en ) ;
t + = en ;
/*@=mayaliasunique@*/
/* Replace I18N string array */
entry - > length - = strlen ( be ) + 1 ;
entry - > length + = sn ;
if ( ENTRY_IN_REGION ( entry ) ) {
entry - > info . offset = 0 ;
} else
entry - > data = _free ( entry - > data ) ;
/*@-dependenttrans@*/
entry - > data = buf ;
/*@=dependenttrans@*/
}
return 0 ;
}
/** \ingroup header
* Modify tag in header .
* If there are multiple entries with this tag , the first one gets replaced .
* @ param h header
* @ param tag tag
* @ param type tag value data type
* @ param p pointer to tag value ( s )
* @ param c number of values
* @ return 1 on success , 0 on failure
*/
static
int headerModifyEntry ( Header h , int_32 tag , int_32 type ,
const void * p , int_32 c )
/*@modifies h @*/
{
indexEntry entry ;
void * oldData ;
/* First find the tag */
entry = findEntry ( h , tag , type ) ;
if ( ! entry )
return 0 ;
/* make sure entry points to the first occurence of this tag */
while ( entry > h - > index & & ( entry - 1 ) - > info . tag = = tag )
entry - - ;
/* free after we've grabbed the new data in case the two are intertwined;
that ' s a bad idea but at least we won ' t break */
oldData = entry - > data ;
entry - > info . count = c ;
entry - > info . type = type ;
entry - > data = grabData ( type , p , c , & entry - > length ) ;
/*@-branchstate@*/
if ( ENTRY_IN_REGION ( entry ) ) {
entry - > info . offset = 0 ;
} else
oldData = _free ( oldData ) ;
/*@=branchstate@*/
return 1 ;
}
/**
*/
static char escapedChar ( const char ch ) /*@*/
{
switch ( ch ) {
case ' a ' : return ' \a ' ;
case ' b ' : return ' \b ' ;
case ' f ' : return ' \f ' ;
case ' n ' : return ' \n ' ;
case ' r ' : return ' \r ' ;
case ' t ' : return ' \t ' ;
case ' v ' : return ' \v ' ;
default : return ch ;
}
}
/**
* Destroy headerSprintf format array .
* @ param format sprintf format array
* @ param num number of elements
* @ return NULL always
*/
static /*@null@*/ sprintfToken
freeFormat ( /*@only@*/ /*@null@*/ sprintfToken format , int num )
/*@modifies *format @*/
{
int i ;
if ( format = = NULL ) return NULL ;
for ( i = 0 ; i < num ; i + + ) {
switch ( format [ i ] . type ) {
case PTOK_ARRAY :
format [ i ] . u . array . format =
freeFormat ( format [ i ] . u . array . format ,
format [ i ] . u . array . numTokens ) ;
/*@switchbreak@*/ break ;
case PTOK_COND :
format [ i ] . u . cond . ifFormat =
freeFormat ( format [ i ] . u . cond . ifFormat ,
format [ i ] . u . cond . numIfTokens ) ;
format [ i ] . u . cond . elseFormat =
freeFormat ( format [ i ] . u . cond . elseFormat ,
format [ i ] . u . cond . numElseTokens ) ;
2002-07-20 14:39:40 +04:00
switch ( format [ i ] . u . cond . test . type ) {
case TRIVIAL :
/* the usual old way; format[i].test.u.tag is a struct */
break ;
case StringTAG_String3 :
/* the added option */
format [ i ] . u . cond . test . u . tag_str3 . headFormat =
freeFormat ( format [ i ] . u . cond . test . u . tag_str3 . headFormat ,
format [ i ] . u . cond . test . u . tag_str3 . numHeadTokens ) ;
/* We don't need to free the strings; we have not allocated them.
And PTOK_STRING case is analogous and also doesn ' t free the string . */
break ;
default :
/* report an error */
rpmMessage ( RPMMESS_WARNING , _ ( " Unknown test type in \ %|?:|; perhaps some memory is leaking right now. \n " ) ) ;
break ;
}
2002-03-25 23:16:26 +03:00
/*@switchbreak@*/ break ;
case PTOK_NONE :
case PTOK_TAG :
case PTOK_STRING :
default :
/*@switchbreak@*/ break ;
}
}
format = _free ( format ) ;
return NULL ;
}
/**
*/
static void findTag ( char * name , const headerTagTableEntry tags ,
const headerSprintfExtension extensions ,
/*@out@*/ headerTagTableEntry * tagMatch ,
/*@out@*/ headerSprintfExtension * extMatch )
/*@modifies *tagMatch, *extMatch @*/
{
headerTagTableEntry entry ;
headerSprintfExtension ext ;
const char * tagname ;
* tagMatch = NULL ;
* extMatch = NULL ;
if ( strncmp ( " RPMTAG_ " , name , sizeof ( " RPMTAG_ " ) - 1 ) ) {
char * t = alloca ( strlen ( name ) + sizeof ( " RPMTAG_ " ) ) ;
( void ) stpcpy ( stpcpy ( t , " RPMTAG_ " ) , name ) ;
tagname = t ;
} else {
tagname = name ;
}
/* Search extensions first to permit overriding header tags. */
ext = extensions ;
while ( ext - > type ! = HEADER_EXT_LAST ) {
if ( ext - > name ! = NULL & & ext - > type = = HEADER_EXT_TAG
& & ! xstrcasecmp ( ext - > name , tagname ) )
break ;
if ( ext - > type = = HEADER_EXT_MORE )
ext = ext - > u . more ;
else
ext + + ;
}
if ( ext - > type = = HEADER_EXT_TAG ) {
* extMatch = ext ;
return ;
}
/* Search header tags. */
for ( entry = tags ; entry - > name ; entry + + )
if ( entry - > name & & ! xstrcasecmp ( entry - > name , tagname ) )
break ;
if ( entry - > name ) {
* tagMatch = entry ;
return ;
}
}
/* forward ref */
static int parseExpression ( sprintfToken token , char * str ,
const headerTagTableEntry tags ,
const headerSprintfExtension extensions ,
/*@out@*/ char * * endPtr , /*@null@*/ /*@out@*/ errmsg_t * errmsg )
/*@modifies str, *str, *token, *endPtr, *errmsg @*/ ;
/**
*/
static int parseFormat ( char * str , const headerTagTableEntry tags ,
const headerSprintfExtension extensions ,
/*@out@*/ sprintfToken * formatPtr , /*@out@*/ int * numTokensPtr ,
/*@null@*/ /*@out@*/ char * * endPtr , int state ,
/*@null@*/ /*@out@*/ errmsg_t * errmsg )
/*@modifies str, *str, *formatPtr, *numTokensPtr, *endPtr, *errmsg @*/
{
char * chptr , * start , * next , * dst ;
sprintfToken format ;
int numTokens ;
int currToken ;
headerTagTableEntry tag ;
headerSprintfExtension ext ;
int i ;
int done = 0 ;
/* upper limit on number of individual formats */
numTokens = 0 ;
for ( chptr = str ; * chptr ! = ' \0 ' ; chptr + + )
2005-06-30 02:26:42 +04:00
if ( * chptr = = ' % ' | | * chptr = = ' [ ' ) numTokens + + ;
2002-03-25 23:16:26 +03:00
numTokens = numTokens * 2 + 1 ;
format = xcalloc ( numTokens , sizeof ( * format ) ) ;
if ( endPtr ) * endPtr = NULL ;
/*@-infloops@*/ /* LCL: can't detect done termination */
dst = start = str ;
currToken = - 1 ;
while ( * start ! = ' \0 ' ) {
switch ( * start ) {
case ' % ' :
/* handle %% */
if ( * ( start + 1 ) = = ' % ' ) {
if ( currToken < 0 | | format [ currToken ] . type ! = PTOK_STRING ) {
currToken + + ;
format [ currToken ] . type = PTOK_STRING ;
/*@-temptrans -assignexpose@*/
dst = format [ currToken ] . u . string . string = start ;
/*@=temptrans =assignexpose@*/
}
start + + ;
* dst + + = * start + + ;
/*@switchbreak@*/ break ;
}
currToken + + ;
* dst + + = ' \0 ' ;
start + + ;
if ( * start = = ' | ' ) {
char * newEnd ;
start + + ;
if ( parseExpression ( format + currToken , start , tags ,
extensions , & newEnd , errmsg ) )
{
format = freeFormat ( format , numTokens ) ;
return 1 ;
}
start = newEnd ;
/*@switchbreak@*/ break ;
}
/*@-assignexpose@*/
format [ currToken ] . u . tag . format = start ;
/*@=assignexpose@*/
format [ currToken ] . u . tag . pad = 0 ;
format [ currToken ] . u . tag . justOne = 0 ;
format [ currToken ] . u . tag . arrayCount = 0 ;
chptr = start ;
while ( * chptr & & * chptr ! = ' { ' & & * chptr ! = ' % ' ) chptr + + ;
if ( ! * chptr | | * chptr = = ' % ' ) {
/*@-observertrans -readonlytrans@*/
if ( errmsg ) * errmsg = _ ( " missing { after % " ) ;
/*@=observertrans =readonlytrans@*/
format = freeFormat ( format , numTokens ) ;
return 1 ;
}
* chptr + + = ' \0 ' ;
while ( start < chptr ) {
if ( xisdigit ( * start ) ) {
i = strtoul ( start , & start , 10 ) ;
format [ currToken ] . u . tag . pad + = i ;
} else {
start + + ;
}
}
if ( * start = = ' = ' ) {
format [ currToken ] . u . tag . justOne = 1 ;
start + + ;
} else if ( * start = = ' # ' ) {
format [ currToken ] . u . tag . justOne = 1 ;
format [ currToken ] . u . tag . arrayCount = 1 ;
start + + ;
}
next = start ;
while ( * next & & * next ! = ' } ' ) next + + ;
if ( ! * next ) {
/*@-observertrans -readonlytrans@*/
if ( errmsg ) * errmsg = _ ( " missing } after %{ " ) ;
/*@=observertrans =readonlytrans@*/
format = freeFormat ( format , numTokens ) ;
return 1 ;
}
* next + + = ' \0 ' ;
chptr = start ;
while ( * chptr & & * chptr ! = ' : ' ) chptr + + ;
if ( * chptr ! = ' \0 ' ) {
* chptr + + = ' \0 ' ;
if ( ! * chptr ) {
/*@-observertrans -readonlytrans@*/
if ( errmsg ) * errmsg = _ ( " empty tag format " ) ;
/*@=observertrans =readonlytrans@*/
format = freeFormat ( format , numTokens ) ;
return 1 ;
}
/*@-assignexpose@*/
format [ currToken ] . u . tag . type = chptr ;
/*@=assignexpose@*/
} else {
format [ currToken ] . u . tag . type = NULL ;
}
if ( ! * start ) {
/*@-observertrans -readonlytrans@*/
if ( errmsg ) * errmsg = _ ( " empty tag name " ) ;
/*@=observertrans =readonlytrans@*/
format = freeFormat ( format , numTokens ) ;
return 1 ;
}
i = 0 ;
findTag ( start , tags , extensions , & tag , & ext ) ;
if ( tag ) {
format [ currToken ] . u . tag . ext = NULL ;
format [ currToken ] . u . tag . tag = tag - > val ;
} else if ( ext ) {
format [ currToken ] . u . tag . ext = ext - > u . tagFunction ;
format [ currToken ] . u . tag . extNum = ext - extensions ;
} else {
/*@-observertrans -readonlytrans@*/
if ( errmsg ) * errmsg = _ ( " unknown tag " ) ;
/*@=observertrans =readonlytrans@*/
format = freeFormat ( format , numTokens ) ;
return 1 ;
}
format [ currToken ] . type = PTOK_TAG ;
start = next ;
/*@switchbreak@*/ break ;
case ' [ ' :
* dst + + = ' \0 ' ;
* start + + = ' \0 ' ;
currToken + + ;
if ( parseFormat ( start , tags , extensions ,
& format [ currToken ] . u . array . format ,
& format [ currToken ] . u . array . numTokens ,
& start , PARSER_IN_ARRAY , errmsg ) ) {
format = freeFormat ( format , numTokens ) ;
return 1 ;
}
if ( ! start ) {
/*@-observertrans -readonlytrans@*/
if ( errmsg ) * errmsg = _ ( " ] expected at end of array " ) ;
/*@=observertrans =readonlytrans@*/
format = freeFormat ( format , numTokens ) ;
return 1 ;
}
dst = start ;
format [ currToken ] . type = PTOK_ARRAY ;
/*@switchbreak@*/ break ;
case ' ] ' :
case ' } ' :
if ( ( * start = = ' ] ' & & state ! = PARSER_IN_ARRAY ) | |
( * start = = ' } ' & & state ! = PARSER_IN_EXPR ) ) {
if ( * start = = ' ] ' ) {
/*@-observertrans -readonlytrans@*/
if ( errmsg ) * errmsg = _ ( " unexpected ] " ) ;
/*@=observertrans =readonlytrans@*/
} else {
/*@-observertrans -readonlytrans@*/
if ( errmsg ) * errmsg = _ ( " unexpected } " ) ;
/*@=observertrans =readonlytrans@*/
}
format = freeFormat ( format , numTokens ) ;
return 1 ;
}
* start + + = ' \0 ' ;
if ( endPtr ) * endPtr = start ;
done = 1 ;
/*@switchbreak@*/ break ;
default :
if ( currToken < 0 | | format [ currToken ] . type ! = PTOK_STRING ) {
currToken + + ;
format [ currToken ] . type = PTOK_STRING ;
/*@-temptrans -assignexpose@*/
dst = format [ currToken ] . u . string . string = start ;
/*@=temptrans =assignexpose@*/
}
if ( * start = = ' \\ ' ) {
start + + ;
* dst + + = escapedChar ( * start + + ) ;
} else {
* dst + + = * start + + ;
}
/*@switchbreak@*/ break ;
}
if ( done )
break ;
}
/*@=infloops@*/
* dst = ' \0 ' ;
currToken + + ;
for ( i = 0 ; i < currToken ; i + + ) {
if ( format [ i ] . type = = PTOK_STRING )
format [ i ] . u . string . len = strlen ( format [ i ] . u . string . string ) ;
}
* numTokensPtr = currToken ;
* formatPtr = format ;
return 0 ;
}
/**
*/
static int parseExpression ( sprintfToken token , char * str ,
const headerTagTableEntry tags ,
const headerSprintfExtension extensions ,
/*@out@*/ char * * endPtr ,
/*@null@*/ /*@out@*/ errmsg_t * errmsg )
{
headerTagTableEntry tag ;
headerSprintfExtension ext ;
char * chptr ;
char * end ;
if ( errmsg ) * errmsg = NULL ;
chptr = str ;
while ( * chptr & & * chptr ! = ' ? ' ) chptr + + ;
if ( * chptr ! = ' ? ' ) {
/*@-observertrans -readonlytrans@*/
if ( errmsg ) * errmsg = _ ( " ? expected in expression " ) ;
/*@=observertrans =readonlytrans@*/
return 1 ;
}
* chptr + + = ' \0 ' ; ;
if ( * chptr ! = ' { ' ) {
/*@-observertrans -readonlytrans@*/
if ( errmsg ) * errmsg = _ ( " { expected after ? in expression " ) ;
/*@=observertrans =readonlytrans@*/
return 1 ;
}
chptr + + ;
if ( parseFormat ( chptr , tags , extensions , & token - > u . cond . ifFormat ,
& token - > u . cond . numIfTokens , & end , PARSER_IN_EXPR , errmsg ) )
return 1 ;
2002-07-20 14:23:28 +04:00
if ( ! end | | ! * end ) {
2002-03-25 23:16:26 +03:00
/*@-observertrans -readonlytrans@*/
if ( errmsg ) * errmsg = _ ( " } expected in expression " ) ;
/*@=observertrans =readonlytrans@*/
token - > u . cond . ifFormat =
freeFormat ( token - > u . cond . ifFormat , token - > u . cond . numIfTokens ) ;
return 1 ;
}
chptr = end ;
if ( * chptr ! = ' : ' & & * chptr ! = ' | ' ) {
/*@-observertrans -readonlytrans@*/
if ( errmsg ) * errmsg = _ ( " : expected following ? subexpression " ) ;
/*@=observertrans =readonlytrans@*/
token - > u . cond . ifFormat =
freeFormat ( token - > u . cond . ifFormat , token - > u . cond . numIfTokens ) ;
return 1 ;
}
if ( * chptr = = ' | ' ) {
( void ) parseFormat ( xstrdup ( " " ) , tags , extensions ,
& token - > u . cond . elseFormat ,
& token - > u . cond . numElseTokens , & end , PARSER_IN_EXPR ,
errmsg ) ;
} else {
chptr + + ;
if ( * chptr ! = ' { ' ) {
/*@-observertrans -readonlytrans@*/
if ( errmsg ) * errmsg = _ ( " { expected after : in expression " ) ;
/*@=observertrans =readonlytrans@*/
token - > u . cond . ifFormat =
freeFormat ( token - > u . cond . ifFormat , token - > u . cond . numIfTokens ) ;
return 1 ;
}
chptr + + ;
if ( parseFormat ( chptr , tags , extensions , & token - > u . cond . elseFormat ,
& token - > u . cond . numElseTokens , & end , PARSER_IN_EXPR ,
errmsg ) )
return 1 ;
2002-07-20 14:23:28 +04:00
if ( ! end | | ! * end ) {
2002-03-25 23:16:26 +03:00
/*@-observertrans -readonlytrans@*/
if ( errmsg ) * errmsg = _ ( " } expected in expression " ) ;
/*@=observertrans =readonlytrans@*/
token - > u . cond . ifFormat =
freeFormat ( token - > u . cond . ifFormat , token - > u . cond . numIfTokens ) ;
return 1 ;
}
chptr = end ;
if ( * chptr ! = ' | ' ) {
/*@-observertrans -readonlytrans@*/
if ( errmsg ) * errmsg = _ ( " | expected at end of expression " ) ;
/*@=observertrans =readonlytrans@*/
token - > u . cond . ifFormat =
freeFormat ( token - > u . cond . ifFormat , token - > u . cond . numIfTokens ) ;
token - > u . cond . elseFormat =
freeFormat ( token - > u . cond . elseFormat , token - > u . cond . numElseTokens ) ;
return 1 ;
}
}
chptr + + ;
* endPtr = chptr ;
2002-07-20 14:39:40 +04:00
{ /* branching between the trivial old test for the conditional and
the added test for EVR comparison . */
char * str2 = strchr ( str , ' > ' ) ;
if ( str2 ) {
char * endOfParsed = NULL ;
rpmMessage ( RPMMESS_WARNING , _ ( " Parsing non-standard test (>) for \ %|?{}:{}|. \n " ) ) ;
* str2 = ' \0 ' ;
+ + str2 ; /* str2 is the beginning of the second part: after the > sign. */
if ( parseFormat ( str , tags , extensions ,
& token - > u . cond . test . u . tag_str3 . headFormat ,
& token - > u . cond . test . u . tag_str3 . numHeadTokens ,
& endOfParsed , PARSER_IN_EXPR , errmsg )
/* this doesn't work, NULL is returned: || ( endOfParsed != str2 ) */ )
{
/*@-observertrans -readonlytrans@*/
if ( errmsg
& & ! * errmsg /* *errmsg was set to NULL at the function beginnging */ )
* errmsg = _ ( " the left part of >-expr finished before the > sign " ) ;
/*@=observertrans =readonlytrans@*/
token - > u . cond . ifFormat =
freeFormat ( token - > u . cond . ifFormat , token - > u . cond . numIfTokens ) ;
token - > u . cond . elseFormat =
freeFormat ( token - > u . cond . elseFormat , token - > u . cond . numElseTokens ) ;
return 1 ;
}
token - > u . cond . test . type = StringTAG_String3 ;
token - > u . cond . test . u . tag_str3 . predicate = & isChangeNameMoreFresh ;
parseEVR ( str2 ,
& token - > u . cond . test . u . tag_str3 . tail [ 0 ] ,
& token - > u . cond . test . u . tag_str3 . tail [ 1 ] ,
& token - > u . cond . test . u . tag_str3 . tail [ 2 ] ) ;
/* We could strdup tail[j], but it seems we don't need this: the rest of similar code
doesn ' t perform this . And we don ' t have to free them . */
rpmMessage ( RPMMESS_DEBUG , " Will cmp with e=%s, v=%s, r=%s \n " ,
token - > u . cond . test . u . tag_str3 . tail [ 0 ] ,
token - > u . cond . test . u . tag_str3 . tail [ 1 ] ,
token - > u . cond . test . u . tag_str3 . tail [ 2 ] ) ;
}
else {
struct sprintfTag head ;
rpmMessage ( RPMMESS_DEBUG , _ ( " The usual way of parsing the test part for \ %|?:| \n " ) ) ;
findTag ( str , tags , extensions , & tag , & ext ) ;
if ( tag ) {
head . ext = NULL ;
head . tag = tag - > val ;
} else if ( ext ) {
head . ext = ext - > u . tagFunction ;
head . extNum = ext - extensions ;
} else {
head . ext = NULL ;
head . tag = - 1 ;
}
token - > u . cond . test . type = TRIVIAL ;
token - > u . cond . test . u . tag = head ;
}
2002-03-25 23:16:26 +03:00
}
2002-07-20 14:39:40 +04:00
2002-03-25 23:16:26 +03:00
token - > type = PTOK_COND ;
return 0 ;
}
/**
* @ return 0 on success , 1 on failure
*/
static int getExtension ( Header h , headerTagTagFunction fn ,
/*@out@*/ hTYP_t typeptr ,
/*@out@*/ hPTR_t * data ,
/*@out@*/ hCNT_t countptr ,
extensionCache ext )
/*@modifies *typeptr, *data, *countptr, ext @*/
{
if ( ! ext - > avail ) {
if ( fn ( h , & ext - > type , & ext - > data , & ext - > count , & ext - > freeit ) )
return 1 ;
ext - > avail = 1 ;
}
if ( typeptr ) * typeptr = ext - > type ;
if ( data ) * data = ext - > data ;
if ( countptr ) * countptr = ext - > count ;
return 0 ;
}
/**
*/
static char * formatValue ( sprintfTag tag , Header h ,
const headerSprintfExtension extensions ,
2002-04-05 12:04:32 +04:00
extensionCache extCache , int element ,
char * * valp , int * vallenp , int * allocedp )
/*@modifies extCache, *valp, *vallenp, *allocedp @*/
2002-03-25 23:16:26 +03:00
{
2002-04-05 12:04:32 +04:00
char * val = NULL ;
int need = 0 ;
char * t , * te ;
2002-03-25 23:16:26 +03:00
char buf [ 20 ] ;
int_32 count , type ;
hPTR_t data ;
unsigned int intVal ;
const char * * strarray ;
2002-04-05 12:04:32 +04:00
int datafree = 0 ;
2002-03-25 23:16:26 +03:00
int countBuf ;
headerTagFormatFunction tagtype = NULL ;
headerSprintfExtension ext ;
memset ( buf , 0 , sizeof ( buf ) ) ;
/*@-branchstate@*/
if ( tag - > ext ) {
if ( getExtension ( h , tag - > ext , & type , & data , & count ,
extCache + tag - > extNum ) )
{
count = 1 ;
type = RPM_STRING_TYPE ;
2002-04-05 12:04:32 +04:00
data = " (none) " ;
2002-03-25 23:16:26 +03:00
}
} else {
if ( ! headerGetEntry ( h , tag - > tag , & type , ( void * * ) & data , & count ) ) {
count = 1 ;
type = RPM_STRING_TYPE ;
2002-04-05 12:04:32 +04:00
data = " (none) " ;
2002-03-25 23:16:26 +03:00
}
2002-04-05 12:04:32 +04:00
datafree = 1 ;
2002-03-25 23:16:26 +03:00
}
/*@=branchstate@*/
if ( tag - > arrayCount ) {
/*@-observertrans -modobserver@*/
data = headerFreeData ( data , type ) ;
/*@=observertrans =modobserver@*/
countBuf = count ;
data = & countBuf ;
count = 1 ;
type = RPM_INT32_TYPE ;
}
( void ) stpcpy ( stpcpy ( buf , " % " ) , tag - > format ) ;
if ( tag - > type ) {
ext = extensions ;
while ( ext - > type ! = HEADER_EXT_LAST ) {
if ( ext - > name ! = NULL & & ext - > type = = HEADER_EXT_FORMAT
& & ! strcmp ( ext - > name , tag - > type ) )
{
tagtype = ext - > u . formatFunction ;
break ;
}
if ( ext - > type = = HEADER_EXT_MORE )
ext = ext - > u . more ;
else
ext + + ;
}
}
/*@-branchstate@*/
switch ( type ) {
case RPM_STRING_ARRAY_TYPE :
strarray = ( const char * * ) data ;
if ( tagtype )
val = tagtype ( RPM_STRING_TYPE , strarray [ element ] , buf , tag - > pad , 0 ) ;
2002-04-05 12:04:32 +04:00
if ( val ) {
need = strlen ( val ) ;
2003-04-24 02:00:43 +04:00
} else if ( strarray & & strarray [ element ] ) {
2002-04-05 12:04:32 +04:00
need = strlen ( strarray [ element ] ) + tag - > pad + 20 ;
val = xmalloc ( need + 1 ) ;
2002-03-25 23:16:26 +03:00
strcat ( buf , " s " ) ;
/*@-formatconst@*/
sprintf ( val , buf , strarray [ element ] ) ;
/*@=formatconst@*/
}
/*@-observertrans -modobserver@*/
2002-04-05 12:04:32 +04:00
if ( datafree ) data = _free ( data ) ;
2002-03-25 23:16:26 +03:00
/*@=observertrans =modobserver@*/
break ;
case RPM_STRING_TYPE :
if ( tagtype )
val = tagtype ( RPM_STRING_ARRAY_TYPE , data , buf , tag - > pad , 0 ) ;
2002-04-05 12:04:32 +04:00
if ( val ) {
need = strlen ( val ) ;
2003-04-24 02:00:43 +04:00
} else if ( data ) {
2002-04-05 12:04:32 +04:00
need = strlen ( data ) + tag - > pad + 20 ;
val = xmalloc ( need + 1 ) ;
2002-03-25 23:16:26 +03:00
strcat ( buf , " s " ) ;
/*@-formatconst@*/
sprintf ( val , buf , data ) ;
/*@=formatconst@*/
}
break ;
case RPM_CHAR_TYPE :
case RPM_INT8_TYPE :
case RPM_INT16_TYPE :
case RPM_INT32_TYPE :
switch ( type ) {
case RPM_CHAR_TYPE :
case RPM_INT8_TYPE :
intVal = * ( ( ( int_8 * ) data ) + element ) ;
/*@innerbreak@*/ break ;
case RPM_INT16_TYPE :
intVal = * ( ( ( uint_16 * ) data ) + element ) ;
/*@innerbreak@*/ break ;
default : /* keep -Wall quiet */
case RPM_INT32_TYPE :
intVal = * ( ( ( int_32 * ) data ) + element ) ;
/*@innerbreak@*/ break ;
}
if ( tagtype )
val = tagtype ( RPM_INT32_TYPE , & intVal , buf , tag - > pad , element ) ;
2002-04-05 12:04:32 +04:00
if ( val ) {
need = strlen ( val ) ;
} else {
need = 10 + tag - > pad + 20 ;
val = xmalloc ( need + 1 ) ;
2018-01-31 12:17:33 +03:00
strcat ( buf , " u " ) ;
2002-03-25 23:16:26 +03:00
/*@-formatconst@*/
sprintf ( val , buf , intVal ) ;
/*@=formatconst@*/
}
break ;
case RPM_BIN_TYPE :
if ( tagtype )
val = tagtype ( RPM_BIN_TYPE , data , buf , tag - > pad , count ) ;
2002-04-05 12:04:32 +04:00
if ( val ) {
need = count ; /* XXX broken iff RPM_BIN_TYPE extension */
} else {
2002-03-25 23:16:26 +03:00
# ifdef NOTYET
val = memcpy ( xmalloc ( count ) , data , count ) ;
# else
/* XXX format string not used */
static char hex [ ] = " 0123456789abcdef " ;
const char * s = data ;
2002-04-05 12:04:32 +04:00
need = 2 * count + tag - > pad ;
val = t = xmalloc ( need + 1 ) ;
2002-03-25 23:16:26 +03:00
while ( count - - > 0 ) {
unsigned int i ;
i = * s + + ;
* t + + = hex [ ( i > > 4 ) & 0xf ] ;
* t + + = hex [ ( i ) & 0xf ] ;
}
* t = ' \0 ' ;
# endif
}
break ;
default :
2002-04-05 12:04:32 +04:00
need = sizeof ( " (unknown type) " ) - 1 ;
val = xstrdup ( " (unknown type) " ) ;
2002-03-25 23:16:26 +03:00
break ;
}
/*@=branchstate@*/
2002-04-05 12:04:32 +04:00
if ( val & & need > 0 ) {
if ( ( ( * vallenp ) + need ) > = ( * allocedp ) ) {
if ( ( * allocedp ) < = need )
( * allocedp ) + = need ;
( * allocedp ) < < = 1 ;
( * valp ) = xrealloc ( ( * valp ) , ( * allocedp ) + 1 ) ;
}
t = ( * valp ) + ( * vallenp ) ;
te = stpcpy ( t , val ) ;
( * vallenp ) + = ( te - t ) ;
val = _free ( val ) ;
}
return ( ( * valp ) + ( * vallenp ) ) ;
2002-03-25 23:16:26 +03:00
}
/**
*/
2002-04-05 12:04:32 +04:00
static char * singleSprintf ( Header h , sprintfToken token ,
2002-03-25 23:16:26 +03:00
const headerSprintfExtension extensions ,
2002-04-05 12:04:32 +04:00
extensionCache extCache , int element ,
char * * valp , int * vallenp , int * allocedp )
/*@modifies h, extCache, *valp, *vallenp, *allocedp @*/
2002-03-25 23:16:26 +03:00
{
2002-04-05 12:04:32 +04:00
char * t , * te ;
2002-03-25 23:16:26 +03:00
int i , j ;
int numElements ;
int type ;
sprintfToken condFormat ;
int condNumFormats ;
2002-04-05 12:04:32 +04:00
int need ;
2002-03-25 23:16:26 +03:00
/* we assume the token and header have been validated already! */
switch ( token - > type ) {
case PTOK_NONE :
break ;
case PTOK_STRING :
2002-04-05 12:04:32 +04:00
need = token - > u . string . len ;
if ( need < = 0 ) break ;
if ( ( ( * vallenp ) + need ) > = ( * allocedp ) ) {
if ( ( * allocedp ) < = need )
( * allocedp ) + = need ;
( * allocedp ) < < = 1 ;
( * valp ) = xrealloc ( ( * valp ) , ( * allocedp ) + 1 ) ;
}
t = ( * valp ) + ( * vallenp ) ;
te = stpcpy ( t , token - > u . string . string ) ;
( * vallenp ) + = ( te - t ) ;
2002-03-25 23:16:26 +03:00
break ;
case PTOK_TAG :
2002-04-05 12:04:32 +04:00
t = ( * valp ) + ( * vallenp ) ;
te = formatValue ( & token - > u . tag , h , extensions , extCache ,
( token - > u . tag . justOne ? 0 : element ) ,
valp , vallenp , allocedp ) ;
2002-03-25 23:16:26 +03:00
break ;
case PTOK_COND :
2002-07-20 14:39:40 +04:00
{
int testResult = 0 ; /* false by default */
switch ( token - > u . cond . test . type ) {
case TRIVIAL :
testResult = token - > u . cond . test . u . tag . ext | |
headerIsEntry ( h , token - > u . cond . test . u . tag . tag ) ;
break ;
case StringTAG_String3 :
{ /* this piece if code is based on headerSprintf() */
char * head = NULL ; int head_en = 0 ;
int head_alloced = 0 ;
head = xstrdup ( " " ) ;
for ( i = 0 ; i < token - > u . cond . test . u . tag_str3 . numHeadTokens ; i + + )
/* head_t = -- what do we need the return value for? headerSprintf() discards it. */
singleSprintf ( h , token - > u . cond . test . u . tag_str3 . headFormat + i ,
extensions , extCache ,
element , & head , & head_en , & head_alloced ) ;
if ( head ! = NULL & & head_en < head_alloced )
head = xrealloc ( head , head_en + 1 ) ;
testResult = head
& & token - > u . cond . test . u . tag_str3 . predicate ( head , token - > u . cond . test . u . tag_str3 . tail ) ;
/* Do we free all the alloced data heer? */
_free ( head ) ;
}
break ;
default :
/* report an error */
rpmMessage ( RPMMESS_WARNING , _ ( " Unknown test type in \ %|?:|; assuming false. \n " ) ) ;
testResult = 0 ;
break ;
}
if ( testResult ) {
2002-03-25 23:16:26 +03:00
condFormat = token - > u . cond . ifFormat ;
condNumFormats = token - > u . cond . numIfTokens ;
} else {
condFormat = token - > u . cond . elseFormat ;
condNumFormats = token - > u . cond . numElseTokens ;
}
2002-07-20 14:39:40 +04:00
}
2002-03-25 23:16:26 +03:00
2002-04-05 12:04:32 +04:00
need = condNumFormats * 20 ;
if ( condFormat = = NULL | | need < = 0 ) break ;
if ( ( ( * vallenp ) + need ) > = ( * allocedp ) ) {
if ( ( * allocedp ) < = need )
( * allocedp ) + = need ;
( * allocedp ) < < = 1 ;
( * valp ) = xrealloc ( ( * valp ) , ( * allocedp ) + 1 ) ;
2002-03-25 23:16:26 +03:00
}
2002-04-05 12:04:32 +04:00
t = ( * valp ) + ( * vallenp ) ;
for ( i = 0 ; i < condNumFormats ; i + + )
te = singleSprintf ( h , condFormat + i , extensions , extCache ,
element , valp , vallenp , allocedp ) ;
2002-03-25 23:16:26 +03:00
break ;
case PTOK_ARRAY :
numElements = - 1 ;
for ( i = 0 ; i < token - > u . array . numTokens ; i + + ) {
if ( token - > u . array . format [ i ] . type ! = PTOK_TAG | |
token - > u . array . format [ i ] . u . tag . arrayCount | |
token - > u . array . format [ i ] . u . tag . justOne ) continue ;
if ( token - > u . array . format [ i ] . u . tag . ext ) {
const void * data ;
if ( getExtension ( h , token - > u . array . format [ i ] . u . tag . ext ,
& type , & data , & numElements ,
extCache +
token - > u . array . format [ i ] . u . tag . extNum ) )
continue ;
} else {
if ( ! headerGetEntry ( h , token - > u . array . format [ i ] . u . tag . tag ,
2002-04-05 12:04:32 +04:00
& type , NULL , & numElements ) )
2002-03-25 23:16:26 +03:00
continue ;
}
/*@loopbreak@*/ break ;
}
if ( numElements = = - 1 ) {
2002-04-05 12:04:32 +04:00
need = sizeof ( " (none) " ) - 1 ;
if ( ( ( * vallenp ) + need ) > = ( * allocedp ) ) {
if ( ( * allocedp ) < = need )
( * allocedp ) + = need ;
( * allocedp ) < < = 1 ;
( * valp ) = xrealloc ( ( * valp ) , ( * allocedp ) + 1 ) ;
}
t = ( * valp ) + ( * vallenp ) ;
te = stpcpy ( t , " (none) " ) ;
( * vallenp ) + = ( te - t ) ;
2002-03-25 23:16:26 +03:00
} else {
2002-04-05 12:04:32 +04:00
need = numElements * token - > u . array . numTokens * 10 ;
if ( need < = 0 ) break ;
if ( ( ( * vallenp ) + need ) > = ( * allocedp ) ) {
if ( ( * allocedp ) < = need )
( * allocedp ) + = need ;
( * allocedp ) < < = 1 ;
( * valp ) = xrealloc ( ( * valp ) , ( * allocedp ) + 1 ) ;
}
2002-03-25 23:16:26 +03:00
2002-04-05 12:04:32 +04:00
t = ( * valp ) + ( * vallenp ) ;
2002-03-25 23:16:26 +03:00
for ( j = 0 ; j < numElements ; j + + ) {
2002-04-05 12:04:32 +04:00
for ( i = 0 ; i < token - > u . array . numTokens ; i + + )
te = singleSprintf ( h , token - > u . array . format + i ,
extensions , extCache , j ,
valp , vallenp , allocedp ) ;
2002-03-25 23:16:26 +03:00
}
}
break ;
}
2002-04-05 12:04:32 +04:00
return ( ( * valp ) + ( * vallenp ) ) ;
2002-03-25 23:16:26 +03:00
}
/**
*/
static /*@only@*/ extensionCache
allocateExtensionCache ( const headerSprintfExtension extensions )
/*@*/
{
headerSprintfExtension ext = extensions ;
int i = 0 ;
while ( ext - > type ! = HEADER_EXT_LAST ) {
i + + ;
if ( ext - > type = = HEADER_EXT_MORE )
ext = ext - > u . more ;
else
ext + + ;
}
/*@-sizeoftype@*/
return xcalloc ( i , sizeof ( struct extensionCache ) ) ;
/*@=sizeoftype@*/
}
/**
* @ return NULL always
*/
static /*@null@*/ extensionCache
freeExtensionCache ( const headerSprintfExtension extensions ,
/*@only@*/ extensionCache cache )
/*@*/
{
headerSprintfExtension ext = extensions ;
int i = 0 ;
while ( ext - > type ! = HEADER_EXT_LAST ) {
if ( cache [ i ] . freeit ) cache [ i ] . data = _free ( cache [ i ] . data ) ;
i + + ;
if ( ext - > type = = HEADER_EXT_MORE )
ext = ext - > u . more ;
else
ext + + ;
}
cache = _free ( cache ) ;
return NULL ;
}
/** \ingroup header
* Return formatted output string from header tags .
* The returned string must be free ( ) d .
*
* @ param h header
* @ param fmt format to use
* @ param tbltags array of tag name / value pairs
* @ param extensions chained table of formatting extensions .
* @ retval errmsg error message ( if any )
* @ return formatted output string ( malloc ' ed )
*/
static /*@only@*/ /*@null@*/
char * headerSprintf ( Header h , const char * fmt ,
const struct headerTagTableEntry_s * tbltags ,
const struct headerSprintfExtension_s * extensions ,
/*@null@*/ /*@out@*/ errmsg_t * errmsg )
/*@modifies *errmsg @*/
{
/*@-castexpose@*/ /* FIX: legacy API shouldn't change. */
headerSprintfExtension exts = ( headerSprintfExtension ) extensions ;
headerTagTableEntry tags = ( headerTagTableEntry ) tbltags ;
/*@=castexpose@*/
char * fmtString ;
sprintfToken format ;
int numTokens ;
2002-04-05 12:04:32 +04:00
char * val = NULL ;
int vallen = 0 ;
int alloced = 0 ;
2002-03-25 23:16:26 +03:00
int i ;
extensionCache extCache ;
/*fmtString = escapeString(fmt);*/
fmtString = xstrdup ( fmt ) ;
if ( parseFormat ( fmtString , tags , exts , & format , & numTokens ,
NULL , PARSER_BEGIN , errmsg ) ) {
fmtString = _free ( fmtString ) ;
return NULL ;
}
extCache = allocateExtensionCache ( exts ) ;
2002-04-22 21:06:31 +04:00
val = xstrdup ( " " ) ;
2002-03-25 23:16:26 +03:00
for ( i = 0 ; i < numTokens ; i + + ) {
/*@-mods@*/
2014-02-16 02:44:35 +04:00
singleSprintf ( h , format + i , exts , extCache , 0 ,
2002-04-05 12:04:32 +04:00
& val , & vallen , & alloced ) ;
2002-03-25 23:16:26 +03:00
/*@=mods@*/
}
2002-04-05 12:04:32 +04:00
if ( val ! = NULL & & vallen < alloced )
val = xrealloc ( val , vallen + 1 ) ;
2002-03-25 23:16:26 +03:00
fmtString = _free ( fmtString ) ;
extCache = freeExtensionCache ( exts , extCache ) ;
format = _free ( format ) ;
2002-04-05 12:04:32 +04:00
return val ;
2002-03-25 23:16:26 +03:00
}
/**
*/
static char * octalFormat ( int_32 type , hPTR_t data ,
char * formatPrefix , int padding , /*@unused@*/ int element )
/*@modifies formatPrefix @*/
{
char * val ;
if ( type ! = RPM_INT32_TYPE ) {
val = xstrdup ( _ ( " (not a number) " ) ) ;
} else {
val = xmalloc ( 20 + padding ) ;
strcat ( formatPrefix , " o " ) ;
/*@-formatconst@*/
sprintf ( val , formatPrefix , * ( ( int_32 * ) data ) ) ;
/*@=formatconst@*/
}
return val ;
}
/**
*/
static char * hexFormat ( int_32 type , hPTR_t data ,
char * formatPrefix , int padding , /*@unused@*/ int element )
/*@modifies formatPrefix @*/
{
char * val ;
if ( type ! = RPM_INT32_TYPE ) {
val = xstrdup ( _ ( " (not a number) " ) ) ;
} else {
val = xmalloc ( 20 + padding ) ;
strcat ( formatPrefix , " x " ) ;
/*@-formatconst@*/
sprintf ( val , formatPrefix , * ( ( int_32 * ) data ) ) ;
/*@=formatconst@*/
}
return val ;
}
/**
*/
static char * realDateFormat ( int_32 type , hPTR_t data ,
char * formatPrefix , int padding , /*@unused@*/ int element ,
const char * strftimeFormat )
/*@modifies formatPrefix @*/
{
char * val ;
if ( type ! = RPM_INT32_TYPE ) {
val = xstrdup ( _ ( " (not a number) " ) ) ;
} else {
struct tm * tstruct ;
char buf [ 50 ] ;
val = xmalloc ( 50 + padding ) ;
strcat ( formatPrefix , " s " ) ;
/* this is important if sizeof(int_32) ! sizeof(time_t) */
{ time_t dateint = * ( ( int_32 * ) data ) ;
tstruct = localtime ( & dateint ) ;
}
buf [ 0 ] = ' \0 ' ;
if ( tstruct )
( void ) strftime ( buf , sizeof ( buf ) - 1 , strftimeFormat , tstruct ) ;
/*@-formatconst@*/
sprintf ( val , formatPrefix , buf ) ;
/*@=formatconst@*/
}
return val ;
}
/**
*/
static char * dateFormat ( int_32 type , hPTR_t data ,
char * formatPrefix , int padding , int element )
/*@modifies formatPrefix @*/
{
return realDateFormat ( type , data , formatPrefix , padding , element , " %c " ) ;
}
/**
*/
static char * dayFormat ( int_32 type , hPTR_t data ,
char * formatPrefix , int padding , int element )
/*@modifies formatPrefix @*/
{
return realDateFormat ( type , data , formatPrefix , padding , element ,
" %a %b %d %Y " ) ;
}
/**
*/
static char * shescapeFormat ( int_32 type , hPTR_t data ,
char * formatPrefix , int padding , /*@unused@*/ int element )
/*@modifies formatPrefix @*/
{
char * result , * dst , * src , * buf ;
if ( type = = RPM_INT32_TYPE ) {
result = xmalloc ( padding + 20 ) ;
2018-01-31 12:17:33 +03:00
strcat ( formatPrefix , " u " ) ;
2002-03-25 23:16:26 +03:00
/*@-formatconst@*/
sprintf ( result , formatPrefix , * ( ( int_32 * ) data ) ) ;
/*@=formatconst@*/
} else {
buf = alloca ( strlen ( data ) + padding + 2 ) ;
strcat ( formatPrefix , " s " ) ;
/*@-formatconst@*/
sprintf ( buf , formatPrefix , data ) ;
/*@=formatconst@*/
result = dst = xmalloc ( strlen ( buf ) * 4 + 3 ) ;
* dst + + = ' \' ' ;
for ( src = buf ; * src ! = ' \0 ' ; src + + ) {
if ( * src = = ' \' ' ) {
* dst + + = ' \' ' ;
* dst + + = ' \\ ' ;
* dst + + = ' \' ' ;
* dst + + = ' \' ' ;
} else {
* dst + + = * src ;
}
}
* dst + + = ' \' ' ;
* dst = ' \0 ' ;
}
return result ;
}
/*@-type@*/ /* FIX: cast? */
const struct headerSprintfExtension_s headerDefaultFormats [ ] = {
{ HEADER_EXT_FORMAT , " octal " , { octalFormat } } ,
{ HEADER_EXT_FORMAT , " hex " , { hexFormat } } ,
{ HEADER_EXT_FORMAT , " date " , { dateFormat } } ,
{ HEADER_EXT_FORMAT , " day " , { dayFormat } } ,
{ HEADER_EXT_FORMAT , " shescape " , { shescapeFormat } } ,
{ HEADER_EXT_LAST , NULL , { NULL } }
} ;
/*@=type@*/
/** \ingroup header
* Duplicate tag values from one header into another .
* @ param headerFrom source header
* @ param headerTo destination header
* @ param tagstocopy array of tags that are copied
*/
static
void headerCopyTags ( Header headerFrom , Header headerTo , hTAG_t tagstocopy )
/*@modifies headerTo @*/
{
int * p ;
if ( headerFrom = = headerTo )
return ;
for ( p = tagstocopy ; * p ! = 0 ; p + + ) {
char * s ;
int_32 type ;
int_32 count ;
if ( headerIsEntry ( headerTo , * p ) )
continue ;
if ( ! headerGetEntryMinMemory ( headerFrom , * p , & type ,
( hPTR_t * ) & s , & count ) )
continue ;
( void ) headerAddEntry ( headerTo , * p , type , s , count ) ;
s = headerFreeData ( s , type ) ;
}
}
/**
* Header tag iterator data structure .
*/
struct headerIteratorS {
/*@unused@*/ Header h ; /*!< Header being iterated. */
/*@unused@*/ int next_index ; /*!< Next tag index. */
} ;
/** \ingroup header
* Destroy header tag iterator .
* @ param hi header tag iterator
* @ return NULL always
*/
static /*@null@*/
HeaderIterator headerFreeIterator ( /*@only@*/ HeaderIterator hi )
/*@modifies hi @*/
{
hi - > h = headerFree ( hi - > h ) ;
hi = _free ( hi ) ;
return hi ;
}
/** \ingroup header
* Create header tag iterator .
* @ param h header
* @ return header tag iterator
*/
static
HeaderIterator headerInitIterator ( Header h )
/*@modifies h */
{
HeaderIterator hi = xmalloc ( sizeof ( * hi ) ) ;
headerSort ( h ) ;
hi - > h = headerLink ( h ) ;
hi - > next_index = 0 ;
return hi ;
}
/** \ingroup header
* Return next tag from header .
* @ param hi header tag iterator
* @ retval tag address of tag
* @ retval type address of tag value data type
* @ retval p address of pointer to tag value ( s )
* @ retval c address of number of values
* @ return 1 on success , 0 on failure
*/
static
int headerNextIterator ( HeaderIterator hi ,
/*@null@*/ /*@out@*/ hTAG_t tag ,
/*@null@*/ /*@out@*/ hTYP_t type ,
/*@null@*/ /*@out@*/ hPTR_t * p ,
/*@null@*/ /*@out@*/ hCNT_t c )
/*@modifies hi, *tag, *type, *p, *c @*/
{
Header h = hi - > h ;
int slot = hi - > next_index ;
indexEntry entry = NULL ;
int rc ;
for ( slot = hi - > next_index ; slot < h - > indexUsed ; slot + + ) {
entry = h - > index + slot ;
if ( ! ENTRY_IS_REGION ( entry ) )
break ;
}
hi - > next_index = slot ;
if ( entry = = NULL | | slot > = h - > indexUsed )
return 0 ;
/*@-noeffect@*/ /* LCL: no clue */
hi - > next_index + + ;
/*@=noeffect@*/
if ( tag )
* tag = entry - > info . tag ;
rc = copyEntry ( entry , type , p , c , 0 ) ;
/* XXX 1 on success */
return ( ( rc = = 1 ) ? 1 : 0 ) ;
}
2010-08-19 02:03:03 +04:00
unsigned int headerGetInstance ( Header h )
{
return h ? h - > instance : 0 ;
}
void headerSetInstance ( Header h , unsigned int instance )
{
h - > instance = instance ;
}
2002-03-25 23:16:26 +03:00
/** \ingroup header
* Duplicate a header .
* @ param h header
* @ return new header instance
*/
static /*@null@*/
Header headerCopy ( Header h )
/*@modifies h @*/
{
Header nh = headerNew ( ) ;
HeaderIterator hi ;
int_32 tag , type , count ;
hPTR_t ptr ;
/*@-branchstate@*/
for ( hi = headerInitIterator ( h ) ;
headerNextIterator ( hi , & tag , & type , & ptr , & count ) ;
ptr = headerFreeData ( ( void * ) ptr , type ) )
{
if ( ptr ) ( void ) headerAddEntry ( nh , tag , type , ptr , count ) ;
}
hi = headerFreeIterator ( hi ) ;
/*@=branchstate@*/
return headerReload ( nh , HEADER_IMAGE ) ;
}
/*@observer@*/ /*@unchecked@*/
static struct HV_s hdrVec1 = {
headerNew ,
headerFree ,
headerLink ,
headerSort ,
headerUnsort ,
headerSizeof ,
headerUnload ,
headerReload ,
headerCopy ,
headerLoad ,
headerCopyLoad ,
headerRead ,
headerWrite ,
headerIsEntry ,
headerFreeTag ,
headerGetEntry ,
headerGetEntryMinMemory ,
headerAddEntry ,
headerAppendEntry ,
headerAddOrAppendEntry ,
headerAddI18NString ,
headerModifyEntry ,
headerRemoveEntry ,
headerSprintf ,
headerCopyTags ,
headerFreeIterator ,
headerInitIterator ,
headerNextIterator ,
headerUnlink ,
NULL , NULL ,
1
} ;
/*@-compmempass -redef@*/
/*@observer@*/ /*@unchecked@*/
HV_t hdrVec = & hdrVec1 ;
/*@=compmempass =redef@*/