Changes in uspace/lib/sif/src/sif.c [ca95ccd:153dd3b] in mainline
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
uspace/lib/sif/src/sif.c
rca95ccd r153dd3b 1 1 /* 2 * Copyright (c) 202 4Jiri Svoboda2 * Copyright (c) 2023 Jiri Svoboda 3 3 * All rights reserved. 4 4 * … … 33 33 * 34 34 * 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 37 40 * 38 41 * SIF is meant to be used as the basis for the storage backend used to … … 41 44 * similar to an XML document that contains just tags with attributes 42 45 * (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. 43 60 */ 44 61 45 62 #include <adt/list.h> 46 63 #include <adt/odict.h> 47 #include <ctype.h>48 64 #include <errno.h> 49 #include <stdbool.h>50 65 #include <stdio.h> 51 66 #include <stdlib.h> … … 55 70 56 71 static errno_t sif_export_node(sif_node_t *, FILE *); 57 static errno_t sif_import_node(sif_node_t *, FILE *, sif_node_t ** , bool *);72 static errno_t sif_import_node(sif_node_t *, FILE *, sif_node_t **); 58 73 static sif_attr_t *sif_node_first_attr(sif_node_t *); 59 74 static sif_attr_t *sif_node_next_attr(sif_attr_t *); … … 62 77 static int sif_attr_cmp(void *, void *); 63 78 64 /** Determine if character can start a name.65 *66 * @param c Character67 * @return @c true iff the character can start a name68 */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 Character77 * @return @c true iff the character can continue a name78 */79 static bool sif_is_name_char(char c)80 {81 return isalnum(c) || c == '-' || c == '.';82 }83 84 79 /** Create new SIF node. 85 80 * … … 177 172 } 178 173 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. 182 178 * 183 179 * @return EOK on success or error code 184 180 */ 185 errno_t sif_ new(sif_doc_t **rdoc)186 { 187 sif_ doc_t *doc;181 errno_t sif_create(const char *fname, sif_sess_t **rsess) 182 { 183 sif_sess_t *sess; 188 184 sif_node_t *root = NULL; 185 sif_trans_t *trans = NULL; 189 186 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 } 194 198 195 199 root = sif_node_new(NULL); … … 205 209 } 206 210 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; 210 230 return EOK; 211 231 error: 232 if (trans != NULL) 233 sif_trans_abort(trans); 212 234 sif_node_delete(root); 213 free(doc); 235 if (sess->fname != NULL) 236 free(sess->fname); 237 free(sess); 214 238 return rc; 215 239 } 216 240 217 /** Load SIF document.241 /** Open an existing SIF repository. 218 242 * 219 243 * @param fname File name 220 * @param r doc Place to store pointer to new document.244 * @param rsess Place to store pointer to new session. 221 245 * 222 246 * @return EOK on success or error code 223 247 */ 224 errno_t sif_ load(const char *fname, sif_doc_t **rdoc)225 { 226 sif_ doc_t *doc;248 errno_t sif_open(const char *fname, sif_sess_t **rsess) 249 { 250 sif_sess_t *sess; 227 251 sif_node_t *root = NULL; 228 252 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) { 238 261 rc = ENOMEM; 239 262 goto error; … … 246 269 } 247 270 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) 250 273 goto error; 251 274 … … 255 278 } 256 279 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; 260 285 return EOK; 261 286 error: 262 287 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); 266 291 return rc; 267 292 } 268 293 269 /** Delete SIF document.270 * 271 * @param doc SIF document294 /** Close SIF session. 295 * 296 * @param sess SIF session 272 297 * @return EOK on success or error code 273 298 */ 274 void sif_delete(sif_doc_t *doc) 275 { 276 sif_node_delete(doc->root); 277 free(doc); 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; 278 312 } 279 313 280 314 /** Return root node. 281 315 * 282 * @param doc SIF document283 */ 284 sif_node_t *sif_get_root(sif_ doc_t *doc)285 { 286 return doc->root;316 * @param sess SIF session 317 */ 318 sif_node_t *sif_get_root(sif_sess_t *sess) 319 { 320 return sess->root; 287 321 } 288 322 … … 349 383 } 350 384 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 355 388 * @return EOK on success or error code 356 389 */ 357 errno_t sif_save(sif_doc_t *doc, const char *fname) 358 { 359 FILE *f = NULL; 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 { 360 413 errno_t rc; 361 414 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); 369 422 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 */ 439 void sif_trans_abort(sif_trans_t *trans) 440 { 441 free(trans); 383 442 } 384 443 … … 388 447 * @a parent. 389 448 * 449 * @param trans Transaction 390 450 * @param parent Parent node 391 451 * @param ctype Child type … … 394 454 * @return EOK on success or ENOMEM if out of memory 395 455 */ 396 errno_t sif_node_prepend_child(sif_ node_t *parent, const char *ctype,397 sif_node_t **rchild)456 errno_t sif_node_prepend_child(sif_trans_t *trans, sif_node_t *parent, 457 const char *ctype, sif_node_t **rchild) 398 458 { 399 459 sif_node_t *child; … … 419 479 * Create a new child and append it at the end of children list of @a parent. 420 480 * 481 * @param trans Transaction 421 482 * @param parent Parent node 422 483 * @param ctype Child type … … 425 486 * @return EOK on success or ENOMEM if out of memory 426 487 */ 427 errno_t sif_node_append_child(sif_ node_t *parent, const char *ctype,428 sif_node_t **rchild)488 errno_t sif_node_append_child(sif_trans_t *trans, sif_node_t *parent, 489 const char *ctype, sif_node_t **rchild) 429 490 { 430 491 sif_node_t *child; … … 450 511 * Create a new child and insert it before an existing child. 451 512 * 513 * @param trans Transaction 452 514 * @param sibling Sibling before which to insert 453 515 * @param ctype Child type … … 456 518 * @return EOK on success or ENOMEM if out of memory 457 519 */ 458 errno_t sif_node_insert_before(sif_ node_t *sibling, const char *ctype,459 sif_node_t **rchild)520 errno_t sif_node_insert_before(sif_trans_t *trans, sif_node_t *sibling, 521 const char *ctype, sif_node_t **rchild) 460 522 { 461 523 sif_node_t *child; … … 481 543 * Create a new child and insert it after an existing child. 482 544 * 545 * @param trans Transaction 483 546 * @param sibling Sibling after which to insert 484 547 * @param ctype Child type … … 487 550 * @return EOK on success or ENOMEM if out of memory 488 551 */ 489 errno_t sif_node_insert_after(sif_ node_t *sibling, const char *ctype,490 sif_node_t **rchild)552 errno_t sif_node_insert_after(sif_trans_t *trans, sif_node_t *sibling, 553 const char *ctype, sif_node_t **rchild) 491 554 { 492 555 sif_node_t *child; … … 510 573 /** Destroy SIF node. 511 574 * 575 * This function does not return an error, but the transaction may still 576 * fail to complete. 577 * 578 * @param trans Transaction 512 579 * @param node Node to destroy 513 580 */ 514 void sif_node_destroy(sif_ node_t *node)581 void sif_node_destroy(sif_trans_t *trans, sif_node_t *node) 515 582 { 516 583 list_remove(&node->lparent); … … 520 587 /** Set node attribute. 521 588 * 589 * @param trans Transaction 522 590 * @param node SIF node 523 591 * @param aname Attribute name … … 526 594 * @return EOK on success, ENOMEM if out of memory 527 595 */ 528 errno_t sif_node_set_attr(sif_ node_t *node, const char *aname,529 const char *a value)596 errno_t sif_node_set_attr(sif_trans_t *trans, sif_node_t *node, 597 const char *aname, const char *avalue) 530 598 { 531 599 odlink_t *link; … … 568 636 /** Unset node attribute. 569 637 * 638 * This function does not return an error, but the transaction may still 639 * fail to complete. 640 * 641 * @param trans Transaction 570 642 * @param node Node 571 643 * @param aname Attribute name 572 644 */ 573 void sif_node_unset_attr(sif_node_t *node, const char *aname) 645 void sif_node_unset_attr(sif_trans_t *trans, sif_node_t *node, 646 const char *aname) 574 647 { 575 648 odlink_t *link; … … 585 658 } 586 659 587 /** Export node nameto 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). 590 663 * 591 664 * @param str String … … 593 666 * @return EOK on success, EIO on I/O error 594 667 */ 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 String608 * @param f File609 * @return EOK on success, EIO on I/O error610 */611 668 static errno_t sif_export_string(const char *str, FILE *f) 612 669 { 613 670 const char *cp; 614 671 615 if (fputc(' "', f) == EOF)672 if (fputc('[', f) == EOF) 616 673 return EIO; 617 674 618 675 cp = str; 619 676 while (*cp != '\0') { 620 if (*cp == '<') { 621 if (fputs("<", f) == EOF) 622 return EIO; 623 } else if (*cp == '"') { 624 if (fputs(""", f) == EOF) 625 return EIO; 626 } else { 627 if (fputc(*cp, f) == EOF) 677 if (*cp == ']' || *cp == '\\') { 678 if (fputc('\\', f) == EOF) 628 679 return EIO; 629 680 } 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) 656 682 return EIO; 657 683 ++cp; 658 684 } 659 685 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 * 665 697 * @param f File 666 698 * @param rstr Place to store pointer to newly allocated string 667 699 * @return EOK on success, EIO on I/O error 668 700 */ 669 static errno_t sif_import_ name(FILE *f, char **rstr)701 static errno_t sif_import_string(FILE *f, char **rstr) 670 702 { 671 703 char *str; … … 683 715 684 716 c = fgetc(f); 685 if ( !sif_is_name_start_char(c)) {717 if (c != '[') { 686 718 rc = EIO; 687 719 goto error; … … 689 721 690 722 while (true) { 723 c = fgetc(f); 724 if (c == EOF) { 725 rc = EIO; 726 goto error; 727 } 728 729 if (c == ']') 730 break; 731 732 if (c == '\\') { 733 c = fgetc(f); 734 if (c == EOF) { 735 rc = EIO; 736 goto error; 737 } 738 } 739 691 740 if (sidx >= str_size) { 692 741 str_size *= 2; … … 701 750 702 751 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 be722 * properly quoted and escaped).723 *724 * @param f File725 * @param rstr Place to store pointer to newly allocated string726 * @return EOK on success, EIO on I/O error727 */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 != '"') {745 rc = EIO;746 goto error;747 }748 749 while (true) {750 c = fgetc(f);751 if (c == EOF) {752 rc = EIO;753 goto error;754 }755 756 if (c == '"')757 break;758 759 if (c == '&') {760 c = fgetc(f);761 if (c == EOF) {762 rc = EIO;763 goto error;764 }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 }778 }779 780 if (sidx >= str_size) {781 str_size *= 2;782 nstr = realloc(str, str_size + 1);783 if (nstr == NULL) {784 rc = ENOMEM;785 goto error;786 }787 788 str = nstr;789 }790 791 str[sidx++] = c;792 752 } 793 753 … … 815 775 int c; 816 776 817 rc = sif_import_ name(f, &aname);777 rc = sif_import_string(f, &aname); 818 778 if (rc != EOK) 819 779 goto error; … … 858 818 errno_t rc; 859 819 860 rc = sif_export_ name(attr->aname, f);820 rc = sif_export_string(attr->aname, f); 861 821 if (rc != EOK) 862 822 return rc; … … 884 844 sif_node_t *child; 885 845 886 if (fputc('<', f) == EOF) 887 return EIO; 888 889 rc = sif_export_name(node->ntype, f); 846 rc = sif_export_string(node->ntype, f); 890 847 if (rc != EOK) 891 848 return rc; … … 893 850 /* Attributes */ 894 851 852 if (fputc('(', f) == EOF) 853 return EIO; 854 895 855 attr = sif_node_first_attr(node); 896 856 while (attr != NULL) { 897 if (fputc(' ', f) == EOF)898 return EIO;899 900 857 rc = sif_export_attr(attr, f); 901 858 if (rc != EOK) … … 905 862 } 906 863 907 if (fput s(">\n", f) == EOF)864 if (fputc(')', f) == EOF) 908 865 return EIO; 909 866 910 867 /* Child nodes */ 868 869 if (fputc('{', f) == EOF) 870 return EIO; 911 871 912 872 child = sif_node_first_child(node); … … 919 879 } 920 880 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) 881 if (fputc('}', f) == EOF) 929 882 return EIO; 930 883 … … 937 890 * @param f File 938 891 * @param rnode Place to store pointer to imported node 939 * @param rendtag Place to store @c true iff end tag is encountered940 892 * @return EOK on success, EIO on I/O error 941 893 */ 942 static errno_t sif_import_node(sif_node_t *parent, FILE *f, sif_node_t **rnode, 943 bool *rendtag) 894 static errno_t sif_import_node(sif_node_t *parent, FILE *f, sif_node_t **rnode) 944 895 { 945 896 errno_t rc; … … 947 898 sif_node_t *child; 948 899 sif_attr_t *attr = NULL; 949 bool endtag;950 bool cendtag;951 900 char *ntype; 952 901 int c; … … 956 905 return ENOMEM; 957 906 907 rc = sif_import_string(f, &ntype); 908 if (rc != EOK) 909 goto error; 910 911 node->ntype = ntype; 912 913 /* Attributes */ 914 958 915 c = fgetc(f); 959 while (isspace(c)) 960 c = fgetc(f); 961 962 if (c != '<') { 916 if (c != '(') { 963 917 rc = EIO; 964 918 goto error; 965 919 } 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 */982 920 983 921 c = fgetc(f); … … 987 925 } 988 926 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); 927 while (c != ')') { 998 928 ungetc(c, f); 999 929 … … 1013 943 /* Child nodes */ 1014 944 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); 945 c = fgetc(f); 946 if (c != '{') { 947 rc = EIO; 948 goto error; 949 } 950 951 c = fgetc(f); 952 if (c == EOF) { 953 rc = EIO; 954 goto error; 955 } 956 957 while (c != '}') { 958 ungetc(c, f); 959 960 rc = sif_import_node(node, f, &child); 961 if (rc != EOK) 962 goto error; 963 964 list_append(&child->lparent, &node->children); 965 966 c = fgetc(f); 967 if (c == EOF) { 968 rc = EIO; 969 goto error; 1027 970 } 1028 971 } 1029 972 1030 973 *rnode = node; 1031 *rendtag = endtag;1032 974 return EOK; 1033 975 error:
Note:
See TracChangeset
for help on using the changeset viewer.