Login
Documentation
Login
/* -*- 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/

  ******************************************************************************
   This file declares public APIs for working with fossil-managed content.
*/

#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_checkin_opt fsl_checkin_opt;
  typedef struct fsl_deck fsl_deck;

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

      Another special case: when t==FSL_CATYPE_CHECKIN and card=='F',
      this returns a negative value because the table linked to above
      says F-cards are optional. In practice we have yet to find a use
      for checkins with no F-cards, so this library currently requires
      F-cards at checkin-time even though this function reports that
      they are optional.
  */
  char fsl_card_is_legal( fsl_catype_t t, char card );

  /**
      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" indicator, 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
  };

  /**
     Returns one of '-', '+', or '*' for a valid input parameter, 0
     for any other value.
  */
  char fsl_tag_prefix_char( fsl_tag_type t );

  /**
      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 (a.k.a. "checkin") 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 (e.g. fsl_card::C holds a C-card and fsl_card::F
      holds a list of F-card). Each type of artifact only allows
      certain types of card. The complete list of valid card/construct
      combinations can be found 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 fsl_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(). NEVER EVER EVER directly
      modify a member of this struct - always use the APIs. The
      library performs some optimizations which can lead to corrupt
      memory and invalid free()s if certain members' values are
      directly replaced by the client (as opposed to via the APIs).

      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 artifact), 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_malloc()
      @see fsl_deck_init()
      @see fsl_deck_parse()
      @see fsl_deck_load_rid()
      @see fsl_deck_finalize()
      @see fsl_deck_clean()
      @see fsl_deck_save()
      @see fsl_deck_A_set()
      @see fsl_deck_B_set()
      @see fsl_deck_D_set()
      @see fsl_deck_E_set()
      @see fsl_deck_F_add()
      @see fsl_deck_J_add()
      @see fsl_deck_K_set()
      @see fsl_deck_L_set()
      @see fsl_deck_M_add()
      @see fsl_deck_N_set()
      @see fsl_deck_P_add()
      @see fsl_deck_Q_add()
      @see fsl_deck_R_set()
      @see fsl_deck_T_add()
      @see fsl_deck_U_set()
      @see fsl_deck_W_set()
  */
  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. Relatively few deck operations make
        use of this.
    */
    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.
      */
      char * 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 by routines which need it, typically by calling
          fsl_deck_F_rewind() (unintuitively enough!). 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.
     */
    char * 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 primarily for
          properly traversing the file list in delta manifests.

          Maintenance note: internal updates to this member are the
          only reason some of the deck APIs require a non-const deck.
       */
      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.
     */
    char * L;

    /**
        UUID for the 'K' (ticket) card.
     */
    fsl_uuid_str K;

    /**
        List of UUIDs (fsl_uuid_str) in a cluster ('M' cards).
     */
    fsl_list M;

    /**
        The 'N' (content mime type) card.
     */
    char * 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_Q*) entries.
     */
    fsl_list Q;

    /**
        The R-card holds an MD5 hash which is calculated based on the
        names, sizes, and contents of the files included in a
        manifest. See the class-level docs for a link to a page which
        describes how this is calculated.
     */
    char * R;

    /**
        List of 'T' (tag) cards. Holds (fsl_card_T*) instances.
     */
    fsl_list T;

    /**
        The U (user) card.
     */
    char * U;

    /**
        The W (wiki content) card.
     */
    fsl_buffer W;

    /**
        For propagating error state through certain parts of the API.
     */
    fsl_error error;

    /**
       This is part of an optimization used when parsing fsl_deck
       instances from source text. For most types of card we re-use
       string values in the raw source text rather than duplicate
       them, and that requires storing the original text (as passed to
       fsl_deck_parse()). This requires that clients never tinker
       directly with values in a fsl_deck, in particular never assign
       over them or assume they know who allocate the memory for that
       bit.
    */
    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 fsl_free() it).
     */
    void const * allocStamp;

  };

  /**
      Initialized-with-defaults fsl_deck structure, intended for copy
      initialization.
   */
  extern const fsl_deck fsl_deck_empty;

  /**
      Initialized-with-defaults fsl_deck structure, intended for
      in-struct and const copy initialization.
   */
