Ignore:
File:
1 edited

Legend:

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

    ree8d4d6 rca95ccd  
    11/*
    2  * Copyright (c) 2018 Jiri Svoboda
     2 * Copyright (c) 2024 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
    36  *
    37  *  - is structured (and hence extensible)
    38  *  - allows atomic (transactional) updates
    39  *  - allows efficient updates
     35 * to maintain data in a persistent repository in a format that is
     36 * structured (and hence extensible).
    4037 *
    4138 * SIF is meant to be used as the basis for the storage backend used to
     
    4441 * similar to an XML document that contains just tags with attributes
    4542 * (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.
    6043 */
    6144
    6245#include <adt/list.h>
    6346#include <adt/odict.h>
     47#include <ctype.h>
    6448#include <errno.h>
     49#include <stdbool.h>
    6550#include <stdio.h>
    6651#include <stdlib.h>
     
    7055
    7156static errno_t sif_export_node(sif_node_t *, FILE *);
    72 static errno_t sif_import_node(sif_node_t *, FILE *, sif_node_t **);
     57static errno_t sif_import_node(sif_node_t *, FILE *, sif_node_t **, bool *);
    7358static sif_attr_t *sif_node_first_attr(sif_node_t *);
    7459static sif_attr_t *sif_node_next_attr(sif_attr_t *);
     
    7762static int sif_attr_cmp(void *, void *);
    7863
     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 */
     69static 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 */
     79static bool sif_is_name_char(char c)
     80{
     81        return isalnum(c) || c == '-' || c == '.';
     82}
     83
    7984/** Create new SIF node.
    8085 *
     
    172177}
    173178
    174 /** Create and open a SIF repository.
    175  *
    176  * @param fname File name
    177  * @param rsess Place to store pointer to new session.
     179/** Create SIF document.
     180 *
     181 * @param rdoc Place to store pointer to new document.
    178182 *
    179183 * @return EOK on success or error code
    180184 */
    181 errno_t sif_create(const char *fname, sif_sess_t **rsess)
    182 {
    183         sif_sess_t *sess;
     185errno_t sif_new(sif_doc_t **rdoc)
     186{
     187        sif_doc_t *doc;
    184188        sif_node_t *root = NULL;
    185         sif_trans_t *trans = NULL;
    186189        errno_t rc;
    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         }
     190
     191        doc = calloc(1, sizeof(sif_doc_t));
     192        if (doc == NULL)
     193                return ENOMEM;
    198194
    199195        root = sif_node_new(NULL);
     
    209205        }
    210206
    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;
     207        doc->root = root;
     208
     209        *rdoc = doc;
    230210        return EOK;
    231211error:
    232         if (trans != NULL)
    233                 sif_trans_abort(trans);
    234212        sif_node_delete(root);
    235         if (sess->fname != NULL)
    236                 free(sess->fname);
    237         free(sess);
     213        free(doc);
    238214        return rc;
    239215}
    240216
    241 /** Open an existing SIF repository.
     217/** Load SIF document.
    242218 *
    243219 * @param fname File name
    244  * @param rsess Place to store pointer to new session.
     220 * @param rdoc Place to store pointer to new document.
    245221 *
    246222 * @return EOK on success or error code
    247223 */
    248 errno_t sif_open(const char *fname, sif_sess_t **rsess)
    249 {
    250         sif_sess_t *sess;
     224errno_t sif_load(const char *fname, sif_doc_t **rdoc)
     225{
     226        sif_doc_t *doc;
    251227        sif_node_t *root = NULL;
    252228        errno_t rc;
    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) {
     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) {
    261238                rc = ENOMEM;
    262239                goto error;
     
    269246        }
    270247
    271         rc = sif_import_node(NULL, f, &root);
    272         if (rc != EOK)
     248        rc = sif_import_node(NULL, f, &root, &endtag);
     249        if (rc != EOK || endtag == true)
    273250                goto error;
    274251
     
    278255        }
    279256
    280         sess->root = root;
    281 
    282         sess->f = f;
    283         sess->root = root;
    284         *rsess = sess;
     257        fclose(f);
     258        doc->root = root;
     259        *rdoc = doc;
    285260        return EOK;
    286261error:
    287262        sif_node_delete(root);
    288         if (sess->fname != NULL)
    289                 free(sess->fname);
    290         free(sess);
     263        free(doc);
     264        if (f != NULL)
     265                fclose(f);
    291266        return rc;
    292267}
    293268
    294 /** Close SIF session.
    295  *
    296  * @param sess SIF session
     269/** Delete SIF document.
     270 *
     271 * @param doc SIF document
    297272 * @return EOK on success or error code
    298273 */
    299 errno_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;
     274void sif_delete(sif_doc_t *doc)
     275{
     276        sif_node_delete(doc->root);
     277        free(doc);
    312278}
    313279
    314280/** Return root node.
    315281 *
    316  * @param sess SIF session
    317  */
    318 sif_node_t *sif_get_root(sif_sess_t *sess)
    319 {
    320         return sess->root;
     282 * @param doc SIF document
     283 */
     284sif_node_t *sif_get_root(sif_doc_t *doc)
     285{
     286        return doc->root;
    321287}
    322288
     
    383349}
    384350
    385 /** Begin SIF transaction.
    386  *
    387  * @param trans Transaction
     351/** Save SIF document to file.
     352 * *
     353 * @param doc SIF document
     354 * @param fname File name
    388355 * @return EOK on success or error code
    389356 */
    390 errno_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  */
    411 errno_t sif_trans_end(sif_trans_t *trans)
    412 {
     357errno_t sif_save(sif_doc_t *doc, const char *fname)
     358{
     359        FILE *f = NULL;
    413360        errno_t rc;
    414361
    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);
     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);
    422369        if (rc != EOK)
    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  */
    439 void sif_trans_abort(sif_trans_t *trans)
    440 {
    441         free(trans);
     370                goto error;
     371
     372        if (fflush(f) == EOF) {
     373                rc = EIO;
     374                goto error;
     375        }
     376
     377        fclose(f);
     378        return EOK;
     379error:
     380        if (f != NULL)
     381                fclose(f);
     382        return rc;
    442383}
    443384
     
    447388 * @a parent.
    448389 *
    449  * @param trans Transaction
    450390 * @param parent Parent node
    451391 * @param ctype Child type
     
    454394 * @return EOK on success or ENOMEM if out of memory
    455395 */
    456 errno_t sif_node_prepend_child(sif_trans_t *trans, sif_node_t *parent,
    457     const char *ctype, sif_node_t **rchild)
     396errno_t sif_node_prepend_child(sif_node_t *parent, const char *ctype,
     397    sif_node_t **rchild)
    458398{
    459399        sif_node_t *child;
     
    479419 * Create a new child and append it at the end of children list of @a parent.
    480420 *
    481  * @param trans Transaction
    482421 * @param parent Parent node
    483422 * @param ctype Child type
     
    486425 * @return EOK on success or ENOMEM if out of memory
    487426 */
    488 errno_t sif_node_append_child(sif_trans_t *trans, sif_node_t *parent,
    489     const char *ctype, sif_node_t **rchild)
     427errno_t sif_node_append_child(sif_node_t *parent, const char *ctype,
     428    sif_node_t **rchild)
    490429{
    491430        sif_node_t *child;
     
    511450 * Create a new child and insert it before an existing child.
    512451 *
    513  * @param trans Transaction
    514452 * @param sibling Sibling before which to insert
    515453 * @param ctype Child type
     
    518456 * @return EOK on success or ENOMEM if out of memory
    519457 */
    520 errno_t sif_node_insert_before(sif_trans_t *trans, sif_node_t *sibling,
    521     const char *ctype, sif_node_t **rchild)
     458errno_t sif_node_insert_before(sif_node_t *sibling, const char *ctype,
     459    sif_node_t **rchild)
    522460{
    523461        sif_node_t *child;
     
    543481 * Create a new child and insert it after an existing child.
    544482 *
    545  * @param trans Transaction
    546483 * @param sibling Sibling after which to insert
    547484 * @param ctype Child type
     
    550487 * @return EOK on success or ENOMEM if out of memory
    551488 */
    552 errno_t sif_node_insert_after(sif_trans_t *trans, sif_node_t *sibling,
    553     const char *ctype, sif_node_t **rchild)
     489errno_t sif_node_insert_after(sif_node_t *sibling, const char *ctype,
     490    sif_node_t **rchild)
    554491{
    555492        sif_node_t *child;
     
    573510/** Destroy SIF node.
    574511 *
    575  * This function does not return an error, but the transaction may still
    576  * fail to complete.
    577  *
    578  * @param trans Transaction
    579512 * @param node Node to destroy
    580513 */
    581 void sif_node_destroy(sif_trans_t *trans, sif_node_t *node)
     514void sif_node_destroy(sif_node_t *node)
    582515{
    583516        list_remove(&node->lparent);
     
    587520/** Set node attribute.
    588521 *
    589  * @param trans Transaction
    590522 * @param node SIF node
    591523 * @param aname Attribute name
     
    594526 * @return EOK on success, ENOMEM if out of memory
    595527 */
    596 errno_t sif_node_set_attr(sif_trans_t *trans, sif_node_t *node,
    597     const char *aname, const char *avalue)
     528errno_t sif_node_set_attr(sif_node_t *node, const char *aname,
     529    const char *avalue)
    598530{
    599531        odlink_t *link;
     
    636568/** Unset node attribute.
    637569 *
    638  * This function does not return an error, but the transaction may still
    639  * fail to complete.
    640  *
    641  * @param trans Transaction
    642570 * @param node Node
    643571 * @param aname Attribute name
    644572 */
    645 void sif_node_unset_attr(sif_trans_t *trans, sif_node_t *node,
    646     const char *aname)
     573void sif_node_unset_attr(sif_node_t *node, const char *aname)
    647574{
    648575        odlink_t *link;
     
    658585}
    659586
    660 /** Export string to file.
    661  *
    662  * Export string to file (the string is bracketed and escaped).
     587/** Export node name to file.
     588 *
     589 * Export node name to file.
    663590 *
    664591 * @param str String
     
    666593 * @return EOK on success, EIO on I/O error
    667594 */
     595static 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 */
    668611static errno_t sif_export_string(const char *str, FILE *f)
    669612{
    670613        const char *cp;
    671614
    672         if (fputc('[', f) == EOF)
     615        if (fputc('"', f) == EOF)
    673616                return EIO;
    674617
    675618        cp = str;
    676619        while (*cp != '\0') {
    677                 if (*cp == ']' || *cp == '\\') {
    678                         if (fputc('\\', f) == EOF)
     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)
    679628                                return EIO;
    680629                }
    681                 if (fputc(*cp, f) == EOF)
     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 */
     647static 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)
    682656                        return EIO;
    683657                ++cp;
    684658        }
    685659
    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  *
     660        return EOK;
     661}
     662
     663/** Import name from file.
     664 * *
    697665 * @param f File
    698666 * @param rstr Place to store pointer to newly allocated string
    699667 * @return EOK on success, EIO on I/O error
    700668 */
    701 static errno_t sif_import_string(FILE *f, char **rstr)
     669static errno_t sif_import_name(FILE *f, char **rstr)
    702670{
    703671        char *str;
     672        char *nstr;
    704673        size_t str_size;
    705674        size_t sidx;
     
    714683
    715684        c = fgetc(f);
    716         if (c != '[') {
     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;
     714error:
     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 */
     728static 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 != '"') {
    717745                rc = EIO;
    718746                goto error;
     
    726754                }
    727755
    728                 if (c == ']')
     756                if (c == '"')
    729757                        break;
    730758
    731                 if (c == '\\') {
     759                if (c == '&') {
    732760                        c = fgetc(f);
    733761                        if (c == EOF) {
     
    735763                                goto error;
    736764                        }
     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                        }
    737778                }
    738779
    739780                if (sidx >= str_size) {
    740781                        str_size *= 2;
    741                         str = realloc(str, str_size + 1);
    742                         if (str == NULL) {
     782                        nstr = realloc(str, str_size + 1);
     783                        if (nstr == NULL) {
    743784                                rc = ENOMEM;
    744785                                goto error;
    745786                        }
     787
     788                        str = nstr;
    746789                }
    747790
     
    772815        int c;
    773816
    774         rc = sif_import_string(f, &aname);
     817        rc = sif_import_name(f, &aname);
    775818        if (rc != EOK)
    776819                goto error;
     
    815858        errno_t rc;
    816859
    817         rc = sif_export_string(attr->aname, f);
     860        rc = sif_export_name(attr->aname, f);
    818861        if (rc != EOK)
    819862                return rc;
     
    841884        sif_node_t *child;
    842885
    843         rc = sif_export_string(node->ntype, f);
     886        if (fputc('<', f) == EOF)
     887                return EIO;
     888
     889        rc = sif_export_name(node->ntype, f);
    844890        if (rc != EOK)
    845891                return rc;
     
    847893        /* Attributes */
    848894
    849         if (fputc('(', f) == EOF)
    850                 return EIO;
    851 
    852895        attr = sif_node_first_attr(node);
    853896        while (attr != NULL) {
     897                if (fputc(' ', f) == EOF)
     898                        return EIO;
     899
    854900                rc = sif_export_attr(attr, f);
    855901                if (rc != EOK)
     
    859905        }
    860906
    861         if (fputc(')', f) == EOF)
     907        if (fputs(">\n", f) == EOF)
    862908                return EIO;
    863909
    864910        /* Child nodes */
    865 
    866         if (fputc('{', f) == EOF)
    867                 return EIO;
    868911
    869912        child = sif_node_first_child(node);
     
    876919        }
    877920
    878         if (fputc('}', f) == EOF)
     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)
    879929                return EIO;
    880930
     
    887937 * @param f File
    888938 * @param rnode Place to store pointer to imported node
     939 * @param rendtag Place to store @c true iff end tag is encountered
    889940 * @return EOK on success, EIO on I/O error
    890941 */
    891 static errno_t sif_import_node(sif_node_t *parent, FILE *f, sif_node_t **rnode)
     942static errno_t sif_import_node(sif_node_t *parent, FILE *f, sif_node_t **rnode,
     943    bool *rendtag)
    892944{
    893945        errno_t rc;
     
    895947        sif_node_t *child;
    896948        sif_attr_t *attr = NULL;
     949        bool endtag;
     950        bool cendtag;
    897951        char *ntype;
    898952        int c;
     
    902956                return ENOMEM;
    903957
    904         rc = sif_import_string(f, &ntype);
     958        c = fgetc(f);
     959        while (isspace(c))
     960                c = fgetc(f);
     961
     962        if (c != '<') {
     963                rc = EIO;
     964                goto error;
     965        }
     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);
    905976        if (rc != EOK)
    906977                goto error;
     
    909980
    910981        /* Attributes */
    911 
    912         c = fgetc(f);
    913         if (c != '(') {
    914                 rc = EIO;
    915                 goto error;
    916         }
    917982
    918983        c = fgetc(f);
     
    922987        }
    923988
    924         while (c != ')') {
     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);
    925998                ungetc(c, f);
    926999
     
    9401013        /* Child nodes */
    9411014
    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;
     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);
    9671027                }
    9681028        }
    9691029
    9701030        *rnode = node;
     1031        *rendtag = endtag;
    9711032        return EOK;
    9721033error:
Note: See TracChangeset for help on using the changeset viewer.