1 /*
2 * Copyright 2020 University Corporation for Atmospheric Research
3 *
4 * This file is part of the UDUNITS-2 package. See the file COPYRIGHT
5 * in the top-level source-directory of the package for copying and
6 * redistribution conditions.
7 */
8 /*
9 * Identifier-to-unit map.
10 */
11
12 /*LINTLIBRARY*/
13
14 #include "config.h"
15
16 #include "udunits2.h"
17 #include "unitAndId.h"
18 #include "systemMap.h"
19
20 #include <assert.h>
21 #ifdef _MSC_VER
22 #include "tsearch.h"
23 #else
24 #include <search.h>
25 #endif
26
27 #include <stdlib.h>
28
29 #include <string.h>
30 #ifndef _MSC_VER
31 #include <strings.h>
32 #endif
33
34 typedef struct {
35 int (*compare)(const void*, const void*);
36 void* tree;
37 } IdToUnitMap;
38
39 static SystemMap* systemToNameToUnit;
40 static SystemMap* systemToSymbolToUnit;
41
42
43 static int
sensitiveCompare(const void * const node1,const void * const node2)44 sensitiveCompare(
45 const void* const node1,
46 const void* const node2)
47 {
48 return strcmp(((const UnitAndId*)node1)->id,
49 ((const UnitAndId*)node2)->id);
50 }
51
52
53 static int
insensitiveCompare(const void * const node1,const void * const node2)54 insensitiveCompare(
55 const void* const node1,
56 const void* const node2)
57 {
58 return strcasecmp(((const UnitAndId*)node1)->id,
59 ((const UnitAndId*)node2)->id);
60 }
61
62
63 static IdToUnitMap*
itumNew(int (* compare)(const void *,const void *))64 itumNew(
65 int (*compare)(const void*, const void*))
66 {
67 IdToUnitMap* map = (IdToUnitMap*)malloc(sizeof(IdToUnitMap));
68
69 if (map != NULL) {
70 map->tree = NULL;
71 map->compare = compare;
72 }
73
74 return map;
75 }
76
77
78 /*
79 * Frees an identifier-to-unit map. All entries are freed.
80 *
81 * Arguments:
82 * map Pointer to the identifier-to-unit map.
83 * Returns:
84 */
85 static void
itumFree(IdToUnitMap * map)86 itumFree(
87 IdToUnitMap* map)
88 {
89 if (map != NULL) {
90 while (map->tree != NULL) {
91 UnitAndId* uai = *(UnitAndId**)map->tree;
92
93 (void)tdelete(uai, &map->tree, map->compare);
94 uaiFree(uai);
95 }
96
97 free(map);
98 } /* valid arguments */
99 }
100
101
102 /*
103 * Adds an entry to an identifier-to-unit map.
104 *
105 * Arguments:
106 * map The database.
107 * id The identifier. May be freed upon return.
108 * unit The unit. May be freed upon return.
109 * Returns:
110 * UT_OS Operating-system error. See "errno".
111 * UT_EXISTS "id" already maps to a different unit.
112 * UT_SUCCESS Success.
113 */
114 static ut_status
itumAdd(IdToUnitMap * map,const char * const id,const ut_unit * const unit)115 itumAdd(
116 IdToUnitMap* map,
117 const char* const id,
118 const ut_unit* const unit)
119 {
120 ut_status status;
121 UnitAndId* targetEntry;
122
123 assert(map != NULL);
124 assert(id != NULL);
125 assert(unit != NULL);
126
127 targetEntry = uaiNew(unit, id);
128
129 if (targetEntry == NULL) {
130 status = ut_get_status();
131 }
132 else {
133 UnitAndId** treeEntry = tsearch(targetEntry, &map->tree,
134 map->compare);
135
136 if (treeEntry == NULL) {
137 uaiFree(targetEntry);
138 status = UT_OS;
139 }
140 else {
141 if (ut_compare((*treeEntry)->unit, unit) == 0) {
142 status = UT_SUCCESS;
143 }
144 else {
145 status = UT_EXISTS;
146 ut_set_status(status);
147 ut_handle_error_message(
148 "\"%s\" already maps to existing but different unit", id);
149 }
150
151 if (targetEntry != *treeEntry)
152 uaiFree(targetEntry);
153 } /* found entry */
154 } /* "targetEntry" allocated */
155
156 return status;
157 }
158
159
160 /*
161 * Removes an entry to an identifier-to-unit map.
162 *
163 * Arguments:
164 * map The database.
165 * id The identifier. May be freed upon return.
166 * Returns:
167 * UT_SUCCESS Success.
168 */
169 static ut_status
itumRemove(IdToUnitMap * map,const char * const id)170 itumRemove(
171 IdToUnitMap* map,
172 const char* const id)
173 {
174 UnitAndId targetEntry;
175 UnitAndId** treeEntry;
176
177 assert(map != NULL);
178 assert(id != NULL);
179
180 targetEntry.id = (char*)id;
181 treeEntry = tfind(&targetEntry, &map->tree, map->compare);
182
183 if (treeEntry != NULL) {
184 UnitAndId* uai = *treeEntry;
185
186 (void)tdelete(uai, &map->tree, map->compare);
187 uaiFree(uai);
188 }
189
190 return UT_SUCCESS;
191 }
192
193
194 /*
195 * Finds the entry in an identifier-to-unit map that corresponds to an
196 * identifer.
197 *
198 * Arguments:
199 * map The identifier-to-unit map.
200 * id The identifier to be used as the key in the search.
201 * Returns:
202 * NULL Failure. "map" doesn't contain an entry that corresponds
203 * to "id".
204 * else Pointer to the entry corresponding to "id".
205 */
206 static const UnitAndId*
itumFind(IdToUnitMap * map,const char * const id)207 itumFind(
208 IdToUnitMap* map,
209 const char* const id)
210 {
211 UnitAndId* entry = NULL; /* failure */
212 UnitAndId targetEntry;
213 UnitAndId** treeEntry;
214
215 assert(map != NULL);
216 assert(id != NULL);
217
218 targetEntry.id = (char*)id;
219 treeEntry = tfind(&targetEntry, &map->tree, map->compare);
220
221 if (treeEntry != NULL)
222 entry = *treeEntry;
223
224 return entry;
225 }
226
227
228 /*
229 * Adds to a particular unit-system a mapping from an identifier to a unit.
230 *
231 * Arguments:
232 * systemMap Address of the pointer to the system-map.
233 * id Pointer to the identifier. May be freed upon return.
234 * unit Pointer to the unit. May be freed upon return.
235 * compare Pointer to comparison function for unit-identifiers.
236 * Returns:
237 * UT_BAD_ARG "id" is NULL or "unit" is NULL.
238 * UT_OS Operating-sytem failure. See "errno".
239 * UT_SUCCESS Success.
240 */
241 static ut_status
mapIdToUnit(SystemMap ** const systemMap,const char * const id,const ut_unit * const unit,int (* compare)(const void *,const void *))242 mapIdToUnit(
243 SystemMap** const systemMap,
244 const char* const id,
245 const ut_unit* const unit,
246 int (*compare)(const void*, const void*))
247 {
248 ut_status status = UT_SUCCESS;
249
250 if (id == NULL) {
251 status = UT_BAD_ARG;
252 }
253 else if (unit == NULL) {
254 status = UT_BAD_ARG;
255 }
256 else {
257 ut_system* system = ut_get_system(unit);
258
259 if (*systemMap == NULL) {
260 *systemMap = smNew();
261
262 if (*systemMap == NULL)
263 status = UT_OS;
264 }
265
266 if (*systemMap != NULL) {
267 IdToUnitMap** const idToUnit =
268 (IdToUnitMap**)smSearch(*systemMap, system);
269
270 if (idToUnit == NULL) {
271 status = UT_OS;
272 }
273 else {
274 if (*idToUnit == NULL) {
275 *idToUnit = itumNew(compare);
276
277 if (*idToUnit == NULL)
278 status = UT_OS;
279 }
280
281 if (*idToUnit != NULL)
282 status = itumAdd(*idToUnit, id, unit);
283 } /* have system-map entry */
284 } /* have system-map */
285 } /* valid arguments */
286
287 return status;
288 }
289
290
291 /*
292 * Removes the mapping from an identifier to a unit.
293 *
294 * Arguments:
295 * systemMap Address of the pointer to the system-map.
296 * id Pointer to the identifier. May be freed upon return.
297 * system Pointer to the unit-system associated with the mapping.
298 * Returns:
299 * UT_BAD_ARG "id" is NULL, "system" is NULL, or "compare" is NULL.
300 * UT_SUCCESS Success.
301 */
302 static ut_status
unmapId(SystemMap * const systemMap,const char * const id,ut_system * system)303 unmapId(
304 SystemMap* const systemMap,
305 const char* const id,
306 ut_system* system)
307 {
308 ut_status status;
309
310 if (systemMap == NULL || id == NULL || system == NULL) {
311 status = UT_BAD_ARG;
312 }
313 else {
314 IdToUnitMap** const idToUnit =
315 (IdToUnitMap**)smFind(systemMap, system);
316
317 status =
318 (idToUnit == NULL || *idToUnit == NULL)
319 ? UT_SUCCESS
320 : itumRemove(*idToUnit, id);
321 } /* valid arguments */
322
323 return status;
324 }
325
326
327 /*
328 * Adds a mapping from a name to a unit.
329 *
330 * Arguments:
331 * name Pointer to the name to be mapped to "unit". May be
332 * freed upon return.
333 * encoding The character encoding of "name".
334 * unit Pointer to the unit to be mapped-to by "name". May be
335 * freed upon return.
336 * Returns:
337 * UT_BAD_ARG "name" or "unit" is NULL.
338 * UT_OS Operating-system error. See "errno".
339 * UT_EXISTS "name" already maps to a different unit.
340 * UT_SUCCESS Success.
341 */
342 ut_status
ut_map_name_to_unit(const char * const name,const ut_encoding encoding,const ut_unit * const unit)343 ut_map_name_to_unit(
344 const char* const name,
345 const ut_encoding encoding,
346 const ut_unit* const unit)
347 {
348 ut_set_status(
349 mapIdToUnit(&systemToNameToUnit, name, unit, insensitiveCompare));
350
351 return ut_get_status();
352 }
353
354
355 /*
356 * Removes a mapping from a name to a unit. After this function,
357 * ut_get_unit_by_name(system,name) will no longer return a unit.
358 *
359 * Arguments:
360 * system The unit-system to which the unit belongs.
361 * name The name of the unit.
362 * encoding The character encoding of "name".
363 * Returns:
364 * UT_SUCCESS Success.
365 * UT_BAD_ARG "system" or "name" is NULL.
366 */
367 ut_status
ut_unmap_name_to_unit(ut_system * system,const char * const name,const ut_encoding encoding)368 ut_unmap_name_to_unit(
369 ut_system* system,
370 const char* const name,
371 const ut_encoding encoding)
372 {
373 ut_set_status(unmapId(systemToNameToUnit, name, system));
374
375 return ut_get_status();
376 }
377
378
379 /*
380 * Adds a mapping from a symbol to a unit.
381 *
382 * Arguments:
383 * symbol Pointer to the symbol to be mapped to "unit". May be
384 * freed upon return.
385 * encoding The character encoding of "symbol".
386 * unit Pointer to the unit to be mapped-to by "symbol". May
387 * be freed upon return.
388 * Returns:
389 * UT_BAD_ARG "symbol" or "unit" is NULL.
390 * UT_OS Operating-system error. See "errno".
391 * UT_EXISTS "symbol" already maps to a different unit.
392 * UT_SUCCESS Success.
393 */
394 ut_status
ut_map_symbol_to_unit(const char * const symbol,const ut_encoding encoding,const ut_unit * const unit)395 ut_map_symbol_to_unit(
396 const char* const symbol,
397 const ut_encoding encoding,
398 const ut_unit* const unit)
399 {
400 ut_set_status(
401 mapIdToUnit(&systemToSymbolToUnit, symbol, unit, sensitiveCompare));
402
403 return ut_get_status();
404 }
405
406
407 /*
408 * Removes a mapping from a symbol to a unit. After this function,
409 * ut_get_unit_by_symbol(system,symbol) will no longer return a unit.
410 *
411 * Arguments:
412 * system The unit-system to which the unit belongs.
413 * symbol The symbol of the unit.
414 * encoding The character encoding of "symbol".
415 * Returns:
416 * UT_SUCCESS Success.
417 * UT_BAD_ARG "system" or "symbol" is NULL.
418 */
419 ut_status
ut_unmap_symbol_to_unit(ut_system * system,const char * const symbol,const ut_encoding encoding)420 ut_unmap_symbol_to_unit(
421 ut_system* system,
422 const char* const symbol,
423 const ut_encoding encoding)
424 {
425 ut_set_status(unmapId(systemToSymbolToUnit, symbol, system));
426
427 return ut_get_status();
428 }
429
430
431 /*
432 * Returns the unit to which an identifier maps in a particular unit-system.
433 *
434 * Arguments:
435 * systemMap NULL or pointer to the system-map. If NULL, then
436 * NULL will be returned.
437 * system Pointer to the unit-system.
438 * id Pointer to the identifier.
439 * Returns:
440 * NULL Failure. "ut_get_status()" will be:
441 * UT_BAD_ARG "system" is NULL or "id" is NULL.
442 * else Pointer to the unit in "system" with the identifier "id".
443 * Should be passed to ut_free() when no longer needed.
444 */
445 static ut_unit*
getUnitById(const SystemMap * const systemMap,const ut_system * const system,const char * const id)446 getUnitById(
447 const SystemMap* const systemMap,
448 const ut_system* const system,
449 const char* const id)
450 {
451 ut_unit* unit = NULL; /* failure */
452
453 if (system == NULL) {
454 ut_set_status(UT_BAD_ARG);
455 ut_handle_error_message("getUnitById(): NULL unit-system argument");
456 }
457 else if (id == NULL) {
458 ut_set_status(UT_BAD_ARG);
459 ut_handle_error_message("getUnitById(): NULL identifier argument");
460 }
461 else if (systemMap != NULL) {
462 IdToUnitMap** const idToUnit =
463 (IdToUnitMap**)smFind(systemMap, system);
464
465 if (idToUnit != NULL) {
466 const UnitAndId* uai = itumFind(*idToUnit, id);
467
468 if (uai != NULL)
469 unit = ut_clone(uai->unit);
470 }
471 } /* valid arguments */
472
473 return unit;
474 }
475
476
477 /*
478 * Returns the unit with a given name from a unit-system. Name comparisons
479 * are case-insensitive.
480 *
481 * Arguments:
482 * system Pointer to the unit-system.
483 * name Pointer to the name of the unit to be returned.
484 * Returns:
485 * NULL Failure. "ut_get_status()" will be
486 * UT_SUCCESS "name" doesn't map to a unit of
487 * "system".
488 * UT_BAD_ARG "system" or "name" is NULL.
489 * else Pointer to the unit of the unit-system with the given name.
490 * The pointer should be passed to ut_free() when the unit is
491 * no longer needed.
492 */
493 ut_unit*
ut_get_unit_by_name(const ut_system * const system,const char * const name)494 ut_get_unit_by_name(
495 const ut_system* const system,
496 const char* const name)
497 {
498 ut_set_status(UT_SUCCESS);
499
500 return getUnitById(systemToNameToUnit, system, name);
501 }
502
503
504 /*
505 * Returns the unit with a given symbol from a unit-system. Symbol
506 * comparisons are case-sensitive.
507 *
508 * Arguments:
509 * system Pointer to the unit-system.
510 * symbol Pointer to the symbol associated with the unit to be
511 * returned.
512 * Returns:
513 * NULL Failure. "ut_get_status()" will be
514 * UT_SUCCESS "symbol" doesn't map to a unit of
515 * "system".
516 * UT_BAD_ARG "system" or "symbol" is NULL.
517 * else Pointer to the unit in the unit-system with the given symbol.
518 * The pointer should be passed to ut_free() when the unit is no
519 * longer needed.
520 */
521 ut_unit*
ut_get_unit_by_symbol(const ut_system * const system,const char * const symbol)522 ut_get_unit_by_symbol(
523 const ut_system* const system,
524 const char* const symbol)
525 {
526 ut_set_status(UT_SUCCESS);
527
528 return getUnitById(systemToSymbolToUnit, system, symbol);
529 }
530
531
532 /*
533 * Frees resources associated with a unit-system.
534 *
535 * Arguments:
536 * system Pointer to the unit-system to have its associated
537 * resources freed.
538 */
539 void
itumFreeSystem(ut_system * system)540 itumFreeSystem(
541 ut_system* system)
542 {
543 if (system != NULL) {
544 SystemMap* systemMaps[2];
545 int i;
546
547 systemMaps[0] = systemToNameToUnit;
548 systemMaps[1] = systemToSymbolToUnit;
549
550 for (i = 0; i < 2; i++) {
551 if (systemMaps[i] != NULL) {
552 IdToUnitMap** const idToUnit =
553 (IdToUnitMap**)smFind(systemMaps[i], system);
554
555 if (idToUnit != NULL)
556 itumFree(*idToUnit);
557
558 smRemove(systemMaps[i], system);
559 }
560 }
561 } /* valid arguments */
562 }
563