1 /**
2  * \file lib/tagname.c
3  */
4 
5 #include "system.h"
6 
7 #include <pthread.h>
8 
9 #include <rpm/header.h>
10 #include <rpm/rpmstring.h>
11 #include "debug.h"
12 
13 /** \ingroup header
14  * Associate tag names with numeric values.
15  */
16 typedef const struct headerTagTableEntry_s * headerTagTableEntry;
17 struct headerTagTableEntry_s {
18     const char * name;		/*!< Tag name. */
19     const char * shortname;	/*!< "Human readable" short name. */
20     rpmTagVal val;		/*!< Tag numeric value. */
21     rpmTagType type;		/*!< Tag type. */
22     rpmTagReturnType retype;	/*!< Tag return type. */
23     int extension;		/*!< Extension or "real" tag */
24 };
25 
26 #include "lib/tagtbl.C"
27 
28 #define TABLESIZE (sizeof(rpmTagTable) / sizeof(rpmTagTable[0]) - 1)
29 static const int rpmTagTableSize = TABLESIZE;
30 
31 static headerTagTableEntry tagsByName[TABLESIZE]; /*!< tags sorted by name. */
32 static headerTagTableEntry tagsByValue[TABLESIZE]; /*!< tags sorted by value. */
33 
34 /**
35  * Compare tag table entries by name.
36  * @param *avp		tag table entry a
37  * @param *bvp		tag table entry b
38  * @return		comparison
39  */
tagCmpName(const void * avp,const void * bvp)40 static int tagCmpName(const void * avp, const void * bvp)
41 {
42     headerTagTableEntry a = *(const headerTagTableEntry *) avp;
43     headerTagTableEntry b = *(const headerTagTableEntry *) bvp;
44     return strcmp(a->name, b->name);
45 }
46 
47 /**
48  * Compare tag table entries by value.
49  * @param *avp		tag table entry a
50  * @param *bvp		tag table entry b
51  * @return		comparison
52  */
tagCmpValue(const void * avp,const void * bvp)53 static int tagCmpValue(const void * avp, const void * bvp)
54 {
55     headerTagTableEntry a = *(const headerTagTableEntry *) avp;
56     headerTagTableEntry b = *(const headerTagTableEntry *) bvp;
57     int ret = (a->val - b->val);
58     /* Make sure that sort is stable, longest name first. */
59     if (ret == 0)
60 	ret = (strlen(b->name) - strlen(a->name));
61     return ret;
62 }
63 
64 static pthread_once_t tagsLoaded = PTHREAD_ONCE_INIT;
65 
66 /* Initialize tag by-value and by-name lookup tables */
loadTags(void)67 static void loadTags(void)
68 {
69     for (int i = 0; i < rpmTagTableSize; i++) {
70 	tagsByValue[i] = &rpmTagTable[i];
71 	tagsByName[i] = &rpmTagTable[i];
72     }
73 
74     qsort(tagsByValue, rpmTagTableSize, sizeof(*tagsByValue), tagCmpValue);
75     qsort(tagsByName, rpmTagTableSize, sizeof(*tagsByName), tagCmpName);
76 }
77 
entryByTag(rpmTagVal tag)78 static headerTagTableEntry entryByTag(rpmTagVal tag)
79 {
80     headerTagTableEntry entry = NULL;
81     int i, comparison;
82     int l = 0;
83     int u = rpmTagTableSize;
84 
85     while (l < u) {
86 	i = (l + u) / 2;
87 	comparison = (tag - tagsByValue[i]->val);
88 
89 	if (comparison < 0) {
90 	    u = i;
91 	} else if (comparison > 0) {
92 	    l = i + 1;
93 	} else {
94 	    /* Make sure that the bsearch retrieve is stable. */
95 	    while (i > 0 && tag == tagsByValue[i-1]->val) {
96 		i--;
97 	    }
98 	    entry = tagsByValue[i];
99 	    break;
100 	}
101     }
102     return entry;
103 }
104 
entryByName(const char * tag)105 static headerTagTableEntry entryByName(const char *tag)
106 {
107     headerTagTableEntry entry = NULL;
108     int i, comparison;
109     int l = 0;
110     int u = rpmTagTableSize;
111 
112     while (l < u) {
113 	i = (l + u) / 2;
114 	comparison = rstrcasecmp(tag, tagsByName[i]->shortname);
115 
116 	if (comparison < 0) {
117 	    u = i;
118 	} else if (comparison > 0) {
119 	    l = i + 1;
120 	} else {
121 	    entry = tagsByName[i];
122 	    break;
123 	}
124     }
125     return entry;
126 }
127 
rpmTagGetName(rpmTagVal tag)128 const char * rpmTagGetName(rpmTagVal tag)
129 {
130     const char *name = "(unknown)";
131     const struct headerTagTableEntry_s *t;
132 
133     pthread_once(&tagsLoaded, loadTags);
134 
135     switch (tag) {
136     case RPMDBI_PACKAGES:
137 	name = "Packages";
138 	break;
139     /* XXX make sure rpmdb indices are identically named. */
140     case RPMTAG_CONFLICTS:
141 	name = "Conflictname";
142 	break;
143     case RPMTAG_HDRID:
144 	name = "Sha1header";
145 	break;
146 
147     default:
148 	t = entryByTag(tag);
149 	if (t && t->shortname)
150 	    name = t->shortname;
151 	break;
152     }
153     return name;
154 }
155 
rpmTagGetType(rpmTagVal tag)156 rpmTagType rpmTagGetType(rpmTagVal tag)
157 {
158     const struct headerTagTableEntry_s *t;
159     rpmTagType tagtype = RPM_NULL_TYPE;
160 
161     pthread_once(&tagsLoaded, loadTags);
162 
163     t = entryByTag(tag);
164     if (t) {
165 	/* XXX this is dumb */
166 	tagtype = (rpmTagType)(t->type | t->retype);
167     }
168     return tagtype;
169 }
170 
rpmTagGetValue(const char * tagstr)171 rpmTagVal rpmTagGetValue(const char * tagstr)
172 {
173     const struct headerTagTableEntry_s *t;
174     rpmTagType tagval = RPMTAG_NOT_FOUND;
175 
176     pthread_once(&tagsLoaded, loadTags);
177 
178     if (!rstrcasecmp(tagstr, "Packages"))
179 	return RPMDBI_PACKAGES;
180 
181     t = entryByName(tagstr);
182     if (t)
183 	tagval = t->val;
184 
185     return tagval;
186 }
187 
rpmTagGetTagType(rpmTagVal tag)188 rpmTagType rpmTagGetTagType(rpmTagVal tag)
189 {
190     return (rpmTagGetType(tag) & RPM_MASK_TYPE);
191 }
192 
rpmTagGetReturnType(rpmTagVal tag)193 rpmTagReturnType rpmTagGetReturnType(rpmTagVal tag)
194 {
195     return (rpmTagGetType(tag) & RPM_MASK_RETURN_TYPE);
196 }
197 
rpmTagTypeGetClass(rpmTagType type)198 rpmTagClass rpmTagTypeGetClass(rpmTagType type)
199 {
200     rpmTagClass tclass;
201     switch (type & RPM_MASK_TYPE) {
202     case RPM_CHAR_TYPE:
203     case RPM_INT8_TYPE:
204     case RPM_INT16_TYPE:
205     case RPM_INT32_TYPE:
206     case RPM_INT64_TYPE:
207 	tclass = RPM_NUMERIC_CLASS;
208 	break;
209     case RPM_STRING_TYPE:
210     case RPM_STRING_ARRAY_TYPE:
211     case RPM_I18NSTRING_TYPE:
212 	tclass = RPM_STRING_CLASS;
213 	break;
214     case RPM_BIN_TYPE:
215 	tclass = RPM_BINARY_CLASS;
216 	break;
217     case RPM_NULL_TYPE:
218     default:
219 	tclass = RPM_NULL_CLASS;
220 	break;
221     }
222     return tclass;
223 }
224 
rpmTagGetClass(rpmTagVal tag)225 rpmTagClass rpmTagGetClass(rpmTagVal tag)
226 {
227     return rpmTagTypeGetClass(rpmTagGetTagType(tag));
228 }
229 
rpmTagGetNames(rpmtd tagnames,int fullname)230 int rpmTagGetNames(rpmtd tagnames, int fullname)
231 {
232     const char **names;
233     const char *name;
234 
235     pthread_once(&tagsLoaded, loadTags);
236 
237     if (tagnames == NULL)
238 	return 0;
239 
240     rpmtdReset(tagnames);
241     tagnames->count = rpmTagTableSize;
242     tagnames->data = names = xmalloc(tagnames->count * sizeof(*names));
243     tagnames->type = RPM_STRING_ARRAY_TYPE;
244     tagnames->flags = RPMTD_ALLOCED | RPMTD_IMMUTABLE;
245 
246     for (int i = 0; i < tagnames->count; i++) {
247 	name = fullname ? tagsByName[i]->name :
248 			  tagsByName[i]->shortname;
249 	names[i] = name;
250     }
251     return tagnames->count;
252 }
253