/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=2 et sw=2 tw=80: */ #if !defined(NET_FOSSIL_SCM_FSL_CONTENT_H_INCLUDED) #define NET_FOSSIL_SCM_FSL_CONTENT_H_INCLUDED /* ** Copyright (c) 2013 D. Richard Hipp ** ** This program is free software; you can redistribute it and/or ** modify it under the terms of the Simplified BSD License (also ** known as the "2-Clause License" or "FreeBSD License".) ** ** This program is distributed in the hope that it will be useful, ** but without any warranty; without even the implied warranty of ** merchantability or fitness for a particular purpose. ** ** Author contact information: ** drh@hwaci.com ** http://www.hwaci.com/drh/ ** ******************************************************************************* */ #include "fossil-db.h" /* MUST come first b/c of config macros */ #if defined(__cplusplus) extern "C" { #endif typedef struct fsl_card_F fsl_card_F; typedef struct fsl_card_J fsl_card_J; typedef struct fsl_card_Q fsl_card_Q; typedef struct fsl_card_T fsl_card_T; typedef struct fsl_deck fsl_deck; /** ** The types of control artifacts used by Fossil. Their numeric ** values are a hard-coded part of the Fossil architecture and must ** not be changed. */ enum fsl_catype_t { /** ** Sentinel value used for some error reporting. */ FSL_CATYPE_INVALID = -1, /** ** Sentinel value used to mark a deck as being "any" type. This is ** a placeholder on a deck's way to completion. */ FSL_CATYPE_ANY = 0, /** ** Indicates a "manifest" artifact (a checkin record). */ FSL_CATYPE_MANIFEST = 1, /** ** Indicates a "cluster" artifact. These are used during synchronization. */ FSL_CATYPE_CLUSTER = 2, /** ** Indicates a "control" artifact (a tag change). */ FSL_CATYPE_CONTROL = 3, /** ** Indicates a "wiki" artifact. */ FSL_CATYPE_WIKI = 4, /** ** Indicates a "ticket" artifact. */ FSL_CATYPE_TICKET = 5, /** ** Indicates a "manifest" artifact (a checkin record). */ FSL_CATYPE_ATTACHMENT = 6, /** ** Indicates a "manifest" artifact (a checkin record). */ FSL_CATYPE_EVENT = 7 }; typedef enum fsl_catype_t fsl_catype_t; /** ** Returns some arbitrary but distinct string for the given ** fsl_catype_t. The returned bytes are static and ** NUL-terminated. Intended primarily for debugging and informative ** purposes, not actual user output. */ char const * fsl_catype_cstr(fsl_catype_t t); /** ** Categories of artifacts Fossil internally supports. ** ** The numeric values come from the closely-related fsl_catype_t, ** but are not part of the internal Fossil architecture in ** this particular context (they are in a fsl_catype_t context, ** however). ** ** TODO: we can consolidate this with fsl_catype_t, which covers ** these, if a bit more cryptically. */ enum fsl_atype_t { /** ** Sentinel value indicating "any artifact type." */ FSL_ATYPE_ANY = FSL_CATYPE_ANY, /** ** Indicates a "checkin" artifact or timeline event. */ FSL_ATYPE_CHECKIN = FSL_CATYPE_MANIFEST, /** ** Indicates an "event" artifact or timeline event. */ FSL_ATYPE_EVENT = FSL_CATYPE_EVENT, /** ** Indicates a "tag" artifact or timeline event. */ FSL_ATYPE_TAG = FSL_CATYPE_CONTROL, /** ** Indicates a "ticket" artifact or timeline event. */ FSL_ATYPE_TICKET = FSL_CATYPE_TICKET, /** ** Indicates a "wiki" artifact or timeline event. */ FSL_ATYPE_WIKI = FSL_CATYPE_WIKI }; typedef enum fsl_atype_t fsl_atype_t; /** ** For a given artifact type, it returns the key string used in the ** event.type db table. Returns NULL if passed an unknown value, ** otherwise the returned bytes are static and NUL-terminated. The ** type FSL_ATYPE_ANY returns "*", which can be used in GLOB ** clauses in queries. */ char const * fsl_atype_cstr(fsl_atype_t t); /** ** This function is a programmatic interpretation of ** this table: ** ** http://fossil-scm.org/index.html/doc/trunk/www/fileformat.wiki#summary ** ** For a given control artifact type and a card name in the form of ** the card name's letter (e.g. 'A', 'W', ...), this function ** returns 0 (false) if that card type is not permitted in this ** control artifact type, a negative value if the card is optional ** for this artifact type, and a positive value if the card type is ** required for this artifact type. ** ** As a special case, if t==FSL_CATYPE_ANY then this function ** always returns a negative value as long as card is a valid card ** letter. **/ char fsl_card_is_legal( fsl_catype_t t, char card ); /** ** Permissions flags supported by Manifests. Their numeric values ** are a hard-coded part of the Fossil architecture and must not be ** changed. */ enum fsl_mf_perm_t { /** Indicates a regular, writable file. */ FSL_MF_PERM_REGULAR = 0, /** Indicates a regular file with the executable bit set. */ FSL_MF_PERM_EXE = 0x1, /** Indicates a symlink. Note that symlinks do not have the executable bit set separately on Unix systems. */ FSL_MF_PERM_LINK = 0x2 }; typedef enum fsl_mf_perm_t fsl_mf_perm_t; /** ** Artifact tag types used by the Fossil framework. Their values ** are a hard-coded part of the Fossil format, and not subject to ** change (only extension, possibly). **/ enum fsl_tag_type { /** ** Sentinel value for use with constructors/initializers. */ FSL_TAGTYPE_INVALID = -1, /** ** The "cancel tag" indiciator, a.k.a. an anti-tag. */ FSL_TAGTYPE_CANCEL = 0, /** ** The "add tag" indicator, a.k.a. a singleton tag. */ FSL_TAGTYPE_ADD = 1, /** ** The "propagating tag" indicator. */ FSL_TAGTYPE_PROPAGATING = 2 }; typedef enum fsl_tag_type fsl_tag_type; /** ** Hard-coded IDs used by the 'tag' table of repository DBs. These ** values get installed as part of the base Fossil db schema in new ** repos, and they must never change. */ enum fsl_tag_ids { /** ** DB string tagname='bgcolor'. */ FSL_TAGID_BGCOLOR = 1, /** ** DB: tag.tagname='comment'. */ FSL_TAGID_COMMENT = 2, /** ** DB: tag.tagname='user'. */ FSL_TAGID_USER = 3, /** ** DB: tag.tagname='date'. */ FSL_TAGID_DATE = 4, /** ** DB: tag.tagname='hidden'. */ FSL_TAGID_HIDDEN = 5, /** ** DB: tag.tagname='private'. */ FSL_TAGID_PRIVATE = 6, /** ** DB: tag.tagname='cluster'. */ FSL_TAGID_CLUSTER = 7, /** ** DB: tag.tagname='branch'. */ FSL_TAGID_BRANCH = 8, /** ** DB: tag.tagname='closed'. */ FSL_TAGID_CLOSED = 9, /** ** DB: tag.tagname='parent'. */ FSL_TAGID_PARENT = 10, /** ** Largest tag ID reserved for internal use. */ FSL_TAGID_MAX_INTERNAL = 16 }; /** ** A "deck" stores (predictably enough) a collection of "cards." ** Cards are constructs embedded within Fossil's Control Artifacts ** to denote various sorts of changes in a Fossil repository, and a ** Deck encapsulates the cards for a single Control Artifact of an ** arbitrary type, e.g. Manifest or Cluster. A card is basically a ** command with a single-letter name and a well-defined signature ** for its arguments. Each card is represented by a member of this ** struct whose name is the same as the card type. Not all cards ** are valid in all Control Artifact types. See the list of valid ** card/construct combinations here: ** ** http://fossil-scm.org/index.html/doc/trunk/www/fileformat.wiki#summary ** ** fsl_card_is_legal() can be used determine if a given card type ** is legal (per the above chart) with a given Control Artifiact ** type (as stored in the fsl_deck::type member). ** ** The type member is used by some algorithms to determine which ** operations are legal on a given Control Artifact type, so that ** they can fail long before the user gets a chance to add a ** malformed Control Artifact to the database. Clients who bypass ** the Deck APIs and manipulate the deck's members "by hand" (so to ** say) effectively invoke undefined behaviour. ** ** The various routines to add/set cards in the deck are named ** fsl_deck_CARDNAME_add() resp. fsl_deck_CARDNAME_set(). The "add" ** functions represent cards which may appear multiple times ** (e.g. the 'F' card) or have multiple values (the 'P' card), and ** those named "set" represent unique or optional cards. The R-card ** is the outlier, with fsl_deck_R_calc(). ** ** Note that the 'Z' card is not in this structure because it is a ** hash of the other inputs and is calculated incrementally and ** appended automatically by fsl_deck_output(). Adding the Z card ** to this class would require that fsl_deck_output() and friends ** take a non-const deck object (because Z is calculated ** incrementally during output of the CA), which just seems ** philosophically wrong for an output operation. It might be ** useful to expand fsl_deck_output() to write the Z card's result ** to an optional output parameter. ** ** Maintenance reminder: please keep the card members alpha sorted to ** simplify eyeball-searching through their docs. ** ** @see fsl_deck_init() ** @see fsl_deck_parse() ** @see fsl_deck_load_rid() ** @see fsl_deck_finalize() ** @see fsl_deck_malloc() ** @see fsl_deck_clean() ** @see fsl_deck_save() */ struct fsl_deck { /** ** Specifies the the type (or eventual type) of this ** artifact. The function fsl_card_is_legal() can be used to ** determined if a given card type is legal for a given value of ** this member. APIs which add/set cards use that to determine if ** the operation requested by the client is semantically legal. */ fsl_catype_t type; /** ** DB repo.blob.rid value. Normally set by fsl_deck_parse(). */ fsl_id_t rid; /** ** Gets set by fsl_deck_parse() to the hash/UUID of the ** manifest it parsed. Normally set by fsl_deck_parse(). */ fsl_uuid_str uuid; /** ** The Fossil context responsible for this deck. We store ** this so that some API routines to not require the caller ** to explicitly pass around the context. */ fsl_cx * f; /** ** The 'A' (attachment) card. Only used by FSL_CATYPE_ATTACHMENT ** decks. The spec currently specifies only 1 A card per ** manifest, but conceptually this could/should be a list. */ struct { /** ** Filename of the A card. */ fsl_buffer name; /** ** Name of event or UUID of ticket or wiki page to which the ** attachment applies. For tickets/events, the "name" is its ** UUID. */ char * tgt; /** ** UUID of the file being attached via the A card. */ fsl_uuid_str src; } A; struct { /** ** The 'B' (baseline) card holds the UUID of baseline manifest. ** This is empty for baseline manifests and holds the UUID of ** the parent for delta manifests. */ fsl_uuid_str uuid; /** ** Baseline manifest corresponding to this->B. It is loaded on ** demand. The parent/child relationship in Fossil is the ** reverse of conventional - children own their parents, not ** the other way around. i.e. this->baseline will get cleaned ** up (recursively) when this instance is cleaned up (when the ** containing deck is cleaned up). */ fsl_deck * baseline; } B; /** ** The 'C' (comment) card. */ fsl_buffer C; /** ** The 'D' (date) card, in Julian format. */ fsl_double_t D; /** ** The 'E' (event) card. */ struct { /** ** The 'E' card's date in Julian Day format. */ fsl_double_t julian; /** ** The 'E' card's UUID. */ fsl_uuid_str uuid; } E; /** ** The 'F' (file) card container. */ struct { /** ** A list of 'F' (file) cards. Contains (fsl_card_F*) ** entries. */ fsl_list list; /** ** An internal cursor into this->list used for certain ** operations. */ fsl_int_t cursor; } F; /** ** The 'J' card specifies changes to "value" of "fields" in ** tickets (FSL_CATYPE_TICKET). ** ** Holds (fsl_card_J*) entries. */ fsl_list J; /** ** The 'L' (wiki name/title) card. */ fsl_buffer L; /** ** UUID for the 'K' (ticket) card. */ fsl_uuid_str K; /** ** List of UUIDs (char*) of ref'd objects in a cluster ('M' cards). */ fsl_list M; /** ** The 'N' (content mime type) card. */ fsl_buffer N; /** ** List of UUIDs of parents ('P' cards). Entries are of type ** (fsl_uuid_str*). */ fsl_list P; /** ** 'Q' (cherry pick) cards. Holds (fsl_card_J*) entries. */ fsl_list Q; /** ** The R card holds a hash which is calculated based on the ** names, sizes, and contents of the files included in a ** manifest. */ fsl_uuid_str R; /** ** List of 'T' (tag) cards. Holds (fsl_card_T*) instances. */ fsl_list T; /** ** The U (user) card. */ fsl_buffer U; /** ** The W (wiki content) card. */ fsl_buffer W; /** ** For propagating error state through certain parts of the API. */ fsl_error error; /** ** Porting artifact - original content blob. Quite possibly not ** needed at this level of API abstraction. */ fsl_buffer content; /** ** To be used for a manifest cache. */ fsl_deck * next; /** ** A marker which tells fsl_deck_finalize() whether or not ** fsl_deck_malloc() allocated this instance (in which case ** fsl_deck_finalize() will fsl_free() it) or not (in which case ** it does not free() it). */ void const * allocStamp; }; /** ** Empty-initialized fsl_deck structure. */ extern const fsl_deck fsl_deck_empty; /** ** Empty-initialized fsl_deck structure. */ #define fsl_deck_empty_m { \ FSL_CATYPE_ANY /*type*/, \ -1/*rid*/,\ NULL/*uuid*/,\ NULL/*f*/,\ {/*A*/ fsl_buffer_empty_m /* name */, \ NULL /* tgt */, \ NULL /* src */}, \ {/*B*/ NULL /*uuid*/, \ NULL /*baseline*/}, \ fsl_buffer_empty_m /* C */, \ 0.0 /*D*/, \ {/*E*/ 0.0 /* julian */, \ NULL /* uuid */}, \ {/*F*/ fsl_list_empty_m /*list*/,0/*cursor*/}, \ fsl_list_empty_m /* J */, \ fsl_buffer_empty_m /* L */, \ NULL /* K */, \ fsl_list_empty_m /* M */, \ fsl_buffer_empty_m /* N */, \ fsl_list_empty_m /* P */, \ fsl_list_empty_m /* Q */, \ NULL /* R */, \ fsl_list_empty_m /* T */, \ fsl_buffer_empty_m /* U */, \ fsl_buffer_empty_m /* W */, \ fsl_error_empty_m /* error */, \ fsl_buffer_empty_m/*content*/, \ NULL/*next*/,\ NULL/*allocStamp*/ \ } /** ** Allocates a new fsl_deck instance. Returns NULL on allocation ** error. The returned value must eventually be passed to ** fsl_deck_finalize() to free its resources. ** ** @see fsl_deck_finalize() **/ fsl_deck * fsl_deck_malloc(); /** ** Frees all resources belonging to the given deck's members ** (including its parents, recursively), and wipes deck clean, but ** does not free() deck. Is a no-op if deck is NULL. ** ** @see fsl_deck_finalize() */ void fsl_deck_clean(fsl_deck *deck); /** ** Frees all memory owned by deck (see fsl_deck_clean()). If deck ** was allocated using fsl_deck_malloc() then this function ** fsl_free()'s it, otherwise it does not free it. ** ** @see fsl_deck_malloc() ** @see fsl_deck_clean() */ void fsl_deck_finalize(fsl_deck *deck); /** ** Sets the 'A' card for an Attachment (FSL_CATYPE_ATTACHMENT) ** deck. Returns 0 on success. ** ** Returns FSL_RC_MISUSE if any of (mf, filename, target) are NULL, ** FSL_RC_RANGE if !*filename or if uuidSrc is not NULL and ** fsl_is_uuid(uuidSrc) returns false. ** ** Returns FSL_RC_TYPE if mf is not (as determined by its mf->type ** member) of a deck type capable of holding 'A' cards. (Only decks ** of type FSL_CATYPE_ATTACHMENT may hold an 'A' card.) If uuidSrc ** is NULL or starts with a NUL byte then it is ignored, otherwise ** the same restrictions apply to it as to target. ** ** The target parameter represents the "name" of the ** wiki/ticket/event record to which the attachment applies. For ** wiki pages this is their normal name (e.g. "MyWikiPage"). For ** events and tickets it is their full 40-byte UUID. ** ** uuidSrc is the UUID of the attachment blob itself. If it is NULL ** or empty then this card indicates that the attachment will be ** "deleted" (insofar as anything is ever deleted in Fossil). */ int fsl_deck_A_set( fsl_deck * mf, char const * filename, char const * target, fsl_uuid_cstr uuidSrc); /** ** Sets or unsets (if uuidBaseline is NULL or empty) the B card for ** the given manifest. Returns 0 on success, FSL_RC_MISUSE if !mf, ** FSL_RC_OOM on allocation error. ** ** Returns FSL_RC_RANGE if uuidBaseline is not NULL or exactly ** FSL_UUID_STRLEN bytes. If it is NULL the current value is ** freed. ** ** Returns FSL_RC_TYPE if mf is not syntactically allowed to have ** this card card (as determined by ** fsl_card_is_legal(mf->type,...)) or if the value is not a ** syntactically valid UUID. **/ int fsl_deck_B_set( fsl_deck * mf, fsl_uuid_cstr uuidBaseline); /** ** Semantically identical fsl_deck_B_set() but sets the C card and ** does not place a practical limit on the comment's length. ** comment must be the comment text for the change being applied. */ int fsl_deck_C_set( fsl_deck * mf, char const * comment, fsl_int_t cardLen); /** ** Sets mf's D card as a Julian Date value. Returns FSL_RC_MISUSE ** if !mf, FSL_RC_RANGE if date is negative, else 0. Has no ** side-effects on error. ** */ int fsl_deck_D_set( fsl_deck * mf, fsl_double_t date); /** ** Sets the E card in the given deck. date may not be negative - use ** fsl_db_julian_now() or similar to get a default time if needed. ** Retursn FSL_RC_MISUSE if !mf or !uuid, FSL_RC_RANGE if date is ** not positive, FSL_RC_RANGE if uuid is not a valid UUID string. ** ** Note that the UUID for an event, unlike most other UUIDs, need ** not be calculated - it may be a random hex string, but it must ** pass the fsl_is_uuid() test. Use fsl_db_random_hex() to generate ** random UUIDs. When editing events, e.g. using the HTML UI, only ** the most recent event with the same UUID is shown. So when ** updating events, be sure to apply the same UUID to the edited ** copies before saving them. */ int fsl_deck_E_set( fsl_deck * mf, fsl_double_t date, fsl_uuid_cstr uuid); /** ** Adds a new F card to the given deck. The uuid argument is ** required to pass the fsl_is_uuid() test. The name must be a ** "simplified path name" (as determined by ** fsl_is_simple_pathname()), or FSL_RC_RANGE is returned. ** ** perms should be one of the fsl_mf_perm_t values (0 is the usual ** case). ** ** oldName must only be non-NULL when renaming a file, and it must ** follow the same naming rules as the name parameter. ** ** Returns 0 on success. **/ int fsl_deck_F_add( fsl_deck * mf, char const * name, fsl_uuid_cstr uuid, fsl_mf_perm_t perms, char const * oldName); /** ** Adds the given file instance to the given deck. Returns 0 on ** success, FSL_RC_MISUSE if either argument is NULL, FSL_RC_OOM if ** appending the file to the list fails. ** ** On success ownership of t is passed to mf. On error ownership is ** not modified. */ int fsl_deck_F_add2( fsl_deck * mf, fsl_card_F * t); /** ** Callback type for use with fsl_deck_F_foreach() and ** friends. Implementations must return 0 on success, FSL_RC_BREAK ** to abort looping without an error, and any other value on error. */ typedef int (*fsl_card_F_visitor_f)(fsl_card_F const * fc, void * state); /** ** For each F-card in d, cb(card,visitorState) is called. Returns ** the result of that loop. If cb returns FSL_RC_BREAK, the ** visitation loop stops immediately and this function returns ** 0. If cb returns any other non-0 code, looping stops and that ** code is returned immediately. ** ** If includeBaseline is false or d has no baseline then this ** function behaves identically to fsl_deck_F_foreach_own(), ** otherwise it iterates through the F-cards such that d's copy of ** a given card takes precedence, if it is set. ** ** If includeBaseline is true and d appears to have a baseline, but ** it is not loaded, this function loads it. ** ** The F-cards will be visited in the order they are declared in ** d. For loaded manifests this is always lexical order (consistent ** across the delta and baseline for delta manifests). For ** hand-created decks which have not been fsl_deck_unshuffle()'d, ** the order is unspecified. */ int fsl_deck_F_foreach( fsl_deck * d, char includeBaseline, fsl_card_F_visitor_f cb, void * visitorState ); /** ** Works similarly to fsl_deck_F_foreach() except that it only ** loops over d's own F-cards (which allows d to remain const), ** ignoring any F-cards from its baseline manifest (if any). */ int fsl_deck_F_foreach_own( fsl_deck const * d, fsl_card_F_visitor_f cb, void * visitorState ); /** ** Fetches the next F-card entry from d. fsl_deck_F_rewind() must ** have be successfully executed one time before calling this, ** as that routine ensures that the baseline is loaded (if needed), ** which is needed for proper iteration. ** ** On success 0 is returned and *f is assigned to the next F-card. ** If *f is NULL when returning 0 then the end of the list has been ** reached (fsl_deck_F_rewind() can be used to re-set it). ** ** Example usage: ** ** @code ** int rc; ** fsl_card_F const * fc = NULL; ** rc = fsl_deck_F_rewind(d); ** if(!rc) while( !(rc=fsl_deck_F_next(d, &fc)) && fc) {...} ** @endcode ** */ int fsl_deck_F_next( fsl_deck * d, fsl_card_F const **f ); /** ** Rewinds d's F-card traversal iterator and loads d's baseline ** manifest, if it has one and it is not loaded already. Returns 0 ** on success. The only error condition is if loading of the ** a baseline manifest fails. */ int fsl_deck_F_rewind( fsl_deck * d ); /** ** Adds a J card to the given deck, setting/updating the given ticket ** property key to the given value. The key is required but the value ** is optional (may be NULL). If isAppend then the value is appended ** to any existing value, otherwise it replaces any existing value. ** ** It is currently unclear whether it is legal to include multiple ** J cards for the same key in the same control artifact, in ** particular if their isAppend values differ. ** ** Returns 0 on success, FSL_RC_MISUSE if !mf or !key, FSL_RC_RANGE ** if !*field, FSL_RC_TYPE if mf is of a type for which J cards are ** not legal (see fsl_card_is_legal()), FSL_RC_OOM on allocation ** error. */ int fsl_deck_J_add( fsl_deck * mf, char isAppend, char const * key, char const * value ); /** ** Semantically identical fsl_deck_B_set() but sets the K card. uuid ** must be the UUID of the ticket this change is being applied to. */ int fsl_deck_K_set( fsl_deck * mf, fsl_uuid_cstr uuid); /** ** Semantically identical fsl_deck_B_set() but sets the L card. ** title must be the wiki page title text of the wiki page this ** change is being applied to. */ int fsl_deck_L_set( fsl_deck * mf, char const *title, fsl_int_t len); /** ** Adds the given UUID as an M-card entry. Returns 0 on success, or: ** ** FSL_RC_MISUSE if !mf or !uuid ** ** FSL_RC_TYPE if fsl_deck_check_type(mf,'M') returns false. ** ** FSL_RC_RANGE if !fsl_is_uuid(uuid). ** ** FSL_RC_OOM if memory allocation fails while adding the entry. */ int fsl_deck_M_add( fsl_deck * mf, fsl_uuid_cstr uuid ); /** ** Semantically identical fsl_deck_B_set() but sets the N card. ** mimeType must be the content mime type for COMMENT text of the ** change being applied. */ int fsl_deck_N_set( fsl_deck * mf, char const *mimeType, fsl_int_t len); /** ** Adds the given UUID as a parent of the mf change record. If len ** is less than 0 then fsl_strlen(parentUuid) is used to determine ** its length. Returns FSL_RC_MISUE if !mf, !parentUuid, or ** !*parentUuid. Returns FSL_RC_RANGE if parentUuid is not 40 ** bytes long. ** ** The first P card added to a deck MUST be the UUID of its primary ** parent (one which was not involved in a merge operation). All others ** are considered "non-primary." ** ***/ int fsl_deck_P_add( fsl_deck * mf, fsl_uuid_cstr parentUuid); /** ** If d contains a P card with the given index, this returns the ** RID corresponding to the UUID at that index. Returns a negative ** value on error, 0 if there is no for that index or the index ** is out of bounds. */ fsl_id_t fsl_deck_P_get_id(fsl_deck * d, int index); /** ** Adds a Q card record to the given deck. type must be negative ** for a backed-out change, positive for a cherrypicked change. ** target must be a valid UUID string. If baseline is not NULL then ** it also must be a valid UUID. ** ** Returns 0 on success, non-0 on error. FSL_RC_MISUSE if !mf ** or !target, FSL_RC_RANGE if target/baseline are not valid ** UUID strings (baseline may be NULL). */ int fsl_deck_Q_add( fsl_deck * mf, char type, fsl_uuid_cstr target, fsl_uuid_cstr baseline ); /** ** Functionally identical to fsl_deck_B_set() except that it sets ** the R card. Returns 0 on succes, FSL_RC_RANGE if md5 is not NULL ** or exactly FSL_MD5_STRLEN bytes long (not including trailing ** NUL). If md5==NULL the current R value is cleared. ** ** Reminder: calculating/verifying the proper R hash value is ** currently (20130814) beyond the capabilities of the library - ** there is still work to do here. */ int fsl_deck_R_set( fsl_deck * mf, char const *md5); /** ** Adds a new T card entry to the given deck. ** ** If uuid is not NULL and fsl_is_uuid(uuid) returns false then ** this function returns NULL. If it is NULL then it is assumed ** to be the UUID of the currently-being-constructed manifest in which ** the tag is contained. ** ** Returns 0 on success. Returns FSL_RC_MISUE if !mf or ** !name. Returns FSL_RC_TYPE (and update's mf's error state with a ** message) if the T card is not legal for mf (see ** fsl_card_is_legal()). Returns FSL_RC_RANGE if !*name, tagType ** is invalid, or if uuid is not NULL and fsl_is_uuid(uuid) ** return false. Returns FSL_RC_OOM if an allocation fails. **/ int fsl_deck_T_add( fsl_deck * mf, fsl_tag_type tagType, fsl_uuid_cstr uuid, char const * name, char const * value); /** ** Adds the given tag instance to the given manifest. ** Returns 0 on success, FSL_RC_MISUSE if either argument ** is NULL, FSL_RC_OOM if appending the tag to the list ** fails. ** ** On success ownership of t is passed to mf. On error ownership is ** not modified. ** */ int fsl_deck_T_add2( fsl_deck * mf, fsl_card_T * t); /** ** Calculates the value of d's R-card based on its F-cards and ** updates d->R. It may also, as a side-effect, sort d->F.list ** lexically (a requirement of a R-card calculation). Returns 0 on ** success. This requires an opened repository db but not a ** checkout. ** ** Note that this calculation is exceedingly memory-hungry. ** While Fossil originally required R cards, the cost of ** calculation eventually caused the R-card to be made ** optional. This API currently requires the card if F-cards used ** but will at some point offer a flag to disable its generation. ** ** This requires that d->f have an opened repository. */ int fsl_deck_R_calc(fsl_deck * d); /** ** Semantically identical fsl_deck_B_set() but sets the U card. ** userName must be the user who's name should be recorded for ** this change. */ int fsl_deck_U_set( fsl_deck * mf, char const *userName, fsl_int_t len); /** ** Semantically identical fsl_deck_B_set() but sets the W card. ** content must be the page content of the wiki page this change is ** being applied to. */ int fsl_deck_W_set( fsl_deck * mf, char const *content, fsl_int_t len); /** ** Must be called to initialize a newly-created/allocated deck ** instance. This function clears out all contents of the d ** parameter except for its (f, type, allocStamp) members, sets its ** (f, type) members, and leaves d->allocStamp intact. */ void fsl_deck_init( fsl_cx * cx, fsl_deck * d, fsl_catype_t type ); /** ** Returns true (non-0) if d contains data for all _required_ ** cards, as determined by the value of d->type, else returns false ** (0). It returns 0 if d->type==FSL_CATYPE_ANY, as that is a ** placeholder value intended to be re-set by the deck's user. */ char fsl_deck_has_required_cards( fsl_deck const * d ); /** ** Prepares the given deck for output by ensuring that cards ** which need to be sorted are sorted, and it may run some ** last-minute validation checks. ** ** The cards which get sorted are: F, J, M, Q, T. The P-card list ** is _not_ sorted - the client is responsible for ensuring that ** the primary parent is added to that list first, and after that ** the ordering is largely irrelevant. ** ** If calculateRCard is true and fsl_card_is_legal(d,'R') then this ** function calculates the R-card for the deck if it has ** F-cards. The R-card calculation is _extremely_ memory-hungry but ** adds another level of integrity checking to fossil. ** ** The R card, if used, must be calculated before ** fsl_deck_output()ing a deck containing F cards. Clients may ** alternately call fsl_deck_R_calc() to calculate the R card ** separately, but there is little reason to do so. Historically ** speaking the R-card was required when F-cards were used, but it ** was eventually made optional because (A) the memory cost and (B) ** it's part of a 3rd or 4th level of integrity-related checks, and ** is somewhat superfluous. */ int fsl_deck_unshuffle( fsl_deck * d, char calculateRCard ); /** ** Renders the given control artifact's contents to the given ** output function and calculates any cards which cannot be ** calculated until the contents are complete (namely the R and Z ** cards). ** ** fsl_deck_unshuffle() must have been called on the deck first to ** finalize any pending values to be calculated for the deck. ** ** Returns 0 on success, FSL_RC_MISUSE if !mf or !f. If ** f() returns non-0, that code is returned. outputState is passed ** as the first argument to f(). f() may be called an arbitrary ** number of times by this routine. ** ** The cx argument is the fossil context for which the manifest is ** being created. It must have an open checkout database, or ** FSL_RC_MISUSE is returned. It is required because generating ** parts of the manifest require access to the underlying database ** API. ** ** The exact structure of the ouput depends on the value of ** mf->type, and FSL_RC_TYPE is returned if this function cannot ** figure out what to do with the given cardset's type. ** ** Returns FSL_RC_CA_SYNTAX if fsl_deck_has_required_cards(mf) ** returns false. ** ** If the calls to out() fail then its result code is returned ** to the caller of this function. ** ** In most non-trivial error cases f's error state will be updated ** with an explaination of the problem. ** ** @see fsl_deck_unshuffle(). ** **/ int fsl_deck_output( fsl_cx * f, fsl_deck const * mf, fsl_output_f out, void * outputState ); /** ** Saves the given deck into f's repository database as new control ** artifact content. If isPrivate is true then the content is ** marked as private, otherwise it is not. Note that the isPrivate ** is a suggestion and might be trumped by existing state within f ** or its repository, and such a trumping is not treated as an ** error. e.g. tags are automatically private when they tag private ** content. ** ** Before saving, the deck is passed through fsl_deck_unshuffle() ** and fsl_deck_output(), which will fail for a variety of ** easy-to-make errors such as the deck missing required cards. ** For unshuffle purposes, the R-card currently gets calculated if ** the deck has any F-cards, but the plan is to make that a ** flag/option. ** ** Returns 0 on success, the usual non-0 suspects on error. ** ** If d->rid and d->uuid are set when this is called, it is assumed ** that we are saving existing or phantom content, and that case: ** ** - An existing phantom is populated with the new content. ** ** - If an existing record is found with a non-0 size then it is ** not modified but this is currently not treated as an error (for ** historical reasons, though one could argue that it should result ** in FSL_RC_ALREADY_EXISTS). ** ** ACHTUNG: the pre-existing blob case is as yet untested! ** ** If d->rid and d->uuid are not set when this is called then... on ** success, d->rid and d->uuid will contain the values held by ** their counterparts in the blob table. They will only be set on ** success because they would otherwise refer to db records which ** get destroyed when the transaction rolls back. ** ** After saving, the deck gets crosslinked (fsl_deck_crosslink()) ** to update any relationships described by the deck. ** ** The save operation happens within a transaction, of course, and ** on any sort of error, db-side changes are rolled back. Note that ** is _is_ legal to start the transaction before calling this, ** which effectively makes this operation part of that transaction. ** ** Maintenance reminder: this function also does a small bit of ** artifact-type-specific processing. ** ** @see fsl_deck_output() ** @see fsl_content_put_ex() */ int fsl_deck_save( fsl_deck * d, char isPrivate ); /** ** Subject to change. ** ** Runs postprocessing on the manifest represented by d. d->rid ** must be set and valid and d's contents must accurately represent ** the stored manifest for the given rid. This is normally run just ** after the insertion of a new manifest, but will (eventually) ** also be run as part of a rebuild. ** ** Returns FSL_RC_MISUSE if !f or !d, FSL_RC_RANGE if d->rid<=0, ** FSL_RC_MISUSE (with more error info in f) if d does not ** contain all required cards for its d->type value. ** ** Parts which are currently missing: ** ** - Tickets ** ** TODO: d "should" be const but some internals (d->F.cursor and ** delayed baseline loading) currently prohibit it. */ int fsl_deck_crosslink( fsl_cx * f, fsl_deck /* const */ * d ); /** ** Parses src as Control Artifact content and populates d with it. ** ** d will be cleaned up by this function if it has any contents. ** ** Pass 0 for the rid parameter for the time being. It is not yet clear ** whether we need that parameter here or not. A negative value will ** result in a FSL_RC_RANGE error. ** ** The artifact content in the source blob is modified by this ** function because (A) that simplifies tokenization greatly, (B) ** saves us having to make another copy to work on, (C) the ** original implementation did it and way and (D) because in ** historical use the source is normally thrown away after parsing, ** anyway. ** ** The fsl_cx argument must (in the current implementation) have ** opened at least one database handle because we use db functionality ** during parsing (unfortunately). ** ** On success it returns 0 and d will be updated with the state ** from the input manifest. (Ideally, outputing d via ** fsl_deck_output() will produce a lossless copy of the original.) ** If the manifest has a counterpart in the database (determined ** via an SHA1 match) then d->rid is set to a positive value upon ** return. d->uuid will be set to the SHA1 of the input artifact, ** ignoring any surrounding PGP signature for hashing purposes. ** ** On error f's error state might be updated with more info ** (depends on the type of error, but anything non-trivial will be ** noted there). d might be partially populated on error. src is ** modified during parsing, and will be modified if the error ** happens after basic argument validation and validation of the ** Z-card (if any) takes place. ** ** Error result codes include: ** ** - FSL_RC_MISUSE if any pointer argument is NULL. ** ** - FSL_RC_CA_SYNTAX on syntax errors. ** ** - FSL_RC_CONSISTENCY if validation of a Z-card fails. ** ** - Any number of errors coming from the allocator, database, or ** fsl_deck APIs used here. ** ** ** Design notes/reminders: ** ** - The rid argument doesn't seem to be necessary here. It is left ** in for the time being because the internal logic (ported in) ** implies that there are cases where the rid is expected to be 0 ** vs. positive. ** ** - We only need the fsl_cx argument for a database handle and ** only need that for the string-to-Julian conversion. But since we ** have it, this function also (on success) sets d->rid and ** d->uuid. */ int fsl_deck_parse(fsl_cx * f, fsl_id_t rid, fsl_deck * d, fsl_buffer * src); /** ** Loads the content from given rid and tries to parse it as a ** manifest. If type==FSL_CATYPE_ANY then it will allow any type of ** control artifact, else it returns FSL_RC_TYPE if the loaded ** manifest is of the wrong type. ** ** Returns 0 on success. ** ** d may be partially populated on error, and the caller must ** eventually pass it to fsl_deck_finalize() resp. fsl_deck_clean() ** regardless of success or error. This function "could" clean it ** up on error, but leaving it partially populated makes debugging ** easier. If the error was an artifact type mismatch then d will ** be properly populated but will not hold the type of artifact ** requested (but should otherwise be well-formed because parsing ** errors occur before the type check can happen). ** ** f's error state may be updated on error. */ int fsl_deck_load_rid( fsl_cx * f, fsl_id_t rid, fsl_deck * d, fsl_catype_t type ); /** ** Loads the baseline manifest specified in d->B.uuid. Returns 0 on ** success, if d->B.baseline is already set, or d->B.uuid is NULL ** (in which case there is no baseline). ** ** Neither argument may be NULL and d must be a fully-populated ** object, complete with a proper d->rid, before calling this. ** ** On success 0 is returned. If d->B.baseline is NULL then ** it means that d has no baseline manifest (and d->B.uuid will be NULL ** in that case). If d->B.baseline is not NULL then it is owned by ** d and will be cleaned up when d is cleaned/finalized. ** ** Error codes include, but are not limited to: ** ** - FSL_RC_MISUSE if !d or !d->f. ** ** - FSL_RC_NOT_A_REPO if f has no opened repo db. ** ** - FSL_RC_RANGE if d->rid<=0, but that might propagate up from ** a lower-level call as well. ** ** On non-trivial errors f's error state will be updated to hold ** a description of the problem. ** ** Some misuses trigger assertions in debug builds. ** */ int fsl_deck_baseline_fetch( fsl_deck * d ); /** ** Not yet implemented: a callback interface for manifest crosslinking, ** so that we can farm out the updating of the event table. That would ** allow, e.g., native-language updates to the timeline. */ typedef int (*fsl_deck_xlink_f)(fsl_cx * f, fsl_deck const * d, void * state); /** ** Adds the given function as a "crosslink callback" for the given ** Fossil context. The callback is called at the end of a ** successfull fsl_deck_crosslink() operation and provides a way ** for the client to perform their own work based on the app having ** crosslinked an artifact. Crosslinking happens when artifacts are ** saved or upon a rebuild operation. ** ** Crosslink callbacks are called in an unspecified order and the ** library may register its own before the client gets a chance to. ** ** If _any_ crosslinking callback fails (returns non-0) then the ** _whole_ crosslinking fails and is rolled back (which may very ** well include pending tags/commits/whatever getting rolled back). ** ** The state parameter has no meaning for this function, but is ** passed on as the final argument to cb(). If not NULL, cbState ** must outlive f, as there is currently no API to remove ** registered crosslink listeners. ** ** The name parameter is for future use, to allow clients to ** replace built-in implementations and remove crosslink listeners ** using published symbolic names. The name must be ** non-NULL/empty. If a listener is registered with a duplicate ** name then the first one is replaced. This function does not copy ** the name bytes - they are assumed to be static or otherwise to ** live at least as long as f. It is recommended that clients ** choose a namespace/prefix to apply to the names they ** register. The library reserves the prefix "fsl-" for its own ** use. The name string need not be stable across appication ** sessions and maybe be a randomly-generated string. ** ** TODO: publish the name(s) of overrideable crosslinkers ** once we have them in place. */ int fsl_xlink_listener( fsl_cx * f, char const * name, fsl_deck_xlink_f cb, void * cbState ); /** ** For the given blob.rid value, returns the blob.size value of ** that record via *rv. Returns 0 or higher on success, -1 if a ** phantom record is found, -2 if no entry is found, or a smaller ** negative value on error (dig around the sources to decode them - ** this is not expects to fail unless your system is undergoing a ** catastrophe). ** ** @see fsl_content_blob() ** @see fsl_content_get() **/ fsl_int_t fsl_content_size( fsl_cx * f, fsl_id_t blobRid ); /** ** For the given blob.rid value, fetches the content field of that ** record and overwrites tgt's contents with it (reusing tgt's ** memory if it has any and if it can). The blob's contents are ** uncompressed if they were stored in compressed form. This ** extracts a raw blob and does not apply any deltas - use ** fsl_content_get() to fully expand a delta-stored blob. ** ** Returns 0 on success. On error tgt might be partially updated, ** e.g. it might be populated with compressed data instead of ** uncompressed. On error tgt's contents should be recycled ** (e.g. fsl_buffer_reset()) or discarded (e.g. fsl_buffer_clear()) ** by the client. ** ** @see fsl_content_get() ** @see fsl_content_size() */ int fsl_content_blob( fsl_cx * f, fsl_id_t blobRid, fsl_buffer * tgt ); /** ** Functionally similar to fsl_content_blob() but does a lot of ** work to ensure that the returned blob is expanded from its ** deltas, if any. The tgt buffer's memory, if any, will be replaced/reused ** if it has any. Returns 0 on success. There are no less than 50 ** potental different errors, so we won't bother to list them all. ** The basic error cases are: ** ** - FSL_RC_MISUSE if !tgt or !f. ** ** - FSL_RC_RANGE if rid<=0 or if an infinite loop is discovered in ** the repo delta table links (that is a consistency check to avoid ** an infinite loop - that condition "cannot happen" because the ** verify-before-commit logic catches that error case). ** ** - FSL_RC_NOT_A_REPO if f has no repo db opened. ** ** - FSL_RC_NOT_FOUND if the given rid is not in the repo db. ** ** - FSL_RC_OOM if an allocation fails. ** ** ** @see fsl_content_blob() ** @see fsl_content_size() */ int fsl_content_get( fsl_cx * f, fsl_id_t blobRid, fsl_buffer * tgt ); /** ** Returns true if the given rid is marked as PRIVATE in f's current ** repository. Returns false (0) on error or if the content is not ** marked as private. */ char fsl_content_is_private(fsl_cx * f, fsl_id_t rid); /** ** Marks the given rid public, if it was previously marked as ** private. Returns 0 on success, non-0 on error. ** ** Note that it is not possible to make public content private. */ int fsl_content_make_public(fsl_cx * f, fsl_id_t rid); /** ** If the given blob ID refers to deltified repo content, this routine ** undeltifies it and replaces its content with its expanded ** form. ** ** Returns 0 on success, FSL_RC_MISUSE if !f, FSL_RC_NOT_A_REPO if ** f has no opened repository, FSL_RC_RANGE if rid is not positive, ** and any number of other potential errors during the db and ** content operations. This function treats already unexpanded ** content as success. ** ** @see fsl_content_deltify() */ int fsl_content_undelta(fsl_cx * f, fsl_id_t rid); /** ** The converse of fsl_content_undelta(), this replaces the storage ** of the given blob record so that it is a delta of srcid. ** ** If rid is already a delta from some other place then no ** conversion occurs and this is a no-op unless force is true. ** ** It never generates a delta that carries a private artifact into ** a public artifact. Otherwise, when we go to send the public ** artifact on a sync operation, the other end of the sync will ** never be able to receive the source of the delta. It is OK to ** delta private->private and public->private and public->public. ** Just no private->public delta. For such cases this function ** returns 0, as opposed to FSL_RC_ACCESS or some similar code, and ** leaves the content untouched. ** ** If srcid is a delta that depends on rid, then srcid is ** converted to undelta'd text. ** ** If either rid or srcid contain less than 50 bytes, or if the ** resulting delta does not achieve a compression of at least 25% ** the rid is left untouched. ** ** Return 0 if a delta is successfully made or none needs to be ** made, non-0 on error. ** ** @see fsl_content_undelta() */ int fsl_content_deltify(fsl_cx * f, fsl_id_t rid, fsl_id_t srcid, char force); /** ** Creates a new phantom blob with the given UUID and return its ** artifact ID via *newId. Returns 0 on success, FSL_RC_MISUSE if ** !f or !uuid, FSL_RC_RANGE if uuid is not FSL_UUID_STRLEN bytes ** long or syntactically invalid, FSL_RC_NOT_A_REPO if f has no ** repository opened, FSL_RC_ACCESS if the given uuid has been ** shunned, and about 20 other potential error codes from the ** underlying db calls. If isPrivate is true _or_ f has been flag ** as being in "private mode" then the new content is flagged as ** private. newId may be NULL but if it is then the caller will ** have to find the record id himself by looking for the uuid ** in the "phantom" table. */ int fsl_content_new( fsl_cx * f, fsl_uuid_cstr uuid, char isPrivate, fsl_id_t * newId ); /** ** Experimental generic callback interface for visiting decks. The ** interface does not generically require that d survive after this ** call returns. */ typedef int (*fsl_deck_cb_f)( fsl_cx * f, fsl_deck const * d, void * state ); /** ** For each unique wiki page name in f's repostory, this calls ** cb(), passing it the manifest of the most recent version of that ** page. The callback should return 0 on success, FSL_RC_BREAK to ** stop looping without an error, or any other non-0 code ** (preferably a value from fsl_rc_t) on error. ** ** The 3rd parameter has no meaning for this function but it is ** passed on as-is to the callback. ** ** ACHTUNG: the deck passed to the callback is transient and will ** be cleaned up after the callback has returned, so the callback ** must not hold a pointer to it or its contents. **/ int fsl_wiki_foreach_page( fsl_cx * f, fsl_deck_cb_f cb, void * state ); /** ** Fetches the most recent RID for the given wiki page name and ** assigns *newId (if it is not NULL) to that value. Returns 0 on ** success, FSL_RC_MISUSE if !f or !pageName, FSL_RC_RANGE if ** !*pageName, and a host of other potential db-side errors ** indicating more serious problems. If no such page is found,n ** newRid is not modified and this function returns 0 (as opposed ** to FSL_RC_NOT_FOUND) because that simplifies usage (so far). ** ** On error *newRid is not modified. */ int fsl_wiki_latest_rid( fsl_cx * f, char const * pageName, fsl_id_t * newRid ); /** ** Loads the artifact for the most recent version of the given wiki page, ** populating d with its contents. ** ** Returns 0 on success. On error d might be partially populated, ** so it needs to be passed to fsl_deck_finalize() regardless of ** whether this function succeeds or fails. ** ** Returns FSL_RC_NOT_FOUND if no page with that name is found. */ int fsl_wiki_load_latest( fsl_cx * f, char const * pageName, fsl_deck * d ); /** ** Returns true (non-0) if f's repo database contains a page with the ** given name, else false. */ char fsl_wiki_page_exists(fsl_cx * f, char const * pageName); /** ** Under development - do not use. ** ** Saves wiki content to f's repository db. ** ** pageName is the name of the page to update or create. ** ** b contains the content for the page. ** ** userName specifies the user name to apply to the change. If NULL ** or empty then fsl_guess_user_name() is used to determine the name. ** ** mimeType specifies the mime type for the content (may be NULL). ** Mime type names supported directly by fossil(1) include (as of ** this writing): text/x-fossil-wiki, text/x-markdown, ** text/plain ** ** Whether or not this function is allowed to create a new page ** or not is determined by isNew. If isNew is true then then this ** will fail with FSL_RC_ALREADY_EXISTS if a page with the given ** name already exists. If it is false and the page does _not_ ** exist, FSL_RC_NOT_FOUND is returned. ** ** TODO: find better semantics for isNew, so clients don't have to ** check if it exists first. e.g. a policy enum which specifies ** whether to allow creation of a new page. ** ** @see fsl_wiki_page_exists() */ int fsl_wiki_save(fsl_cx * f, char const * pageName, fsl_buffer const * b, char const * userName, char const * mimeType, char isNew ); /** ** Fetches the list of all wiki page names in f's current repo db ** and appends them as new (char *) strings to tgt. On error tgt ** might be partially populated (but this will only happen on an ** OOM). ** ** It is up to the caller free the entries added to the list. Some ** of the many possibilities include: ** ** @code ** fsl_list_visit( list, 0, fsl_list_v_fsl_free, NULL ); ** fsl_list_reserve(list,0); ** // Or: ** fsl_list_clear(list, fsl_list_v_fsl_free, NULL); ** // Or simply: ** fsl_list_visit_free( list, 1 ); ** @endcode ** */ int fsl_wiki_names_get( fsl_cx * f, fsl_list * tgt ); /** ** Returns n bytes of random lower-case hexidecimal characters ** using the given db as its data source. The returned memory must ** eventually be freed using fsl_free(). Returns NULL if !db, !n, ** or on a db-level error. */ char * fsl_db_random_hex(fsl_db * db, fsl_size_t n); /** ** Represents one file entry in a Manifest/Control Artifact. */ struct fsl_card_F { /** ** Name of the file. */ char * name; /** ** UUID of the underlying blob record for the file. NULL for ** removed entries. */ fsl_uuid_str uuid; /** ** Previous name, if the file was renamed. */ char * priorName; /** ** File permissions. ** ** TODO: rename to perm (singular), as fossil only supports ** permissions letter. ** ** @see fsl_mf_perm_t */ fsl_mf_perm_t perms; }; /** ** Empty-initialized fsl_card_F structure, intended for use in ** initialization when embedding fsl_card_F in another struct or ** copy-initializing a const struct. */ #define fsl_card_F_empty_m {NULL, NULL, NULL, 0} /** ** Empty-initialized fsl_card_F instance, intended for use in ** copy-constructing. */ extern const fsl_card_F fsl_card_F_empty; /** ** Represents a J card in a Ticket Control Artifact. */ struct fsl_card_J { /** ** If true, the new value should be appended to any existing one ** with the same key, else it will replace any old one. */ char isAppend; /** ** The ticket field to update. */ char const * field; /** ** The value for the field. */ char const * value; /** ** Memory storage for the string properties. */ fsl_buffer storage; }; /** Empty-initialized fsl_card_J struct. */ #define fsl_card_J_empty_m {0,NULL, NULL, fsl_buffer_empty_m} /** Empty-initialized fsl_card_J struct. */ extern const fsl_card_J fsl_card_J_empty; /** ** Represents a tag in a Manifest or Control Artifact. */ struct fsl_card_T { /** ** The type of tag. */ fsl_tag_type type; /** ** UUID of the artifact this tag is tagging. When applying a tag ** to a new checkin, this value is left empty and gets replaced by ** a '*' in the resulting control artifact. */ fsl_uuid_str uuid; /** ** The tag's name. */ fsl_buffer name; /** ** The tag's value. May be empty. */ fsl_buffer value; }; /** Defaults-initialized fsl_card_T instance. */ #define fsl_card_T_empty_m {FSL_TAGTYPE_INVALID, NULL, fsl_buffer_empty_m,fsl_buffer_empty_m} /** Defaults-initialized fsl_card_T instance. */ extern const fsl_card_T fsl_card_T_empty; /** ** Represents a Q card in a Manifest or Control Artifact. */ struct fsl_card_Q { /** 0==invalid, negative==backed out, positive=cherrypicked. */ char type; /** ** UUID of the target of the cherrypick. */ fsl_uuid_cstr target; /** ** UUID of the baseline for the cherrypick. */ fsl_uuid_cstr baseline; /** ** Memory storage for the target and baseline properties. */ fsl_buffer storage; }; /** Empty-initialized fsl_card_Q struct. */ #define fsl_card_Q_empty_m {-1, NULL, NULL, fsl_buffer_empty_m} /** Empty-initialized fsl_card_Q struct. */ extern const fsl_card_Q fsl_card_Q_empty; /** ** Allocates a new J-card record instance ** ** On success it returns a new record which must eventually be ** passed to fsl_card_J_free() to free its resources. On ** error (invalid arguments or allocation error) it returns NULL. ** field may not be NULL or empty but value may be either. ** ** These records are immutable - the API provides no way to change ** them one they are instantiated. */ fsl_card_J * fsl_card_J_malloc(char isAppend, char const * field, char const * value); /** ** Frees a J-card record created by fsl_card_J_malloc(). ** Is a no-op if cp is NULL. */ void fsl_card_J_free( fsl_card_J * cp ); /** ** Allocates a new fsl_card_F instance for use with ** fsl_deck_F_add2(). Returns NULL for any of these error cases: ** ** - name is NULL ** - uuid is not NULL and fsl_is_uuid(uuid) returns false. ** - allocation error ** ** On success it returns a new object which must eventually be ** passed to fsl_card_F_free() to free it. ** ** @see fsl_card_F_free() */ fsl_card_F * fsl_card_F_malloc(char const * name, fsl_uuid_cstr uuid, int perms, char const * oldName); /** ** Frees up any memory owned by t and clears out t's state, ** but does not free t. ** ** @see fsl_card_F_free() */ void fsl_card_F_clean(fsl_card_F *t); /** ** Calls fsl_card_F_clean(t) and then passes t to ** fsl_free(). ** ** @see fsl_card_F_clean() */ void fsl_card_F_free(fsl_card_F * f); /** ** Allocates a new fsl_card_T instance. If any of the pointer ** parameters are non-NULL, their values are assumed to be ** NUL-terminated strings, which this function copies. Returns ** NULL on allocation error. The returned value must eventually be ** passed to fsl_card_T_finalize() to free its resources. ** ** If uuid is not NULL and fsl_is_uuid(uuid) returns false then ** this function returns NULL. If it is NULL and gets assigned ** later, it must conform to fsl_is_uuid()'s rules or downstream ** results are undefined. ** ** @see fsl_card_T_free() **/ fsl_card_T * fsl_card_T_malloc(fsl_tag_type tagType, fsl_uuid_cstr uuid, char const * name, char const * value); /** ** Calls fsl_card_T_clean(t) and then passes t to ** fsl_free(). ** ** @see fsl_card_T_clean() */ void fsl_card_T_free(fsl_card_T *t); /** ** Frees up any memory owned by t and clears out t's state, ** but does not free t. ** ** @see fsl_card_T_free() */ void fsl_card_T_clean(fsl_card_T *t); /** ** Allocates a new cherrypick record instance. The type argument ** must be negative for a backed-out change, positive for a ** cherrypicked change. target must be a valid UUID string. If ** baseline is not NULL then it also must be a valid UUID. ** ** On success it returns a new record which must eventually be ** passed to fsl_card_Q_free() to free its resources. On ** error (invalid arguments or allocation error) it returns NULL. ** ** These records are immutable - the API provides no way to change ** them once they are instantiated. */ fsl_card_Q * fsl_card_Q_malloc(char type, fsl_uuid_cstr target, fsl_uuid_cstr baseline); /** ** Frees a cherrypick record created by fsl_card_Q_malloc(). ** Is a no-op if cp is NULL. */ void fsl_card_Q_free( fsl_card_Q * cp ); /** ** Resolves client-provided symbol as an artifact's db record ID. ** f must have an opened repository db, and some symbols can only ** be looked up if it has an opened checkout (see the list below). ** ** Returns 0 and sets *rv to the id if it finds an unambiguous ** match. ** ** Returns FSL_RC_MISUSE if !f, !sym, !*sym, or !rv. ** ** Returns FSL_RC_NOT_A_REPO if f has no opened repository. ** ** Returns FSL_RC_AMBIGUOUS if sym is a partial UUID which matches ** multiple full UUIDs. ** ** Returns FSL_RC_NOT_FOUND if it cannot find anything. ** ** Symbols supported by this function: ** ** - SHA1 hash ** - SHA1 hash prefix of at least 4 characters ** - Symbolic Name ** - "tag:" + symbolic name ** - Date or date-time ** - "date:" + Date or date-time ** - symbolic-name ":" date-time ** - "tip" ** - "root:" resolves to the root manifest of the given checkin. In ** the trunk this will always resolve to the first "empty checkin" ** manifest. ** ** The following additional forms are available in local checkouts: ** ** - "current" ** - "prev" or "previous" ** - "next" ** */ int fsl_repo_sym_to_rid( fsl_cx * f, char const * sym, fsl_atype_t type, fsl_id_t * rv ); /** ** Similar to fsl_repo_sym_to_rid() but on success if returns a UUID ** string by assigning it to *rv. If rid is not NULL then on ** success the db record ID corresponding to the returned UUID is ** assigned to *rid. The caller must eventually free the returned ** string memory by passing it to fsl_free(). **/ int fsl_repo_sym_to_uuid( fsl_cx * f, char const * sym, fsl_atype_t type, fsl_uuid_str * rv, fsl_id_t * rid ); /** ** Searches f's repo database for the a blob with the given uuid ** (any unique UUID prefix). On success a positive record ID is ** returned. On error a negative value is returned. If no uuid ** match is found 0 is returned. */ fsl_id_t fsl_repo_uuid_to_rid( fsl_cx * f, char const * uuid ); /** ** The opposite of fsl_repo_uuid_to_rid(), this returns the UUID ** string of the given blob record ID. Ownership of the string is ** passed to the caller and it must eventually be freed using ** fsl_free(). Returns NULL on error (invalid arguments or f has no ** repo opened) or if no blob record is found. */ fsl_uuid_str fsl_repo_rid_to_uuid(fsl_cx * f, fsl_id_t rid); /** ** Convenience wrapper around fsl_db_g_int32() which returns the ** value of a property from f's repo db's config table. Returns ** dflt if !f, f has no opened repo db, no entry is found, or on ** db-level errors. */ fsl_int32_t fsl_repo_g_int32( fsl_cx * f, fsl_int32_t dflt, char const * key ); /** ** fsl_in64_t counterpart of fsl_repo_g_int32(). */ fsl_int64_t fsl_repo_g_int64( fsl_cx * f, fsl_int64_t dflt, char const * key ); /** ** fsl_double_t counterpart of fsl_repo_g_int32(). */ fsl_double_t fsl_repo_g_double( fsl_cx * f, fsl_double_t dflt, char const * key ); /** ** fsl_id_t counterpart of fsl_repo_g_int32(). */ fsl_id_t fsl_repo_g_id( fsl_cx * f, fsl_id_t dflt, char const * key ); /** ** The boolean counterpart of fsl_repo_g_int32(). A boolean value ** is any for which fsl_str_bool() returns a true value. If the ** given config entry is not found, or on any error, dflt is returned. */ char fsl_repo_g_bool( fsl_cx * f, char dflt, char const * key ); /** ** String counterpart of fsl_repo_g_int32(). The returned memory ** must eventually be freed using fsl_free(). If len is not NULL ** then it is set to the length of the returned string. Returns NULL ** for any sort of error. */ char * fsl_repo_g_text( fsl_cx * f, char const * key, fsl_size_t * len); /** ** Sets a configuration variable in f's checkout database. Returns ** 0 on success. val may be NULL. Returns FSL_RC_MISUSE if !f, f ** has no opened checkout, or !key, FSL_RC_RANGE if !key. */ int fsl_repo_set_text( fsl_cx * f, char const * key, char const * val ); /** ** Int32 counterpart of fsl_repo_set_text(). */ int fsl_repo_set_int32( fsl_cx * f, char const * key, fsl_int32_t val ); /** ** Int64 counterpart of fsl_repo_set_text(). */ int fsl_repo_set_int64( fsl_cx * f, char const * key, fsl_int64_t val ); /** ** Double counterpart of fsl_repo_set_text(). */ int fsl_repo_set_double( fsl_cx * f, char const * key, fsl_double_t val ); /** ** Convenience wrapper around fsl_db_g_int32() which returns the ** value of a property from f's checkout db's vvar table. Returns ** dflt if !f, f has no checkout, no entry is found, or on db-level ** errors. */ fsl_int32_t fsl_local_g_int32( fsl_cx * f, fsl_int32_t dflt, char const * key ); /** ** fsl_in64_t counterpart of fsl_local_g_int32(). */ fsl_int64_t fsl_local_g_int64( fsl_cx * f, fsl_int64_t dflt, char const * key ); /** ** fsl_double_t counterpart of fsl_local_g_int32(). */ fsl_double_t fsl_local_g_double( fsl_cx * f, fsl_double_t dflt, char const * key ); /** ** fsl_id_t counterpart of fsl_local_g_int32(). */ fsl_id_t fsl_local_g_id( fsl_cx * f, fsl_id_t dflt, char const * key ); /** ** The "checkout" db counterpart of fsl_repo_g_bool() */ char fsl_local_g_bool( fsl_cx * f, char dflt, char const * key ); /** ** String counterpart of fsl_local_g_int32(). The returned memory ** must eventually be freed using fsl_free(). If len is not NULL ** then it is set to the length of the returned string. Returns NULL ** for any sort of error. */ char * fsl_local_g_text( fsl_cx * f, char const * key, fsl_size_t * len); /** ** Sets a configuration variable in f's checkout database. Returns ** 0 on success. val may be NULL. Returns FSL_RC_MISUSE if !f, f ** has no opened checkout, or !key, FSL_RC_RANGE if !key. */ int fsl_local_set_text( fsl_cx * f, char const * key, char const * val ); /** ** Int32 counterpart of fsl_local_set_text(). */ int fsl_local_set_int32( fsl_cx * f, char const * key, fsl_int32_t val ); /** ** Int64 counterpart of fsl_local_set_text(). */ int fsl_local_set_int64( fsl_cx * f, char const * key, fsl_int64_t val ); /** ** Double counterpart of fsl_local_set_text(). */ int fsl_local_set_double( fsl_cx * f, char const * key, fsl_double_t val ); /** TODO */ int fsl_local_set_bool( fsl_cx * f, char const * key, char val ); /** ** Incomplete/experimental, subject to much change. But it seems to ** work. ** ** Adds a control record to f's repositoriy that either creates or ** cancels a tag. ** ** symToTag is a symbolic name (as per fsl_repo_sym_to_rid()) of the ** artifact to tag. ** ** tagType is the type (add, cancel, or propagate) of tag. ** ** tagName is the name of the tag. Must not be NULL/empty. ** ** tagValue is the optional value for the tag. May be NULL. ** ** userName is the user's name to apply to the artifact. May not be ** empty/NULL. Use fsl_guess_user_name() to try to figure out ** a proper user name based on the environment. ** ** mtime is the timestamp for the new artifact. Pass a value <=0 to ** use the current time. ** ** If newId is not NULL then on success the rid of the new tag control ** artifact is assigned to *newId. ** ** Returns 0 on success and has about a million and thirteen ** possible error conditions. ** ** If the artifact being tagged is private, the new tag is also ** marked as private. ** */ int fsl_repo_tag_sym( fsl_cx * f, fsl_tag_type tagType, char const * symToTag, char const * tagName, char const * tagValue, char const * userName, fsl_double_t mtime, fsl_id_t * newId ); /** ** Equivalent to fsl_repo_tag_sym() except that it takes an RID ** instead of a symbolic name as the artifact-to-tag argument (its ** 3rd arg). */ int fsl_repo_tag_rid( fsl_cx * f, fsl_tag_type tagType, fsl_id_t artifactRidToTag, char const * tagName, char const * tagValue, char const * userName, fsl_double_t mtime, fsl_id_t * newId ); /** ** Returns non-0 (true) if the checkin with the given rid is a ** leaf, 0 (false) if not. Returns 0 if !f, f has no repo db ** opened, the query fails (likely indicating that it is not a ** repository db), or just about any other conceivable non-success ** case. ** ** A leaf is a commit which has no children in the same branch. */ char fsl_repo_id_is_leaf(fsl_cx * f, fsl_id_t rid); /** ** Counts the number of primary non-branch children for the given ** check-in. ** ** A primary child is one where the parent is the primary parent, not ** a merge parent. A "leaf" is a node that has zero children of any ** kind. This routine counts only primary children. ** ** A non-branch child is one which is on the same branch as the parent. ** ** Returns a negative value on error. */ fsl_int_t fsl_repo_count_nonbranch_children(fsl_cx * f, fsl_id_t rid); /** ** Looks for the delta table record where rid==deltaRid, and ** returns that record's srcid via *rv. Returns 0 on success, non-0 ** on error. *rv is set to 0 before running the query, and will have ** that value if no record is found. **/ int fsl_repo_delta_src_id( fsl_cx * f, fsl_id_t deltaRid, fsl_id_t * rv ); /** ** Return true if the given artifact ID should is listed in he shun ** table, else false. */ char fsl_repo_uuid_is_shunned(fsl_cx * f, const char *zUuid); #if defined(__cplusplus) } /*extern "C"*/ #endif #endif /* NET_FOSSIL_SCM_FSL_CONTENT_H_INCLUDED */