/* -*- 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_INTERNAL_H_INCLUDED) #define NET_FOSSIL_SCM_FSL_INTERNAL_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 l, ** 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/ ** ******************************************************************************* ** This file declares library-level internal APIs which are shared ** across the library. */ #include "fossil-scm/fossil.h" /* MUST come first b/c of config macros */ #if defined(__cplusplus) extern "C" { #endif typedef struct fsl_acache fsl_acache; typedef struct fsl_acache_line fsl_acache_line; typedef struct fsl_id_bag fsl_id_bag; typedef struct fsl_pq fsl_pq; typedef struct fsl_pq_e fsl_pq_e; /** @internal ** ** Queue entry type for the fsl_pq class. */ struct fsl_pq_e { /** RID of the entry. */ fsl_id_t id; /** Raw data associated with this entry. */ void * p; /** Priority of this element. */ fsl_double_t value; }; /** @internal ** Empty-initialized fsl_pq_e structure. */ #define fsl_pq_e_empty_m {0,NULL,0.0} /** @internal ** ** A simple priority queue class. Instances _must_ be initialized ** by copying fsl_pq_empty or fsl_pq_empty_m (depending on where ** the instance lives). */ struct fsl_pq { /** Number of items allocated in this->list. */ fsl_size_t capacity; /** Number of items used in this->list. */ fsl_size_t used; /** The queue. It is kept sorted by entry->value. */ fsl_pq_e * list; }; /** @internal ** Empty-initialized fsl_pq struct. */ #define fsl_pq_empty_m {0,0,NULL} /** @internal ** Empty-initialized fsl_pq struct. */ extern const fsl_pq fsl_pq_empty; /** @internal ** ** Clears the contents of p, freeing any memory it owns, but not ** freeing p. Results are undefined if !p. */ void fsl_pq_clear(fsl_pq * p); /** @internal ** Insert element e into the queue. Returns 0 on success, ** FSL_RC_OOM on error. Results are undefined if !p or e ** is invalid. pData may be NULL. */ int fsl_pq_insert(fsl_pq *p, fsl_id_t e, fsl_double_t v, void *pData); /** @internal ** ** Extracts (removes) the first element from the queue (the element ** with the smallest value) and return its ID. Return 0 if the ** queue is empty. If pp is not NULL then *pp is (on success) ** assigned to the value member of the extracted element. */ fsl_id_t fsl_pq_extract(fsl_pq *p, void **pp); /** @internal ** ** A container type for lists of db record IDs. This is used in ** several places as a cache for record IDs, to keep track of ones ** we know about, ones we know that we don't know about, and to ** avoid duplicate processing in some contexts. */ struct fsl_id_bag { /** ** Number of entries of this->list which are in use (have ** a positive value). They need not be contiguous! */ fsl_size_t entryCount; /** ** The number of elements allocated for this->list. */ fsl_size_t capacity; /** ** The number of elements in this->list which are ** "in used". Must be <= capacity. */ fsl_size_t used; /** ** Array of IDs this->capacity elements long. "Used" elements ** have a positive value. Unused ones are set to 0. */ fsl_id_t * list; }; /** @internal ** Initialized-with-defaults fsl_id_bag structure, ** intended for copy initialization. */ extern const fsl_id_bag fsl_id_bag_empty; /** @internal ** Initialized-with-defaults fsl_id_bag structure, ** intended for in-struct initialization. */ #define fsl_id_bag_empty_m {\ 0/*entryCount*/, 0/*capacity*/, \ 0/*used*/, NULL/*list*/ } /** @internal ** ** Holds one "line" of a fsl_acache cache. */ struct fsl_acache_line { /** ** RID of the cached record. */ fsl_id_t rid; /** ** Age. Newer is larger. */ fsl_int_t age; /** ** Content of the artifact. */ fsl_buffer content; }; /** @internal ** ** Empty-initialized fsl_acache_line structure. */ #define fsl_acache_line_empty_m { 0,0,fsl_buffer_empty_m } /** @internal ** ** A cache for tracking the existence of artifacts while the ** internal goings-on of control artifacts are going on. */ struct fsl_acache { /** ** Total amount of memory used by cached content. */ fsl_int_t szTotal; /** ** Number of entries "used" in this->list. */ fsl_size_t used; /** ** Number of slots in this->list. */ fsl_size_t capacity; /** ** Next cache counter age. Higher is newer. */ fsl_int_t nextAge; /** ** List of cached content, ordered by age. */ fsl_acache_line * list; /** ** All artifacts currently in the cache. */ fsl_id_bag inCache; /** ** Cache of known-missing content. */ fsl_id_bag missing; /** ** Cache of of known-existing content. */ fsl_id_bag available; }; /** @internal ** ** Empty-initialized fsl_acache structure. */ #define fsl_acache_empty_m {\ 0/*szTotal*/,0/*used*/,0/*capacity*/,\ 0/*nextAge*/,NULL/*list*/,\ fsl_id_bag_empty_m/*inCache*/,\ fsl_id_bag_empty_m/*missing*/,\ fsl_id_bag_empty_m/*available*/\ } /** @internal ** ** Empty-initialized fsl_acache structure. */ extern const fsl_acache fsl_acache_empty; /** @internal ** ** A type for holding a callback/state pair for manifest ** crosslinking callbacks. */ struct fsl_xlinker { char const * name; /** Callback function. */ fsl_deck_xlink_f f; /** State for this->f's last argument. */ void * state; }; typedef struct fsl_xlinker fsl_xlinker; /** Empty-initializes fsl_xlinker struct. */ #define fsl_xlinker_empty_m {NULL,NULL,NULL} /** Empty-initializes fsl_xlinker struct. */ extern const fsl_xlinker fsl_xlinker_empty; /** @internal ** ** An array of fsl_xlinker instances. */ struct fsl_xlinker_list { /** Number of used items in this->list. */ fsl_size_t used; /** Number of slots allocated in this->list. */ fsl_size_t capacity; /** Array of this->used elements. */ fsl_xlinker * list; }; typedef struct fsl_xlinker_list fsl_xlinker_list; /** Empty-initializes fsl_xlinker_list struct. */ #define fsl_xlinker_list_empty_m {0,0,NULL} /** Empty-initializes fsl_xlinker_list struct. */ extern const fsl_xlinker_list fsl_xlinker_list_empty; /** @internal ** ** Searches f's crosslink callbacks for an entry with the given ** name and returns that entry, or NULL if no match is found. The ** returned object is owned by f. */ fsl_xlinker * fsl_xlinker_by_name( fsl_cx * f, char const * name ); /* The fsl_cx class is documented in main public header. */ struct fsl_cx { /** ** The main db handle. It is a pointer to the first one of ** repo.db, ckout.db, config.db which is opened. Internal code ** should rely as little as possible on the actual arrangement of ** internal DB handles, and should use fsl_cx_db_repo(), ** fsl_cx_db_checkout(), and fsl_cx_db_config() to get a handle ** to the specific db they want. Currently they will always ** return NULL or the same handle, but that design decision might ** change at some point, so the public API treats them as ** separate entities. */ fsl_db * dbMain; /** ** Marker which tells us whether fsl_cx_finalize() needs ** to fsl_free() this instance or not. */ void const * allocStamp; /** ** Handle to the currently opened config database. */ /** ** Holds info directly related to a checkout database. */ struct { /** ** Handle to the currently opened checkout database IF the checkout ** is the main db. */ fsl_db db; /** ** Possibly not needed, but useful for doing absolute-to-relative ** path conversions for checking file lists. ** ** The directory part of an opened checkout db. This is currently ** only set by fsl_checkout_open_dir(). */ char * dir; fsl_id_t rid; fsl_uuid_str uuid; } ckout; /** ** Holds info directly related to a repo database. */ struct { /** ** Handle to the currently opened repository database IF repo ** is the main db. */ fsl_db db; /** ** The default user name, for operations which need one. ** See fsl_cx_user_set(). */ char * user; } repo; /** ** Holds info directly related to a global config database. */ struct { /** ** Handle to the currently opened global config database IF config ** is the main db. */ fsl_db db; } config; /** ** Output channel used by fsl_output() and friends. */ fsl_outputer output; /** ** Can be used to tie client-specific data to the context. Its ** finalizer is called when fsl_cx_finalize() cleans up. */ fsl_state clientState; /** ** Holds error state. As a general rule, this information is ** updated only by routines which need to return more info than a ** simple integer error code. This is primarily db-related ** routines, where we add the db-driver-provided error state ** here. It is not used by "simple" routines for which an integer ** code always suffices. APIs which set this should denote it ** with a comment like "updates the context's error state on ** error." */ fsl_error error; /** ** Optimization: reusable scratchpad for ** creating/encoding/decoding strings. */ fsl_buffer scratch; /** ** A scratchpad specifically for dealing with filename-related, ** non-recursive caching. */ fsl_buffer fsScratch; /** ** A copy of the config object passed to fsl_cx_init() (or some ** default). */ fsl_cx_config cxConfig; /** ** Flags, some (or one) of which is runtime-configurable by the ** client (see fsl_cx_flag_t). We can get rid of this and add the ** flags to the cache member along with the rest of them. */ int flags; /** ** List of callbacks for deck crosslinking purposes. */ fsl_xlinker_list xlinkers; /** ** A place for caching generic things. */ struct { /** ** Cached copy of the allow-symlinks config option, because it ** is needed on each stat() call. Negative value=="not yet ** determined", 0==no, positive==yes. The negative value means ** we need to check the repo config resp. the global config to ** see if this is on. We can probably default this to true, ** though i don't think(?) fossil(1) does so(?). */ char allowSymlinks; /** ** The case-insensitive repo flag. */ char caseInsensitive; /** ** Porting artifact. Not yet used. */ char ignoreDephantomizations; /** ** Record ID of rcvfrom entry during commits. */ fsl_id_t rcvId; /** ** Bool flag: whether or not a running commit process should be ** marked as private. */ char markPrivate; /** ** Analog to v1's content.c:ignoreDephantomizations flag. */ char deferCrosslink; /** ** True if fsl_mf_crosslink_begin() has been called but ** fsl_mf_crosslink_end() is still pending. */ char isCrosslinking; /** ** Flag indicating that only cluster control artifacts ** should be processed by manifest crosslinking. */ char xlinkClustersOnly; /** ** Used to tell the content-save internals that a "final ** verification" (a.k.a. verify-before-commit) is underway. */ char inFinalVerify; /** ** Indicates whether or not this repo has ever seen a manifest. ** negative==undetermined, 0==no, positive==yes. */ char seenManifest; /** ** TODO: a cache of most recently loaded/freed manifests, ** analog to v1's manifest.c:manifestCache array. Won't need ** this until we get to a point where we can rebuild or similar ** intensive operations. */ struct { /** ** Head of the cache list. All insertions/removals happen at ** the head. */ fsl_deck * head; /** ** TODO: maximum number of entries in the mf linked list. */ fsl_uint32_t limit; /** ** Current number of entries in the mf linked list. */ fsl_uint32_t size; } mf; /** ** Artifact cache used during processing of manifests. */ fsl_acache arty; /** ** Used during manifest parsing. */ fsl_id_bag mfSeen; /** ** Used during the processing of manifests to keep track of ** "leaf checks" which need to be done downstream. */ fsl_id_bag leafCheck; /** ** Holds the RID of every record awaiting verification ** during the verify-at-commit checks. */ fsl_id_bag toVerify; /** ** Holds a list of "selected files." */ fsl_list selectedFiles; /** ** Infrastructure for fsl_mtime_of_manifest_file(). It ** remembers the previous RID so that it knows when it has to ** invalidate/rebuild its ancestry cache. */ fsl_id_t mtimeManifest; } cache; struct { /** ** Holds a list of (fsl_card_J*) records representing custom ** ticket table fields available in the db. ** ** Each entry's flags member denote (using fsl_card_J_flags) ** whether that field is used by the ticket or ticketchng ** tables. ** ** TODO, eventually: add a separate type for these entries. We ** use fsl_card_J because the infrastructure is there and they ** provide what we need, but fsl_card_J::flags only exists for ** this list. A custom type would be smaller than fsl_card_J ** (only two members) but adding it requires adding some ** infrastructure which isn't worth the effort at the moment. */ fsl_list customFields; /** ** Gets set to true (at some point) if the client has the ** ticket db table. */ char hasTicket; /** ** Gets set to true (at some point) if the client has the ** ticket.tkt_ctime db field. */ char hasCTime; /** ** Gets set to true (at some point) if the client has the ** ticketchnk db table. */ char hasChng; /** ** Gets set to true (at some point) if the client has the ** ticketchng.rid db field. */ char hasChngRid; } ticket; /* Note: no state related to server/user/etc. That is higher-level stuff. We might need to allow the user to set a default user name to avoid that he has to explicitly set it on all of the various Control Artifact-generation bits which need it. */ }; /** @internal ** ** Initialized-with-defaults fsl_cx struct. */ #define fsl_cx_empty_m { \ NULL /*dbMain*/, \ NULL/*allocStamp*/,\ {/*ckout*/ fsl_db_empty_m /*db*/, NULL /*dir*/, 0/*rid*/, NULL/*uuid*/}, \ {/*repo*/ fsl_db_empty_m /*db*/, NULL/*user*/}, \ {/*config*/ fsl_db_empty_m /*db*/ }, \ fsl_outputer_FILE_m /*output*/, \ fsl_state_empty_m /*clientState*/, \ fsl_error_empty_m /*error*/, \ fsl_buffer_empty_m /*scratch*/, \ fsl_buffer_empty_m /*fsScratch*/, \ fsl_cx_config_empty_m /*cxConfig*/, \ FSL_CX_F_DEFAULTS/*flags*/, \ fsl_xlinker_list_empty_m/*xlinkers*/,\ {/*cache*/ \ -1/*allowSymlinks*/, \ 0/*caseInsensitive*/,\ 0/*ignoreDephantomizations*/,\ 0/*rcvId*/, \ 0/*markPrivate*/, \ 0/*deferCrosslink*/, \ 0/*isCrosslinking*/,\ 0/*xlinkClustersOnly*/,\ 0/*inFinalVerify*/, \ -1/*seenManifest*/,\ {/*mf*/ NULL/*head*/,5/*max*/,0/*size*/}, \ fsl_acache_empty_m/*arty*/, \ fsl_id_bag_empty_m/*mfSeen*/,\ fsl_id_bag_empty_m/*leafCheck*/,\ fsl_id_bag_empty_m/*toVerify*/, \ fsl_list_empty_m/*selectedFiles*/, \ 0/*mtimeManifest*/,\ }, \ {/*ticket*/ \ fsl_list_empty_m/*customFields*/, \ 0/*hasTicket*/, \ 0/*hasCTime*/, \ 0/*hasChng*/, \ 0/*hasCngRid*/ \ }\ } /** @internal ** Initialized-with-defaults fsl_cx instance. */ extern const fsl_cx fsl_cx_empty; /* TODO: int fsl_buffer_append_getenv( fsl_buffer * b, char const * env ) Fetches the given env var and appends it to b. Returns FSL_RC_NOT_FOUND if the env var is not set. The primary use for this would be to simplify the Windows implementation of fsl_find_home_dir(). */ /** @internal ** ** Searches for a repo.tag entry given name in the given context's ** repository db. If found, it returns the record's id. If no ** record is found and create is true (non-0) then a tag is created ** and its entry id is returned. Returns 0 if it finds no entry, a ** negative value on error. On db-level error, f's error state is ** updated. */ fsl_id_t fsl_tag_id( fsl_cx * f, char const * tag, char create ); /** @internal ** ** Return the number of elements in the bag. */ fsl_size_t fsl_id_bag_count(fsl_id_bag *p); /** @internal ** ** Remove element e from the bag if it exists in the bag. If e is ** not in the bag, this is a no-op. */ void fsl_id_bag_remove(fsl_id_bag *p, fsl_id_t e); /** @internal ** ** Return true (non-0) if e in the bag. Return false if it is not. */ char fsl_id_bag_contains(fsl_id_bag *p, fsl_id_t e); /** @internal ** ** Insert element e into the bag if it is not there already. ** Returns 0 if it actually inserts something or if it already ** contains such an entry, and some other value on error (e.g. FSL_RC_OOM). */ int fsl_id_bag_insert(fsl_id_bag *p, fsl_id_t e); /** @internal ** ** Return the ID of the first element in the bag. Return 0 if the ** bag is empty. */ fsl_id_t fsl_id_bag_first(fsl_id_bag *p); /** @internal ** ** Return the next element in the bag after e. Return 0 if ** e is the last element in the bag. Any insert or removal from ** the bag might reorder the bag. */ fsl_id_t fsl_id_bag_next(fsl_id_bag *p, fsl_id_t e); /** @internal ** ** Frees any memory owned by p, but does not free p. */ void fsl_id_bag_clear(fsl_id_bag *p); /** @internal ** ** Expires the single oldest entry in c. Returns true (non-0) if it ** removes an item, else 0. */ char fsl_acache_expire_oldest(fsl_acache * c); /** @internal ** ** Add an entry to the content cache. ** ** This routines transfers the contents of pBlob over to c, ** regardless of success or failure. The cache will deallocate ** memory when it has finished with it. ** ** Returns 0 on success, FSL_RC_OOM on allocation error. Has undefined ** behaviour if !c, rid is not semantically valid, !pBlob (or pBlob ** has no content???). */ int fsl_acache_insert(fsl_acache * c, fsl_id_t rid, fsl_buffer *pBlob); /** @internal ** ** Frees all memory held by c, and clears out c's state, but does ** not free c. Results are undefined if !c. */ void fsl_acache_clear(fsl_acache * c); /** @internal ** ** Checks f->cache.arty to see if rid is available in the ** repository opened by f. ** ** Returns 0 if the content for the given rid is available in the ** repo or the cache. Returns FSL_RC_NOT_FOUND if it is not in the ** repo nor the cache. Returns some other non-0 code for "real ** errors," e.g. FSL_RC_OOM if a cache allocation fails. This ** operation may update the cache's contents. ** ** If this function detects a loop in artifact lineage, it fails an ** assert() in debug builds and returns FSL_RC_CONSISTENCY in ** non-debug builds. That doesn't happen in real life, though. */ int fsl_acache_check_available(fsl_cx * f, fsl_id_t rid); /** @internal ** ** Writes content into the repository database. Returns the record ** ID via outRid (if it is not NULL). If the content is already in ** the database, it fetches the *outRid but has no side effects ** in the repo. ** ** If srcId is >0 then pBlob must contain delta content from ** the srcId record. srcId might be a phantom. ** ** pBlob is normally uncompressed text, but if uncompSize>0 then ** the pBlob value is assumed to be compressed and uncompSize is ** its uncompressed size. If uncompSize>0 then zUuid must be valid. ** TODO: we can use fsl_buffer_is_compressed() and friends to ** determine this, and remove this parameter. ** ** zUuid is the UUID of the artifact, if it is not NULL. When srcId is ** specified then zUuid must always be specified. If srcId is zero, ** and zUuid is zero then the correct zUuid is computed from pBlob. ** ** If isPrivate is true, the blob is created as a private record. ** ** If the record already exists but is a phantom, the pBlob content ** is inserted and the phatom becomes a real record. ** ** The original content of pBlob is not disturbed. The caller continues ** to be responsible for pBlob. This routine does *not* take over ** responsibility for freeing pBlob. ** ** If outRid is not NULL the on success *outRid is assigned to the ** ID of the underlying blob record. ** ** Returns 0 on success and there are too many potential error cases ** to name - this function is a massive beast. ** ** Potential TODO: we don't really need the uncompSize param - we ** can deduce it, if needed, based on pBlob's content. We cannot, ** however, know the UUID of the decompressed content unless the ** client passes it in to us. ** ** ** @see fsl_content_put() */ int fsl_content_put_ex( fsl_cx * f, fsl_buffer const * pBlob, fsl_uuid_cstr zUuid, fsl_id_t srcId, fsl_size_t uncompSize, char isPrivate, fsl_id_t * outRid); /** @internal ** ** Equivalent to fsl_content_put_ex(f,pBlob,NULL,0,0,0,newRid). ** ** This must only be used for saving raw (non-delta) content. ** ** @see fsl_content_put_ex() */ int fsl_content_put( fsl_cx * f, fsl_buffer const * pBlob, fsl_id_t * newRid); /** @internal ** ** 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_undeltify(fsl_cx * f, fsl_id_t rid); /** @internal ** ** The converse of fsl_content_undeltify(), 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, 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 some "small, ** unspecified number" of bytes (currently 50), or if the resulting ** delta does not achieve a compression of at least 25%, the rid is ** left untouched. ** ** Returns 0 if a delta is successfully made or none needs to be ** made, non-0 on error. ** ** @see fsl_content_undeltify() */ int fsl_content_deltify(fsl_cx * f, fsl_id_t rid, fsl_id_t srcid, char force); /** @internal ** ** 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 fsl_is_uuid(uuid) returns false, ** 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 flagged 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 using ** the UUID (see fsl_uuid_to_rid()). */ int fsl_content_new( fsl_cx * f, fsl_uuid_cstr uuid, char isPrivate, fsl_id_t * newId ); /** @internal ** ** Recompute/rebuild the entire repo.leaf table. ** ** This can be expensive (in time) for a really large repository. ** So it is only done for things like a full rebuild. ** ** Returns 0 on success. Error may indicate that f is not a repo. ** On error f's error state may be updated. */ int fsl_repo_leaves_rebuild(fsl_cx * f); /** @internal ** ** Check to see if checkin "rid" is a leaf and either add it to the LEAF ** table if it is, or remove it if it is not. ** ** Returns 0 on success, FSL_RC_MISUSE if !f or f has no repo db ** opened, FSL_RC_RANGE if pid is <=0. Other errors ** (e.g. FSL_RC_DB) may indicate that db is not a repo. On error ** db's error state may be updated. */ int fsl_repo_leaf_check(fsl_cx * f, fsl_id_t pid); /** @internal ** ** Schedules a leaf check for "rid" and its parents. Returns 0 on ** success. */ int fsl_repo_leaf_eventually_check( fsl_cx * f, fsl_id_t rid); /** @internal ** ** Perform all pending leaf checks. Returns 0 on success or if it ** has nothing to do. */ int fsl_repo_leaf_do_pending_checks(fsl_cx *f); /** @internal ** ** UNTESTED - the bits which use this are not yet ported. ** ** This initializes some temporary db state for the crosslinking ** of tickets. ** ** Returns 0 on success. If it returns 0, the caller must ** eventually call fsl_mf_crosslink_end(), otherwise he must not ** call fsl_mf_crosslink_end(). ** ** This function starts a db transaction if one is not already ** active. fsl_mf_crosslink_end() will end it. */ int fsl_mf_crosslink_begin(fsl_cx * f); /** @internal ** ** UNTESTED - the bits which use this are not yet ported. ** ** Must not be called unless fsl_mf_crosslink_begin() has been ** called. ** ** Returns 0 on success. On error it initiates (or propagates) a ** rollback for the current transaction. ** */ int fsl_mf_crosslink_end(fsl_cx * f); /** @internal ** ** Inserts a tag into f's repo db. It does not create the related ** control artifact - use fsl_tag_add_artifact() for that. ** ** rid is the artifact to which the tag is being applied. ** ** srcId is the artifact that contains the tag. It is often, ** but not always, the same as rid. ** ** (TODO: clarify the difference between rid/srcId.) ** ** mtime is the Julian timestamp for the tag. Defaults to ** the current time if mtime <= 0.0. ** ** If outRid is not NULL then on success *outRid is assigned ** the new record ID of the generated tag. ** ** If a more recent (compared to mtime) entry already exists for ** this tag/rid combination then its rid is returned via *outRid ** (if outRid is not NULL) and no new entry is created. ** ** Returns 0 on success, and has a huge number of potential error codes. */ int fsl_tag_insert( fsl_cx * f, fsl_tag_type tagtype, char const * zTag, char const * zValue, fsl_id_t srcId, fsl_double_t mtime, fsl_id_t rid, fsl_id_t *outRid ); /** @internal ** ** Propagate all propagatable tags in pid to the children of pid. */ int fsl_tag_propagate_all(fsl_cx * f, fsl_id_t pid); /** @internal ** ** Propagates a tag through the various internal pipelines. ** ** pid is the artifact id to whose children the tag should be ** propagated. ** ** tagid is the id of the tag to propagate (the tag.tagid db value). ** ** tagType is the type of tag to propagate. Must be either FSL_TAGTYPE_CANCEL ** or FSL_TAGTYPE_PROPAGATING. ** ** origId is the artifact id of the origin tag if tagType == ** FSL_TAGTYPE_PROPAGATING, otherwise it is ignored. ** ** zValue is the optional value for the tag. May be NULL. ** ** mtime is the Julian timestamp for the tag. Must be a valid time ** (no defaults here). ** ** This function is unforgiving of invalid values/ranges, and may assert ** in debug mode if passed invalid ids (values<=0), a NULL f, or if f has ** no opened repo. */ int fsl_tag_propagate(fsl_cx *f, fsl_tag_type tagType, fsl_id_t pid, fsl_id_t tagid, fsl_id_t origId, const char *zValue, fsl_double_t mtime ); /** @internal ** ** Remove the PGP signature from a raw artifact, if there is one. ** ** Expects *pz to point to *pn bytes of string memory which might ** or might not be prefixed by a PGP signature. If the string is ** enveloped in a signature, then upon returning *pz will point to ** the first byte after the end of the PGP header and *pn will ** contain the length of the content up to, but not including, the ** PGP footer. ** ** If *pz does not look like a PGP header then this is a no-op. ** ** Neither pointer may be NULL and *pz must point to *pn bytes of ** valid memory. */ void fsl_remove_pgp_signature(unsigned char const **pz, fsl_size_t *pn); /** @internal ** ** Clears the "seen" cache used by manifest parsing. Should be ** called by routines which initialize parsing, but not until their ** work has finished all parsing (so that recursive parsing can ** use it). */ void fsl_cx_clear_mf_seen(fsl_cx * f); /** @internal ** ** Generates an fsl_appendf()-formatted message to stderr and ** fatally aborts the application by calling exit(). This is only ** (ONLY!) intended for us as a placeholder for certain test cases ** and is not thread-safe. ** ** fmt may be empty or NULL, in which case only the code and its ** fsl_rc_cstr() representation are output. ** ** This function does not return. */ void fsl_fatal( int code, char const * fmt, ... ) #ifdef __GNUC__ __attribute__ ((noreturn)) #endif ; /** @internal ** ** Translate a filename into a filename-id (fnid). Create a new ** fnid if none previously exists. On success returns 0 and sets *rv ** to the filename.fnid record value. Returns non-0 on error. ** Results are undefined if any parameter is NULL. */ int fsl_repo_filename_to_fnid( fsl_cx * f, char const * filename, fsl_id_t * rv ); /** @internal ** ** Clears all fsl_buffer members of db but leaves the rest ** intact. If alsoErrorState is true then the error state is also ** cleared, else it is kept as well. */ void fsl_db_clear_strings(fsl_db * db, char alsoErrorState ); /** @internal ** ** Returns 0 if db appears to have a current repository schema, 1 ** if it appears to have an out of date schema, and -1 if it ** appears to not be a repository. Results are undefined if db is ** NULL or not opened. */ int fsl_db_repo_verify_schema(fsl_db * db); /** @internal ** ** Flags for APIs which add phantom blobs to the repository. The ** values in this enum derive from fossil(1) code and should not be ** changed without careful forethought and (afterwards) testing. A ** phantom blob is a blob about whose existence we know but for ** which we have no content. This normally happens during sync ** or rebuild operations. */ enum fsl_phantom_t { /** ** Indicates to fsl_uuid_to_rid2() that no phantom artifact ** should be created. */ FSL_PHANTOM_NONE = 0, /** ** Indicates to fsl_uuid_to_rid2() that a public phantom ** artifact should be created if no artifact is found. */ FSL_PHANTOM_PUBLIC = 1, /** ** Indicates to fsl_uuid_to_rid2() that a private phantom ** artifact should be created if no artifact is found. */ FSL_PHANTOM_PRIVATE = 2 }; typedef enum fsl_phantom_t fsl_phantom_t; /** @internal ** ** Works like fsl_uuid_to_rid(), with these differences: ** ** - uuid is expected to be a complete UUID, not a prefix. ** ** - If it finds no entry and the mode argument specifies so then ** it will add either a public or private phantom entry and return ** its new rid. If mode is FSL_PHANTOM_NONE then this this behaves ** just like fsl_uuid_to_rid(). ** ** Returns a positive value on success, 0 if it finds no entry and ** mode==FSL_PHANTOM_NONE, and a negative value on error (e.g. if ** fsl_is_uuid(uuid) returns false). Errors which happen after ** argument validation will "most likely" update f's error state ** with details. */ fsl_id_t fsl_uuid_to_rid2( fsl_cx * f, fsl_uuid_cstr uuid, fsl_phantom_t mode ); /** @internal ** ** Schedules the given rid to be verified at the next commit. This ** is used by routines which add artifact records to the blob ** table. ** ** The only error case, assuming the arguments are valid, is an ** allocation error while appending rid to the internal to-verify ** queue. ** ** @see fsl_repo_verify_at_commit() ** @see fsl_repo_verify_cancel() */ int fsl_repo_verify_before_commit( fsl_cx * f, fsl_id_t rid ); /** @internal ** ** Clears f's verify-at-commit list of RIDs. ** ** @see fsl_repo_verify_at_commit() ** @see fsl_repo_verify_before_commit() */ void fsl_repo_verify_cancel( fsl_cx * f ); /** @internal ** ** Processes all pending verify-at-commit entries and clears the ** to-verify list. Returns 0 on success. On error f's error state ** will likely be updated. ** ** ONLY call this from fsl_db_transaction_end() or its delegate (if ** refactored). ** ** Verification calls fsl_content_get() to "unpack" content added ** in the current transaction. If fetching the content (which ** applies any deltas it may need to) fails or a checksum does not ** match then this routine fails and returns non-0. Any error f's ** error state will be updated. ** ** @see fsl_repo_verify_cancel() ** @see fsl_repo_verify_before_commit() */ int fsl_repo_verify_at_commit( fsl_cx * f ); /** @internal ** ** Removes all entries from the repo's blob table which are listed ** in the shun table. Returns 0 on success. This operation is ** wrapped in a transaction. Delta contant which depend on ** to-be-shunned content are replaced with their undeltad forms. ** ** Returns 0 on success. */ int fsl_repo_shun_artifacts(fsl_cx * f); /** @internal. ** ** Return a pointer to a string that contains the RHS of an SQL IN ** operator which will select config.name values that are part of ** the configuration that matches iMatch (a bitmask of ** fsl_configset_t values). Ownership of the returned string is ** passed to the caller, who must eventually pass it to ** fsl_free(). Returns NULL on allocation error. */ char *fsl_config_inop_rhs(int iMask); /** @internal ** ** Return a pointer to a string that contains the RHS of an IN ** operator that will select config.name values that are in the ** list of control settings. Ownership of the returned string is ** passed to the caller, who must eventually pass it to ** fsl_free(). Returns NULL on allocation error. */ char *fsl_db_setting_inop_rhs(); /** ** Hard-coded range of values of the vfile.chnged db field. */ enum fsl_vfile_change_t { FSL_VFILE_CHANGE_NONE = 0, FSL_VFILE_CHANGE_MOD = 1, FSL_VFILE_CHANGE_MERGE_MOD = 2, FSL_VFILE_CHANGE_MERGE_ADD = 3, FSL_VFILE_CHANGE_INTEGRATE_MOD = 4, FSL_VFILE_CHANGE_INTEGRATE_ADD = 5 }; /** @internal ** ** Populates f's checkout vfile table with all files from the given ** Manifest RID. If vfile already contains entries for that ** manifest, it assumes they are loaded and does not insert them ** (returning 0 unless cleanup (see below) fails). If manifestRid ** is 0 or less then the current checkout's RID is used. ** ** The clearOthers flag is a compatibility flag for fossil(1). The ** vfile model allows an arbitrary number of checkin versions to be ** installed in it at once, but several of fossil(1)'s reports do ** not account for that, and assume only one version is loaded at a ** time. ** ** Returns 0 on success, any number of codes on any number of errors. ** ** f must not be NULL and must have opened checkout and repository ** databases. In debug builds it will assert that that is so. ** */ int fsl_vfile_load_from_rid(fsl_cx * f, fsl_id_t manifestRid, char clearOthers); /** ** @internal ** ** A bitmask of flags for fsl_vfile_changes_scan(). */ enum fsl_ckout_sig_t { /** ** The empty flags set. */ FSL_VFILE_CKSIG_NONE = 0, /** ** Non-file FS objects throw an error. Not yet implemented. */ FSL_VFILE_CKSIG_ENOTFILE = 0x001, /** ** Verify file content using sha1sum, regardless of whether or not ** file timestamps differ. */ FSL_VFILE_CKSIG_SHA1 = 0x002, /** ** Set mtime to last check-out time, as determined by ** fsl_mtime_of_manifest_file(). */ FSL_VFILE_CKSIG_SETMTIME = 0x004, /** ** Indicates that when populating the vfile table, it should ** be cleared of entries for other checkins. This is primarily ** for compatibility with fossil(1), which generally assumes ** only a single checkin's worth of state is in vfile. */ FSL_VFILE_CKSIG_CLEAR_VFILE = 0x008 }; /** @internal ** ** This function populates the vfile table for the given checkin ** version ID then compares it against files in the checkout ** directory, updating vfile's status for the current checkout ** version id as its goes. If vid is 0 or negative then the ** current checkout's RID is used in its place. cksigflags ** must be a bitmask of fsl_ckout_sig_t values. ** ** Returns 0 on success, non-0 on error. ** ** BUG: this does not properly catch one particular change, where ** a file has been replaced by a same-named non-file. */ int fsl_vfile_changes_scan(fsl_cx * f, fsl_id_t vid, int cksigFlags); /** @internal ** ** Creates the ticket and ticketchng tables in f's repository db, ** DROPPING them if they already exist. The schema comes from ** fsl_cx_schema_ticket(). ** ** Returns 0 on success. */ int fsl_cx_ticket_create_table(fsl_cx * f); /** @internal ** ** li is assumed to be empty or contain (fsl_card_J*) ** instances. Its contents are freed, along with any memory owned ** by li if alsoListMem is true, but li itself is not freed. ** ** Returns 0 on success. Results are undefined if li is NULL. */ void fsl_card_J_list_free( fsl_list * li, char alsoListMem ); /** @internal ** ** Values for fsl_card_J::flags. */ enum fsl_card_J_flags { /** ** Indicates that the field is used by the ticket table. */ FSL_CARD_J_TICKET = 0x01, /** ** Indicates that the field is used by the ticketchng table. */ FSL_CARD_J_CHNG = 0x02, /** ** Indicates that the field is used by both the ticket and ** ticketchng tables. */ FSL_CARD_J_BOTH = FSL_CARD_J_TICKET | FSL_CARD_J_CHNG }; /** @internal ** ** Loads all custom/customizable ticket fields from f's repo's ** ticket table info f. If f has already loaded the list and ** forceReload is false, this is a no-op. ** ** Returns 0 on success. ** ** @see fsl_cx::ticket::customFields */ int fsl_cx_ticket_load_fields(fsl_cx * f, char forceReload); /** @internal ** ** A comparison routine for qsort(3) which compares fsl_card_J ** instances in a lexical manner based on their names. The order is ** important for card ordering in generated manifests. ** ** This routine expects to get passed (fsl_card_J**) (namely from ** fsl_list entries), and will not work on an array of J-cards. */ int fsl_qsort_cmp_J_cards( void const * lhs, void const * rhs ); /** @internal ** ** The internal version of fsl_deck_parse(). See that function ** for details regarding everything but the 3rd argument. ** ** If you happen to know the _correct_ RID for the deck being ** parsed, pass it as the rid argument, else pass 0. A negative ** value will result in a FSL_RC_RANGE error. This value is (or ** will be) only used as an optimization in other places and only ** if d->f is not NULL. Passing a positive value has no effect on ** how the content is parsed or on the result - it only affects ** internal details/optimizations. ** */ int fsl_deck_parse2(fsl_deck * d, fsl_buffer * src, fsl_id_t rid); /** @internal ** ** This function updates the repo and/or global config databases ** with links between the dbs intended for various fossil-level ** bookkeeping and housecleaning. These links are not essential to ** fossil's functionality but assist in certain "global" ** operations. ** ** If no checkout is opened but a repo is, the global config (if ** opened) is updated to know about the opened repo db. ** ** If a checkout is opened, global config (if opened) and the ** repo are updated to point to the checked-out db. ** */ int fsl_repo_record_filename(fsl_cx * f); /** @internal ** ** Updates f->ckout.uuid and f->ckout.rid to reflect the current ** checkout state. If no checkout is opened, the uuid is ** freed/NULLed and the rid is set to 0. Returns 0 on success. If ** it returns an error, the f->ckout state is left in a potentially ** inconsistent state, and it should not be relied upon ** until/unless the error is resolved. */ int fsl_cx_update_checkout_uuid( fsl_cx *f ); /** @internal ** ** Not yet implemented - this is a placeholder for a to-be-ported ** feature. */ int fsl_cx_crosslink_rid( fsl_cx * f, fsl_id_t rid ); #if defined(__cplusplus) } /*extern "C"*/ #endif #endif /* NET_FOSSIL_SCM_FSL_INTERNAL_H_INCLUDED */