#define fsl_deck_empty_m {                      \
    FSL_CATYPE_ANY /*type*/,                    \
    -1/*rid*/,\
    NULL/*uuid*/,\
    NULL/*f*/,\
    {/*A*/ NULL /* name */,       \
           NULL /* tgt */,           \
           NULL /* src */},          \
    {/*B*/ NULL /*uuid*/,                        \
           NULL /*baseline*/},                      \
     NULL /* C */,               \
     0.0 /*D*/,                        \
     {/*E*/ 0.0 /* julian */,                    \
            NULL /* uuid */},      \
     {/*F*/ fsl_list_empty_m /*list*/,0/*cursor*/},    \
     fsl_list_empty_m /* J */,                 \
     NULL /* L */,               \
     NULL /* K */,               \
     fsl_list_empty_m /* M */,                 \
     NULL /* N */,               \
     fsl_list_empty_m /* P */,                 \
     fsl_list_empty_m /* Q */,                 \
     NULL /* R */,                           \
     fsl_list_empty_m /* T */,                 \
     NULL /* 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()
      @see fsl_deck_clean()
   */
  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. As a special
      case, the (allocStamp, f) members of deck are kept intact.

      @see fsl_deck_finalize()
      @see fsl_deck_malloc()
   */
  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. Setting this will delete any
      prior values in mf->B, including a previously loaded
      mf->B.baseline.

      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 to 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 fsl_julian_now() 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 be NULL or 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_file_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_file_perm_t perm, 
                      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.

      This function calls fsl_deck_F_rewind() if includeBaseline is
      true, so the caller need not do so.
   */
  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 over delta manifests.

      This routine always assigns *f to NULL before starting its work,
      so the client can be assured that it will never contain the same
      value as before calling this.

      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

      Unfortunately there is some internal inconsistency in the
      handling and traversal of "deleted" files. Delta manifests mark
      them as an F-card with a NULL UUID. Baseline checkins do not
      contain F-cards for deleted files. For most purposes deleted
      entries can be skipped over, but it is sometimes interesting to
      know about them. That said, because baseline manifests do not
      explicitly record deletion entries, it is not trivial to figure
      out if a given file was deleted between two baselines (it
      requires traversing both of them in full). FIXME: There is
      currently a case where this routine will _not_ return a deleted
      file, though it does for other cases.

      Implementation notes: for baseline manifests this is a very
      fast and simple operation. For delta manifests it gets
      rather complicated.

      Bugs:

      - There are some inconsistencies in exactly when deleted
      files are reported by this function. Some deleted entries
      will get through and some won't. This is due to a change
      in historical behaviour which "missed a spot."
  */
  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 );

  /**
     Looks for a file in a manifest or (for a delta manifest) its
     baseline. No normalization of the given filename is performed -
     it is assumed to be relative to the root of the checkout.

     It requires that d->type be FSL_CATYPE_CHECKIN and that d be
     loaded from a stored manifest or have been fsl_deck_unshuffle()'d
     (if called on an under-construction deck). Specifically, this
     routine requires that d->F be sorted properly or results are
     undefined.

     If d->f is set then it is assumed to be the fsl_cx instance which
     deck was loaded from. In that case this function takes on two new
     behaviours:

     - The search take's d->f's underlying case-insensitive option into
     account. i.e. if case-insensitivy is on then files in any case
     will match.

     - If no match is found in d and is a delta manifest (d->B.uuid
     is set) then d's baseline is lazily loaded (if needed) and
     the search continues there. (Delta manifests are only one level
     deep, so this is not recursive.)

     Returns NULL if !d, !d->f, or d->type!=FSL_CATYPE_CHECKIN, if no
     entry is found, or if delayed loading of the parent manifest (if
     needed) of a delta manifest fails (in which case d->f's error
     state should hold more information about the problem).

     In debug builds this function asserts that d is not NULL.

     Design note: d "should" be const, but search optimizations for
     the typical use case require potentially lazy-loading
     d->B.baseline and updating d->F.
  */
  fsl_card_F const * fsl_deck_F_search(fsl_deck *d, const char *zName);

  /**
     Given two F-card instances, this function compares their names
     (case-insensitively). Returns a negative value if lhs is
     lexically less than rhs, a positive value if lhs is lexically
     greater than rhs, and 0 if they are lexically equivalent (or are
     the same pointer).

     Results are undefined if either argument is NULL.
  */
  int fsl_card_F_compare( fsl_card_F const * lhs,
                          fsl_card_F const * rhs);

  /**
     If fc->uuid refers to a blob in f's repository database then that
     content is placed into dest (as per fsl_content_get()) and 0 is
     returned. Returns FSL_RC_NOT_FOUND if fc->uuid is not
     found. Returns FSL_RC_MISUSE if any argument is NULL.  If
     fc->uuid is NULL (indicating that it refers to a file deleted in
     a delta manifest) then FSL_RC_RANGE is returned. Returns
     FSL_RC_NOT_A_REPO if f has no repository opened.

     On any error but FSL_RC_MISUSE (basic argument validation) f's
     error state is updated to describe the error.

     @see fsl_content_get()
   */
  int fsl_card_F_content( fsl_cx * f, fsl_card_F const * fc,
                          fsl_buffer * dest );


  /**
      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 given 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 (from merges) 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. 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.

      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.

      It would be highly unusual to have to set the R-card manually,
      as its calculation is quite intricate/intensive. See
      fsl_deck_R_calc() and fsl_deck_unshuffle() for details
   */
  int fsl_deck_R_set( fsl_deck * mf, char const *md5);

  /**
      Adds a new T-card (tag) entry to the given deck.

      If uuid is not NULL and fsl_is_uuid(uuid) returns false then
      this function returns FSL_RC_RANGE. If uuid is NULL then it is
      assumed to be the UUID of the currently-being-constructed
      artifact in which the tag is contained (which appears as the '*'
      character in generated artifacts).

      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 that d->f have an opened repository db.
      If d has no F-cards then the R-card's value is that of the
      initial MD5 hash state.

      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
      allows the client to decide on whether to use them (for more
      (admittedly redundant!) integrity checking) or not (must faster
      but "not strictly historically correct"), but defaults to having
      them enabled for symmetry with fossil(1).
  */
  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 or Event 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 );

  /**
     Equivalent to fsl_deck_required_cards_check(d, NULL);
  */
  char fsl_deck_has_required_cards( fsl_deck const * d );

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

      The library calls this as needed, but clients may, if they want
      to. Note, however, that for FSL_CATYPE_CHECKIN decks it may fail
      if the deck has not been fsl_deck_unshuffle()d yet because the
      R-card gets calculated there (if needed).

      Returns 0 if d is NULL, otherwise if it returns 0 and err is not
      NULL then err will contain a description of the first missing
      card. Assuming the caller has a non-const reference to d, it is
      legal to pass &d->error as the second parameter.
      
      As a special case, if d->f is not NULL, d->f is configured to
      calculate R-cards, d->type==FSL_CATYPE_CHECKIN AND d->R is not
      set, this will fail (with a descriptive error message).

      Another special case: for FSL_CATYPE_CHECKIN decks, if no
      F-cards are in th deck then an R-card is required to avoid a
      potental (admittedly harmless) syntactic ambiguity with
      FSL_CATYPE_CONTROL artifacts. The only legal R-card for a
      checkin with no F-cards has the initial MD5 hash state value
      (defined in the constant FSL_MD5_INITIAL_HASH), and that
      precondition is checked in this routine. fsl_deck_unshuffle()
      recognizes this case and adds the initial-state R-card, so
      clients normally need not concern themselves with this. If d has
      F-cards, whether or not an R-card is required depends on
      conditions described above.

      Enough about the R-card. In all other cases not described above,
      R-cards are not required (and they are only ever required on
      FSL_CATYPE_CHECKIN manifests).

      Though fossil(1) does not technically require F-cards in
      FSL_CATYPE_CHECKIN decks, so far none of the Fossil developers
      have found a use for a checkin without F-cards except the
      initial empty checkin. Additionally, a checkin without F-cards
      is potentially syntactically ambiguous (it could be an EVENT or
      ATTACHMENT artifact if it has no F- or R-card). So... this
      library _normally_ requires that CHECKIN decks have at least one
      F-card. This function, however, does not consider F-cards to be
      strictly required.
  */
  char fsl_deck_required_cards_check( fsl_deck const * d,
                                      fsl_error * err);


  /**
      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. It is not possible(?) for
      the library to determine a proper order for P-cards, nor to
      validate that order at input-time.

      If calculateRCard is true and fsl_card_is_legal(d,'R') then this
      function calculates the R-card for the deck. The R-card
      calculation is _extremely_ memory-hungry but adds another level
      of integrity checking to Fossil. If d->type is not
      FSL_CATYPE_MANIFEST then calculateRCard is ignored.

      If calculateRCard is false then the F-cards (if any) are sorted.
      If no F-cards are present AND d->type is FSL_CATYPE_CHECKIN then
      the R-card is set to the initial MD5 hash state (the only legal
      R-card value for an empty F-card list).

      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. There are rare
      cases where the client can call fsl_deck_R_set()
      legally. 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.

      @see fsl_deck_output()
      @see fsl_deck_save()
   */
  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 Z-card).

      fsl_deck_unshuffle() must have been called on the deck first to
      do any required sorting and to finalize any pending values which
      need to be calculated for the deck (namely the R-card).

      Returns 0 on success, FSL_RC_MISUSE if !mf or !f. If out()
      returns non-0, output stops and that code is
      returned. outputState is passed as the first argument to
      out(). out() may be called an arbitrary number of times by this
      routine.

      The errOut argument may be NULL. If it is not NULL then most
      types of errors will update errOut with information about the
      problem. Tip: this function cannot update mf->error because mf
      is const (because this is an output-only operation), but the
      client _may_ pass (&mf->error) as the 4th argument, assuming his
      own copy of mf is non-const.

      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()
      returns false.

      @see fsl_deck_unshuffle()
      @see fsl_deck_save()
   */
  int fsl_deck_output( fsl_deck const * mf,
                       fsl_output_f out, void * outputState,
                       fsl_error * errOut );


  /**
      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 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 gets calculated if the deck
      has any F-cards AND if the caller has not already set/calculated
      it AND if f's FSL_CX_F_CALC_R_CARD flag is set (it is on by
      default for historical reasons, but this may change at some
      point).

      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 in 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
      it _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 );

  /**
      Runs postprocessing on the Control Artifact represented by
      d. d->f must be set, 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.

      Crosslinking of ticket artifacts is currently missing.

      Design note: d "really should" be const here but some internals
      (d->F.cursor and delayed baseline loading) currently prohibit
      it. In addition, we need d->f to be non-const.
   */
  int fsl_deck_crosslink( fsl_deck /* const */ * d );

  /**
      Parses src as Control Artifact content and populates d with it.

      d will be cleaned up before parsing if it has any contents.

      This function _might_ take over the contents of the source
      buffer on success or it _might_ leave it for the caller to clean
      up or re-use, as he sees fit. If the caller does not intend to
      re-use the buffer, he should simply pass it to
      fsl_buffer_clear() after calling this (no need to check if it
      has contents or not first).

      When taking over the contents then on success, after returning
      src->mem will be NULL, and all other members will be reset to
      their default state. This function only takes over the contents
      if it decides to implement certain memory optimizations.

      Ownership of src itself is never changed by this function, only
      (possibly!) the ownership of its contents.

      In any case, the content of the source buffer 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 that way, (D) because in
      historical use the source is normally thrown away after parsing,
      anyway, and (E) in combination with taking ownership of src's
      contents it allows us to optimize away some memory allocations
      by re-using the internal memory of the buffer. This function
      never changes src's size, but it mutilates its contents
      (injecting NUL bytes as token delimiters).

      If d->type is _not_ FSL_CATYPE_ANY when this is called, then
      this function requires that the input to be of that type. We can
      fail relatively quickly in that case, and this can be used to
      save some downstream code some work. Note that the initial type
      for decks created using fsl_deck_malloc() or copy-initialized
      from ::fsl_deck_empty is FSL_CATYPE_ANY, so normally clients do
      not need to set this (unless they want to, as a small
      optimization).

      On success it returns 0 and d will be updated with the state
      from the input artifact. (Ideally, outputing d via
      fsl_deck_output() will produce a lossless copy of the original.)
      d->uuid will be set to the SHA1 of the input artifact, ignoring
      any surrounding PGP signature for hashing purposes.

      If d->f is not NULL, has an opened repository db, and the parsed
      artifact has a counterpart in the database (determined via an
      SHA1 match) then d->rid is set to the record ID.

      On error, if there is error information to propagate beyond the
      result code then it is stored in d->f (if that is not NULL),
      else in d->error. Whether or not such error info is propagated
      depends on the type of error, but anything more trivial than
      invalid arguments will be noted there.

      d might be partially populated on error, so regardless of success
      or failure, the client must eventually pass d to
      fsl_deck_finalize() to free its memory.

      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.
  */
  int fsl_deck_parse(fsl_deck * d, fsl_buffer * src);

  /**
      Loads the content from given rid and tries to parse it as a
      Fossil artifact. If rid==0 the current checkout (if opened) is
      used. (Trivia: there can never be a checkout with rid==0 but
      rid==0 is sometimes valid for an new/empty repo devoid of
      commits). If type==FSL_CATYPE_ANY then it will allow any type of
      control artifact, else it returns FSL_RC_TYPE if the loaded
      artifact 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
      "probably" be properly populated but will not hold the type of
      artifact requested. It "should" otherwise be well-formed because
      parsing errors occur before the type check can happen, but
      parsing of invalid manifests might also trigger a FSL_RC_TYPE
      error of a different nature. The morale of the storage is: if
      this function returns non-0, assume d is useless and needs to be
      cleaned up.

      f's error state may be updated on error (for anything more
      serious than basic argument validation errors).

      On success d->f is set to f.

      @see fsl_deck_load_sym()
  */
  int fsl_deck_load_rid( fsl_cx * f, fsl_deck * d,
                         fsl_id_t rid, fsl_catype_t type );

  /**
      A convenience form of fsl_deck_load_rid() which uses
      fsl_sym_to_rid() to convert symbolicName into an artifact RID.
      See fsl_deck_load_rid() for the symantics of the first, third,
      and fourth arguments, as well as the return value. See
      fsl_sym_to_rid() for the allowable values of symbolicName.

      @see fsl_deck_load_rid()
   */
  int fsl_deck_load_sym( fsl_cx * f, fsl_deck * d,
                         char const * symbolicName, 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 );

  /**
      A callback interface for manifest crosslinking, so that we can
      farm out the updating of the event table. Each callback
      registered via fsl_xlink_listener() will be called at the end of
      the so-called crosslinking process, which is run every time a
      control artifact is processed for f's repository database,
      passed the fossil context, the deck being crosslinked, and the
      client-provided state which was registered with
      fsl_xlink_listener().

      Implementations must return 0 on success or some other fsl_rc_t
      value on error. Returning non-0 causes the database transaction
      for the crosslinking operation to roll back, effectively
      cancelling whatever pending operation triggered the
      crosslink. If any callback fails, processing stops immediately -
      no other callbacks are executed.

      Implementations which want to report more info than an integer
      should call fsl_cx_err_set() to set f's error state, as that
      will be propagated up to the code which initiated the failed
      crosslink.

      Potential TODO: we might need to make the deck parameter
      non-const so that clients can, e.g., use fsl_deck_F_rewind() and
      friends.

      @see fsl_xlink_listener()
   */
  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
      "may" be required to outlive f, depending on cbState's exact
      client-side internal semantics/use, 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. The name may be arbitrarily long, but must have a
      terminating NUL byte. 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, and will
      happily overwrite client-registered entries with the same
      names. 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 expected 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. On error
      tgt might be partially populated. 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 );

  /**
      Uses fsl_sym_to_rid() to convert sym to a record ID, then
      passes that to fsl_content_get(). Returns 0 on success.
   */
  int fsl_content_get_sym( fsl_cx * f, char const * sym, 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);

  /**
      Generic callback interface for visiting decks. The interface
      does not generically require that d survive after this call
      returns.

      Implementations must return 0 on success, non-0 on error. Some
      APIs using this interface may specify that FSL_RC_BREAK can be
      used to stop iteration over a loop without signaling an error.
      In such cases the APIs will translate FSL_RC_BREAK to 0 for
      result purposes, but will stop looping over whatever it is they
      are looping over.
   */
  typedef int (*fsl_deck_visitor_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.

      @see fsl_wiki_load_latest()
      @see fsl_wiki_latest_rid()
      @see fsl_wiki_names_get()
      @see fsl_wiki_page_exists()
   */
  int fsl_wiki_foreach_page( fsl_cx * f, fsl_deck_visitor_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,
      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.

      @see fsl_wiki_load_latest()
      @see fsl_wiki_foreach_page()
      @see fsl_wiki_names_get()
      @see fsl_wiki_page_exists()
   */
  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.

      @see fsl_wiki_latest_rid()
      @see fsl_wiki_names_get()
      @see fsl_wiki_page_exists()
   */
  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.

      @see fsl_wiki_load_latest()
      @see fsl_wiki_latest_rid()
      @see fsl_wiki_names_get()
      @see fsl_wiki_names_get()
   */
  char fsl_wiki_page_exists(fsl_cx * f, char const * pageName);

  /**
      A helper type for use with fsl_wiki_save(), intended primarily
      to help client-side code readability somewhat.
   */
  enum fsl_wiki_save_mode_t {
  /**
      Indicates that fsl_wiki_save() must only allow the creation of
      a new page, and must fail if such an entry already exists.
   */
  FSL_WIKI_SAVE_MODE_CREATE = -1,
  /**
      Indicates that fsl_wiki_save() must only allow the update of an
      existing page, and will not create a branch new page.
   */
  FSL_WIKI_SAVE_MODE_UPDATE = 0,
  /**
      Indicates that fsl_wiki_save() must allow both the update and
      creation of pages. Trivia: "upsert" is a common SQL slang
      abbreviation for "update or insert."
   */
  FSL_WIKI_SAVE_MODE_UPSERT = 1
  };

  typedef enum fsl_wiki_save_mode_t fsl_wiki_save_mode_t;

  /**
      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_cx_user_get() or fsl_guess_user_name() are
      used (in that order) 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 is
      determined by creationPolicy. If it is
      FSL_WIKI_SAVE_MODE_UPDATE, this function will fail with
      FSL_RC_NOT_FOUND if no page with the given name already exists.
      If it is FSL_WIKI_SAVE_MODE_CREATE and a previous version _does_
      exist, it fails with FSL_RC_ALREADY_EXISTS. If it is
      FSL_WIKI_SAVE_MODE_UPSERT then both the save-exiting and
      create-new cases are allowed. In summary:

      - use FSL_WIKI_SAVE_MODE_UPDATE to allow updates to existing pages
      but disallow creation of new pages,

      - use FSL_WIKI_SAVE_MODE_CREATE to allow creating of new pages
      but not of updating an existing page.

      - FSL_WIKI_SAVE_MODE_UPSERT allows both updating and creating
      a new page on demand.

      Returns 0 on success, or any number fsl_rc_t codes on error. On
      error no content changes are saved, and any transaction is
      rolled back or a rollback is scheduled if this function is
      called while a transaction is active.


      Potential TODO: add an optional (fsl_id_t*) output parameter
      which gets set to the new record's RID.

      @see fsl_wiki_page_exists()
      @see fsl_wiki_names_get()
   */
  int fsl_wiki_save(fsl_cx * f, char const * pageName,
                    fsl_buffer const * b, char const * userName,
                    char const * mimeType, fsl_wiki_save_mode_t creationPolicy );

  /**
      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 or serious system-level error).

      It is up to the caller free the entries added to the list. Some
      of the 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 );

  /**
      Represents one file entry in a Manifest/Control Artifact (i.e.,
      a checkin version).

      All of the non-const pointers in this class are owned by the
      respective instance of the class, and must neither be modified
      nor freed except via the appropriate APIs.
  */
  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, else NULL.
     */
    char * priorName;
    /**
        File permissions. Fossil only supports one "permission" per
        file, and it does not necessarily map to a real
        filesystem-level permissino.

        @see fsl_file_perm_t
     */
    fsl_file_perm_t perm;

    /**
       An internal optimization. Do not mess with this.  When this is
       true, the various string members of this struct are not owned
       by this struct, but by the deck which created this struct. This
       is used when loading decks from storage - the strings are
       pointed to the original content data, rather than strdup()'d
       copies of it. fsl_card_F_free() will DTRT and delete the
       strings (or not).
    */
    char externalStrings;
  };

  /**
      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/*name*/, \
    NULL/*uuid*/, \
    NULL/*priorName*/, \
    0/*perm*/, \
    0/*externalStrings*/\
  }

  /**
      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 append;
    /**
        For internal use only.
     */
    unsigned char flags;
    /**
       The ticket field to update. The bytes are owned by this object.
    */
    char * field;
    /**
       The value for the field. The bytes are owned by this object.
    */
    char * value;
  };
  /** Empty-initialized fsl_card_J struct. */
