Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • uspace/lib/sif/src/sif.c

    rca95ccd ree8d4d6  
    11/*
    2  * Copyright (c) 2024 Jiri Svoboda
     2 * Copyright (c) 2018 Jiri Svoboda
    33 * All rights reserved.
    44 *
     
    3333 *
    3434 * Structured Information Format (SIF) is an API that allows an application
    35  * to maintain data in a persistent repository in a format that is
    36  * structured (and hence extensible).
     35 * to maintain data in a persistent repository in a format that
     36 *
     37 *  - is structured (and hence extensible)
     38 *  - allows atomic (transactional) updates
     39 *  - allows efficient updates
    3740 *
    3841 * SIF is meant to be used as the basis for the storage backend used to
     
    4144 * similar to an XML document that contains just tags with attributes
    4245 * (but no text).
     46 *
     47 * SIF can thus naturally express ordered lists (unlike a relational database).
     48 * When contrasted to a (relational) database, SIF is much more primitive.
     49 *
     50 * In particular, SIF
     51 *
     52 *  - does not run on a separate server
     53 *  - des not handle concurrency
     54 *  - does not have a notion of types or data validation
     55 *  - does not understand relations
     56 *  - does not implement any kind of search/queries
     57 *  - does not deal with data sets large enough not to fit in primary memory
     58 *
     59 * any kind of structure data validation is left up to the application.
    4360 */
    4461
    4562#include <adt/list.h>
    4663#include <adt/odict.h>
    47 #include <ctype.h>
    4864#include <errno.h>
    49 #include <stdbool.h>
    5065#include <stdio.h>
    5166#include <stdlib.h>
     
    5570
    5671static errno_t sif_export_node(sif_node_t *, FILE *);
    57 static errno_t sif_import_node(sif_node_t *, FILE *, sif_node_t **, bool *);
     72static errno_t sif_import_node(sif_node_t *, FILE *, sif_node_t **);
    5873static sif_attr_t *sif_node_first_attr(sif_node_t *);
    5974static sif_attr_t *sif_node_next_attr(sif_attr_t *);
     
    6277static int sif_attr_cmp(void *, void *);
    6378
    64 /** Determine if character can start a name.
    65  *
    66  * @param c Character
    67  * @return @c true iff the character can start a name
    68  */
    69 static bool sif_is_name_start_char(char c)
    70 {
    71         return isalpha(c) || c == '_';
    72 }
    73 
    74 /** Determine if character can continue a name.
    75  *
    76  * @param c Character
    77  * @return @c true iff the character can continue a name
    78  */
    79 static bool sif_is_name_char(char c)
    80 {
    81         return isalnum(c) || c == '-' || c == '.';
    82 }
    83 
    8479/** Create new SIF node.
    8580 *
     
    177172}
    178173
    179 /** Create SIF document.
    180  *
    181  * @param rdoc Place to store pointer to new document.
     174/** Create and open a SIF repository.
     175 *
     176 * @param fname File name
     177 * @param rsess Place to store pointer to new session.
    182178 *
    183179 * @return EOK on success or error code
    184180 */
    185 errno_t sif_new(sif_doc_t **rdoc)
    186 {
    187         sif_doc_t *doc;
     181errno_t sif_create(const char *fname, sif_sess_t **rsess)
     182{
     183        sif_sess_t *sess;
    188184        sif_node_t *root = NULL;
     185        sif_trans_t *trans = NULL;
    189186        errno_t rc;
    190 
    191         doc = calloc(1, sizeof(sif_doc_t));
    192         if (doc == NULL)
    193                 return ENOMEM;
     187        FILE *f;
     188
     189        sess = calloc(1, sizeof(sif_sess_t));
     190        if (sess == NULL)
     191                return ENOMEM;
     192
     193        sess->fname = str_dup(fname);
     194        if (sess->fname == NULL) {
     195                rc = ENOMEM;
     196                goto error;
     197        }
    194198
    195199        root = sif_node_new(NULL);
     
    205209        }
    206210
    207         doc->root = root;
    208 
    209         *rdoc = doc;
     211        f = fopen(fname, "wx");
     212        if (f == NULL) {
     213                rc = EIO;
     214                goto error;
     215        }
     216
     217        sess->f = f;
     218        sess->root = root;
     219
     220        /* Run a dummy trasaction to marshall initial repo state to file */
     221        rc = sif_trans_begin(sess, &trans);
     222        if (rc != EOK)
     223                goto error;
     224
     225        rc = sif_trans_end(trans);
     226        if (rc != EOK)
     227                goto error;
     228
     229        *rsess = sess;
    210230        return EOK;
    211231error:
     232        if (trans != NULL)
     233                sif_trans_abort(trans);
    212234        sif_node_delete(root);
    213         free(doc);
     235        if (sess->fname != NULL)
     236                free(sess->fname);
     237        free(sess);
    214238        return rc;
    215239}
    216240
    217 /** Load SIF document.
     241/** Open an existing SIF repository.
    218242 *
    219243 * @param fname File name
    220  * @param rdoc Place to store pointer to new document.
     244 * @param rsess Place to store pointer to new session.
    221245 *
    222246 * @return EOK on success or error code
    223247 */
    224 errno_t sif_load(const char *fname, sif_doc_t **rdoc)
    225 {
    226         sif_doc_t *doc;
     248errno_t sif_open(const char *fname, sif_sess_t **rsess)
     249{
     250        sif_sess_t *sess;
    227251        sif_node_t *root = NULL;
    228252        errno_t rc;
    229         bool endtag;
    230         FILE *f = NULL;
    231 
    232         doc = calloc(1, sizeof(sif_doc_t));
    233         if (doc == NULL)
    234                 return ENOMEM;
    235 
    236         doc->fname = str_dup(fname);
    237         if (doc->fname == NULL) {
     253        FILE *f;
     254
     255        sess = calloc(1, sizeof(sif_sess_t));
     256        if (sess == NULL)
     257                return ENOMEM;
     258
     259        sess->fname = str_dup(fname);
     260        if (sess->fname == NULL) {
    238261                rc = ENOMEM;
    239262                goto error;
     
    246269        }
    247270
    248         rc = sif_import_node(NULL, f, &root, &endtag);
    249         if (rc != EOK || endtag == true)
     271        rc = sif_import_node(NULL, f, &root);
     272        if (rc != EOK)
    250273                goto error;
    251274
     
    255278        }
    256279
    257         fclose(f);
    258         doc->root = root;
    259         *rdoc = doc;
     280        sess->root = root;
     281
     282        sess->f = f;
     283        sess->root = root;
     284        *rsess = sess;
    260285        return EOK;
    261286error:
    262287        sif_node_delete(root);
    263         free(doc);
    264         if (f != NULL)
    265                 fclose(f);
     288        if (sess->fname != NULL)
     289                free(sess->fname);
     290        free(sess);
    266291        return rc;
    267292}
    268293
    269 /** Delete SIF document.
    270  *
    271  * @param doc SIF document
     294/** Close SIF session.
     295 *
     296 * @param sess SIF session
    272297 * @return EOK on success or error code
    273298 */
    274 void sif_delete(sif_doc_t *doc)
    275 {
    276         sif_node_delete(doc->root);
    277         free(doc);
     299errno_t sif_close(sif_sess_t *sess)
     300{
     301        sif_node_delete(sess->root);
     302
     303        if (fclose(sess->f) < 0) {
     304                free(sess);
     305                return EIO;
     306        }
     307
     308        if (sess->fname != NULL)
     309                free(sess->fname);
     310        free(sess);
     311        return EOK;
    278312}
    279313
    280314/** Return root node.
    281315 *
    282  * @param doc SIF document
    283  */
    284 sif_node_t *sif_get_root(sif_doc_t *doc)
    285 {
    286         return doc->root;
     316 * @param sess SIF session
     317 */
     318sif_node_t *sif_get_root(sif_sess_t *sess)
     319{
     320        return sess->root;
    287321}
    288322
     
    349383}
    350384
    351 /** Save SIF document to file.
    352  * *
    353  * @param doc SIF document
    354  * @param fname File name
     385/** Begin SIF transaction.
     386 *
     387 * @param trans Transaction
    355388 * @return EOK on success or error code
    356389 */
    357 errno_t sif_save(sif_doc_t *doc, const char *fname)
    358 {
    359         FILE *f = NULL;
     390errno_t sif_trans_begin(sif_sess_t *sess, sif_trans_t **rtrans)
     391{
     392        sif_trans_t *trans;
     393
     394        trans = calloc(1, sizeof(sif_trans_t));
     395        if (trans == NULL)
     396                return ENOMEM;
     397
     398        trans->sess = sess;
     399        *rtrans = trans;
     400        return EOK;
     401}
     402
     403/** Commit SIF transaction.
     404 *
     405 * Commit and free the transaction. If an error is returned, that means
     406 * the transaction has not been freed (and sif_trans_abort() must be used).
     407 *
     408 * @param trans Transaction
     409 * @return EOK on success or error code
     410 */
     411errno_t sif_trans_end(sif_trans_t *trans)
     412{
    360413        errno_t rc;
    361414
    362         f = fopen(fname, "w");
    363         if (f == NULL) {
    364                 rc = EIO;
    365                 goto error;
    366         }
    367 
    368         rc = sif_export_node(doc->root, f);
     415        (void) fclose(trans->sess->f);
     416
     417        trans->sess->f = fopen(trans->sess->fname, "w");
     418        if (trans->sess->f == NULL)
     419                return EIO;
     420
     421        rc = sif_export_node(trans->sess->root, trans->sess->f);
    369422        if (rc != EOK)
    370                 goto error;
    371 
    372         if (fflush(f) == EOF) {
    373                 rc = EIO;
    374                 goto error;
    375         }
    376 
    377         fclose(f);
    378         return EOK;
    379 error:
    380         if (f != NULL)
    381                 fclose(f);
    382         return rc;
     423                return rc;
     424
     425        if (fputc('\n', trans->sess->f) == EOF)
     426                return EIO;
     427
     428        if (fflush(trans->sess->f) == EOF)
     429                return EIO;
     430
     431        free(trans);
     432        return EOK;
     433}
     434
     435/** Abort SIF transaction.
     436 *
     437 * @param trans Transaction
     438 */
     439void sif_trans_abort(sif_trans_t *trans)
     440{
     441        free(trans);
    383442}
    384443
     
    388447 * @a parent.
    389448 *
     449 * @param trans Transaction
    390450 * @param parent Parent node
    391451 * @param ctype Child type
     
    394454 * @return EOK on success or ENOMEM if out of memory
    395455 */
    396 errno_t sif_node_prepend_child(sif_node_t *parent, const char *ctype,
    397     sif_node_t **rchild)
     456errno_t sif_node_prepend_child(sif_trans_t *trans, sif_node_t *parent,
     457    const char *ctype, sif_node_t **rchild)
    398458{
    399459        sif_node_t *child;
     
    419479 * Create a new child and append it at the end of children list of @a parent.
    420480 *
     481 * @param trans Transaction
    421482 * @param parent Parent node
    422483 * @param ctype Child type
     
    425486 * @return EOK on success or ENOMEM if out of memory
    426487 */
    427 errno_t sif_node_append_child(sif_node_t *parent, const char *ctype,
    428     sif_node_t **rchild)
     488errno_t sif_node_append_child(sif_trans_t *trans, sif_node_t *parent,
     489    const char *ctype, sif_node_t **rchild)
    429490{
    430491        sif_node_t *child;
     
    450511 * Create a new child and insert it before an existing child.
    451512 *
     513 * @param trans Transaction
    452514 * @param sibling Sibling before which to insert
    453515 * @param ctype Child type
     
    456518 * @return EOK on success or ENOMEM if out of memory
    457519 */
    458 errno_t sif_node_insert_before(sif_node_t *sibling, const char *ctype,
    459     sif_node_t **rchild)
     520errno_t sif_node_insert_before(sif_trans_t *trans, sif_node_t *sibling,
     521    const char *ctype, sif_node_t **rchild)
    460522{
    461523        sif_node_t *child;
     
    481543 * Create a new child and insert it after an existing child.
    482544 *
     545 * @param trans Transaction
    483546 * @param sibling Sibling after which to insert
    484547 * @param ctype Child type
     
    487550 * @return EOK on success or ENOMEM if out of memory
    488551 */
    489 errno_t sif_node_insert_after(sif_node_t *sibling, const char *ctype,
    490     sif_node_t **rchild)
     552errno_t sif_node_insert_after(sif_trans_t *trans, sif_node_t *sibling,
     553    const char *ctype, sif_node_t **rchild)
    491554{
    492555        sif_node_t *child;
     
    510573/** Destroy SIF node.
    511574 *
     575 * This function does not return an error, but the transaction may still
     576 * fail to complete.
     577 *
     578 * @param trans Transaction
    512579 * @param node Node to destroy
    513580 */
    514 void sif_node_destroy(sif_node_t *node)
     581void sif_node_destroy(sif_trans_t *trans, sif_node_t *node)
    515582{
    516583        list_remove(&node->lparent);
     
    520587/** Set node attribute.
    521588 *
     589 * @param trans Transaction
    522590 * @param node SIF node
    523591 * @param aname Attribute name
     
    526594 * @return EOK on success, ENOMEM if out of memory
    527595 */
    528 errno_t sif_node_set_attr(sif_node_t *node, const char *aname,
    529     const char *avalue)
     596errno_t sif_node_set_attr(sif_trans_t *trans, sif_node_t *node,
     597    const char *aname, const char *avalue)
    530598{
    531599        odlink_t *link;
     
    568636/** Unset node attribute.
    569637 *
     638 * This function does not return an error, but the transaction may still
     639 * fail to complete.
     640 *
     641 * @param trans Transaction
    570642 * @param node Node
    571643 * @param aname Attribute name
    572644 */
    573 void sif_node_unset_attr(sif_node_t *node, const char *aname)
     645void sif_node_unset_attr(sif_trans_t *trans, sif_node_t *node,
     646    const char *aname)
    574647{
    575648        odlink_t *link;
     
    585658}
    586659
    587 /** Export node name to file.
    588  *
    589  * Export node name to file.
     660/** Export string to file.
     661 *
     662 * Export string to file (the string is bracketed and escaped).
    590663 *
    591664 * @param str String
     
    593666 * @return EOK on success, EIO on I/O error
    594667 */
    595 static errno_t sif_export_name(const char *str, FILE *f)
    596 {
    597         if (fputs(str, f) == EOF)
    598                 return EIO;
    599 
    600         return EOK;
    601 }
    602 
    603 /** Export string to file.
    604  *
    605  * Export string to file (the string is double-quoted and escaped).
    606  *
    607  * @param str String
    608  * @param f File
    609  * @return EOK on success, EIO on I/O error
    610  */
    611668static errno_t sif_export_string(const char *str, FILE *f)
    612669{
    613670        const char *cp;
    614671
    615         if (fputc('"', f) == EOF)
     672        if (fputc('[', f) == EOF)
    616673                return EIO;
    617674
    618675        cp = str;
    619676        while (*cp != '\0') {
    620                 if (*cp == '<') {
    621                         if (fputs("&lt;", f) == EOF)
    622                                 return EIO;
    623                 } else if (*cp == '"') {
    624                         if (fputs("&quot;", f) == EOF)
    625                                 return EIO;
    626                 } else {
    627                         if (fputc(*cp, f) == EOF)
     677                if (*cp == ']' || *cp == '\\') {
     678                        if (fputc('\\', f) == EOF)
    628679                                return EIO;
    629680                }
    630 
    631                 ++cp;
    632         }
    633 
    634         if (fputc('"', f) == EOF)
    635                 return EIO;
    636 
    637         return EOK;
    638 }
    639 
    640 /** Read characters from file, make sure they match the specified sequence.
    641  *
    642  * @param f File
    643  * @param chars Expected sequence of characters to be read
    644  *
    645  * @return EOK on success, EIO on I/O error or mismatch
    646  */
    647 static errno_t sif_get_verify_chars(FILE *f, const char *chars)
    648 {
    649         const char *cp;
    650         char c;
    651 
    652         cp = chars;
    653         while (*cp != '\0') {
    654                 c = fgetc(f);
    655                 if (c != *cp)
     681                if (fputc(*cp, f) == EOF)
    656682                        return EIO;
    657683                ++cp;
    658684        }
    659685
    660         return EOK;
    661 }
    662 
    663 /** Import name from file.
    664  * *
     686        if (fputc(']', f) == EOF)
     687                return EIO;
     688
     689        return EOK;
     690}
     691
     692/** Import string from file.
     693 *
     694 * Import string from file (the string in the file must be
     695 * properly bracketed and escaped).
     696 *
    665697 * @param f File
    666698 * @param rstr Place to store pointer to newly allocated string
    667699 * @return EOK on success, EIO on I/O error
    668700 */
    669 static errno_t sif_import_name(FILE *f, char **rstr)
     701static errno_t sif_import_string(FILE *f, char **rstr)
    670702{
    671703        char *str;
    672         char *nstr;
    673704        size_t str_size;
    674705        size_t sidx;
     
    683714
    684715        c = fgetc(f);
    685         if (!sif_is_name_start_char(c)) {
    686                 rc = EIO;
    687                 goto error;
    688         }
    689 
    690         while (true) {
    691                 if (sidx >= str_size) {
    692                         str_size *= 2;
    693                         nstr = realloc(str, str_size + 1);
    694                         if (nstr == NULL) {
    695                                 rc = ENOMEM;
    696                                 goto error;
    697                         }
    698 
    699                         str = nstr;
    700                 }
    701 
    702                 str[sidx++] = c;
    703 
    704                 c = fgetc(f);
    705                 if (!sif_is_name_char(c))
    706                         break;
    707         }
    708 
    709         ungetc(c, f);
    710 
    711         str[sidx] = '\0';
    712         *rstr = str;
    713         return EOK;
    714 error:
    715         free(str);
    716         return rc;
    717 }
    718 
    719 /** Import string from file.
    720  *
    721  * Import string from file (the string in the file must be
    722  * properly quoted and escaped).
    723  *
    724  * @param f File
    725  * @param rstr Place to store pointer to newly allocated string
    726  * @return EOK on success, EIO on I/O error
    727  */
    728 static errno_t sif_import_string(FILE *f, char **rstr)
    729 {
    730         char *str;
    731         char *nstr;
    732         size_t str_size;
    733         size_t sidx;
    734         int c;
    735         errno_t rc;
    736 
    737         str_size = 1;
    738         sidx = 0;
    739         str = malloc(str_size + 1);
    740         if (str == NULL)
    741                 return ENOMEM;
    742 
    743         c = fgetc(f);
    744         if (c != '"') {
     716        if (c != '[') {
    745717                rc = EIO;
    746718                goto error;
     
    754726                }
    755727
    756                 if (c == '"')
     728                if (c == ']')
    757729                        break;
    758730
    759                 if (c == '&') {
     731                if (c == '\\') {
    760732                        c = fgetc(f);
    761733                        if (c == EOF) {
     
    763735                                goto error;
    764736                        }
    765 
    766                         if (c == 'q') {
    767                                 rc = sif_get_verify_chars(f, "uot;");
    768                                 if (rc != EOK)
    769                                         goto error;
    770                         } else if (c == 'l') {
    771                                 rc = sif_get_verify_chars(f, "t;");
    772                                 if (rc != EOK)
    773                                         goto error;
    774                         } else {
    775                                 rc = EIO;
    776                                 goto error;
    777                         }
    778737                }
    779738
    780739                if (sidx >= str_size) {
    781740                        str_size *= 2;
    782                         nstr = realloc(str, str_size + 1);
    783                         if (nstr == NULL) {
     741                        str = realloc(str, str_size + 1);
     742                        if (str == NULL) {
    784743                                rc = ENOMEM;
    785744                                goto error;
    786745                        }
    787 
    788                         str = nstr;
    789746                }
    790747
     
    815772        int c;
    816773
    817         rc = sif_import_name(f, &aname);
     774        rc = sif_import_string(f, &aname);
    818775        if (rc != EOK)
    819776                goto error;
     
    858815        errno_t rc;
    859816
    860         rc = sif_export_name(attr->aname, f);
     817        rc = sif_export_string(attr->aname, f);
    861818        if (rc != EOK)
    862819                return rc;
     
    884841        sif_node_t *child;
    885842
    886         if (fputc('<', f) == EOF)
    887                 return EIO;
    888 
    889         rc = sif_export_name(node->ntype, f);
     843        rc = sif_export_string(node->ntype, f);
    890844        if (rc != EOK)
    891845                return rc;
     
    893847        /* Attributes */
    894848
     849        if (fputc('(', f) == EOF)
     850                return EIO;
     851
    895852        attr = sif_node_first_attr(node);
    896853        while (attr != NULL) {
    897                 if (fputc(' ', f) == EOF)
    898                         return EIO;
    899 
    900854                rc = sif_export_attr(attr, f);
    901855                if (rc != EOK)
     
    905859        }
    906860
    907         if (fputs(">\n", f) == EOF)
     861        if (fputc(')', f) == EOF)
    908862                return EIO;
    909863
    910864        /* Child nodes */
     865
     866        if (fputc('{', f) == EOF)
     867                return EIO;
    911868
    912869        child = sif_node_first_child(node);
     
    919876        }
    920877
    921         if (fputs("</", f) == EOF)
    922                 return EIO;
    923 
    924         rc = sif_export_name(node->ntype, f);
    925         if (rc != EOK)
    926                 return rc;
    927 
    928         if (fputs(">\n", f) == EOF)
     878        if (fputc('}', f) == EOF)
    929879                return EIO;
    930880
     
    937887 * @param f File
    938888 * @param rnode Place to store pointer to imported node
    939  * @param rendtag Place to store @c true iff end tag is encountered
    940889 * @return EOK on success, EIO on I/O error
    941890 */
    942 static errno_t sif_import_node(sif_node_t *parent, FILE *f, sif_node_t **rnode,
    943     bool *rendtag)
     891static errno_t sif_import_node(sif_node_t *parent, FILE *f, sif_node_t **rnode)
    944892{
    945893        errno_t rc;
     
    947895        sif_node_t *child;
    948896        sif_attr_t *attr = NULL;
    949         bool endtag;
    950         bool cendtag;
    951897        char *ntype;
    952898        int c;
     
    956902                return ENOMEM;
    957903
     904        rc = sif_import_string(f, &ntype);
     905        if (rc != EOK)
     906                goto error;
     907
     908        node->ntype = ntype;
     909
     910        /* Attributes */
     911
    958912        c = fgetc(f);
    959         while (isspace(c))
    960                 c = fgetc(f);
    961 
    962         if (c != '<') {
     913        if (c != '(') {
    963914                rc = EIO;
    964915                goto error;
    965916        }
    966 
    967         c = fgetc(f);
    968         if (c == '/') {
    969                 endtag = true;
    970         } else {
    971                 endtag = false;
    972                 ungetc(c, f);
    973         }
    974 
    975         rc = sif_import_name(f, &ntype);
    976         if (rc != EOK)
    977                 goto error;
    978 
    979         node->ntype = ntype;
    980 
    981         /* Attributes */
    982917
    983918        c = fgetc(f);
     
    987922        }
    988923
    989         while (c != '>') {
    990                 /* End tags cannot have attributes */
    991                 if (endtag) {
    992                         rc = EIO;
    993                         goto error;
    994                 }
    995 
    996                 while (isspace(c))
    997                         c = fgetc(f);
     924        while (c != ')') {
    998925                ungetc(c, f);
    999926
     
    1013940        /* Child nodes */
    1014941
    1015         if (!endtag) {
    1016                 while (true) {
    1017                         rc = sif_import_node(node, f, &child, &cendtag);
    1018                         if (rc != EOK)
    1019                                 goto error;
    1020 
    1021                         if (cendtag) {
    1022                                 sif_node_delete(child);
    1023                                 break;
    1024                         }
    1025 
    1026                         list_append(&child->lparent, &node->children);
     942        c = fgetc(f);
     943        if (c != '{') {
     944                rc = EIO;
     945                goto error;
     946        }
     947
     948        c = fgetc(f);
     949        if (c == EOF) {
     950                rc = EIO;
     951                goto error;
     952        }
     953
     954        while (c != '}') {
     955                ungetc(c, f);
     956
     957                rc = sif_import_node(node, f, &child);
     958                if (rc != EOK)
     959                        goto error;
     960
     961                list_append(&child->lparent, &node->children);
     962
     963                c = fgetc(f);
     964                if (c == EOF) {
     965                        rc = EIO;
     966                        goto error;
    1027967                }
    1028968        }
    1029969
    1030970        *rnode = node;
    1031         *rendtag = endtag;
    1032971        return EOK;
    1033972error:
Note: See TracChangeset for help on using the changeset viewer.