Login
Artifact [e9c1527653]
Login

Artifact e9c1527653bc2880e9e41f43ff10bffde2a56049:


/* -*- 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!  Must be <=
        capacity.
     */
    fsl_size_t entryCount;
    /**
        The number of elements allocated for this->list.
     */
    fsl_size_t capacity;
    /**
        The number of elements in this->list which have a zero or
        positive value. 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, intended
      for const-copy initialization.
   */
#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, intended
      for copy initialization.
   */
  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-initialized fsl_xlinker struct, intended for const-copy
      intialization. */
#define fsl_xlinker_empty_m {NULL,NULL,NULL}

  /** Empty-initialized fsl_xlinker struct, intended for copy intialization. */
  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, intended for
      const-copy intialization. */
#define fsl_xlinker_list_empty_m {0,0,NULL}

  /** Empty-initializes fsl_xlinker_list struct, intended for copy intialization. */
  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(). It contains a
          trailing slash, largely because that simplifies porting
          fossil(1) code.
       */
      char * dir;
      /**
         Optimization: fsl_strlen() of dir. Guaranteed to be set to
         dir's length if dir is not NULL.
       */
      fsl_size_t dirLen;
      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;


    /**
       State for incrementally proparing a checkin operation.
    */
    struct {
      /**
         Tells us if vfile_selected has been created or not.
      */
      char hasSetupVfile;

      /**
         Holds a list of "selected files" in the form
         of vfile.id values.
      */
      fsl_id_bag selectedIds;

      /**
         The deck used for incrementally building certain parts of a
         checkin.
      */
      fsl_deck mf;
    } ckin;

    /**
        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 place for holding file content. We use this in places where
       we have to loop over files and read their entire contents.  The
       loop and the reading might be happening in different functions,
       though.
    */
    fsl_buffer fileContent;

    /**
        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;

      /**
          If true, SOME repository-level file-name
          comparisons/searches will work case-insensitively.
      */
      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;

      /**
          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/*dirLen*/, \
        0/*rid*/, NULL/*uuid*/ \
    },                                                              \
    {/*repo*/ fsl_db_empty_m /*db*/, NULL/*user*/},                     \
    {/*config*/ fsl_db_empty_m /*db*/ }, \
    {/*ckin*/\
      0/*hasSetupVfile*/,\
      fsl_id_bag_empty_m/*selectedIds*/, \
      fsl_deck_empty_m/*mf*/\
    }, \
    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_buffer_empty_m /*fileContent*/,           \
    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*/,         \
      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 (0) if it is
      not. It is illegal to pass an e value of 0, and that will
      trigger an assertion in debug builds.
   */
  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).

      e must be positive or an assertion is triggered in debug builds.
   */
  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.

      Example usage:

      @code
      fsl_id_t nid;
      for( nid = fsl_id_bag_first(&list);
           nid > 0;
           nid = fsl_id_bag_next(&list, nid)){
     
          ...do something...
      }
      @endcode
   */
  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. It is illegal to pass this 0 (and will
      trigger an assertion in debug builds). Pass it the non-0 return
      value from fsl_id_bag_first().
   */
  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. This is often the RID of the
      manifest containing tags added as part of the commit, in which
      case rid==srcId. A Control Artifact which tags a different
      artifact will have 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
      record ID of the generated tag (the tag.tagid db field).

      If a more recent (compared to mtime) entry already exists for
      this tag/rid combination then its tag.tagid 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 normalized, repo-relative filename into a
      filename-id (fnid). Create a new fnid if none previously exists
      and createNew is true. On success returns 0 and sets *rv to the
      filename.fnid record value. If createNew is false and no match
      is found, 0 is returned but *rv will be set to 0. Returns non-0
      on error.  Results are undefined if any parameter is NULL.


      In debug builds, this function asserts that no pointer arguments
      are NULL and that f has an opened repository.
  */
  int fsl_repo_filename_fnid( fsl_cx * f, char const * filename, fsl_id_t * rv, char createNew );

  /** @internal

      Functionally similar to fsl_repo_filename_fnid(), but fetches
      a vfile.id value. vid is the vfile.vid to filter on. If vid is
      <=0 then f->ckout.rid is used. It only matches on
      vfile.pathname, not vfile.origname, and requires an exact match
      because it is expected that the input filename will typically
      come from fsl_card_F entries.

      On success returns 0 and sets *rv to the vfile.id or to 0 if no
      entry is found. On error, returns non-0 and it should be taken
      seriously.

      In debug builds, this function asserts that no pointer arguments
      are NULL and that f has an opened checkout.
   */
  int fsl_checkout_filename_vfile_id( fsl_cx * f, char const * fn, fsl_id_t vid,
                                      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,
  /**
      For unchanged or changed-by-merge files, set the 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 and can get confused
      if that is not the case.
  */
  FSL_VFILE_CKSIG_CLEAR_VFILE = 0x008
  };


  /** @internal

      This function populates (if needed) 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 corner-case
      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. If alsoListMem is true then any memory owned
      by li is also freed. li itself is not freed.

      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 {
  /**
     Sentinel value.
  */
  FSL_CARD_J_INVALID = 0,
  /**
      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

      On Windows platforms (only), if fsl_isalpha(*zFile)
      and ':' == zFile[1] then this returns zFile+2,
      otherwise it returns zFile.
  */
  char * fsl_file_without_drive_letter(char * zFile);

  /** @internal

      This is identical to the public-API member fsl_deck_F_search(),
      except that it returns a non-const F-card.

      Locate a file named zName in d->F.list.  Return a pointer to the
      appropriate fsl_mf_card object. Return NULL if not found.
   
      If d->f is set (as it is when loading decks via
      fsl_deck_load_rid() and friends), this routine works even if p is
      a delta-manifest. The pointer returned might be to the baseline
      and d->B.baseline is loaded on demand if needed.
   
      If the returned card's uuid member is NULL, it means that the file
      was removed in the checkin represented by d.

      If !d, zName is NULL or empty, or FSL_CATYPE_CHECKIN!=d->type, it
      asserts in debug builds and returns NULL in non-debug builds.

      We assume that filenames are in sorted order and use a binary
      search. As an optimization, to support the most common use case,
      searches through a deck update d->F.cursor to the last position a
      search was found. Because searches are normally done in lexical
      order (because of architectural reasons), this is normally an O(1)
      operation. It degrades to O(N) if out-of-lexical-order searches
      are performed.
   */
  fsl_card_F * fsl_deck_F_seek(fsl_deck * const d, const char *zName);

  /** @internal

      Part of the fsl_cx::fileContent optimization. This sets
      f->fileContent.used to 0 and if its capacity is over a certain
      (unspecified, unconfigurable) size then it is trimmed to that
      size.
  */
  void fsl_cx_yield_file_buffer(fsl_cx * f);

#if defined(__cplusplus)
} /*extern "C"*/
#endif
#endif
/* NET_FOSSIL_SCM_FSL_INTERNAL_H_INCLUDED */