#define fsl_card_J_empty_m {0,0,NULL, NULL}
  /** 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. The bytes are owned by this object.
     */
    char * name;
    /**
        The tag's value. May be NULL/empty. The bytes are owned by
        this object.
     */
    char * value;
  };
  /** Defaults-initialized fsl_card_T instance. */
#define fsl_card_T_empty_m {FSL_TAGTYPE_INVALID, NULL, NULL,NULL}
  /** 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. The bytes are owned by
       this object.
    */
    fsl_uuid_str target;
    /**
       UUID of the baseline for the cherrypick. The bytes are owned by
       this object.
    */
    fsl_uuid_str baseline;
  };
  /** Empty-initialized fsl_card_Q struct. */
#define fsl_card_Q_empty_m {0, NULL, NULL}
  /** 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,
                                 fsl_file_perm_t perm,
                                 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 );

  /**
      Returns true (non-0) if f is not NULL and f has an opened repo
      which contains a checkin with the given rid, else it returns
      false.

      As a special case, if rid==0 then this only returns true
      if the repository currently has no content in the blob
      table.
   */
  char fsl_rid_is_a_checkin(fsl_cx * f, fsl_id_t rid);


  /**
      Fetches the list of all directory names for a given record id or
      (if rid is negative) the whole repo over all of its combined
      history. Each name entry in the list is appended to tgt. The
      results are reduced to unique names only and are sorted
      lexically. If addSlash is true then each entry will include a
      trailing slash character, else it will not. The list does not
      include an entry for the top-most directory.

      If rid is less than 0 then the directory list across _all_
      versions is returned. If it is 0 then the current checkout's RID
      is used (if a checkout is opened, otherwise a usage error is
      triggered). If it is positive then only directories for the
      given checkin RID are returned. If rid is specified, it is
      assumed to be the record ID of a commit (manifest) record, and
      it is impossible to distinguish between the results "invalid
      rid" and "empty directory list" (which is a legal result).

      On success it returns 0 and tgt will have a number of (char *)
      entries appended to it equal to the number of subdirectories in
      the repo (possibly 0).

      Returns non-0 on error, FSL_RC_MISUSE if !f, !tgt. On other
      errors error tgt might have been partially populated and the
      list contents should not be considered valid/complete.

      Ownership of the returned strings is transfered to the caller,
      who must eventually free each one using
      fsl_free(). fsl_list_visit_free() is the simplest way to free
      them all at once.
   */
  int fsl_repo_dir_names( fsl_cx * f, fsl_id_t rid, fsl_list * tgt, char addSlash );


  /**
      ZIPs up a copy of the contents of a specific version from f's
      opened repository db. sym is the symbolic name for the checkin
      to ZIP. filename is the name of the ZIP file to output the
      result to. See fsl_zip_writer for details and caveats of this
      library's ZIP creation. If vRootDir is not NULL and not empty
      then each file injected into the ZIP gets that directory
      prepended to its name.

      If progressVisitor is not NULL then it is called once just
      before each file is processed, passed the F-card for the file
      about to be zipped and the progressState parameter. If it
      returns non-0, ZIPping is cancelled and that error code is
      returned. This is intended primarily for providing feedback on
      the update process, but could also be used to cancel the
      operation between files.

      BUG: this function does not honor symlink content in a
      fossil-compatible fashion. If it encounters a symlink entry
      during ZIP generation, it will fail and f's error state will be
      updated with an explanation of this shortcoming.

      @see fsl_zip_writer
      @see fsl_card_F_visitor_f()
   */
  int fsl_repo_zip_sym_to_filename( fsl_cx * f, char const * sym,
                                    char const * vRootDir,
                                    char const * fileName,
                                    fsl_card_F_visitor_f progressVisitor,
                                    void * progressState);


  /**
     Callback state for use with fsl_repo_extract_f() implementations
     to stream a given version of a repository's file's, one file at a
     time, to a client. Instances are never created by client code,
     only by fsl_repo_extract() and its delegates, which pass them to
     client-provided fsl_repo_extract_f() functions.
  */
  struct fsl_repo_extract_state {
    /**
       Client state passed to fsl_repo_extract(). Its interpretation
       is callback-implementation-dependent.
    */
    void * state;
    /**
       The F-card being iterated over. This holds the repo-level
       metadata associated with the file.

       Deleted files are NOT reported via the extraction process
       because reporting them accurately is trickier and more
       expensive than it could be. Thus this member's uuid field
       will always be non-NULL.
     */
    fsl_card_F const * fc;

    /**
       Holds the full, undelta'd/uncompressed content of fc's file
       record. The content bytes are owned by fsl_repo_extract() and
       are invalidated as soon as this callback returns, so the
       callback must copy/consume them immediately if needed.
    */
    fsl_buffer const * content;
    /**
       The associated Fossil context.
    */
    fsl_cx * f;
    /**
       RID of the checkin version for this file. For a given call to
       fsl_repo_extract(), this number will be the same across all
       calls to the callback function.
    */
    fsl_id_t versionRid;
    /**
       File-level blob.rid for fc. Can be used with, e.g.,
       fsl_mtime_of_manifest_file().

       It is 0 for files deleted somewhere between the parent and
       checkin version.
    */
    fsl_id_t fileRid;

  };
  typedef struct fsl_repo_extract_state fsl_repo_extract_state;

  /**
     A callback type for use with fsl_repo_extract(). See
     fsl_repo_extract_state for the meanings of xstate's various
     members.  The xstate memory must be considered invalidated
     immediately after this function returns, thus implementations
     must copy or consume anything they need from xstate before
     returning.

     Implementations must return 0 on success. As a special case, if
     FSL_RC_BREAK is returned then fsl_repo_extract() will stop
     looping over files but will report it as success (by returning
     0). Any other code causes extraction looping to stop and is
     returned as-is to the caller of fsl_repo_extract().

     When returning an error, the client may use fsl_cx_err_set() to
     populate state->f with a useful error message which will
     propagate back up through the call stack.

     @see fsl_repo_extract()
  */
  typedef int (*fsl_repo_extract_f)( fsl_repo_extract_state const * xstate );


  /**
     Extracts the contents of the given checkin version ID, sending
     the appropriate version of each file's contents to the given
     callback.

     For each file in the given checkin, callback() is passed a
     fsl_repo_extract_state instance containting enough information
     to, e.g., unpack the contents to a working directory, add it to a
     compressed archive, or send it to some other destination.

     Returns 0 on success, non-0 on error. Will fail if f has no
     opened repository db.

     If the callback returns any code other than 0 or FSL_RC_BREAK,
     looping over the list of files ends and this function returns
     that value. FSL_RC_BREAK causes looping to stop but 0 is
     returned.

     The callback must not expect files to be passed in any particlar
     order. They will seem, except in the case of file deletions
     in 

     See fsl_repo_extract_f() for more details about the semantics of
     the callback.

     Fossil's internal format guarantees that files will passed be
     passed to the callback in "lexical order" (as defined by fossil's
     manifest format definition). i.e. the files will be passed in
     case-sensitive, alphabetical order. Note that upper-case letters
     sort before lower-case ones.
  */
  int fsl_repo_extract( fsl_cx * f, fsl_id_t vid,
                        fsl_repo_extract_f callback, void * callbackState );

  /**
      Equivalent to fsl_tag_rid() except that it takes a symbolic
      artifact name in place of an artifact ID as the third
      argumemnt.

      This function passes symToTag to fsl_sym_to_rid(), and on
      success passes the rest of the parameters as-is to
      fsl_tag_rid(). See that function the semantics of the other
      arguments and the return value, as well as a description of the
      side effects.
   */
  int fsl_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 );

  /**
      Adds a control record to f's repositoriy that either creates or
      cancels a tag.

      artifactRidToTag is the RID of the record to be tagged.

      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. See also:
      fsl_cx_user_get(), but note that the application must first
      use fsl_cx_user_set() to set a context's user name.

      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. On success a new artifact record is
      written to the db, its RID being written into newId as described
      above.

      If the artifact being tagged is private, the new tag is also
      marked as private.

   */
  int fsl_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_rid_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_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. If no record is found, *rv is set to 0 and 0 is
      returned (as opposed to FSL_RC_NOT_FOUND) because that generally
      simplifies the error checking.
   */
  int fsl_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 f's
      shun table, else false.
   */
  char fsl_uuid_is_shunned(fsl_cx * f, fsl_uuid_cstr zUuid);

  /**
      Given a fsl_cx with an opened checkout, and a filename, this
      function canonicalizes zOrigName to a form suitable for use as
      an in-repo filename, _appending_ the results to pOut. If pOut is
      NULL, it performs its normal checking but does not write a
      result, other than to return 0 for success.

      As a special case, if zOrigName refers to the top-level checkout
      directory, it resolves to either "." or "./", depending on whether
      zOrigName contains a trailing slash.

      If relativeToCwd is true then the filename is canonicalized
      based on the current working directory (see fsl_getcwd()),
      otherwise f's current checkout directory is used as the virtual
      root.

      If the input name contains a trailing slash, it is retained in
      the output sent to pOut except in the top-dir case mentioned
      above.

      Returns 0 on success, meaning that the value appended to pOut
      (if not NULL) is a syntactically valid checkout-relative path.

      Returns FSL_RC_RANGE if zOrigName points to a path outside
      of f's current checkout root.

      Returns FSL_RC_NOT_A_CHECKOUT if f has no checkout opened.

      Returns FSL_RC_MISUSE if !f, !zOrigName, FSL_RC_OOM on an
      allocation error.

      This function does not validate whether or not the file actually
      exists, only that its name is potentially valid as a filename
      for use in a checkout (though other, downstream rules might prohibit that, e.g.
      the filename "..../...." is not valid but is not seen as invalid by
      this function). (Reminder to self: we could run the end result through
      fsl_is_simple_pathname() to catch that?)
  */
  int fsl_checkout_filename_check( fsl_cx * f, char relativeToCwd,
                                   char const * zOrigName, fsl_buffer * pOut );

  /**
      Compute the "mtime" of the file given whose blob.rid is "fid"
      that is part of check-in "vid".  The mtime will be the mtime on
      vid or some ancestor of vid where fid first appears. Note that
      fossil does not track the "real" mtimes of files, it only
      computes reasonable estimates for those files based on the
      timestamps of their most recent checkin in the ancestry of vid.

      On success, if pMTime is not null then the result is written to
      *pMTime.

      If fid is 0 or less then the checkin time of vid is written to
      pMTime (this is a much less expensive operation, by the way).
      In this particular case, FSL_RC_NOT_FOUND is returned if vid is
      not a valid checkin version.

      Returns 0 on success, non-0 on error. Returns FSL_RC_NOT_FOUND
      if fid is not found in vid.

      This routine is much more efficient if used to answer several
      queries in a row for the same manifest (the vid parameter). It
      is least efficient when it is passed intermixed manifest IDs,
      e.g. (1, 3, 1, 4, 1,...). This is a side-effect of the caching
      used in the computation of ancestors for a given vid.
   */
  int fsl_mtime_of_manifest_file(fsl_cx * f, fsl_id_t vid, fsl_id_t fid, fsl_time_t *pMTime);

  /**
     A convenience form of fsl_mtime_of_manifest_file() which looks up
     fc's RID based on its UUID. vid must be the RID of the checkin
     version fc originates from. See fsl_mtime_of_manifest_file() for
     full details - this function simply calculates the 3rd argument
     for that one.
   */
  int fsl_mtime_of_F_card(fsl_cx * f, fsl_id_t vid, fsl_card_F const * fc, fsl_time_t *pMTime);

  /**
      Adds the given filename to the current checkout vfile list of
      files as a to-be-added file, or updates an existing record if
      one exists.

      If relativeToCwd is true (non-0) then the filename is
      resolved/canonicalized based on the current working directory
      (see fsl_getcwd()), otherwise f's current checkout directory is
      used as the virtual root. This makes a subtle yet important
      difference in how the name is resolved. CLI apps which take file
      names from the user will generally want to set relativeToCwd to
      true. GUI apps, OTOH, will possibly need it to be false,
      depending on how they resolve and pass on the filenames.

      This function ensures that zFilename gets canonicalized and can
      be found under the checkout directory, and fails if no such file
      exists (checking against the canonicalized name).

      Returns 0 on success, non-0 on error.

      Note that unlike fsl_checkout_file_rm(), this routine cannot
      recursively add files from a directory name. Fixing that is on
      the TODO list.

      @see fsl_checkout_file_rm()
   */
  int fsl_checkout_file_add( fsl_cx * f, char relativeToCwd, char const * zFilename );

  /**
      The converse of fsl_checkout_file_add(), this queues a file for
      removal from the current checkout. The arguments have identical
      meanings as for fsl_checkout_file_add() except that this routine
      does not ensure that the resolved filename actually exists - it
      only normalizes zFilename into its repository-friendly form.

      If recurseDirs is true then if zFilename refers to a directory
      then this operation queues all files under that directory
      (recursively) for removal. In this case, it is irrelevant
      whether or not zFilename ends in a trailing slash or not.

      Returns 0 on success, any of a number of non-0 codes on error.
      Returns FSL_RC_MISUSE if !f, !zFilename, or !*zFilename.
      Returns FSL_RC_NOT_A_CHECKOUT if f has no opened checkout.

      @see fsl_checkout_file_add()
   */
  int fsl_checkout_file_rm( fsl_cx * f, char relativeToCwd, char const * zFilename,
                            char recurseDirs );

  /**
      Change-type flags for use with fsl_checkout_changes_visit() and
      friends.
   */
  enum fsl_checkout_change_t {
  /**
     Sentinel placeholder value.
   */
  FSL_CKOUT_CHANGE_NONE = 0,
  /**
      Indicates that a file was modified in some unspecified way.
   */
  FSL_CKOUT_CHANGE_MOD,
  /**
      Indicates that a file was modified as the result of a merge.
   */
  FSL_CKOUT_CHANGE_MERGE_MOD,
  /**
      Indicates that a file was added as the result of a merge.
   */
  FSL_CKOUT_CHANGE_MERGE_ADD,
  /**
      Indicates that a file was modified as the result of an
      integrate-merge.
   */
  FSL_CKOUT_CHANGE_INTEGRATE_MOD,
  /**
      Indicates that a file was added as the result of an
      integrate-merge.
   */
  FSL_CKOUT_CHANGE_INTEGRATE_ADD,
  /**
      Indicates that a file was added.
   */
  FSL_CKOUT_CHANGE_ADDED,
  /**
      Indicates that a file was removed.
   */
  FSL_CKOUT_CHANGE_REMOVED,
  /**
      Indicates that a file is missing from the local checkout.
   */
  FSL_CKOUT_CHANGE_MISSING,
  /**
      Indicates that a file was renamed.
   */
  FSL_CKOUT_CHANGE_RENAMED,
  /**
      NOT YET USED.

      Indicates that a file contains conflict markers.
   */
  FSL_CKOUT_CHANGE_CONFLICT,
  /**
      NOT YET USED.

      Indicates that a file in the changes table references
      a non-file on disk.
   */
  FSL_CKOUT_CHANGE_NOT_A_FILE,
  /**
      NOT YET USED.

      Indicates that a file is part of a cherrypick merge.
   */
  FSL_CKOUT_CHANGE_CHERRYPICK,
  /**
      NOT YET USED.

      Indicates that a file is part of a backout.
   */
  FSL_CKOUT_CHANGE_BACKOUT
  };

  typedef enum fsl_checkout_change_t fsl_checkout_change_t;

  /**
      Sets up the vfile table in f's opened checkout db and scans the
      checkout root directory's contents for changes compared to the
      pristine checkout state. It records any changes in the vfile
      table.

      Returns 0 on success, non-0 on error, FSL_RC_MISUSE
      if !f.

      For compatibility with fossil(1), this routine clears the vfile
      table of any entries not related to the current checkout.

      @see fsl_checkout_changes_visit()
   */
  int fsl_checkout_changes_scan(fsl_cx * f);

  /**
      A typedef for visitors of checkout status information via
      fsl_checkout_changes_visit(). Implementions will receive the
      last argument passed to fsl_checkout_changes_visit() as their
      first argument. The second argument indicates the type of change
      and the third holds the repository-relative name of the file.

      If changes is FSL_CKOUT_CHANGE_RENAMED then origName will hold
      the original name, else it will be NULL.

      Implementations must return 0 on success, non-zero on error. On
      error any looping performed by fsl_checkout_changes_visit() will
      stop and this function's result code will be returned.

      @see fsl_checkout_changes_visit()
   */
  typedef int (*fsl_checkout_changes_f)(void * state, fsl_checkout_change_t change,
                                        char const * filename,
                                        char const * origName);

  /**
      Compares the changes of f's local checkout against repository
      version vid (checkout version if vid is negative). For each
      change detected it calls visitor(state,...) to report the
      change.  If visitor() returns non-0, that code is returned from
      this function. If doChangeScan is true then
      fsl_checkout_changes_scan() is called by this function before
      iterating, otherwise it is assumed that the caller has called
      that or has otherwise ensured that the checkout db's vfile table
      has been populated.

      Returns 0 on success.

      @see fsl_checkout_changes_scan()
   */
  int fsl_checkout_changes_visit( fsl_cx * f, fsl_id_t vid,
                                  char doChangeScan,
                                  fsl_checkout_changes_f visitor,
                                  void * state );


  /**
      Holds options for use with fsl_branch_create().
   */
  struct fsl_branch_opt {
    /**
        The checkin RID from which the branch should originate.
     */
    fsl_id_t basisRid;
    /**
        The name of the branch. May not be NULL or empty.
     */
    char const * name;
    /**
        User name for the branch. If NULL, fsl_cx_user_get() will
        be used.
     */ 
    char const * user;
    /**
        Optional comment (may be NULL). If NULL or empty, a default
        comment is generated (because fossil requires a non-empty
        comment string).
     */
    char const * comment;
    /**
        Optional background color for the fossil(1) HTML timeline
        view.  Must be in \#RRGGBB format, but this API does not
        validate it as such.
     */
    char const * bgColor;
    /**
        The julian time of the branch. If 0 or less, default is the
        current time.
     */
    fsl_double_t mtime;
    /**
        If true, the branch will be marked as private.
     */
    char isPrivate;
  };
  typedef struct fsl_branch_opt fsl_branch_opt;
