Changeset 19892
- Timestamp:
- Oct 3, 2008, 1:30:27 PM (18 years ago)
- File:
-
- 1 edited
-
trunk/psLib/src/types/psMetadataItemCompare.c (modified) (4 diffs)
Legend:
- Unmodified
- Added
- Removed
-
trunk/psLib/src/types/psMetadataItemCompare.c
r19805 r19892 4 4 5 5 #include <stdio.h> 6 #include <stdlib.h> 6 7 #include <ctype.h> 7 8 #include <string.h> … … 15 16 #include "psMetadataItemCompare.h" 16 17 18 // Parse a string according to some provided format; check for leftovers 19 #define PARSE_STRING(GOOD, RESULT, STRING, FORMAT) \ 20 bool GOOD = true; \ 21 { \ 22 int read; /* Number of characters read */ \ 23 if (sscanf(STRING, "%" FORMAT "%n", &(RESULT), &read) <= 0) { \ 24 GOOD = false; \ 25 } \ 26 if (read < strlen(STRING)) { \ 27 /* Something's left over in the string, so they don't match */ \ 28 GOOD = false; \ 29 } \ 30 } 31 32 // Parse a string as a sexagesimal value 33 static bool parseSexagesimal(double *value, const char *string) 34 { 35 int big, medium; // Big and medium-sized values 36 float small; // Small value 37 int read; // Number of characters read 38 if (sscanf(string, "%d:%d:%f%n", &big, &medium, &small, &read) != 3 && 39 sscanf(string, "%d %d %f%n", &big, &medium, &small, &read) != 3) { 40 return false; 41 } 42 if (read < strlen(string)) { 43 // Something's left over in the string, so they don't match 44 return false; 45 } 46 *value = abs(big) + (float)medium/60.0 + small/3600.0; 47 if (big < 0) { 48 *value *= -1.0; 49 } 50 return true; 51 } 52 53 // Parse a string as a boolean value 54 static bool parseBool(bool *value, const char *string) 55 { 56 if (strcasecmp(string, "T") == 0 || strcasecmp(string, "TRUE") == 0 || strcmp(string, "1") == 0) { 57 *value = true; 58 return true; 59 } 60 if (strcasecmp(string, "F") == 0 || strcasecmp(string, "FALSE") == 0 || strcmp(string, "0") == 0) { 61 *value = false; 62 return true; 63 } 64 return false; 65 } 66 67 68 // Operations for comparison 17 69 typedef enum { 18 PS_MD_OP_EQ,19 PS_MD_OP_LT,20 PS_MD_OP_LE,21 PS_MD_OP_GT,22 PS_MD_OP_GE,23 PS_MD_OP_NE24 } psMetadataItemCompareOp;70 COMPARE_OP_EQ, // Equality 71 COMPARE_OP_LT, // Less than 72 COMPARE_OP_LE, // Less than or equal 73 COMPARE_OP_GT, // Greater than 74 COMPARE_OP_GE, // Greater than or equal 75 COMPARE_OP_NE // Not equal 76 } compareOp; 25 77 26 78 /* determine boolean operator specified in comment: OP: XX (default is ==) */ 27 psMetadataItemCompareOp p_psMetadataItemCompareGetOp (const psMetadataItem *item) { 28 29 if (!item->comment) return PS_MD_OP_EQ; 30 31 char *p1 = strstr (item->comment, "OP:"); 32 if (!p1) return PS_MD_OP_EQ; 79 compareOp getCompareOp(const psMetadataItem *item) { 80 81 if (!item->comment) { 82 return COMPARE_OP_EQ; 83 } 84 85 char *p1 = strstr(item->comment, "OP:"); 86 if (!p1) { 87 return COMPARE_OP_EQ; 88 } 33 89 p1 += 3; // point to first char after @OP: 34 while (isblank(p1[0])) p1++; 90 while (isblank(p1[0])) { 91 p1++; 92 } 35 93 36 94 // the string starting at p1 must contain one of the following: 37 // == or = PS_MD_OP_EQ,38 // < PS_MD_OP_LT,39 // <= PS_MD_OP_LE,40 // > PS_MD_OP_GT,41 // >= PS_MD_OP_GE,42 // ! PS_MD_OP_NE43 95 // == or = COMPARE_OP_EQ, 96 // < COMPARE_OP_LT, 97 // <= COMPARE_OP_LE, 98 // > COMPARE_OP_GT, 99 // >= COMPARE_OP_GE, 100 // ! COMPARE_OP_NE 101 44 102 // XXX a bit crude: does not catch the case of invalid chars after boolean op 45 if (!strncmp (p1, "==", 2)) return PS_MD_OP_EQ;46 if (!strncmp (p1, "=", 1)) return PS_MD_OP_EQ;47 if (!strncmp (p1, "<=", 2)) return PS_MD_OP_LE;48 if (!strncmp (p1, "<", 1)) return PS_MD_OP_LT;49 if (!strncmp (p1, ">=", 2)) return PS_MD_OP_GE;50 if (!strncmp (p1, ">", 1)) return PS_MD_OP_GT;51 52 return PS_MD_OP_EQ;103 if (!strncmp (p1, "==", 2)) return COMPARE_OP_EQ; 104 if (!strncmp (p1, "=", 1)) return COMPARE_OP_EQ; 105 if (!strncmp (p1, "<=", 2)) return COMPARE_OP_LE; 106 if (!strncmp (p1, "<", 1)) return COMPARE_OP_LT; 107 if (!strncmp (p1, ">=", 2)) return COMPARE_OP_GE; 108 if (!strncmp (p1, ">", 1)) return COMPARE_OP_GT; 109 110 return COMPARE_OP_EQ; 53 111 } 54 112 55 113 // XXX better value for tolerance? 56 # define EQ_TOL 1e-6 57 58 # define COMPARE_CASE(TEMPLATENAME, TEMPLATETYPE, COMPARENAME, COMPARETYPENAME) \ 59 case PS_TYPE_##COMPARETYPENAME: { \ 60 TEMPLATETYPE valueC = (TEMPLATETYPE)compare->data.COMPARENAME; \ 61 TEMPLATETYPE valueT = (TEMPLATETYPE)template->data.TEMPLATENAME; \ 62 /* XXX check the validiy of the type casting? */ \ 63 if (valueC != compare->data.COMPARENAME) { \ 64 return false; \ 65 } \ 66 /* does template specify a boolean operation in comment? */ \ 67 psMetadataItemCompareOp op = p_psMetadataItemCompareGetOp (template); \ 68 switch (op) { \ 69 case PS_MD_OP_EQ: \ 70 if ((template->type == PS_TYPE_F32) || (template->type == PS_TYPE_F64)) { \ 71 result = fabs(valueT - valueC) < EQ_TOL; \ 72 } else { \ 73 result = valueT == valueC; \ 74 } \ 75 break; \ 76 case PS_MD_OP_LT: \ 77 result = valueC < valueT; \ 78 break; \ 79 case PS_MD_OP_LE: \ 80 result = valueC <= valueT; \ 81 break; \ 82 case PS_MD_OP_GT: \ 83 result = valueC > valueT; \ 84 break; \ 85 case PS_MD_OP_GE: \ 86 result = valueC >= valueT; \ 87 break; \ 88 case PS_MD_OP_NE: \ 89 result = valueC != valueT; \ 90 break; \ 91 default: \ 92 psAbort ("all cases should have been handled..."); \ 114 #define EQ_TOL 1e-6 // Tolerance for equality 115 116 // Compare values directly 117 #define COMPARE_VALUES(TEMPLATE, COMPARE, FLOATINGPOINT) { \ 118 /* does template specify a boolean operation in comment? */ \ 119 compareOp op = getCompareOp(template); \ 120 switch (op) { \ 121 case COMPARE_OP_EQ: \ 122 if (FLOATINGPOINT) { \ 123 return fabs(TEMPLATE - COMPARE) < EQ_TOL; \ 124 } else { \ 125 return TEMPLATE == COMPARE; \ 126 } \ 127 case COMPARE_OP_LT: \ 128 return COMPARE < TEMPLATE; \ 129 case COMPARE_OP_LE: \ 130 return COMPARE <= TEMPLATE; \ 131 case COMPARE_OP_GT: \ 132 return COMPARE > TEMPLATE; \ 133 case COMPARE_OP_GE: \ 134 return COMPARE >= TEMPLATE; \ 135 case COMPARE_OP_NE: \ 136 return COMPARE != TEMPLATE; \ 137 default: \ 138 psAbort("all cases should have been handled..."); \ 139 } \ 140 } 141 142 #define COMPARE_NUMERICAL_CASE(TEMPLATENAME, TEMPLATETYPE, COMPARENAME, COMPARETYPENAME) \ 143 case PS_TYPE_##COMPARETYPENAME: { \ 144 TEMPLATETYPE valueC = (TEMPLATETYPE)compare->data.COMPARENAME; \ 145 TEMPLATETYPE valueT = (TEMPLATETYPE)template->data.TEMPLATENAME; \ 146 /* XXX check the validiy of the type casting? */ \ 147 if (valueC != compare->data.COMPARENAME) { \ 148 return false; \ 93 149 } \ 94 return (result); \ 95 } 96 97 #define TEMPLATE_CASE(TYPENAME, NAME, TYPE) \ 98 case PS_TYPE_##TYPENAME: \ 99 switch(compare->type) { \ 100 COMPARE_CASE(NAME, TYPE, B , BOOL); \ 101 COMPARE_CASE(NAME, TYPE, U8 , U8 ); \ 102 COMPARE_CASE(NAME, TYPE, U16, U16); \ 103 COMPARE_CASE(NAME, TYPE, U32, U32); \ 104 COMPARE_CASE(NAME, TYPE, S8 , S8 ); \ 105 COMPARE_CASE(NAME, TYPE, S16, S16); \ 106 COMPARE_CASE(NAME, TYPE, S32, S32); \ 107 COMPARE_CASE(NAME, TYPE, F32, F32); \ 108 COMPARE_CASE(NAME, TYPE, F64, F64); \ 109 default: \ 150 COMPARE_VALUES(valueT, valueC, (template->type == PS_TYPE_F32 || template->type == PS_TYPE_F64)); \ 151 psAbort("Should never reach here."); \ 152 } 153 154 // Compare a string template with an int 155 #define COMPARE_STRING_INT_CASE(TYPE, FORMAT) \ 156 case PS_TYPE_##TYPE: { \ 157 ps##TYPE valueT; \ 158 PARSE_STRING(status, valueT, template->data.V, FORMAT); \ 159 if (!status) { \ 160 return false; \ 161 } \ 162 ps##TYPE valueC = compare->data.TYPE; \ 163 COMPARE_VALUES(valueT, valueC, false); \ 164 } 165 166 167 // Compare a boolean template with a string 168 #define COMPARE_BOOL_STRING(TYPE, FORMAT) { \ 169 bool valueC; \ 170 if (!parseBool(&valueC, compare->data.V)) { \ 171 return false; \ 172 } \ 173 return (template->data.B == valueC); \ 174 } 175 176 // Compare an integer template with a string 177 #define COMPARE_INT_STRING(TYPE, FORMAT) { \ 178 ps##TYPE valueC; \ 179 PARSE_STRING(status, valueC, compare->data.V, FORMAT); \ 180 if (!status) { \ 181 return false; \ 182 } \ 183 ps##TYPE valueT = template->data.TYPE; \ 184 COMPARE_VALUES(valueT, valueC, false); \ 185 } 186 187 // Compare a float template with a string 188 #define COMPARE_FLOAT_STRING(TYPE, FORMAT) { \ 189 ps##TYPE valueC; \ 190 PARSE_STRING(status, valueC, compare->data.V, FORMAT); \ 191 if (!status) { \ 192 double sexValue; /* Sexagesimal value; just in case the type is not F32 */ \ 193 if (parseSexagesimal(&sexValue, compare->data.V)) { \ 194 valueC = sexValue; \ 195 } else { \ 196 return false; \ 197 } \ 198 } \ 199 ps##TYPE valueT = template->data.TYPE; \ 200 COMPARE_VALUES(valueT, valueC, true); \ 201 } 202 203 #define TEMPLATE_CASE(TYPENAME, NAME, TYPE, COMPARESTRING, FORMAT) \ 204 case PS_TYPE_##TYPENAME: \ 205 switch(compare->type) { \ 206 COMPARE_NUMERICAL_CASE(NAME, TYPE, B , BOOL); \ 207 COMPARE_NUMERICAL_CASE(NAME, TYPE, U8 , U8 ); \ 208 COMPARE_NUMERICAL_CASE(NAME, TYPE, U16, U16); \ 209 COMPARE_NUMERICAL_CASE(NAME, TYPE, U32, U32); \ 210 COMPARE_NUMERICAL_CASE(NAME, TYPE, U64, U64); \ 211 COMPARE_NUMERICAL_CASE(NAME, TYPE, S8 , S8 ); \ 212 COMPARE_NUMERICAL_CASE(NAME, TYPE, S16, S16); \ 213 COMPARE_NUMERICAL_CASE(NAME, TYPE, S32, S32); \ 214 COMPARE_NUMERICAL_CASE(NAME, TYPE, F32, F32); \ 215 COMPARE_NUMERICAL_CASE(NAME, TYPE, F64, F64); \ 216 case PS_DATA_STRING: { \ 217 if (!template->data.V) { \ 218 return false; \ 219 } \ 220 COMPARESTRING(TYPENAME, FORMAT); \ 221 } \ 222 default: \ 110 223 psError(PS_ERR_BAD_PARAMETER_TYPE, true, "Don't know how to compare types %x and %x\n", \ 111 compare->type, template->type); \112 return false; \224 compare->type, template->type); \ 225 return false; \ 113 226 } 114 227 … … 117 230 const psMetadataItem *template) // The template 118 231 { 119 120 232 // First order checks: 121 233 … … 130 242 } 131 243 132 bool result = false;133 134 244 switch (template->type) { 135 TEMPLATE_CASE(BOOL, B, bool) ; 136 TEMPLATE_CASE(U8, U8, psU8 ); 137 TEMPLATE_CASE(U16, U16, psU16); 138 TEMPLATE_CASE(U32, U32, psU32); 139 TEMPLATE_CASE(S8, S8, psS8 ); 140 TEMPLATE_CASE(S16, S16, psS16); 141 TEMPLATE_CASE(S32, S32, psS32); 142 TEMPLATE_CASE(F32, F32, psF32); 143 TEMPLATE_CASE(F64, F64, psF64); 144 145 case PS_DATA_STRING: 146 147 // for MULTI, try each one & succeed if any match (valid = true is default state) 148 if (compare->type == PS_DATA_METADATA_MULTI) { 149 for (int j = 0; j < compare->data.list->n; j++) { 150 psMetadataItem *entry = psListGet (compare->data.list, j); 151 if (!entry) continue; 152 if (entry->type != PS_DATA_STRING) { 153 continue; 154 } 155 if (template->data.V && !entry->data.V) { // expecting valid data, found NULL 156 continue; 157 } 158 if (!template->data.V && entry->data.V) { // expecting NULL, found valid data 159 continue; 160 } 161 // XXX should we return true for both NULL? 162 if (!template->data.V && !entry->data.V) { // expecting NULL, found NULL 163 return true; 164 } 165 if (!strcasecmp(entry->data.V, template->data.V)) { 166 return true; 167 } 168 // XXX this is a hack : also compare with the comment field (for HISTORY, COMMENT) 169 if (!strcasecmp(entry->comment, template->data.V)) { 170 return true; 171 } 245 TEMPLATE_CASE(BOOL, B, bool, COMPARE_BOOL_STRING, ""); 246 TEMPLATE_CASE(U8, U8, psU8, COMPARE_INT_STRING, SCNu8); 247 TEMPLATE_CASE(U16, U16, psU16, COMPARE_INT_STRING, SCNu16); 248 TEMPLATE_CASE(U32, U32, psU32, COMPARE_INT_STRING, SCNu32); 249 TEMPLATE_CASE(U64, U64, psU64, COMPARE_INT_STRING, SCNu64); 250 TEMPLATE_CASE(S8, S8, psS8, COMPARE_INT_STRING, SCNd8); 251 TEMPLATE_CASE(S16, S16, psS16, COMPARE_INT_STRING, SCNd16); 252 TEMPLATE_CASE(S32, S32, psS32, COMPARE_INT_STRING, SCNd32); 253 TEMPLATE_CASE(S64, S64, psS64, COMPARE_INT_STRING, SCNd64); 254 TEMPLATE_CASE(F32, F32, psF32, COMPARE_FLOAT_STRING, "f"); 255 TEMPLATE_CASE(F64, F64, psF64, COMPARE_FLOAT_STRING, "lf"); 256 case PS_DATA_STRING: { 257 switch (compare->type) { 258 case PS_DATA_STRING: { 259 psTrace("psLib.types", 10, "Comparing '%s' with '%s'\n", 260 compare->data.str, template->data.str); 261 if ((!compare->data.V && template->data.V) || (!template->data.V && compare->data.V)) { 262 return false; 263 } 264 return (strcasecmp(compare->data.V, template->data.V) == 0) ? true : false; 172 265 } 173 return false; 174 } 175 176 // any other (non-MULTI) is a mis-match 177 if (compare->type != PS_DATA_STRING) { 178 return false; 179 } 180 psTrace("psLib.types", 10, "Comparing '%s' with '%s'\n", compare->data.str, template->data.str); 181 if ((!compare->data.V && template->data.V) || (!template->data.V && compare->data.V)) { 182 return false; 183 } 184 return (strcasecmp(compare->data.V, template->data.V) == 0) ? true : false; 185 186 default: 266 case PS_TYPE_BOOL: { 267 bool templateValue; 268 if (!parseBool(&templateValue, template->data.V)) { 269 return false; 270 } 271 return templateValue == compare->data.B; 272 } 273 COMPARE_STRING_INT_CASE(U8, SCNu8); 274 COMPARE_STRING_INT_CASE(U16, SCNu16); 275 COMPARE_STRING_INT_CASE(U32, SCNu32); 276 COMPARE_STRING_INT_CASE(U64, SCNu64); 277 COMPARE_STRING_INT_CASE(S8, SCNd8); 278 COMPARE_STRING_INT_CASE(S16, SCNd16); 279 COMPARE_STRING_INT_CASE(S32, SCNd32); 280 COMPARE_STRING_INT_CASE(S64, SCNd64); 281 case PS_TYPE_F32: 282 case PS_TYPE_F64: { 283 // Check for sexagesimal formating 284 if (!template->data.V) { 285 return false; 286 } 287 double templateValue; // Value of template 288 // Attempt to read the string first as a plain floating-point value, then as sexagesimal 289 PARSE_STRING(status, templateValue, template->data.V, "lf"); 290 if (!status && !parseSexagesimal(&templateValue, template->data.V)) { 291 return false; 292 } 293 double compareValue = compare->type == PS_TYPE_F32 ? compare->data.F32 : compare->data.F64; 294 COMPARE_VALUES(templateValue, compareValue, true); 295 } 296 case PS_DATA_METADATA_MULTI: { 297 // for MULTI, try each one & succeed if any match (valid = true is default state) 298 for (int j = 0; j < compare->data.list->n; j++) { 299 psMetadataItem *entry = psListGet(compare->data.list, j); 300 if (!entry) { 301 continue; 302 } 303 if (entry->type != PS_DATA_STRING) { 304 continue; 305 } 306 if (template->data.V && !entry->data.V) { // expecting valid data, found NULL 307 continue; 308 } 309 if (!template->data.V && entry->data.V) { // expecting NULL, found valid data 310 continue; 311 } 312 // XXX should we return true for both NULL? 313 if (!template->data.V && !entry->data.V) { // expecting NULL, found NULL 314 return true; 315 } 316 if (!strcasecmp(entry->data.V, template->data.V)) { 317 return true; 318 } 319 // XXX this is a hack : also compare with the comment field (for HISTORY, COMMENT) 320 if (!strcasecmp(entry->comment, template->data.V)) { 321 return true; 322 } 323 } 324 return false; 325 } 326 default: 327 // any other type is a mis-match against a string 328 return false; 329 } 330 } 331 default: 187 332 // Simply don't know how to compare more complex types. 188 333 psError(PS_ERR_BAD_PARAMETER_TYPE, true, "Don't know how to compare types %x and %x\n",
Note:
See TracChangeset
for help on using the changeset viewer.
