Changeset ebb1489 in mainline for uspace/lib/sif/src/sif.c
- Timestamp:
- 2024-10-13T08:23:40Z (2 months ago)
- Children:
- 0472cf17
- Parents:
- 2a0c827c (diff), b3b79981 (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the(diff)
links above to see all the changes relative to each parent. - git-author:
- boba-buba <120932204+boba-buba@…> (2024-10-13 08:23:40)
- git-committer:
- GitHub <noreply@…> (2024-10-13 08:23:40)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
uspace/lib/sif/src/sif.c
r2a0c827c rebb1489 1 1 /* 2 * Copyright (c) 202 3Jiri Svoboda2 * Copyright (c) 2024 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 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). 40 37 * 41 38 * SIF is meant to be used as the basis for the storage backend used to … … 44 41 * similar to an XML document that contains just tags with attributes 45 42 * (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, SIF51 *52 * - does not run on a separate server53 * - des not handle concurrency54 * - does not have a notion of types or data validation55 * - does not understand relations56 * - does not implement any kind of search/queries57 * - does not deal with data sets large enough not to fit in primary memory58 *59 * any kind of structure data validation is left up to the application.60 43 */ 61 44 62 45 #include <adt/list.h> 63 46 #include <adt/odict.h> 47 #include <ctype.h> 64 48 #include <errno.h> 49 #include <stdbool.h> 65 50 #include <stdio.h> 66 51 #include <stdlib.h> … … 70 55 71 56 static errno_t sif_export_node(sif_node_t *, FILE *); 72 static errno_t sif_import_node(sif_node_t *, FILE *, sif_node_t ** );57 static errno_t sif_import_node(sif_node_t *, FILE *, sif_node_t **, bool *); 73 58 static sif_attr_t *sif_node_first_attr(sif_node_t *); 74 59 static sif_attr_t *sif_node_next_attr(sif_attr_t *); … … 77 62 static int sif_attr_cmp(void *, void *); 78 63 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 79 84 /** Create new SIF node. 80 85 * … … 172 177 } 173 178 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. 178 182 * 179 183 * @return EOK on success or error code 180 184 */ 181 errno_t sif_ create(const char *fname, sif_sess_t **rsess)182 { 183 sif_ sess_t *sess;185 errno_t sif_new(sif_doc_t **rdoc) 186 { 187 sif_doc_t *doc; 184 188 sif_node_t *root = NULL; 185 sif_trans_t *trans = NULL;186 189 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; 198 194 199 195 root = sif_node_new(NULL); … … 209 205 } 210 206 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; 230 210 return EOK; 231 211 error: 232 if (trans != NULL)233 sif_trans_abort(trans);234 212 sif_node_delete(root); 235 if (sess->fname != NULL) 236 free(sess->fname); 237 free(sess); 213 free(doc); 238 214 return rc; 239 215 } 240 216 241 /** Open an existing SIF repository.217 /** Load SIF document. 242 218 * 243 219 * @param fname File name 244 * @param r sess Place to store pointer to new session.220 * @param rdoc Place to store pointer to new document. 245 221 * 246 222 * @return EOK on success or error code 247 223 */ 248 errno_t sif_ open(const char *fname, sif_sess_t **rsess)249 { 250 sif_ sess_t *sess;224 errno_t sif_load(const char *fname, sif_doc_t **rdoc) 225 { 226 sif_doc_t *doc; 251 227 sif_node_t *root = NULL; 252 228 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) { 261 238 rc = ENOMEM; 262 239 goto error; … … 269 246 } 270 247 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) 273 250 goto error; 274 251 … … 278 255 } 279 256 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; 285 260 return EOK; 286 261 error: 287 262 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); 291 266 return rc; 292 267 } 293 268 294 /** Close SIF session.295 * 296 * @param sess SIF session269 /** Delete SIF document. 270 * 271 * @param doc SIF document 297 272 * @return EOK on success or error code 298 273 */ 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; 274 void sif_delete(sif_doc_t *doc) 275 { 276 sif_node_delete(doc->root); 277 free(doc); 312 278 } 313 279 314 280 /** Return root node. 315 281 * 316 * @param sess SIF session317 */ 318 sif_node_t *sif_get_root(sif_ sess_t *sess)319 { 320 return sess->root;282 * @param doc SIF document 283 */ 284 sif_node_t *sif_get_root(sif_doc_t *doc) 285 { 286 return doc->root; 321 287 } 322 288 … … 383 349 } 384 350 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 388 355 * @return EOK on success or error code 389 356 */ 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 { 357 errno_t sif_save(sif_doc_t *doc, const char *fname) 358 { 359 FILE *f = NULL; 413 360 errno_t rc; 414 361 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); 422 369 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; 379 error: 380 if (f != NULL) 381 fclose(f); 382 return rc; 442 383 } 443 384 … … 447 388 * @a parent. 448 389 * 449 * @param trans Transaction450 390 * @param parent Parent node 451 391 * @param ctype Child type … … 454 394 * @return EOK on success or ENOMEM if out of memory 455 395 */ 456 errno_t sif_node_prepend_child(sif_ trans_t *trans, sif_node_t *parent,457 const char *ctype,sif_node_t **rchild)396 errno_t sif_node_prepend_child(sif_node_t *parent, const char *ctype, 397 sif_node_t **rchild) 458 398 { 459 399 sif_node_t *child; … … 479 419 * Create a new child and append it at the end of children list of @a parent. 480 420 * 481 * @param trans Transaction482 421 * @param parent Parent node 483 422 * @param ctype Child type … … 486 425 * @return EOK on success or ENOMEM if out of memory 487 426 */ 488 errno_t sif_node_append_child(sif_ trans_t *trans, sif_node_t *parent,489 const char *ctype,sif_node_t **rchild)427 errno_t sif_node_append_child(sif_node_t *parent, const char *ctype, 428 sif_node_t **rchild) 490 429 { 491 430 sif_node_t *child; … … 511 450 * Create a new child and insert it before an existing child. 512 451 * 513 * @param trans Transaction514 452 * @param sibling Sibling before which to insert 515 453 * @param ctype Child type … … 518 456 * @return EOK on success or ENOMEM if out of memory 519 457 */ 520 errno_t sif_node_insert_before(sif_ trans_t *trans, sif_node_t *sibling,521 const char *ctype,sif_node_t **rchild)458 errno_t sif_node_insert_before(sif_node_t *sibling, const char *ctype, 459 sif_node_t **rchild) 522 460 { 523 461 sif_node_t *child; … … 543 481 * Create a new child and insert it after an existing child. 544 482 * 545 * @param trans Transaction546 483 * @param sibling Sibling after which to insert 547 484 * @param ctype Child type … … 550 487 * @return EOK on success or ENOMEM if out of memory 551 488 */ 552 errno_t sif_node_insert_after(sif_ trans_t *trans, sif_node_t *sibling,553 const char *ctype,sif_node_t **rchild)489 errno_t sif_node_insert_after(sif_node_t *sibling, const char *ctype, 490 sif_node_t **rchild) 554 491 { 555 492 sif_node_t *child; … … 573 510 /** Destroy SIF node. 574 511 * 575 * This function does not return an error, but the transaction may still576 * fail to complete.577 *578 * @param trans Transaction579 512 * @param node Node to destroy 580 513 */ 581 void sif_node_destroy(sif_ trans_t *trans, sif_node_t *node)514 void sif_node_destroy(sif_node_t *node) 582 515 { 583 516 list_remove(&node->lparent); … … 587 520 /** Set node attribute. 588 521 * 589 * @param trans Transaction590 522 * @param node SIF node 591 523 * @param aname Attribute name … … 594 526 * @return EOK on success, ENOMEM if out of memory 595 527 */ 596 errno_t sif_node_set_attr(sif_ trans_t *trans, sif_node_t *node,597 const char *a name, const char *avalue)528 errno_t sif_node_set_attr(sif_node_t *node, const char *aname, 529 const char *avalue) 598 530 { 599 531 odlink_t *link; … … 636 568 /** Unset node attribute. 637 569 * 638 * This function does not return an error, but the transaction may still639 * fail to complete.640 *641 * @param trans Transaction642 570 * @param node Node 643 571 * @param aname Attribute name 644 572 */ 645 void sif_node_unset_attr(sif_trans_t *trans, sif_node_t *node, 646 const char *aname) 573 void sif_node_unset_attr(sif_node_t *node, const char *aname) 647 574 { 648 575 odlink_t *link; … … 658 585 } 659 586 660 /** Export stringto 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. 663 590 * 664 591 * @param str String … … 666 593 * @return EOK on success, EIO on I/O error 667 594 */ 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 */ 668 611 static errno_t sif_export_string(const char *str, FILE *f) 669 612 { 670 613 const char *cp; 671 614 672 if (fputc(' [', f) == EOF)615 if (fputc('"', f) == EOF) 673 616 return EIO; 674 617 675 618 cp = str; 676 619 while (*cp != '\0') { 677 if (*cp == ']' || *cp == '\\') { 678 if (fputc('\\', f) == EOF) 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) 679 628 return EIO; 680 629 } 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 */ 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) 682 656 return EIO; 683 657 ++cp; 684 658 } 685 659 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 * * 697 665 * @param f File 698 666 * @param rstr Place to store pointer to newly allocated string 699 667 * @return EOK on success, EIO on I/O error 700 668 */ 701 static errno_t sif_import_ string(FILE *f, char **rstr)669 static errno_t sif_import_name(FILE *f, char **rstr) 702 670 { 703 671 char *str; … … 715 683 716 684 c = fgetc(f); 717 if ( c != '[') {685 if (!sif_is_name_start_char(c)) { 718 686 rc = EIO; 719 687 goto error; … … 721 689 722 690 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 740 691 if (sidx >= str_size) { 741 692 str_size *= 2; … … 750 701 751 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 != '"') { 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; 752 792 } 753 793 … … 775 815 int c; 776 816 777 rc = sif_import_ string(f, &aname);817 rc = sif_import_name(f, &aname); 778 818 if (rc != EOK) 779 819 goto error; … … 818 858 errno_t rc; 819 859 820 rc = sif_export_ string(attr->aname, f);860 rc = sif_export_name(attr->aname, f); 821 861 if (rc != EOK) 822 862 return rc; … … 844 884 sif_node_t *child; 845 885 846 rc = sif_export_string(node->ntype, f); 886 if (fputc('<', f) == EOF) 887 return EIO; 888 889 rc = sif_export_name(node->ntype, f); 847 890 if (rc != EOK) 848 891 return rc; … … 850 893 /* Attributes */ 851 894 852 if (fputc('(', f) == EOF)853 return EIO;854 855 895 attr = sif_node_first_attr(node); 856 896 while (attr != NULL) { 897 if (fputc(' ', f) == EOF) 898 return EIO; 899 857 900 rc = sif_export_attr(attr, f); 858 901 if (rc != EOK) … … 862 905 } 863 906 864 if (fput c(')', f) == EOF)907 if (fputs(">\n", f) == EOF) 865 908 return EIO; 866 909 867 910 /* Child nodes */ 868 869 if (fputc('{', f) == EOF)870 return EIO;871 911 872 912 child = sif_node_first_child(node); … … 879 919 } 880 920 881 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) 882 929 return EIO; 883 930 … … 890 937 * @param f File 891 938 * @param rnode Place to store pointer to imported node 939 * @param rendtag Place to store @c true iff end tag is encountered 892 940 * @return EOK on success, EIO on I/O error 893 941 */ 894 static errno_t sif_import_node(sif_node_t *parent, FILE *f, sif_node_t **rnode) 942 static errno_t sif_import_node(sif_node_t *parent, FILE *f, sif_node_t **rnode, 943 bool *rendtag) 895 944 { 896 945 errno_t rc; … … 898 947 sif_node_t *child; 899 948 sif_attr_t *attr = NULL; 949 bool endtag; 950 bool cendtag; 900 951 char *ntype; 901 952 int c; … … 905 956 return ENOMEM; 906 957 907 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); 908 976 if (rc != EOK) 909 977 goto error; … … 912 980 913 981 /* Attributes */ 914 915 c = fgetc(f);916 if (c != '(') {917 rc = EIO;918 goto error;919 }920 982 921 983 c = fgetc(f); … … 925 987 } 926 988 927 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); 928 998 ungetc(c, f); 929 999 … … 943 1013 /* Child nodes */ 944 1014 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; 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); 970 1027 } 971 1028 } 972 1029 973 1030 *rnode = node; 1031 *rendtag = endtag; 974 1032 return EOK; 975 1033 error:
Note:
See TracChangeset
for help on using the changeset viewer.