#define fsl_branch_opt_empty_m {                \
    0/*basisRid*/, NULL/*name*/,               \
      NULL/*user*/, NULL/*comment*/,            \
      NULL/*bgColor*/,                          \
      0.0/*mtime*/, 0/*isPrivate*/             \
      }
  extern const fsl_branch_opt fsl_branch_opt_empty;

  /**
      Creates a new branch in f's repository. The 2nd paramter holds
      the options describing the branch. The 3rd parameter may be
      NULL, but if it is not then on success the RID of the new
      manifest is assigned to *newRid.

      In Fossil branches are implemented as tags. The branch name
      provided by the client will cause the creation of a tag with
      name name plus a "sym-" prefix to be created (if needed).
      "sym-" denotes that it is a "symbolic tag" (fossil's term for
      "symbolic name applying to one or more checkins,"
      i.e. branches).

      Creating a branch cancels all other branch tags which the new
      branch would normally inherit.

      Returns 0 on success, non-0 on error. 
   */
  int fsl_branch_create(fsl_cx * f, fsl_branch_opt const * opt, fsl_id_t * newRid );


  /**
     Don't use this yet - the whole checkin-queue mechanism is an
     experiment at this point.

     Adds the given file f's list of "selected" files - the list of
     filenames which should be included in the next commit
     (see fsl_checkin_commit()).

     Warning: if this function is not called before
     fsl_checkin_commit(), then fsl_checkin_commit() will select all
     modified, added, removed, or renamed files by default.

     zName must be a non-empty NUL-terminated string and its bytes are
     copied. The filename is canonicalized via
     fsl_checkout_filename_check() - see that function for the meaning
     of the relativeToCwd parameter.

     The resolved name must refer to either a single vfile.pathname
     value in the current vfile table (i.e. in the current checkout,
     though possibly newly-added and not necessarily committed), or it
     must refer to a directory, under which all modified, added,
     deleted, or renamed files are queued up for the next commit.

     If given a single file name it returns FSL_RC_NOT_FOUND if the
     file is not in the current checkout. In this case f's error state
     is updated with a description of the problem.

     If given a directory name, it does not validate that any changes
     are actually detected for queuing up (that detection comes at the
     final commit stage).

     The change-state of the file(s) is not actually checked by this
     function, other than to confirm that the file is indeed listed in
     the current checkout. That means that the file may still be
     modified by the client before the commit takes place, and the
     changes on disk at the point of the fsl_checkin_commit() are the
     ones which get saved (or not).

     Returns 0 on success, FSL_RC_MISUSE if either pointer is NULL,
     or *zName is NUL. Returns FSL_RC_OOM on allocation error.

     On error f's error state might (depending on the nature of the
     problem) contain more details.

     Returns 0 and has no side-effects if zName is already in the
     checkin queue. This function honors the
     fsl_cx_is_case_sensitive() setting when comparing names but the
     check for the repo-level file is case-sensitive! That's arguably
     a bug.

     @see fsl_checkin_file_is_enqueued()
     @see fsl_checkin_file_dequeue()
     @see fsl_checkin_discard()
     @see fsl_checkin_commit()
  */
  int fsl_checkin_file_enqueue(fsl_cx * f, char const * zName,
                               char relativeToCwd);

  /**
     The opposite of fsl_checkin_file_enqueue(), then removes the
     given file or directory name from f's checkin queue. Returns 0 on
     succes. Unlike fsl_checkin_file_enqueue(), this function does
     little validation on the input and simply asks the internals to
     clean up. Specifically, it does not return an error if this
     operation finds no entries to unqueue. If zName is empty or NULL
     then ALL files are unqueued from the pending checkin.

     If relativeToCwd is true (non-0) then zName is resolved based on
     the current directory, otherwise it is resolved based on the
     checkout's root directory.


     @see fsl_checkin_file_enqueue()
     @see fsl_checkin_file_is_enqueued()
     @see fsl_checkin_discard()
     @see fsl_checkin_commit()
  */
  int fsl_checkin_file_dequeue(fsl_cx * f, char const * zName,
                               char relativeToCwd);

  /**
     Returns true (non-0) if the file named by zName is in f's current
     file checkin queue.  If NO files are in the current selection
     queue then this routine assumes that ALL files are implicitely
     selected. As long as at least once file is enqueud (via
     fsl_checkin_file_enqueue()) then this function only returns true
     for files which have been explicitly enqueued.

     If relativeToCwd then zName is resolved based on the current
     directory, otherwise it is resolved based on the checkout's
     root directory.

     This function returning true does not necessarily indicate that
     the file _will_ be checked in at the next commit. If the file has
     not been modified at commit-time then it will not be part of the
     commit.

     This function honors the fsl_cx_is_case_sensitive() setting
     when comparing names.

     Achtung: this does not resolve directory names like
     fsl_checkin_file_enqueue() and fsl_checkin_file_dequeue() do. It
     only works with file names.

     @see fsl_checkin_file_enqueue()
     @see fsl_checkin_file_dequeue()
     @see fsl_checkin_discard()
     @see fsl_checkin_commit()
  */
  char fsl_checkin_file_is_enqueued(fsl_cx * f, char const * zName,
                                    char relativeToCwd);

  /**
     Discards any state accumulated for a pending checking,
     including any files queued via fsl_checkin_file_enqueue()
     and tags added via fsl_checkin_T_add().

     @see fsl_checkin_file_enqueue()
     @see fsl_checkin_file_dequeue()
     @see fsl_checkin_file_is_enqueued()
     @see fsl_checkin_commit()
     @see fsl_checkin_T_add()
  */
  void fsl_checkin_discard(fsl_cx * f);

  /**
     Parameters for fsl_checkin_commit().

     Checkins are created in a multi-step process:

     - fsl_checkin_file_enqueue() queues up a file or directory for
     commit at the next commit.

     - fsl_checkin_file_dequeue() removes an entry, allowing
     UIs to toggle files in and out of a checkin before
     committing it.

     - fsl_checkin_file_is_enqueued() can be used to determine whether
     a given name is already enqueued or not.

     - fsl_checkin_T_add() can be used to T-cards (tags) to a
     deck. Branch tags are intended to be applied via the
     fsl_checkin_opt::branch member.

     - fsl_checkin_discard() can be used to cancel any pending file
     enqueuings, effectively cancelling a commit (which can be
     re-started by enqueuing another file).

     - fsl_checkin_commit() creates a checkin for the list of enqueued
     files (defaulting to all modified files in the checkout!). It
     takes an object of this type to specify a variety of parameters
     for the check.

     Note that this API uses the terms "enqueue" and "unqueue" rather
     than "add" and "remove" because those both have very specific
     (and much different) meanings in the overal SCM scheme.
  */
  struct fsl_checkin_opt {
    /**
       The commit message. May not be empty - the library
       forbids empty checkin messages.
    */
    char const * message;

    /**
       The optional mime type for the message. Only set
       this if you know what you're doing.
    */
    char const * messageMimeType;

    /**
       The user name for the checkin. If NULL or empty, it defaults to
       fsl_cx_user_get(). If that is NULL, a FSL_RC_RANGE error is
       triggered.
    */
    char const * user;

    /**
       Don't use this yet - it is not yet tested all that well.

       If not NULL, makes the checkin the start of a new branch with
       this name.
    */
    char const * branch;

    /**
       If this->branch is not NULL, this is applied as its "bgcolor"
       propagating property. If this->branch is NULL then this is
       applied as a one-time color tag to the checkin.

       It must be NULL, empty, or in a form usable by HTML/CSS,
       preferably \#RRGGBB form. Length-0 values are ignored (as if
       they were NULL).
    */
    char const * bgColor;

    /**
       If true, the checkin will be marked as private, otherwise it
       will be marked as private or public, depending on whether or
       not it inherits private content.
    */
    char isPrivate;

    /**
       Whether or not to calculate an R-card. Doing so is very
       expensive (memory and I/O) but it adds another layer of
       consistency checking to manifest files. In practice, the R-card
       is somewhat superfluous and the cost of calculating it has
       proven painful on very large repositories. fossil(1) creates an
       R-card for all checkins but does not require that one be set
       when it reads a manifest.
    */
    char calcRCard;

    /**
       Whether to allow (or try to force) a delta manifest or not. 0
       means no deltas allowed - it will generate a baseline
       manifest. Greater than 0 forces generation of a delta if
       possible (if one can be readily found) even if doing so would not
       save a notable amount of space. Less than 0 means to
       decide via some heuristics.

       A "readily available" baseline means either the current
       checkout is a baseline or has a baseline. In either case, we
       can use that as a baseline for a delta. i.e. a baseline
       "should" basically available except on the initial checkin,
       which has neither a parent checkin nor a baseline.

       The current behaviour for "auto-detect" mode is: it will
       generate a delta if a baseline is "readily available." Once it calculates a delta form, it calculates
       whether that form saves any appreciable space/overhead compared
       to whether a baseline manifest was generated. If so, it
       discards the delta and re-generates the manifest as a
       baseline. The "force" behaviour (deltaPolicy>0) bypasses the
       "is it too big?" test, and is only intended for testing, not
       real-life use.
    */
    char deltaPolicy;

    /**
       Tells the checkin to close merged-in branches (merge type of
       0). INTEGRATE merges (type=-4) are always closed by a
       checkin. This does not apply to CHERRYPICK (type=-1) and
       BACKOUT (type=-2) merges.
    */
    char integrate;

    /**
       Time of the checkin. If 0 or less, the current time
       is used.
    */
    fsl_double_t julianTime;

    /**
       If this is not NULL then the committed manifest will include a
       tag which closes the branch. The value of this string will be
       the value of the "closed" tag, and the value may be an empty
       string. The intention is that this gets set to a comment about
       why the branch is closed, but it is in no way mandatory.
    */
    char const * closeBranch;

    /**
       Tells fsl_checkin_commit() to dump the generated manifest to
       this file. Intended only for debugging and testing. Checking in
       will fail if this file cannot be opened for writing.
    */
    char const * dumpManifestFile;
    /*
      fossil(1) has many more options. We might want to wrap some of
      it up in the "incremental" state (f->ckin.mf).

      TODOs:

      A callback mechanism which supports the user cancelling
      the checkin. It is (potentially) needed for ops like
      confirming the commit of CRNL-only changes.
    */
  };

  /**
     Empty-initialized fsl_checkin_opt instance, intended for use in
     const-copy constructing.
  */
