| 1 | A short introduction to scconf as an API and a file format |
|---|
| 2 | ========================================================== |
|---|
| 3 | written by Jamie Honan <jhonan@optusnet.com.au> |
|---|
| 4 | |
|---|
| 5 | The scconf system is a small system library for handling |
|---|
| 6 | scconf files. Why should anyone care about scconf format? |
|---|
| 7 | It is a handy format for short pieces of structured data. |
|---|
| 8 | |
|---|
| 9 | Handy because: |
|---|
| 10 | - it is readable, which makes support easy |
|---|
| 11 | - it is easy to parse and write |
|---|
| 12 | - it is extensible, you can add fields without breaking things |
|---|
| 13 | |
|---|
| 14 | It isn't |
|---|
| 15 | - XML, so it doesn't need xml parsing |
|---|
| 16 | - suitable for large amounts of data, like a database or text files |
|---|
| 17 | |
|---|
| 18 | It doesn't have |
|---|
| 19 | - anything else but data. No locking, no threads etc. |
|---|
| 20 | |
|---|
| 21 | It has heirarchical data blocks, it has lists. |
|---|
| 22 | |
|---|
| 23 | Similar, but different: |
|---|
| 24 | - .ini files. scconf is block structured, has lists and arrays |
|---|
| 25 | - xml. xml is more complete, but requires a lot of overhead |
|---|
| 26 | - sexp. sexp resembles lisp with it's use of parenthesis. sexp |
|---|
| 27 | has modes for binary. scconf really doesn't have binary |
|---|
| 28 | - yaml. yaml is larger |
|---|
| 29 | |
|---|
| 30 | What does it look like? |
|---|
| 31 | ======================= |
|---|
| 32 | |
|---|
| 33 | Like this: |
|---|
| 34 | |
|---|
| 35 | transport_stream { |
|---|
| 36 | id = 0x0009; |
|---|
| 37 | original_network_id = 0x1000; |
|---|
| 38 | sat_tuning_info { |
|---|
| 39 | frequency = 12278000; |
|---|
| 40 | symbol_rate = 30000000; |
|---|
| 41 | polarization = 0; |
|---|
| 42 | } |
|---|
| 43 | service { |
|---|
| 44 | id = 0x0064; |
|---|
| 45 | pmt_pid = 0x0101; |
|---|
| 46 | type = 144; |
|---|
| 47 | name = "aGuide"; |
|---|
| 48 | provider_name = "A"; |
|---|
| 49 | } |
|---|
| 50 | service { |
|---|
| 51 | id = 0x238D; |
|---|
| 52 | pmt_pid = 0x0623; |
|---|
| 53 | type = 144; |
|---|
| 54 | name = "aCar"; |
|---|
| 55 | provider_name = "A"; |
|---|
| 56 | } |
|---|
| 57 | } |
|---|
| 58 | |
|---|
| 59 | Why doesn't it have X, why don't you use XML? |
|---|
| 60 | ============================================= |
|---|
| 61 | |
|---|
| 62 | Maybe it should. Maybe XML is the answer. Maybe a database |
|---|
| 63 | is more appropriate. It's all a trade-off. You choose. |
|---|
| 64 | |
|---|
| 65 | |
|---|
| 66 | API |
|---|
| 67 | === |
|---|
| 68 | |
|---|
| 69 | There are four useful structures. scconf_block, scconf_list, |
|---|
| 70 | scconf_item, and a scconf_context. |
|---|
| 71 | |
|---|
| 72 | A context is similar to a file, except in memory. Within |
|---|
| 73 | a context there is a root block. Within each block |
|---|
| 74 | there are one or more items. Items can be sub-blocks, lists, or |
|---|
| 75 | comments. Every item can have a name, or key. |
|---|
| 76 | |
|---|
| 77 | A list can have one or more values; boolean, integer or string. |
|---|
| 78 | |
|---|
| 79 | A context contains a root block, which contains one or more blocks. |
|---|
| 80 | |
|---|
| 81 | A block is : |
|---|
| 82 | |
|---|
| 83 | key [[,] name [[,] name ... ] ] { |
|---|
| 84 | block_contents |
|---|
| 85 | } |
|---|
| 86 | |
|---|
| 87 | block_contents is one or more block_items |
|---|
| 88 | |
|---|
| 89 | block_items is one of |
|---|
| 90 | |
|---|
| 91 | # comment string \n |
|---|
| 92 | or |
|---|
| 93 | key [[,] name [[,] name ... ] ] = value [[,] value ... ]]; |
|---|
| 94 | or |
|---|
| 95 | block |
|---|
| 96 | |
|---|
| 97 | Initialising and file handling |
|---|
| 98 | ============================== |
|---|
| 99 | |
|---|
| 100 | Allocate scconf_context |
|---|
| 101 | The filename can be NULL. The file is not read at this point, |
|---|
| 102 | but in the function scconf_parse. |
|---|
| 103 | |
|---|
| 104 | scconf_context *scconf_new(const char *filename); |
|---|
| 105 | |
|---|
| 106 | |
|---|
| 107 | Free scconf_context |
|---|
| 108 | |
|---|
| 109 | void scconf_free(scconf_context * config); |
|---|
| 110 | |
|---|
| 111 | |
|---|
| 112 | Parse configuration |
|---|
| 113 | Returns 1 = ok, 0 = error, -1 = error opening config file |
|---|
| 114 | |
|---|
| 115 | int scconf_parse(scconf_context * config); |
|---|
| 116 | |
|---|
| 117 | |
|---|
| 118 | Write config to a file |
|---|
| 119 | If the filename is NULL, use the config->filename |
|---|
| 120 | Returns 0 = ok, else = errno |
|---|
| 121 | |
|---|
| 122 | int scconf_write(scconf_context * config, const char *filename); |
|---|
| 123 | |
|---|
| 124 | |
|---|
| 125 | Finding items and blocks |
|---|
| 126 | ======================== |
|---|
| 127 | |
|---|
| 128 | Find a block by key |
|---|
| 129 | If the block is NULL, the root block is used |
|---|
| 130 | |
|---|
| 131 | const scconf_block *scconf_find_block(const scconf_context * config, |
|---|
| 132 | const scconf_block * block, const char *item_name); |
|---|
| 133 | |
|---|
| 134 | This finds a block in the given context. This function doesn't descend |
|---|
| 135 | the heirarchy, it only finds blocks in the top level of either |
|---|
| 136 | the context (the root block) or of the block given in the block |
|---|
| 137 | paramter (if not NULL). |
|---|
| 138 | |
|---|
| 139 | The block pointer returned points to data held by the context, hence |
|---|
| 140 | the const qualifier. |
|---|
| 141 | |
|---|
| 142 | |
|---|
| 143 | Find blocks by key and possibly name |
|---|
| 144 | If the block is NULL, the root block is used |
|---|
| 145 | The key can be used to specify what the blocks first name should be |
|---|
| 146 | |
|---|
| 147 | scconf_block **scconf_find_blocks(const scconf_context * config, |
|---|
| 148 | const scconf_block * block, const char *item_name, const char *key); |
|---|
| 149 | |
|---|
| 150 | This function is similar to scconf_find_block above, except that an array |
|---|
| 151 | of pointers to matched blocks is returned. Each pointer points |
|---|
| 152 | to data held by the context. The last entry in the returned table |
|---|
| 153 | is the null pointer. |
|---|
| 154 | |
|---|
| 155 | The table should be freed after use, but the individual pointers to blocks |
|---|
| 156 | point to data held by the context. |
|---|
| 157 | |
|---|
| 158 | The key values for blocks is matched. If name is not NULL, the block |
|---|
| 159 | name must also match. |
|---|
| 160 | |
|---|
| 161 | |
|---|
| 162 | Get a list of values for option |
|---|
| 163 | |
|---|
| 164 | const scconf_list *scconf_find_list(const scconf_block * block, const char *option); |
|---|
| 165 | |
|---|
| 166 | Find an item that has a value (i.e. is not a block nor a comment), and |
|---|
| 167 | return the values for that item as a list. |
|---|
| 168 | |
|---|
| 169 | The list is held in memory owned by the context. |
|---|
| 170 | |
|---|
| 171 | |
|---|
| 172 | Return the first string of the option |
|---|
| 173 | If no option found, return def |
|---|
| 174 | |
|---|
| 175 | const char *scconf_get_str(const scconf_block * block, const char *option, const char *def); |
|---|
| 176 | |
|---|
| 177 | This is similar to scconf_find_list, but instead of returning the whole |
|---|
| 178 | list, just return the first value, as a string. If this is not possible, |
|---|
| 179 | return the default value. |
|---|
| 180 | |
|---|
| 181 | Again the value returned is either a pointer the default value or to |
|---|
| 182 | memory held by the context. |
|---|
| 183 | |
|---|
| 184 | |
|---|
| 185 | Return the first value of the option as integer |
|---|
| 186 | If no option found, return def |
|---|
| 187 | |
|---|
| 188 | int scconf_get_int(const scconf_block * block, const char *option, int def); |
|---|
| 189 | |
|---|
| 190 | This is similar to scconf_get_str, but an integer value is returned. |
|---|
| 191 | |
|---|
| 192 | |
|---|
| 193 | Return the first value of the option as boolean |
|---|
| 194 | If no option found, return def |
|---|
| 195 | |
|---|
| 196 | int scconf_get_bool(const scconf_block * block, const char *option, int def); |
|---|
| 197 | |
|---|
| 198 | This completes the types that can be returned by a find. |
|---|
| 199 | |
|---|
| 200 | |
|---|
| 201 | For parsing blocks and items |
|---|
| 202 | ============================ |
|---|
| 203 | |
|---|
| 204 | A table of scconf_entry values is used, terminated by a NULL name value. |
|---|
| 205 | |
|---|
| 206 | This table is passed to the routine scconf_parse_entries. This |
|---|
| 207 | function walks the current context or block, and adds the data |
|---|
| 208 | to the scconf_entry table entries. |
|---|
| 209 | Sub-blocks can be walked, using SCCONF_BLOCK, and callbacks can be issued |
|---|
| 210 | using SCCONF_CALLBACK. |
|---|
| 211 | |
|---|
| 212 | This is a handy method for accessing scconf data from within a program. |
|---|
| 213 | |
|---|
| 214 | typedef struct _scconf_entry { |
|---|
| 215 | const char *name; |
|---|
| 216 | * Look for blocks with this key, or check if this |
|---|
| 217 | * block has an item with this key. Run the block |
|---|
| 218 | * or blocks found against the rest of this entry |
|---|
| 219 | * Stop after the first one, unless |
|---|
| 220 | * SCCONF_ALL_BLOCKS is set in flags |
|---|
| 221 | unsigned int type; |
|---|
| 222 | * SCCONF_CALLBACK |
|---|
| 223 | * parm contains a function ptr of type |
|---|
| 224 | * int (*callback)(scconf_context* context, |
|---|
| 225 | * scconf_block* block, |
|---|
| 226 | * scconf_entry* entry, |
|---|
| 227 | * int depth); |
|---|
| 228 | * run the callback with the block found |
|---|
| 229 | * |
|---|
| 230 | * SCCONF_BLOCK |
|---|
| 231 | * param contains a pointer to another entry table |
|---|
| 232 | * use the found block against every entry |
|---|
| 233 | * in the pointed entry table |
|---|
| 234 | * |
|---|
| 235 | * SCCONF_LIST |
|---|
| 236 | * SCCONF_BOOLEAN |
|---|
| 237 | * SCCONF_INTEGER |
|---|
| 238 | * SCCONF_STRING |
|---|
| 239 | * find the entry with the key given in name in |
|---|
| 240 | * the found block. Return the value found |
|---|
| 241 | * to parm as follows: |
|---|
| 242 | * SCCONF_INTEGER: |
|---|
| 243 | * if parm not NULL, then |
|---|
| 244 | * points to integer location to put |
|---|
| 245 | * the value |
|---|
| 246 | * SCCONF_BOOLEAN: |
|---|
| 247 | * if parm not NULL, then |
|---|
| 248 | * points to integer location to put |
|---|
| 249 | * the value |
|---|
| 250 | * SCCONF_STRING: |
|---|
| 251 | * if parm not NULL, then |
|---|
| 252 | * if flag bit SCCONF_ALLOC not set |
|---|
| 253 | * then parm points to a buffer |
|---|
| 254 | * else |
|---|
| 255 | * parm points to a pointer where |
|---|
| 256 | * the pointer to an allocated |
|---|
| 257 | * buffer should be stored. |
|---|
| 258 | * if arg is not NULL, points |
|---|
| 259 | * to a location where the buffer |
|---|
| 260 | * length (size_t) is to be stored |
|---|
| 261 | * SCCONF_LIST: |
|---|
| 262 | * if parm not NULL, then |
|---|
| 263 | * if flag bit SCCONF_ALLOC not set |
|---|
| 264 | * then parm points to a location |
|---|
| 265 | * where a pointer to the list |
|---|
| 266 | * can be stored |
|---|
| 267 | * else |
|---|
| 268 | * then parm points to a location |
|---|
| 269 | * where a pointer to a copy of list |
|---|
| 270 | * can be stored |
|---|
| 271 | * |
|---|
| 272 | * |
|---|
| 273 | unsigned int flags; |
|---|
| 274 | * SCCONF_PRESENT |
|---|
| 275 | * This bit is or'ed in when found |
|---|
| 276 | * SCCONF_MANDATORY |
|---|
| 277 | * If not found, this is a fault |
|---|
| 278 | * SCCONF_ALLOC |
|---|
| 279 | * C.f. type above |
|---|
| 280 | * SCCONF_ALL_BLOCKS |
|---|
| 281 | * C.f. name above |
|---|
| 282 | * SCCONF_VERBOSE |
|---|
| 283 | * For debugging |
|---|
| 284 | void *parm; |
|---|
| 285 | void *arg; |
|---|
| 286 | } scconf_entry; |
|---|
| 287 | |
|---|
| 288 | |
|---|
| 289 | For adding blocks and items |
|---|
| 290 | =========================== |
|---|
| 291 | |
|---|
| 292 | A table of scconf_entry values is used, terminated by a NULL name value. |
|---|
| 293 | |
|---|
| 294 | This table is passed to the routine scconf_write_entries. This |
|---|
| 295 | function adds the scconf_entry table entries to the current block. |
|---|
| 296 | Sub-blocks can be added, and callbacks can be issued. |
|---|
| 297 | |
|---|
| 298 | This is a handy method for adding scconf data from within a program. |
|---|
| 299 | |
|---|
| 300 | typedef struct _scconf_entry { |
|---|
| 301 | const char *name; |
|---|
| 302 | * key value for blocks and items * |
|---|
| 303 | unsigned int type; |
|---|
| 304 | * SCCONF_CALLBACK |
|---|
| 305 | * parm contains a function ptr of type |
|---|
| 306 | * int (*callback)(scconf_context* context, |
|---|
| 307 | * scconf_block* block, |
|---|
| 308 | * scconf_entry* entry, |
|---|
| 309 | * int depth); |
|---|
| 310 | * |
|---|
| 311 | * SCCONF_BLOCK |
|---|
| 312 | * param contains a pointer to another entry table |
|---|
| 313 | * the entry table is added as a block to the |
|---|
| 314 | * current block, with name as the key, and |
|---|
| 315 | * arg is a list of names |
|---|
| 316 | * |
|---|
| 317 | * SCCONF_LIST |
|---|
| 318 | * SCCONF_BOOLEAN |
|---|
| 319 | * SCCONF_INTEGER |
|---|
| 320 | * SCCONF_STRING |
|---|
| 321 | * these add key=value pairs to the current |
|---|
| 322 | * block. The value is in parm. |
|---|
| 323 | * |
|---|
| 324 | unsigned int flags; |
|---|
| 325 | * SCCONF_PRESENT |
|---|
| 326 | * This bit is or'ed in when item added |
|---|
| 327 | void *parm; |
|---|
| 328 | void *arg; |
|---|
| 329 | } scconf_entry; |
|---|