1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 
3 #pragma once
4 
5 #include <errno.h>
6 #include <stddef.h>
7 #include <stdio.h>
8 #include <sys/types.h>
9 
10 #include "macro.h"
11 #include "parse-util.h"
12 #include "string-util.h"
13 
14 ssize_t string_table_lookup(const char * const *table, size_t len, const char *key);
15 
16 /* For basic lookup tables with strictly enumerated entries */
17 #define _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type,scope)          \
18         scope const char *name##_to_string(type i) {                    \
19                 if (i < 0 || i >= (type) ELEMENTSOF(name##_table))      \
20                         return NULL;                                    \
21                 return name##_table[i];                                 \
22         }
23 
24 #define _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING(name,type,scope)        \
25         scope type name##_from_string(const char *s) {                  \
26                 return (type) string_table_lookup(name##_table, ELEMENTSOF(name##_table), s); \
27         }
28 
29 #define _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING_WITH_BOOLEAN(name,type,yes,scope) \
30         scope type name##_from_string(const char *s) {                  \
31                 if (!s)                                                 \
32                         return -EINVAL;                                 \
33                 int b = parse_boolean(s);                               \
34                 if (b == 0)                                             \
35                         return (type) 0;                                \
36                 if (b > 0)                                              \
37                         return yes;                                     \
38                 return (type) string_table_lookup(name##_table, ELEMENTSOF(name##_table), s); \
39         }
40 
41 #define _DEFINE_STRING_TABLE_LOOKUP_TO_STRING_FALLBACK(name,type,max,scope) \
42         scope int name##_to_string_alloc(type i, char **str) {          \
43                 char *s;                                                \
44                 if (i < 0 || i > max)                                   \
45                         return -ERANGE;                                 \
46                 if (i < (type) ELEMENTSOF(name##_table) && name##_table[i]) { \
47                         s = strdup(name##_table[i]);                    \
48                         if (!s)                                         \
49                                 return -ENOMEM;                         \
50                 } else {                                                \
51                         if (asprintf(&s, "%i", i) < 0)                  \
52                                 return -ENOMEM;                         \
53                 }                                                       \
54                 *str = s;                                               \
55                 return 0;                                               \
56         }
57 
58 #define _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING_FALLBACK(name,type,max,scope) \
59         scope type name##_from_string(const char *s) {                  \
60                 unsigned u = 0;                                         \
61                 type i;                                                 \
62                 if (!s)                                                 \
63                         return -EINVAL;                                 \
64                 i = (type) string_table_lookup(name##_table, ELEMENTSOF(name##_table), s); \
65                 if (i >= 0)                                             \
66                         return i;                                       \
67                 if (safe_atou(s, &u) < 0)                               \
68                         return -EINVAL;                                 \
69                 if (u > max)                                            \
70                         return -EINVAL;                                 \
71                 return (type) u;                                        \
72         }
73 
74 #define _DEFINE_STRING_TABLE_LOOKUP(name,type,scope)                    \
75         _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type,scope)          \
76         _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING(name,type,scope)
77 
78 #define _DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(name,type,yes,scope)   \
79         _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type,scope)          \
80         _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING_WITH_BOOLEAN(name,type,yes,scope)
81 
82 #define DEFINE_STRING_TABLE_LOOKUP(name,type) _DEFINE_STRING_TABLE_LOOKUP(name,type,)
83 #define DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type) _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type,)
84 #define DEFINE_STRING_TABLE_LOOKUP_FROM_STRING(name,type) _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING(name,type,)
85 #define DEFINE_PRIVATE_STRING_TABLE_LOOKUP(name,type) _DEFINE_STRING_TABLE_LOOKUP(name,type,static)
86 #define DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(name,type) _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type,static)
87 #define DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(name,type) _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING(name,type,static)
88 
89 #define DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(name,type,yes) _DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(name,type,yes,)
90 #define DEFINE_PRIVATE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(name,type,yes) _DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(name,type,yes,static)
91 #define DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING_WITH_BOOLEAN(name,type,yes) \
92         _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING_WITH_BOOLEAN(name,type,yes,static)
93 
94 /* For string conversions where numbers are also acceptable */
95 #define DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(name,type,max)         \
96         _DEFINE_STRING_TABLE_LOOKUP_TO_STRING_FALLBACK(name,type,max,)  \
97         _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING_FALLBACK(name,type,max,)
98 
99 #define DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING_FALLBACK(name,type,max) \
100         _DEFINE_STRING_TABLE_LOOKUP_TO_STRING_FALLBACK(name,type,max,static)
101 #define DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING_FALLBACK(name,type,max) \
102         _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING_FALLBACK(name,type,max,static)
103 
104 #define DUMP_STRING_TABLE(name,type,max)                                \
105         do {                                                            \
106                 flockfile(stdout);                                      \
107                 for (type _k = 0; _k < (max); _k++) {                   \
108                         const char *_t;                                 \
109                         _t = name##_to_string(_k);                      \
110                         if (!_t)                                        \
111                                 continue;                               \
112                         fputs_unlocked(_t, stdout);                     \
113                         fputc_unlocked('\n', stdout);                   \
114                 }                                                       \
115                 funlockfile(stdout);                                    \
116         } while (false)
117