#define fsl_checkin_opt_empty_m {\
    NULL/*message*/,\
    NULL/*messageMimeType*/,                  \
    NULL/*user*/,\
    NULL/*branch*/,\
    NULL/*bgColor*/,               \
    0/*isPrivate*/,                  \
    1/*calcRCard*/,\
    -1/*deltaPolicy*/,\
    0/*integrate*/,     \
    0.0/*julianTime*/,                        \
    NULL/*closeBranch*/,\
    NULL/*dumpManifestFile*/\
  }

  /**
      Empty-initialized fsl_checkin_opt instance, intended for use in
      copy-constructing. It is important that clients copy this value
      (or fsl_checkin_opt_empty_m) to cleanly initialize their
      fsl_checkin_opt instances, as this may set default values which
      (e.g.) a memset() would not.
  */
  extern const fsl_checkin_opt fsl_checkin_opt_empty;

  /**
     Do not use - under construction/testing. Very dangerous. Stay
     away. If you choose to ignore this warning, then read on...

     This creates a "checkin manifest" for the current checkout.

     Its primary inputs is a list of files to commit. This list
     is provided by the client by calling fsl_checkin_file_enqueue()
     one or more times.  If no files are explicitely selected
     (enqueued) then it calculates which local files have changed vs
     the current checkout and selects all of those.

     Non-file inputs are provided via the opt parameter.

     Tip: to implement a "dry-run" mode, simply wrap this call in a
     transaction started on the fsl_cx_db_checkout() db handle
     (passing it to fsl_db_transaction_begin()), then, after this
     call, either cal; fsl_db_transaction_rollback() (to implement
     dry-run mode) or fsl_db_transaction_commit() (for "wet-run"
     mode). If this function returns non-0, such a transaction should
     _always_ be rolled back!

     On success, it returns 0 and...

     - If newRid is not NULL, it is assigned the new checkin's RID
     value.

     - If newUuid is not NULL, it is assigned the new checkin's UUID
     value. Ownership of the bytes is passed to the caller, who must
     eventually pass them to fsl_free() to free them.

     On error non-0 is returned and f's error state may (depending on
     the nature of the problem) contain details about the problem.
     Note, however, that any error codes returned here may have
     arrived from several layers down in the internals, and may not
     have a single specific interpretation here. When
     possible/practical, f's error state gets updated with a
     human-readable description of the problem.

     ACHTUNG: all pending checking state is cleaned if this function
     fails for any reason other than basic argument validation. This
     means any queued files or tags need to be re-applied if the
     client wants to try again. That is somewhat of a bummer, but this
     behaviour is the only way we can ensure that then the pending
     checkin state does not get garbled on a second use. When in doubt
     about the state, the client should call fsl_checkin_discard() to
     clear it before try to re-commit. (Potential TODO: add a
     success/fail state flag to the checkin state and only clean up on
     success? OTOH, since something in the state likely caused the
     problem, we might not want to do that.)

     This operation does all of its db-related work i a transaction,
     so it rolls back any db changes if it fails.

     Some of the more notable, potentially not obvious, error
     conditions:

     - Trying to commit against a closed leaf: FSL_RC_MISUSE

     - An empty/NULL user name or commit message, or no files were
       selected which actually changed: FSL_RC_RANGE. In these cases
       f's error state describes the problem.

     - Some resource is not found (e.g. an expected RID/UUID could not
     be resolved): FSL_RC_NOT_FOUND. This would generally indicate
     some sort of data consistency problem. i.e. it's quite possible
     very bad if this is returned.

     BUGS:

     - The new leaf of the branch is not being properly
     calculated/updated somewhere, but not yet sure where. If this
     function recalculates all leaves then only the current checkout
     version (before this call) is marked as a leaf, otherwise all
     checkin versions made by this function retain their is-a-leaf
     status. When the checkin in synched via fossil(1) the artifact
     arrives and crosslinks just fine, and a 'fossil rebuild' fixes
     this locally, both of which indicate that the problem is probably
     somewhere in our crosslinking (or similar), as opposed to the
     manifest's metadata. i've been through fossil(1)'s checkin and
     crosslinking implementations backwards and forwards and have not
     yet found the missing/broken piece.

     @see fsl_checkin_file_enqueue()
     @see fsl_checkin_file_dequeue()
     @see fsl_checkin_discard()
     @see fsl_checkin_T_add()
  */
  int fsl_checkin_commit(fsl_cx * f, fsl_checkin_opt const * opt,
                         fsl_id_t * newRid, fsl_uuid_str * newUuid);

  /**
     Works like fsl_deck_T_add(), adding the given tag information to
     the pending checkin state. Returns 0 on success, non-0 on
     error. A checkin may, in principal, have any number of tags, and
     this may be called any number of times to add new tags to the
     pending commit. This list of tags gets cleared by a successful
     fsl_checkin_commit() or by fsl_checkin_discard().

     @see fsl_checkin_file_enqueue()
     @see fsl_checkin_file_dequeue()
     @see fsl_checkin_commit()
     @see fsl_checkin_discard()
  */
  int fsl_checkin_T_add( fsl_cx * f, fsl_tag_type tagType,
                         fsl_uuid_cstr uuid, char const * name,
                         char const * value);



  /**
     Tries to determine the [filename.fnid] value for the given
     filename.  Returns a positive value if it finds one, 0 if it
     finds none, and some unspecified negative value(s) for any sort
     of error. filename must be a normalized, relative filename (as it
     is recorded by a repo).
   */
  fsl_id_t fsl_repo_filename_fnid( fsl_cx * f, char const * filename );


  /**
     Imports content to f's opened repository's BLOB table using a
     client-provided input source. f must have an opened repository
     db. inFunc is the source of the data and inState is the first
     argument passed to inFunc(). If inFunc() succeeds in fetching all
     data (i.e. if it always returns 0 when called by this function)
     then that data is inserted into the blob table _if_ no existing
     record with the same SHA1 hash is already in the table. If such a
     record exists, it is assumed that the content is identical and
     this function has no side-effects vis-a-vis the db in that case.

     If rid is not NULL then the BLOB.RID record value (possibly of an
     older record!) is stored in *rid.  If uuid is not NULL then the
     BLOB.UUID record value is stored in *uuid and the caller takes
     ownership of those bytes, which must eventually be passed to
     fsl_free() to release them.

     rid and uuid are only modified on success and only if they are
     not NULL.

     Returns 0 on success, non-0 on error. For errors other than basic
     argument validation and OOM conditions, f's error state is
     updated with a description of the problem. Returns FSL_RC_MISUSE
     if either f or inFunc are NULL. Whether or not inState may be
     NULL depends on inFunc's concrete implementation.

     Be aware that BLOB.RID values can (but do not necessarily) change
     in the life of a repod db (via a rebuild, a full re-clone, or
     similar, or simply when referring to different clones of the same
     repo). Thus clients should always store the full UUID, as opposed
     to the RID, for later reference. RIDs should, in general, be
     treated as session-transient values. That said, for purposes of
     linking tables in the db, the RID is used exclusively (clients
     are free to link their own extension tables using UUIDs, but
     doing so has a performance penalty comared to RIDs). For
     long-term storage of external links, and to guaranty that the
     data be usable with other copies of the same repo, the UUID is
     required.

     Note that Fossil may deltify, compress, or otherwise modify
     content on its way into the blob table, and it may even modify
     content long after its insertion (e.g. to make it a delta against
     a newer version). Thus clients should normally never try
     to read back the blob directly from the database, but should
     instead read it using fsl_content_get().


     Maintenance reminder: this is basically just a glorified form of
     the internal fsl_content_put(). Interestingly, fsl_content_put()
     always sets content to public (by default - the f object may
     override that later). It is not yet clear whether this routine
     needs to have a flag to set the blob private or not. Generally
     speaking, privacy is applied to fossil artifacts, as opposed to
     content blobs.

     @see fsl_repo_import_buffer()
  */
  int fsl_repo_import_blob( fsl_cx * f, fsl_input_f inFunc, void * inState,
                            fsl_id_t * rid, fsl_uuid_str * uuid );

  /**
     A convenience form of fsl_repo_import_blob(), equivalent to:

     @code
     fsl_repo_import_blob(f, fsl_input_f_buffer, bIn, rid, uuid )
     @endcode

     except that (A) bIn is const in this call and non-const in the
     other form (due to cursor traversal requirements) and (B) it
     returns FSL_RC_MISUSE if pIn is NULL.
  */
  int fsl_repo_import_buffer( fsl_cx * f, fsl_buffer const * bIn,
                              fsl_id_t * rid, fsl_uuid_str * uuid );


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