Login
fossil-util.h at [94e51dc289]
Login

File include/fossil-scm/fossil-util.h artifact 5834a9c25f part of check-in 94e51dc289


/* -*- 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_UTIL_H_INCLUDED)
#define NET_FOSSIL_SCM_FSL_UTIL_H_INCLUDED
/*
   Copyright (c) 2014 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 a number of lower-level classes and utility
   routines.  used by libfossil.
*/

#include "fossil-config.h" /* MUST come first b/c of config macros */
#include <stdio.h> /* FILE type */
#include <stdarg.h> /* va_list */
#include <time.h> /* tm struct */
#if defined(__cplusplus)
extern "C" {
#endif
  typedef struct fsl_allocator fsl_allocator;
  typedef struct fsl_buffer fsl_buffer;
  typedef struct fsl_error fsl_error;
  typedef struct fsl_finalizer fsl_finalizer;
  typedef struct fsl_fstat fsl_fstat;
  typedef struct fsl_list fsl_list;
  typedef struct fsl_outputer fsl_outputer;
  typedef struct fsl_state fsl_state;

  /**
      fsl_uuid_str and fsl_uuid_cstr are "for documentation and
      readability purposes" typedefs used to denote strings which the
      API requires to be in the form of Fossil UUID strings. Such
      strings are exactly FSL_UUID_STRLEN bytes long plus a
      terminating NUL byte and contain only lower-case hexadecimal
      bytes. Where this typedef is used, the library requires,
      enforces, and/or assumes (at different times) that fsl_is_uuid()
      returns true for such strings (if they are not NULL, though not
      all contexts allow a NULL UUID). These typedef are _not_ used to
      denote arguments which may refer to partial UUIDs or symbolic
      names, only 100% bonafide Fossil UUIDs (which are different from
      RFC4122 UUIDs).

      The API guarantees that this typedef will always be (char *) and
      that fsl_uuid_cstr will always ben (char const *), and thus it
      is safe/portable to use those type instead of thse. These
      typedefs serve only to improve the readability of certain APIs
      by implying (through the use of this typedef) the preconditions
      defined for UUID strings.

      @see fsl_is_uuid()
   */
  typedef char * fsl_uuid_str;

  /**
      The const counterpart of fsl_uuid_str.

      @see fsl_is_uuid()
   */
  typedef char const * fsl_uuid_cstr;

  /**
      Returns true (non-0) if str is not NULL, is exactly
      FSL_UUID_STRLEN bytes long (meaning its final byte is a NUL),
      and contains only lower-case hexadecimal characters, else
      returns false (0).

      Note that Fossil UUIDs are not RFC4122 UUIDs, but are SHA1
      hash strings. Don't let that disturb you. As Tim Berners-Lee
      writes:

      'The assertion that the space of URIs is a universal space
      sometimes encounters opposition from those who feel there should
      not be one universal space. These people need not oppose the
      concept because it is not of a single universal space: Indeed,
      the fact that URIs form universal space does not prevent anyone
      else from forming their own universal space, which of course by
      definition would be able to envelop within it as a subset the
      universal URI space. Therefore the web meets the "independent
      design" test, that if a similar system had been concurrently and
      independently invented elsewhere, in such a way that the
      arbitrary design decisions were made differently, when they met
      later, the two systems could be made to interoperate.'

      Source: http://www.w3.org/DesignIssues/Axioms.html

      (Just mentally translate URI as UUID.)
   */
  char fsl_is_uuid(char const * str);

  /**
      Expects str to be a string containing an unsigned decimal
      value. Returns its decoded value, or -1 on error.
   */
  fsl_size_t fsl_str_to_size(char const * str);

  /**
      Expects str to be a string containing a decimal value,
      optionally with a leading sign. Returns its decoded value, or
      dflt if !str or on error.
   */
  fsl_int_t fsl_str_to_int(char const * str, fsl_int_t dflt);


  /**
      Generic list container type. This is used heavily by the Fossil
      API for storing arrays of dynamically-allocated objects. It is
      not useful as a non-pointer-array replacement.

      It is up to the APIs using this type to manage the entry count
      member and use fsl_list_reserve() to manage the "capacity"
      member.

      @see fsl_list_reserve()
      @see fsl_list_append()
      @see fsl_list_visit()
   */
  struct fsl_list {
    /**
        Array of entries. It contains this->capacity entries,
        this->count of which are "valid" (in use).
     */
    void ** list;
    /**
        Number of "used" entries in the list.
     */
    fsl_size_t used;
    /**
        Number of slots allocated in this->list. Use fsl_list_reserve()
        to modify this. Doing so might move the this->list pointer but
        the values it points to will stay stable.
     */
    fsl_size_t capacity;
  };

  /**
      Empty-initialized fsl_list structure, intended for const-copy
      initialization.
   */
#define fsl_list_empty_m { NULL, 0, 0 }
  /**
      Empty-initialized fsl_list structure, intended for copy
      initialization.
   */
  extern const fsl_list fsl_list_empty;


  /**
      Generic interface for finalizing/freeing memory. Intended
      primarily for use as a destructor/finalizer for high-level
      structs. Implementations must semantically behave like free(mem),
      regardless of whether or not they actually free the memory. At
      the very least, they generally should clean up any memory owned by
      mem (e.g. db resources or buffers), even if they do not free() mem.
      some implementations assume that mem is stack-allocated
      and they only clean up resources owned by mem.

      The state parameter is any state needed by the finalizer
      (e.g. a memory allocation context) and mem is the memory which is
      being finalized. 

      The exact interpretaion of the state and mem are of course
      implementation-specific.
   */
  typedef void (*fsl_finalizer_f)( void * state, void * mem );

  /**
      Generic interface for memory finalizers.
   */
  struct fsl_finalizer {
    /**
        State to be passed as the first argument to f().
     */
    void * state;
    /**
        Finalizer function. Should be called like this->f( this->state, ... ).
     */
    fsl_finalizer_f f;
  };

  /** Empty-initialized fsl_finalizer struct. */
#define fsl_finalizer_empty_m {NULL,NULL}

  /**
      fsl_finalizer_f() impl which requires that mem be-a
      (fsl_buffer*).  This function frees all memory associated with
      that buffer and zeroes out the structure, but does not free mem
      (because it is rare that fsl_buffers are created on the
      heap). The state parameter is ignored.
   */
  int fsl_finalizer_f_buffer( void * state, void * mem );


  /**
      Generic state-with-finalizer holder. Used for binding
      client-specified state to another other object, such that a
      client-specified finalizer is called with the other object is
      cleaned up.
   */
  struct fsl_state {
    /**
        Arbitrary context-dependent state.
     */
    void * state;
    /**
        Finalizer for this->state. If used, it should be called like:

        @code
        this->finalize.f( this->finalize.state, this->state );
        @endcode

        After which this->state must be treated as if it has been
        free(3)'d.
     */
    fsl_finalizer finalize;
  };

  /** Empty-initialized fsl_state struct. */
#define fsl_state_empty_m {NULL,fsl_finalizer_empty_m}

  /**
      Empty-initialized fsl_state struct, intended for
      copy-initializing.
   */
  extern const fsl_state fsl_state_empty;


  /**
      Generic interface for streaming out data. Implementations must
      write n bytes from s to their destination channel and return 0 on
      success, non-0 on error (assumed to be a value from the fsl_rc_t
      enum). The state parameter is the implementation-specified
      output channel.

      Potential TODO: change the final argument to a pointer, with
      semantics similar to fsl_input_f(): at call-time n is the number
      of bytes to output, and on returning n is the number of bytes
      actually written. This would allow, e.g. the fsl_zip_writer APIs
      to be able to stream a ZIP file (they have to know the real size
      of the output, and this interface doesn't support that
      operation).
   */
  typedef int (*fsl_output_f)( void * state,
                               void const * src, fsl_size_t n );


  /**
      Generic interface for flushing arbitrary output streams.  Must
      return 0 on success, non-0 on error, but the result code
      "should" (to avoid downstream confusion) be one of the fsl_rc_t
      values. When in doubt, return FSL_RC_IO on error. The
      interpretation of the state parameter is
      implementation-specific.
   */
  typedef int (*fsl_flush_f)(void * state);

  /**
      Generic interface for streaming in data. Implementations must
      read (at most) *n bytes from their input, copy it to dest, assign
      *n to the number of bytes actually read, return 0 on success, and
      return non-0 on error (assumed to be a value from the fsl_rc_t
      enum). When called, *n is the max length to read. On return, *n
      is the actual amount read. The state parameter is the
      implementation-specified input file/buffer/whatever channel.
   */
  typedef int (*fsl_input_f)( void * state, void * dest, fsl_size_t * n );

  /**
      fsl_output_f() implementation which requires state to be a
      writeable (FILE*) handle. Is a no-op (returning 0) if
      !n. Returns FSL_RC_MISUSE if !state or !src.
   */
  int fsl_output_f_FILE( void * state, void const * src, fsl_size_t n );

  /**
      An interface which encapsulates data for managing an output
      destination, primarily intended for use with fsl_output(). Why
      abstract it to this level? So that we can do interesting things
      like output to buffers, files, sockets, etc., using the core
      output mechanism. e.g. so script bindings can send their output
      to the same channel used by the library and other library
      clients.
   */
  struct fsl_outputer {
    /**
        Output channel.
     */
    fsl_output_f out;
    /**
        flush() implementation.
     */
    fsl_flush_f flush;
    /**
        State to be used when calling this->out(), namely:
        this->out( this->state.state, ... ).
     */
    fsl_state state;
  };
  /** Empty-initialized fsl_outputer instance. */
#define fsl_outputer_empty_m {NULL,NULL,fsl_state_empty_m}
  /**
      Empty-initialized fsl_outputer instance, intended for
      copy-initializing.
   */
  extern const fsl_outputer fsl_outputer_empty;

  /**
      A fsl_outputer instance which is initialized to output to a
      (FILE*). To use it, this value then set the copy's state.state
      member to an opened-for-write (FILE*) handle. By default it will
      use stdout. Its finalizer (if called!) will fclose(3)
      self.state.state if self.state.state is not one of (stdout,
      stderr). To disable the closing behaviour (and not close the
      file), set self.state.finalize.f to NULL (but then be sure that
      the file handle outlives this object and to fclose(3) it when
      finished with it).
   */
  extern const fsl_outputer fsl_outputer_FILE;

  /**
      fsl_outputer initializer which uses fsl_flush_f_FILE(),
      fsl_output_f_FILE(), and fsl_finalizer_f_FILE().
   */
#define fsl_outputer_FILE_m {                   \
    fsl_output_f_FILE,                          \
      fsl_flush_f_FILE,                         \
      {/*state*/                                \
        NULL,                                   \
        {NULL,fsl_finalizer_f_FILE}             \
      }                                         \
  }
  /**
      Generic stateful alloc/free/realloc() interface.

      Implementations must behave as follows:

      - If 0==n then semantically behave like free(3) and return
      NULL.

      - If 0!=n and !mem then semantically behave like malloc(3), returning
      newly-allocated memory on success and NULL on error.

      - If 0!=n and NULL!=mem then semantically behave like
      realloc(3). Note that realloc specifies: "If n was equal to 0,
      either NULL or a pointer suitable to be passed to free() is
      returned." Which is kind of useless, and thus implementations
      MUST return NULL when n==0.
   */
  typedef void *(*fsl_realloc_f)(void * state, void * mem, fsl_size_t n);

  /**
      Holds an allocator function and its related state.
   */
  struct fsl_allocator {
    /**
        Base allocator function. It must be passed this->state
        as its first parameter.
     */
    fsl_realloc_f f;
    /**
        State intended to be passed as the first parameter to
        this->f().
     */
    void * state;
  };

  /** Empty-initialized fsl_allocator instance. */
#define fsl_allocator_empty_m {NULL,NULL}


  /**
      A fsl_realloc_f() implementation which uses the standard
      malloc()/free()/realloc(). The state parameter is ignored.
   */
  void * fsl_realloc_f_stdalloc(void * state, void * mem, fsl_size_t n);


  /**
      Semantically behaves like malloc(3), but may introduce instrumentation,
      error checking, or similar.
   */
  void * fsl_malloc( fsl_size_t n )
#ifdef __GNUC__
    __attribute__ ((malloc))
#endif
    ;

  /**
      Semantically behaves like free(3), but may introduce instrumentation,
      error checking, or similar.
   */
  void fsl_free( void * mem );

  /**
      Behaves like realloc(3). Clarifications on the behaviour (because
      the standard has one case of unfortunate wording involving what
      it returns when n==0):

      - If passed (NULL, n>0) then it semantically behaves like
      fsl_malloc(f, n).

      - If 0==n then it semantically behaves like free(2) and returns
      NULL (clarifying the aforementioned wording problem).

      - If passed (non-NULL, n) then it semantically behaves like
      realloc(mem,n).

   */
  void * fsl_realloc( void * mem, fsl_size_t n );

  /**
      A fsl_flush_f() impl which expects _FILE to be-a (FILE*) opened
      for writing, which this function passes the call on to
      fflush(). If fflush() returns 0, so does this function, else it
      returns non-0.
   */
  int fsl_flush_f_FILE(void * _FILE);

  /**
      A fsl_finalizer_f() impl which requires that mem be-a (FILE*).
      This function passes that FILE to fsl_fclose(). The state
      parameter is ignored.
   */
  void fsl_finalizer_f_FILE( void * state, void * mem );

  /**
      A fsl_output_f() impl which requires state to be-a (FILE*), which
      this function passes the call on to fwrite(). Returns 0 on
      success, FSL_RC_IO on error.
   */
  int fsl_output_f_FILE( void * state, void const * src, fsl_size_t n );

  /**
      A fsl_output_f() impl which requires state to be-a (fsl_buffer*),
      which this function passes to fsl_buffer_append(). Returns 0 on
      success, FSL_RC_OOM (probably) on error.
   */
  int fsl_output_f_buffer( void * state, void const * src, fsl_size_t n );

  /**
      A fsl_input_f() implementation which requires that state be
      a readable (FILE*) handle.
   */
  int fsl_input_f_FILE( void * state, void * dest, fsl_size_t * n );

  /**
      A fsl_input_f() implementation which requires that state be a
      readable (fsl_buffer*) handle. The buffer's cursor member is
      updated to track input postion. Thus the user may need to reset
      the cursor to 0 if he wishes to start consuming the buffer at
      its starting point. Subsequent calls to this function will
      increment the cursor by the number of bytes returned via *n.
      The buffer's "used" member is used to determine the logical end
      of input.

      Returns 0 on success and has no error conditions. Results are
      undefined if any argument is NULL.
   */
  int fsl_input_f_buffer( void * state, void * dest, fsl_size_t * n );


  /**
      A generic streaming routine which copies data from an
      fsl_input_f() to an fsl_outpuf_f().

      Reads all data from inF() in chunks of an unspecified size and
      passes them on to outF(). It reads until inF() returns fewer
      bytes than requested. Returns the result of the last call to
      outF() or (only if reading fails) inF(). Returns FSL_RC_MISUSE
      if inF or ouF are NULL.

      Here is an example which basically does the same thing as the
      cat(1) command on Unix systems:

      @code
      fsl_stream( fsl_input_f_FILE, stdin, fsl_output_f_FILE, stdout );
      @endcode

      Or copy a FILE to a buffer:

      @code
      fsl_buffer myBuf = fsl_buffer_empty;
      rc = fsl_stream( fsl_input_f_FILE, stdin, fsl_output_f_buffer, &myBuf );
      // Note that on error myBuf might be partially populated.
      // Eventually clean up the buffer:
      fsl_buffer_clear(&myBuf);
      @endcode

  */
  int fsl_stream( fsl_input_f inF, void * inState,
                  fsl_output_f outF, void * outState );

  /**
      Consumes two input streams looking for differences.  It stops
      reading as soon as either or both streams run out of input or a
      byte-level difference is found.  It consumes input in chunks of
      an unspecified size, and after this returns the input cursor of
      the streams is not well-defined.  i.e. the cursor probably does
      not point to the exact position of the difference because this
      level of abstraction does not allow that.

      Returns 0 if both streams emit the same amount of output and
      that ouput is bitwise identical, otherwise it returns non-0.
   */
  int fsl_stream_compare( fsl_input_f in1, void * in1State,
                          fsl_input_f in2, void * in2State );


  /**

      A general-purpose buffer class, analog to Fossil v1's Blob
      class. It is not called fsl_blob to avoid confusion with DB-side
      Blobs. Buffers are used extensively in fossil to do everything
      from reading files to compressing artifacts to creating
      dynamically-formatted strings. Because they are such a pervasive
      low-level type, and have such a simple structure, their members
      (unlike most other structs in this API) may be considered public
      and used directly by client code (as long as they do not mangle
      their state, e.g. by setting this->capacity smaller than
      this->used!).

      @see fsl_buffer_reserve()
      @see fsl_buffer_append()
      @see fsl_buffer_appendf()
      @see fsl_buffer_cstr()
      @see fsl_buffer_size()
      @see fsl_buffer_capacity()
      @see fsl_buffer_clear()
   */
  struct fsl_buffer {
    /**
        The raw memory owned by this buffer. It is this->capacity bytes
        long, of which this->used are considered "used" by the client.
        The difference beween (this->capacity - this->used) represents
        space the buffer has available for use before it will require
        another expansion/reallocation.
     */
    unsigned char * mem;
    /**
        Number of bytes allocated for this buffer.
     */
    fsl_size_t capacity;
    /**
        Number of "used" bytes in the buffer. This is generally
        interpreted as the string length of this->mem, and the buffer
        APIs which add data to a buffer always ensure that
        this->capacity is large enough to account for a trailing NUL
        byte in this->mem.

        Library routines which manipulate buffers must ensure that
        (this->used<=this->capacity) is always true, expanding the
        buffer if necessary. Much of the API assumes that precondition
        is always met, and any violation of it opens the code to
        undefined behaviour (which is okay, just don't ever break that
        precondition). Most APIs ensure that (used<capacity) is always
        true (as opposed to used<=capacity) because they add a
        trailing NUL byte which is not counted in the "used" length.
     */
    fsl_size_t used;

    /**
        Used by some routines to keep a cursor into this->mem.
     */
    fsl_size_t cursor;
  };
  /** Empty-initialized fsl_buffer instance. */
#define fsl_buffer_empty_m {NULL,0U,0U,0U}
  /** Empty-initialized fsl_buffer instance. */
  extern const fsl_buffer fsl_buffer_empty;

  /**
      A container for storing generic error state. It is used
      to propagate error state between layers of the API back
      to the client.

      @see fsl_error_set()
      @see fsl_error_get()
      @see fsl_error_move()
      @see fsl_error_clear()
   */
  struct fsl_error {
    /**
        Error message text is stored in this->msg.mem. The usable text
        part is this->msg.used bytes long.
     */
    fsl_buffer msg;
    /**
        Error code, generally assumed to be a fsl_rc_t value.

        Potential todo: change this to fsl_rc_t to help ensure that we
        don't get cross-API result code polution (e.g. from sqlite)?
     */
    int code;
  };
  /** Empty-initialized fsl_error instance. */
#define fsl_error_empty_m {fsl_buffer_empty_m,0}
  /** Empty-initialized fsl_error instance. */
  extern const fsl_error fsl_error_empty;

  /**
      Populates err with the given code and formatted string, replacing
      any existing state. If fmt==NULL then fsl_rc_cstr(rc) is used to
      get the error string.

      Returns code on success, some other non-0 code on error.

      As a special case, if 0==code then fmt is ignored and the error
      state is cleared. This will not free any memory held by err but
      will re-set its string to start with a NUL byte, ready for
      re-use later on.

      As a special case, if code==FSL_RC_OOM then fmt is ignored
      to avoid a memory allocation (which would presumably fail).

      @see fsl_error_get()
      @see fsl_error_clear()
      @see fsl_error_move()
   */
  int fsl_error_set( fsl_error * err, int code, char const * fmt,
                     ... );

  /**
      va_list counterpart to fsl_error_set().
   */
  int fsl_error_setv( fsl_error * err, int code, char const * fmt,
                      va_list args );

  /**
      Fetches the error state from err. If !err it returns
      FSL_RC_MISUSE without side-effects, else it returns err's current
      error code.

      If str is not NULL then *str will be assigned to the raw
      (NUL-terminated) error string (which might be empty or even
      NULL). The memory for the string is owned by err and may be
      invalidated by any calls which take err as a non-const parameter
      OR which might modify it indirectly through a container object,
      so the client is required to copy it if it is needed for later
      on.

      If len is not NULL then *len will be assigned to the length of
      the returned string (in bytes).

      @see fsl_error_set()
      @see fsl_error_clear()
      @see fsl_error_move()
   */
  int fsl_error_get( fsl_error const * err, char const ** str, fsl_size_t * len );

  /**
      Frees up any resources owned by err and sets its error code to 0,
      but does not free err. This is harmless no-op if !err or if err
      holds no dynamically allocated no memory.

      @see fsl_error_set()
      @see fsl_error_get()
      @see fsl_error_move()
      @see fsl_error_reset()
   */
  void fsl_error_clear( fsl_error * err );

  /**
      Sets err->code to 0 and resets its buffer, but keeps any
      err->msg memory around for later re-use.

      @see fsl_error_clear()
   */
  void fsl_error_reset( fsl_error * err );

  /**
      Copies the error state from src to dest. If dest contains state, it is
      cleared/recycled by this operation.

      Returns 0 on success, FSL_RC_MISUSE if either argument is NULL
      or if (src==dest), and FSL_RC_OOM if allocation of the message
      string fails.

      As a special case, if src->code==FSL_RC_OOM, then the code is
      copied but the message bytes (if any) are not (under the
      assumption that we have no more memory).
   */
  int fsl_error_copy( fsl_error * const src, fsl_error * dest );

  /**
      Swaps the error state of the two given error objects.

      This "uplifts" an error from the 'from' object to the 'to'
      object. After this returns 'to' will contain the prior error
      state of 'from' and 'from' will contain the old error message
      memory of 'to' but will be re-set to the non-error state (we
      keep the buffer memory intact for later reuse, though).

      Results are undefined if either parameter is NULL or either is
      not properly initialized. i.e. neither may refer to uninitialized
      memory. Copying fsl_error_empty at declaration-time is a simple
      way to ensure that instances are cleanly initialized.
   */
  void fsl_error_move( fsl_error * from, fsl_error * to );

  /**
      Returns the given Unix Epoch timestamp value as its approximate
      Julian Day value. Note that the calculation does not account for
      leap seconds.
   */
  fsl_double_t fsl_unix_to_julian( fsl_time_t unixEpoch );

  /**
      Returns the current Unix Epoch time converted to its approximate
      Julian form. Equivalent to fsl_unix_to_julian(time(0)). See
      fsl_unix_to_julian() for details.
   */
  fsl_double_t fsl_julian_now();

#if 0
  /** UNTESTED, possibly broken vis-a-vis timezone conversion.

      Returns the given Unix Epoch time value formatted as an ISO8601
      string.  Returns NULL on allocation error, else a string 19
      bytes long plus a terminating NUL
      (e.g. "2013-08-19T20:35:49"). The returned memory must
      eventually be freed using fsl_free().
   */
  char * fsl_unix_to_iso8601( fsl_time_t j );
#endif

  /**
      Returns non-0 (true) if the first 10 digits of z _appear_ to
      form the start of an ISO date string (YYYY-MM-DD). Whether or
      not the string is really a valid date is left for downstream
      code to determine. Returns 0 (false) in all other cases,
      including if z is NULL.
   */
  char fsl_str_is_date(const char *z);


  /**
      Reserves at least n bytes of capacity in buf. Returns 0 on
      success, FSL_RC_OOM if allocation fails, FSL_RC_MISUSE if !buf.

      This does not change buf->used, nor will it shrink the buffer
      (reduce buf->capacity) unless n is 0, in which case it
      immediately frees buf->mem and sets buf->capacity and buf->used
      to 0.

      @see fsl_buffer_resize()
      @see fsl_buffer_clear()
   */
  int fsl_buffer_reserve( fsl_buffer * buf, fsl_size_t n );

  /**
      Convenience equivalent of fsl_buffer_reserve(buf,0).
      This a no-op if buf==NULL.
   */
  void fsl_buffer_clear( fsl_buffer * buf );

  /**
      Resets buf->used to 0 and sets buf->mem[0] (if buf->mem is not
      NULL) to 0. Does not (de)allocate memory, only changes the
      logical "used" size of the buffer. Returns 0 on success,
      FSL_RC_MISUSE if !buf.

      Achtung for v1 porters: this function's semantics are much
      different from the v1 blob_reset(). To get those semantics, use
      fsl_buffer_reserve(buf, 0) or its convenience form
      fsl_buffer_clear().
   */
  int fsl_buffer_reset( fsl_buffer * buf );

  /**
      Similar to fsl_buffer_reserve() except that...

      - It does not free all memory when n==0. Instead it essentially
      makes the memory a length-0, NUL-terminated string.

      - It will try to shrink (realloc) buf's memory if (n<buf->capacity).

      - It sets buf->capacity to (n+1) and buf->used to n. This routine
      allocates one extra byte to ensure that buf is always
      NUL-terminated.

      - On success it always NUL-terminates the buffer at
      offset buf->used.

      Returns 0 on success, FSL_RC_MISUSE if !buf, FSL_RC_OOM if
      (re)allocation fails.

      @see fsl_buffer_reserve()
      @see fsl_buffer_clear()
   */
  int fsl_buffer_resize( fsl_buffer * buf, fsl_size_t n );

  /**
      Swaps the contents of the left and right arguments. Results are
      undefined if either argument is NULL or points to uninitialized
      memory.
   */
  void fsl_buffer_swap( fsl_buffer * left, fsl_buffer * right );

  /**
      Similar fsl_buffer_swap() but it also optionally frees one of
      the buffer's memories after swapping them. If clearWhich is
      negative then the left buffer (1st arg) is cleared _after_
      swapping (i.e., the NEW left hand side gets cleared). If
      clearWhich is greater than 0 then the right buffer (2nd arg) is
      cleared _after_ swapping (i.e. the NEW right hand side gets
      cleared). If clearWhich is 0, this function behaves identically
      to fsl_buffer_swap().

      A couple examples should clear this up:

      @code
      fsl_buffer_swap_free( &b1, &b2, -1 );
      @endcode

      Swaps the contents of b1 and b2, then frees the contents
      of the left-side buffer (b1).

      @code
      fsl_buffer_swap_free( &b1, &b2, 1 );
      @endcode

      Swaps the contents of b1 and b2, then frees the contents
      of the right-side buffer (b2).
   */
  void fsl_buffer_swap_free( fsl_buffer * left, fsl_buffer * right,
                             char clearWhich );  

  /**
      Appends the first n bytes of src, plus a NUL byte, to b,
      expanding b as necessary and incrementing b->used by n. If n is
      less than 0 then the equivalent of fsl_strlen((char const*)src)
      is used to calculate the length.

      If n is 0 (or negative and !*src), this function ensures that
      b->mem is not NULL and is NUL-terminated, so it may allocate
      to have space for that NUL byte.

      src may only be NULL if n==0. If passed (src==NULL, n!=0) then
      FSL_RC_RANGE is returned.

      Returns 0 on success, FSL_RC_MISUSE if !f, !b, or !src,
      FSL_RC_OOM if allocation of memory fails.

      If this function succeeds, it guarantees that it NUL-terminates
      the buffer (but that the NUL terminator is not counted in
      b->used). 

      @see fsl_buffer_appendf()
      @see fsl_buffer_reserve()
   */
  int fsl_buffer_append( fsl_buffer * b,
                         void const * src, fsl_int_t n );

  /**
      Uses fsl_appendf() to append formatted output to the given
      buffer.  Returns 0 on success, FSL_RC_MISUSE if !f or !dest, and
      FSL_RC_OOM if an allocation fails while expanding dest.

      @see fsl_buffer_append()
      @see fsl_buffer_reserve()
   */
  int fsl_buffer_appendf( fsl_buffer * dest,
                          char const * fmt, ... );

  /** va_list counterpart to fsl_buffer_appendfv(). */
  int fsl_buffer_appendfv( fsl_buffer * dest,
                           char const * fmt, va_list args );

  /**
      Compresses the first pIn->used bytes of pIn to pOut. It is ok for
      pIn and pOut to be the same blob.

      pOut must either be the same as pIn or else a properly
      initialized buffer. Any prior contents will be freed or their
      memory reused.

      Results are undefined if any argument is NULL.

      Returns 0 on success, FSL_RC_OOM on allocation error, and FSL_RC_ERROR
      if the lower-level compression routines fail.

      Use fsl_buffer_uncompress() to uncompress the data. The data is
      encoded with a big-endian, unsigned 32-bit length as the first
      four bytes, and then the data as compressed by zlib.

      TODO: if pOut!=pIn1 then re-use pOut's memory, if it has any.

      @see fsl_buffer_compress2()
      @see fsl_buffer_uncompress()
      @see fsl_buffer_is_compressed()
   */
  int fsl_buffer_compress(fsl_buffer const *pIn, fsl_buffer *pOut);

  /**
      Compress the concatenation of a blobs pIn1 and pIn2 into pOut.

      pOut must be either empty (cleanly initialized or newly
      recycled) or must be the same as either pIn1 or pIn2.

      Results are undefined if any argument is NULL.

      Returns 0 on success, FSL_RC_OOM on allocation error, and FSL_RC_ERROR
      if the lower-level compression routines fail.

      TODO: if pOut!=(pIn1 or pIn2) then re-use its memory, if it has any.

      @see fsl_buffer_compress()
      @see fsl_buffer_uncompress()
      @see fsl_buffer_is_compressed()
   */
  int fsl_buffer_compress2(fsl_buffer const *pIn1,
                           fsl_buffer const *pIn2, fsl_buffer *pOut);

  /**
      Uncompress buffer pIn and store the result in pOut. It is ok for
      pIn and pOut to be the same buffer. Returns 0 on success. On
      error pOut is not modified.

      pOut must be either cleanly initialized/empty or the same as pIn.

      Results are undefined if any argument is NULL.

      Returns 0 on success, FSL_RC_OOM on allocation error, and
      FSL_RC_ERROR if the lower-level decompression routines fail.

      TODO: if pOut!=(pIn1 or pIn2) then re-use its memory, if it has any.

      @see fsl_buffer_compress()
      @see fsl_buffer_compress2()
      @see fsl_buffer_is_compressed()
   */
  int fsl_buffer_uncompress(fsl_buffer const *pIn, fsl_buffer *pOut);

  /**
      Returns true if this function believes that mem (which must be
      at least len bytes of valid memory long) appears to have been
      compressed by fsl_buffer_compress() or equivalent. This is not a
      100% reliable check - it could potentially have false positives
      on certain inputs, but that is thought to be unlikely (at least
      for text data).

      Returns 0 if mem is NULL.
   */
  char fsl_data_is_compressed(unsigned char const * mem, fsl_size_t len);

  /**
      Equivalent to fsl_data_is_compressed(buf->mem, buf->used).
   */
  char fsl_buffer_is_compressed(fsl_buffer const * buf);

  /**
      If fsl_data_is_compressed(mem,len) returns true then this function
      returns the uncompressed size of the data, else it returns a negative
      value.
   */
  fsl_int_t fsl_data_uncompressed_size(unsigned char const *mem, fsl_size_t len);

  /**
      The fsl_buffer counterpart of fsl_data_uncompressed_size().
   */
  fsl_int_t fsl_buffer_uncompressed_size(fsl_buffer const * b);

  /**
      Equivalent to ((char const *)b->mem), but returns NULL if
      !b. The returned string is effectively b->used bytes long unless
      the user decides to apply his own conventions. Note that the buffer APIs
      generally assure that buffers are NUL-terminated, meaning that strings
      returned from this function can (for the vast majority of cases)
      assume that the returned string is NUL-terminated (with a string length
      of b->used _bytes_).

      @see fsl_buffer_str()
      @see fsl_buffer_cstr2()
   */
  char const * fsl_buffer_cstr(fsl_buffer const *b);

  /**
      If buf is not NULL and has any memory allocated to it, that
      memory is returned. If both b and len are not NULL then *len is
      set to b->used. If b has no dynamic memory then NULL is returned
      and *len (if len is not NULL) is set to 0.

      @see fsl_buffer_str()
      @see fsl_buffer_cstr()
   */
  char const * fsl_buffer_cstr2(fsl_buffer const *b, fsl_size_t * len);

  /**
      Equivalent to ((char *)b->mem), but returns NULL if !b. The
      returned memory is effectively b->used bytes long unless the user
      decides to apply his own conventions.
   */
  char * fsl_buffer_str(fsl_buffer const *b);

  /**
      Returns the "used" size of b, or 0 if !b.
   */
  fsl_size_t fsl_buffer_size(fsl_buffer const * b);

  /**
      Returns the current capacity of b, or 0 if !b.
   */
  fsl_size_t fsl_buffer_capacity(fsl_buffer const * b);

  /**
      Compares the contents of buffers lhs and rhs using memcmp(3)
      semantics. Return negative, zero, or positive if the first
      buffer is less then, equal to, or greater than the second.
      Results are undefined if either argument is NULL.

      When buffers of different length match on the first N bytes,
      where N is the shorter of the two buffers' lengths, it treats the
      shorter buffer as being "less than" the longer one.
   */
  int fsl_buffer_compare(fsl_buffer const * lhs, fsl_buffer const * rhs);

  /**
      Bitwise-compares the contents of b against the file named by
      zFile.  Returns 0 if they have the same size and contents, else
      non-zero.  This function has no way to report if zFile cannot be
      opened, and any error results in a non-0 return value.

      This resolves symlinks and returns non-0 if zFile refers (after
      symlink resolution) to a non-file.

      If zFile does not exist, is not readable, or has a different
      size than b->used, non-0 is returned without opening/reading the
      file contents. If a content comparison is performed, it is
      streamed in chunks of an unspecified (but relatively small)
      size, so it does not need to read the whole file into memory
      (unless it is smaller than the chunk size).
   */
  int fsl_buffer_compare_file( fsl_buffer const * b, char const * zFile );

  /**
      Compare two buffers in constant (a.k.a. O(1)) time and return
      zero if they are equal.  Constant time comparison only applies
      for buffers of the same length.  If lengths are different,
      immediately returns 1. This operation is provided for cases
      where the timing/duration of fsl_buffer_compare() (or an
      equivalent memcmp()) might inadvertently leak security-relevant
      information.  Specifically, it address the concern that
      attackers can use timing differences to check for password
      misses, to narrow down an attack to passwords of a specific
      length or content properties.
   */
  int fsl_buffer_compare_O1(fsl_buffer const * lhs, fsl_buffer const * rhs);

  /**
      Overwrites dest's contents with those of src (reusing dest's memory
      if it has any). Results are undefined if either pointer is NULL
      or invalid. Returns 0 on success, FSL_RC_OOM on allocation error.
   */
  int fsl_buffer_copy( fsl_buffer const * src, fsl_buffer * dest );


  /**
      Apply the delta in pDelta to the original content pOriginal to
      generate the target content pTarget. All three pointers must point
      to properly initialized memory.

      If pTarget==pOriginal then this is a destructive operation,
      replacing the original's content with its new form.

      Return 0 on success.

      @see fsl_buffer_delta_apply()
      @see fsl_delta_apply()
      @see fsl_delta_apply2()
   */
  int fsl_buffer_delta_apply( fsl_buffer const * pOriginal,
                              fsl_buffer const * pDelta,
                              fsl_buffer * pTarget);
  /**
      Identical to fsl_buffer_delta_apply() except that if delta
      application fails then any error messages/codes are written to
      pErr if it is not NULL. It is rare that delta application fails
      (only if the inputs are invalid, e.g. do not belong together or
      are corrupt), but when it does, having error information can be
      useful.

      @see fsl_buffer_delta_apply()
      @see fsl_delta_apply()
      @see fsl_delta_apply2()
   */
  int fsl_buffer_delta_apply2( fsl_buffer const * pOriginal,
                               fsl_buffer const * pDelta,
                               fsl_buffer * pTarget,
                               fsl_error * pErr);


  /**
      Uses a fsl_input_f() function to buffer input into a fsl_buffer.

      dest must be a non-NULL, initialized (though possibly empty)
      fsl_buffer object. Its contents, if any, will be overwritten by
      this function, and any memory it holds might be re-used.

      The src function is called, and passed the state parameter, to
      fetch the input. If it returns non-0, this function returns that
      error code. src() is called, possibly repeatedly, until it
      reports that there is no more data.

      Whether or not this function succeeds, dest still owns any memory
      pointed to by dest->mem, and the client must eventually free it
      by calling fsl_buffer_reserve(dest,0).

      dest->mem might (and possibly will) be (re)allocated by this
      function, so any pointers to it held from before this call might
      be invalidated by this call.

      On error non-0 is returned and dest has almost certainly been
      modified but its state must be considered incomplete.

      Errors include:

      dest or src are NULL (FSL_RC_MISUSE)

      Allocation error (FSL_RC_OOM)

      src() returns an error code

      Whether or not the state parameter may be NULL depends on the src
      implementation requirements.

      On success dest will contain the contents read from the input
      source. dest->used will be the length of the read-in data, and
      dest->mem will point to the memory. dest->mem is automatically
      NUL-terminated if this function succeeds, but dest->used does not
      count that terminator. On error the state of dest->mem must be
      considered incomplete, and is not guaranteed to be
      NUL-terminated.

      Example usage:

      @code
      fsl_buffer buf = fsl_buffer_empty;
      int rc = fsl_buffer_fill_from( &buf,
                                     fsl_input_f_FILE,
                                     stdin );
      if( rc ){
        fprintf(stderr,"Error %d (%s) while filling buffer.\n",
        rc, fsl_rc_cstr(rc));
        fsl_buffer_reserve( &buf, 0 );
        return ...;
      }
      ... use the buf->mem ...
      ... clean up the buffer ...
      fsl_buffer_reserve( &buf, 0 );
      @endcode

      To take over ownership of the buffer's memory, do:

      @code
      void * mem = buf.mem;
      buf = fsl_buffer_empty;
      @endcode

      In which case the memory must eventually be passed to fsl_free()
      to free it.
   */
  int fsl_buffer_fill_from( fsl_buffer * dest, fsl_input_f src, void * state );

  /**
      A fsl_buffer_fill_from() proxy which overwrite's dest->mem with
      the contents of the given FILE handler (which must be opened for
      read access).  Returns 0 on success, after which dest->mem
      contains dest->used bytes of content from the input source. On
      error dest may be partially filled.
  */
  int fsl_buffer_fill_from_FILE( fsl_buffer * dest, FILE * src );

  /**
      A wrapper for fsl_buffer_fill_from_FILE() which gets its input
      from the given file name. It uses fsl_fopen() to open the file,
      so it supports "-" as an alias for stdin.

      Uses fsl_fopen() to open the file, so it supports the name '-'
      as an alias for stdin.
  */
  int fsl_buffer_fill_from_filename( fsl_buffer * dest, char const * filename );    

  /**
      Writes the given buffer to the given filename. Returns 0 on success,
      FSL_RC_MISUSE if !b or !fname, FSL_RC_IO if opening or writing fails.

      Uses fsl_fopen() to open the file, so it supports the name '-'
      as an alias for stdout.
   */
  int fsl_buffer_to_filename( fsl_buffer const * b, char const * fname );

  /**
      Copy N lines of text from pFrom into pTo.  The copy begins at
      the current pFrom->cursor position. pFrom->cursor is left
      pointing at the first character past the last \n copied.

      If pTo==NULL then this routine simply skips over N lines.

      Returns 0 if it copies lines or does nothing (because N is 0 or
      pFrom's contents have been exhausted. Copying fewer lines than
      requested (because of EOF) is not an error. Returns non-0 only
      on allocation error. Results are undefined if either of the
      first two arguments are NULL.

      @see fsl_buffer_stream_lines()
   */
  int fsl_buffer_copy_lines(fsl_buffer *pTo, fsl_buffer *pFrom, fsl_size_t N);

  /**
      Works identically to fsl_buffer_copy_lines() except that it sends its output


      @see fsl_buffer_copy_lines()
   */
  int fsl_buffer_stream_lines(fsl_buffer *pFrom, fsl_output_f fTo,
                              void * toState, fsl_size_t N);


  /**
      Works like fsl_appendfv(), but appends all output to a
      dynamically-allocated string, expanding the string as necessary
      to collect all formatted data. The returned NUL-terminated string
      is owned by the caller and it must be cleaned up using
      fsl_free(...). If !fmt, NULL is returned. It is conceivable that
      it returns NULL on a zero-length formatted string, e.g.  (%.*s)
      with (0,"...") as arguments, but it will only do that if the
      whole format string resolves to empty.
   */
  char * fsl_mprintf( char const * fmt, ... );

  /**
      va_list counterpart to fsl_mprintf().
   */
  char * fsl_mprintfv(char const * fmt, va_list vargs );

  /**
      An sprintf(3) clone which uses fsl_appendf() for the formatting.
      Outputs at most n bytes to dest and returns the number of bytes
      output. Returns a negative value if !dest or !fmt. Returns 0
      without side-effects if !n or !*fmt.

      If the destination buffer is long enough (this function returns
      a non-negative value less than n), this function NUL-terminates it.
      If it returns n then there was no space for the terminator.

   */
  fsl_int_t fsl_snprintf( char * dest, fsl_size_t n, char const * fmt, ... );

  /**
      va_list counterpart to fsl_snprintf()
   */
  fsl_int_t fsl_snprintfv( char * dest, fsl_size_t n, char const * fmt, va_list args );

  /**
      Equivalent to fsl_strndup(src,-1).
   */
  char * fsl_strdup( char const * src );

  /**
      Similar to strndup(3) but returns NULL if !src.  The returned
      memory must eventually be passed to fsl_free(). Returns NULL on
      allocation error. If len is less than 0 and src is not NULL then
      fsl_strlen() is used to calculate its length.

      If src is not NULL but len is 0 then it will return an empty
      (length-0) string, as opposed to NULL.
   */
  char * fsl_strndup( char const * src, fsl_int_t len );

  /**
      Equivalent to strlen(3) but returns 0 if src is NULL.
      Note that it counts bytes, not UTF characters.
   */
  fsl_size_t fsl_strlen( char const * src );

  /**
      Like strcmp(3) except that it accepts NULL pointers.  NULL sorts
      before all non-NULL string pointers.  Also, this routine
      performs a binary comparison that does not consider locale.
   */
  int fsl_strcmp( char const * lhs, char const * rhs );

  /**
      Case-insensitive form of fsl_strcmp().
   */
  int fsl_stricmp(const char *zA, const char *zB);

  /**
      fsl_strcmp() variant which compares at most nByte bytes of the
      given strings, case-insensitively.  If nByte is less than 0 then
      fsl_strlen(zB) is used to obtain the length for comparision
      purposes.
   */
  int fsl_strnicmp(const char *zA, const char *zB, fsl_int_t nByte);

  /**
      fsl_strcmp() variant which compares at most nByte bytes of the
      given strings, case-sensitively. Returns 0 if nByte is 0.
   */
  int fsl_strncmp(const char *zA, const char *zB, fsl_size_t nByte);

  /**
      Equivalent to fsl_strncmp(lhs, rhs, FSL_UUID_STRLEN).
   */
  int fsl_uuidcmp( fsl_uuid_cstr lhs, fsl_uuid_cstr rhs );

  /**
      Returns false if s is NULL or starts with any of (0 (NUL), '0'
      (ASCII zero), 'f', 'n', "off"), case-insensitively, else it
      returns true.
   */
  char fsl_str_bool( char const * s );

  /**
      Flags for use with fsl_db_open() and friends.
   */
  enum fsl_open_flags {
  /**
      The "no flags" value.
   */
  FSL_OPEN_F_NONE = 0,
  /**
      Flag for fsl_db_open() specifying that the db should be opened
      in read-only mode.
   */
  FSL_OPEN_F_RO = 0x01,
  /**
      Flag for fsl_db_open() specifying that the db should be opened
      in read-write mode, but should not create the db if it does
      not already exist.
   */
  FSL_OPEN_F_RW = 0x02,
  /**
      Flag for fsl_db_open() specifying that the db should be opened in
      read-write mode, creating the db if it does not already exist.
   */
  FSL_OPEN_F_CREATE = 0x04,
  /**
      Shorthand for RW+CREATE flags.
   */
  FSL_OPEN_F_RWC = FSL_OPEN_F_RW | FSL_OPEN_F_CREATE,
  /**
      Tells fsl_repo_open_xxx() to confirm that the db
      is a repository.
   */
  FSL_OPEN_F_SCHEMA_VALIDATE = 0x20
  };


  /**
      _Almost_ equivalent to fopen(3) but:

      - expects name to be UTF8-encoded.

      - If name=="-", it returns one of stdin or stdout, depending on
      the mode string: stdout is returned if 'w' or '+' appear,
      otherwise stdin.

      If at all possible, use fsl_close() (as opposed to fclose()) to
      close these handles, as it has logic to skip closing the
      standard streams.


      Potential TODOs:

      - extend mode string to support 'x', meaning "exclusive", analog
      to open(2)'s O_EXCL flag. Barring race conditions, we have
      enough infrastructure to implement that. (It turns out that
      glibc's fopen() supports an 'x' with exactly this meaning.)

      - extend mode to support a 't', meaning "temporary". The idea
      would be that we delete the file from the FS right after
      opening, except that Windows can't do that.
   */
  FILE * fsl_fopen(char const * name, char const *mode);

  /**
      Passes f to fclose(3) unless f is NULL or one of (stdin, stdout,
      stderr).
   */
  void fsl_fclose(FILE * f);

  /**
      @typedef fsl_int_t (*fsl_appendf_f)( void * state, char const * data, fsl_int_t n )

      The fsl_appendf_f typedef is used to provide fsl_appendfv() with
      a flexible output routine, so that it can be easily send its
      output to arbitrary targets.

      The policies which implementations need to follow are:

      - state is an implementation-specific pointer (may be 0) which is
      passed to fsl_appendf(). fsl_appendfv() doesn't know what this
      argument is but passes it to its fsl_appendf_f
      argument. Typically this pointer will be an object or resource
      handle to which string data is pushed.

      - The 'data' parameter is the data to append. The API does not
      currently guaranty that data containing embeded NULs will
      survive the ride through fsl_appendf() and its delegates friends
      (but it "should work").

      - n is the number of bytes to read from data. The fact that n is
      of a signed type is historical. It can be treated as an unsigned
      type for purposes of fsl_appendf().

      - Returns, on success, the number of bytes appended (may be 0).

      - Returns, on error, an implementation-specified negative
      number.  Returning a negative error code will cause
      fsl_appendfv() to stop processing and return. Note that 0 is a
      success value (some printf format specifiers do not add anything
      to the output).
   */
  typedef fsl_int_t (*fsl_appendf_f)( void * state,
                                      char const * data,
                                      fsl_int_t n );

  /**
      This function works similarly to classical printf
      implementations, but instead of outputing somewhere specific, it
      uses a callback function to push its output somewhere. This
      allows it to be used for arbitrary external representations. It
      can be used, for example, to output to an external string, a UI
      widget, or file handle (it can also emulate printf by outputing
      to stdout this way).

      INPUTS:

      pfAppend: The is a fsl_appendf_f function which is responsible
      for accumulating the output. If pfAppend returns a negative
      value then processing stops immediately.

      pfAppendArg: is ignored by this function but passed as the first
      argument to pfAppend. pfAppend will presumably use it as a data
      store for accumulating its string.

      fmt: This is the format string, as in the usual printf(3), except
      that it supports more options (detailed below).

      ap: This is a pointer to a list of arguments.  Same as in
      vprintf() and friends.


      OUTPUTS:

      The return value is the total number of characters sent to the
      function "func", or a negative number on a pre-output error. If
      this function returns an integer greater than 1 it is in general
      impossible to know if all of the elements were output. As such
      failure can only happen if the callback function returns an
      error or if one of the formatting options needs to allocate
      memory and cannot. Both of those cases are very rare in a
      printf-like context, so this is not considered to be a
      significant problem. (The same is true for any classical printf
      implementations.) Clients may use their own state objects which
      can propagate errors from their own internals back to the
      caller, but generically speaking it is difficult to trace errors
      back through this routine. Then again, in practice that has
      never proven to be a problem.

      Most printf-style specifiers work as they do in standard printf()
      implementations. There might be some very minor differences, but
      the more common format specifiers work as most developers expect
      them to. In addition...

      Current (documented) printf extensions:

      (If you are NOT reading this via doxygen-processed sources: the
      percent signs below are doubled for the sake of doxygen, and
      each pair refers to only a single percent sign in the format
      string.)

      %%z works like %%s, but takes a non-const (char *) and deletes
      the string (using fsl_free()) after appending it to the output.

      %%h (HTML) works like $%s but converts certain characters (namely
      '<' and '&') to their HTML escaped equivalents.

      %%t (URL encode) works like %%s but converts certain characters
      into a representation suitable for use in an HTTP URL. (e.g. ' ' 
      gets converted to %%20)

      %%T (URL decode) does the opposite of %t - it decodes URL-encoded
      strings and outputs their decoded form.

      %%r requires an int and renders it in "ordinal form". That is,
      the number 1 converts to "1st" and 398 converts to "398th".

      %%q quotes a string as required for SQL. That is, '\''
      characters get doubled. It does NOT included the outer quotes
      and NULL values get replaced by the string "(NULL) (without
      quotes). See %%Q...

      %%Q works like %%q, but includes the outer '\'' characters and
      NULL pointers get output as the string literal "NULL" (without
      quotes), i.e. an SQL NULL.

      %%/: works like %%s but normalizes path-like strings by
      replacing backslashes with the One True Slash.

      %%b: works like %%s but takes its input from a (fsl_buffer
      const*) argument.

      %%B: works like %%Q but takes its input from a (fsl_buffer
      const*) argument.

      %%F: works like %%s but runs the output through
      fsl_bytes_fossilize(). This requires dynamic memory allocation,
      so is less efficient than re-using a client-provided buffer with
      fsl_bytes_fossilize() if the client needs to fossilize more than
      one element. A '#' between the percent and F works like %%.*s -
      it expects an int-type argument to give the maximum number of
      bytes from F to fossilize (not the maximum output length). Be
      very careful to pass (or cast to) an int, not a fsl_size_t or
      any other type which has a different sizeof() than int, or else
      variadic arg handling may Do The Wrong Thing.

      These extensions may be disabled by setting certain macros when
      compiling fsl_appendf.c (see that file for details).


      FIXME? fsl_appendf_f() is an artifact of older code from which
      this implementation derives. The first parameter should arguably
      be replaced with fsl_output_f(), which does the same thing _but_
      has different return semantics (more reliable, because the
      current semantics report partial success as success in some
      cases). Doing this would require us to change the return
      semantics of this function, but that wouldn't necessarily be a
      bad thing (we don't rely on sprintf()-like return semantics all
      that much, if at all). Or we just add a proxy which forwards to
      a fsl_output_f(). (Oh, hey, that's what fsl_outputf() does.) But
      that doesn't catch certain types of errors (namely allocation
      failures) which can happen as side-effects of some formatting
      operations.

      Potential TODO: add fsl_bytes_fossilize_out() which works like
      fsl_bytes_fossilize() but sends its output to an fsl_output_f()
      and fsl_appendf_f(), so that this routine doesn't need to alloc
      for that case.
   */
  fsl_int_t fsl_appendfv(fsl_appendf_f pfAppend, void * pfAppendArg,
                         const char *fmt, va_list ap );

  /**
      Identical to fsl_appendfv() but takes an ellipses list (...)
      instead of a va_list.
   */
  fsl_int_t fsl_appendf(fsl_appendf_f pfAppend,
                        void * pfAppendArg,
                        const char *fmt,
                        ... )
#if 0
  /* Would be nice, but complains about our custom format options: */
    __attribute__ ((__format__ (__printf__, 3, 4)))
#endif
    ;

  /**
      A fsl_appendf_f() impl which requires that state be an opened,
      writable (FILE*) handle.
   */
  fsl_int_t fsl_appendf_f_FILE( void * state,
                                char const * s, fsl_int_t n );


  /**
      Emulates fprintf() using fsl_appendf(). Returns the result of
      passing the data through fsl_appendf() to the given file handle.
   */
  fsl_int_t fsl_fprintf( FILE * fp, char const * fmt, ... );

  /**
      The va_list counterpart of fsl_fprintf().
   */
  fsl_int_t fsl_fprintfv( FILE * fp, char const * fmt, va_list args );


  /**
      Possibly reallocates self->list, changing its size. This function
      ensures that self->list has at least n entries. If n is 0 then
      the list is deallocated (but the self object is not), BUT THIS
      DOES NOT DO ANY TYPE-SPECIFIC CLEANUP of the items. If n is less
      than or equal to self->capacity then there are no side effects. If
      n is greater than self->capacity, self->list is reallocated and
      self->capacity is adjusted to be at least n (it might be bigger -
      this function may pre-allocate a larger value).

      Passing an n of 0 when self->capacity is 0 is a no-op.

      Newly-allocated slots will be initialized with NUL bytes.

      Returns 0 on success, FSL_RC_MISUSE if !self, FSL_RC_OOM if
      reservation of new elements fails.

      The return value should be used like this:

      @code
      fsl_size_t const n = number of bytes to allocate;
      int const rc = fsl_list_reserve( myList, n );
      if( rc ) { ... error ... }
      @endcode

      @see fsl_list_clear()
      @see fsl_list_visit_free()
   */
  int fsl_list_reserve( fsl_list * self, fsl_size_t n );

  /**
      Appends a bitwise copy of cp to self->list, expanding the list as
      necessary and adjusting self->used.

      Ownership of cp is unchanged by this call. cp may not be NULL.

      Returns 0 on success, FSL_RC_MISUSE if any argument is NULL, or
      FSL_RC_OOM on allocation error.
   */
  int fsl_list_append( fsl_list * self, void * cp );

  /** @typedef typedef int (*fsl_list_visitor_f)(void * p, void * visitorState )

      Generic visitor interface for fsl_list lists.  Used by
      fsl_list_visit(). p is the pointer held by that list entry and
      visitorState is the 4th argument passed to fsl_list_visit().

      Implementations must return 0 on success. Any other value causes
      looping to stop and that value to be returned, but interpration
      of the value is up to the caller (it might or might not be an
      error, depending on the context). Note that client code may use
      custom values, and is not strictly required to use FSL_RC_xxx
      values. HOWEVER...  all of the libfossil APIs which take these
      as arguments may respond differently to some codes (most notable
      FSL_RC_BREAK, which they tend to treat as a
      stop-iteration-without-error result), so clients are strongly
      encourage to return an FSL_RC_xxx value on error.
   */
  typedef int (*fsl_list_visitor_f)(void * obj, void * visitorState );

  /**
      A fsl_list_visitor_f() implementation which requires that obj be
      arbitrary memory which can legally be passed to fsl_free()
      (which this function does). The visitorState parameter is
      ignored.
   */
  int fsl_list_v_fsl_free(void * obj, void * visitorState );


  /**
      For each item in self->list, visitor(item,visitorState) is
      called.  The item is owned by self. The visitor function MUST
      NOT free the item (unless the visitor is a finalizer!), but may
      manipulate its contents if application rules do not specify
      otherwise.

      If order is 0 or greater then the list is traversed from start
      to finish, else it is traverse from end to begin.

      Returns 0 on success, non-0 on error.

      If visitor() returns non-0 then looping stops and that code is
      returned.
   */
  int fsl_list_visit( fsl_list const * self, char order,
                      fsl_list_visitor_f visitor, void * visitorState );

  /**
      A list clean-up routine which takes a callback to clean up its
      contents.

      Passes each element in the given list to
      childFinalizer(item,finalizerState). If that returns non-0,
      processing stops and that value is returned, otherwise
      fsl_list_reserve(list,0) is called and 0 is returned.

      WARNING: if cleanup fails because childFinalizer() returns non-0,
      the returned object is effectively left in an undefined state and
      the client has no way (unless the finalizer somehow accounts for it)
      to know which entries in the list were cleaned up. Thus it is highly
      recommended that finalizer functions follow the conventional wisdom
      of "destructors do not throw."

      @see fsl_list_visit_free()
   */
  int fsl_list_clear( fsl_list * list, fsl_list_visitor_f childFinalizer,
                      void * finalizerState );
  /**
      Similar to fsl_list_clear(list, fsl_list_v_fsl_free, NULL), but
      only frees list->list if the second argument is true, otherwise
      it sets the list's length to 0 but keep the list->list memory
      intact for later use. Note that this function never frees the
      list argument, only its contents.

      Be sure only to use this on lists of types for which fsl_free()
      is legal. i.e. don't use it on a list of fsl_deck objects or
      other types which have their own finalizers.

      Results are undefined if list is NULL.

      @see fsl_list_clear()
   */
  void fsl_list_visit_free( fsl_list * list, char freeListMem );

  /**
      Works similarly to the visit operation without the _p suffix
      except that the pointer the visitor function gets is a (**)
      pointing back to the entry within this list. That means that
      callers can assign the entry in the list to another value during
      the traversal process (e.g. set it to 0). If shiftIfNulled is
      true then if the callback sets the list's value to 0 then it is
      removed from the list and self->used is adjusted (self->capacity
      is not changed).
   */
  int fsl_list_visit_p( fsl_list * self, char order, char shiftIfNulled,
                        fsl_list_visitor_f visitor, void * visitorState );


  /**
      Sorts the given list using the given comparison function. Neither
      argument may be NULL. The arugments passed to the comparison function
      will be pointers to pointers to the original entries, and may (depending
      on how the list is used) point to NULL.
   */
  void fsl_list_sort( fsl_list * li, int (*cmp)( void const * lhs, void const * rhs ));

  /**
      Returns 0 if the given file is readable. Flags may be any values
      accepted by the access(2) resp. _waccess() system calls.
   */
  int fsl_file_access(const char *zFilename, int flags);

  /**
      Computes a canonical pathname for a file or directory. Makes the
      name absolute if it is relative. Removes redundant / characters.
      Removes all /./ path elements. Converts /A/../ to just /. If the
      slash parameter is non-zero, the trailing slash, if any, is
      retained.

      If zRoot is not NULL then it is used for transforming a relative
      zOrigName into an absolute path. If zRoot is NULL fsl_getcwd()
      is used to determine the virtual root directory. If zRoot is
      empty (starts with a NUL byte) then this function effectively
      just sends zOrigName through fsl_file_simplify_name().

      Returns 0 on success, FSL_RC_MISUSE if !zOrigName or !pOut,
      FSL_RC_OOM if an allocation fails.

      pOut, if not NULL, is _appended_ to, so be sure to set
      pOut->used=0 (or pass it to fsl_buffer_reset()) before calling
      this if you want to start writing at the beginning. On error
      pOut might conceivably be partially populated, but that is
      highly unlikely.

      This function does not actual filesystem-level processing unless
      zRoot is NULL or empty (and then only to get the current
      directory). This does not confirm whether the resulting file
      exists, nor that it is strictly a valid filename for the current
      filesystem. It simply transforms a potentially relative path
      into an absolute one.


      Example:
      @code
      int rc;
      char const * zRoot = "/a/b/c";
      char const * zName = "../foo.bar";
      fsl_buffer buf = fsl_buffer_empty;
      rc = fsl_file_canonical_name2(zRoot, zName, &buf, 0);
      if(!rc){
        fsl_buffer_clear(&buf);
        return rc;
      }
      assert(0 == fsl_strcmp( "/a/b/foo.bar, fsl_buffer_cstr(&buf)));
      fsl_buffer_clear(&buf);
      @endcode
   */
  int fsl_file_canonical_name2(const char *zRoot,
                               const char *zOrigName,
                               fsl_buffer *pOut, char slash);

  /**
      Equivalent to fsl_file_canonical_name2(NULL, zOrigName, pOut, slash).

      @see fsl_file_canonical_name2()
   */

  int fsl_file_canonical_name(const char *zOrigName,
                              fsl_buffer *pOut, char slash);

  /**
      Calculates the "directory part" of zFilename and _appends_ it to
      pOut. The directory part is all parts up to the final path
      separator ('\\' or '/'). If leaveSlash is true (non-0) then the
      separator part is appended to pOut, otherwise it is not. This
      function only examines the first nLen bytes of zFilename.  If
      nLen is negative then fsl_strlen() is used to determine the
      number of bytes to examine.

      If zFilename ends with a slash then it is considered to be its
      own directory part. i.e.  the dirpart of "foo/" evaluates to
      "foo" (or "foo/" if leaveSlash is true), whereas the dirpart of
      "foo" resolves to nothing (empty - no output except a NUL
      terminator sent to pOut).

      Returns 0 on success, FSL_RC_MISUSE if !zFilename or !pOut,
      FSL_RC_RANGE if 0==nLen or !*zFilename, and FSL_RC_OOM if
      appending to pOut fails. If zFilename contains only a path
      separator and leaveSlash is false then only a NUL terminator is
      appended to pOut if it is not already NUL-terminated.

      This function does no filesystem-level validation of the the
      given path - only string evaluation.
   */
  int fsl_file_dirpart(char const * zFilename, fsl_int_t nLen,
                       fsl_buffer * pOut, char leaveSlash);


  /**
      Writes the absolute path name of the current directory to zBuf,
      which must be at least nBuf bytes long (nBuf includes the space
      for a trailing NUL terminator).

      Returns FSL_RC_RANGE if the name would be too long for nBuf,
      FSL_RC_IO if it cannot determine the current directory (e.g. a
      side effect of having removed the directory at runtime or similar
      things), and 0 on success.

      On success, if outLen is not NULL then the length of the string
      written to zBuf is assigned to *outLen. The output string is
      always NUL-terminated.

      On Windows, the name is converted from unicode to UTF8 and all '\\'
      characters are converted to '/'.  No conversions are needed on
      Unix.
   */
  int fsl_getcwd(char *zBuf, fsl_size_t nBuf, fsl_size_t * outLen);


  /**
      Return true (non-0) if the filename given is a valid filename
      for a file in a repository.  Valid filenames follow all of the
      following rules:

          -  Does not begin with "/"
          -  Does not contain any path element named "." or ".."
          -  Does not contain "/..." (special case)
          -  Does not contain any of these characters in the path: "\"
          -  Does not end with "/".
          -  Does not contain two or more "/" characters in a row.
          -  Contains at least one character

      Invalid UTF8 characters result in a false return if bStrictUtf8 is
      true.  If bStrictUtf8 is false, invalid UTF8 characters are silently
      ignored. See http://en.wikipedia.org/wiki/UTF-8#Invalid_byte_sequences
      and http://en.wikipedia.org/wiki/Unicode (for the noncharacters).

      Fossil compatibility note: the bStrictUtf8 flag must be true
      when parsing new manifests but is false when parsing legacy
      manifests, for backwards compatibility.

      z must be NUL terminated. Results are undefined if !z.

      Note that periods in and of themselves are valid filename
      components, with the special exceptions of "." and "..", one
      implication being that "...." is, for purposes of this function,
      a valid simple filename.
   */
  char fsl_is_simple_pathname(const char *z, char bStrictUtf8);

  /**
      Return the size of a file in bytes. Returns -1 if the file does
      not exist or is not stat(2)able.
   */
  fsl_size_t fsl_file_size(const char *zFilename);

  /**
      Return the modification time for a file.  Return -1 if the file
      does not exist or is not stat(2)able.
   */
  fsl_time_t fsl_file_mtime(const char *zFilename);

  /**
      Don't use this. The wd (working directory) family of functions
      might or might-not be necessary and in any case they require
      a fsl_cx context argument because they require repo-specific
      "allow-symlinks" setting.

      Return TRUE if the named file is an ordinary file or symlink
      and symlinks are allowed.

      Return false for directories, devices, fifos, etc.
   */
  char fsl_wd_isfile_or_link(const char *zFilename);

  /**
      Return TRUE if the named file is an ordinary file.  Return false
      for directories, devices, fifos, symlinks, etc.
   */
  char fsl_is_file(const char *zFilename);

  /**
      Returns true if the given path appears to be absolute, else
      false. On Unix a path is absolute if it starts with a '/'.  On
      Windows a path is also absolute if it starts with a letter, a
      colon, and a backslash.
   */
  char fsl_is_absolute_path(const char *zPath);

  /**
      Simplify a filename by

       * Convert all \ into / on windows and cygwin
       * removing any trailing and duplicate /
       * removing /./
       * removing /A/../

      Changes are made in-place.  Return the new name length.  If the
      slash parameter is non-zero, the trailing slash, if any, is
      retained. If n is <0 then fsl_strlen(z) is used to calculate the
      length.
   */
  fsl_size_t fsl_file_simplify_name(char *z, fsl_int_t n_, char slash);


  /**
      Return true (non-zero) if string z matches glob pattern zGlob
      and zero if the pattern does not match. Always returns 0 if
      either argument is NULL.

      Globbing rules:

           '*'       Matches any sequence of zero or more characters.

           '?'       Matches exactly one character.

          [...]      Matches one character from the enclosed list of
                     characters.

          [^...]     Matches one character not in the enclosed list.

      It goes not support character ranges, character classes, or
      anything fancy like that. (CHECK THAT: we're using sqlite3_strglob()
      now!)
   */
  char fsl_str_glob(const char *zGlob, const char *z);

  /**
      Parses zPatternList as a comma-and/or-space-delimited list of
      glob patterns (as supported by fsl_str_glob()). Each pattern in
      that list is copied and appended to tgt in the form of a new
      (char *) owned by that list. Returns 0 on success, FSL_RC_OOM if
      copying a pattern to tgt fails, FSL_RC_MISUSE if !tgt,
      !zPatternList or !*zPatternList. On allocation error, tgt might
      be partially populated.

      Elements of the glob list may be optionally enclosed in single
      our double-quotes.  This allows a comma to be part of a glob
      pattern.

      Leading and trailing spaces on unquoted glob patterns are
      ignored.

      Note that there is no separate "glob list" class. A "glob list"
      is simply a fsl_list whose list entries are glob-pattern strings
      owned by that list.

      Examples of a legal value for zPatternList:
      @code
      "*.c *.h, *.sh, '*.in'"
      @endcode

      @see fsl_glob_list_append()
      @see fsl_glob_list_matches()
      @see fsl_glob_list_clean()
   */
  int fsl_glob_list_parse( fsl_list * tgt, char const * zPatternList );

  /**
      Appends a single blob pattern to tgt, in the form of a new (char *)
      owned by tgt. This function copies zGlob and appends that copy
      to tgt.

      Returns 0 on success, FSL_RC_MISUSE if !tgt or !zGlob or
      !*zGlob, FSL_RC_OOM if appending to the list fails.

      @see fsl_glob_list_parse()
      @see fsl_glob_list_matches()
      @see fsl_glob_list_clean()
   */
  int fsl_glob_list_append( fsl_list * tgt, char const * zGlob );

  /**
      Assumes globList is a list of (char [const] *) glob values and
      tries to match each one against zHaystack using
      fsl_str_glob(). If any glob matches, it returns a pointer to the
      matched globList->list entry. If no matches are found, or if any
      argument is invalid, NULL is returned.

      The returned bytes are owned by globList and may be invalidated at
      its leisure. It is primarily intended to be used as a boolean,
      for example:

      @code
      if( fsl_glob_list_matches(myGlobs, someFilename) ) { ... }
      @endcode

      @see fsl_glob_list_parse()
      @see fsl_glob_list_append()
      @see fsl_glob_list_clean()
   */
  char const * fsl_glob_list_matches( fsl_list const * globList, char const * zHaystack );

  /**
      If globList is not NULL this is equivalent to
      fsl_list_visit_free(globList, 1), otherwise it is a no-op.

      Note that this does not free the globList object itself, just
      its underlying list entries and list memory. (In practice, lists
      are either allocated on the stack or as part of a higher-level
      structure, and not on the heap.)

      @see fsl_glob_list_parse()
      @see fsl_glob_list_append()
      @see fsl_glob_list_matches()
   */
  void fsl_glob_list_clean( fsl_list * globList );


  /**
      Returns true if the given letter is an ASCII alphabet character.
   */
  char fsl_isalpha(int c);

  /**
      Returns true if c is a lower-case ASCII alphabet character.
   */
  char fsl_islower(int c);

  /**
      Returns true if c is an upper-case ASCII alphabet character.
   */
  char fsl_isupper(int c);

  /**
      Returns true if c is ' ', '\r', or '\t'.
   */
  char fsl_isspace(int c);

  /**
      Returns true if c is an ASCII digit in the range '0' to '9'.
   */
  char fsl_isdigit(int c);

  /**
      Equivalent to fsl_isdigit(c) || fsl_isalpha().
   */
  char fsl_isalnum(int c);

  /**
      Returns the upper-case form of c if c is an ASCII alphabet
      letter, else returns c.
   */
  int fsl_tolower(int c);

  /**
      Returns the lower-case form of c if c is an ASCII alphabet
      letter, else returns c.
   */
  int fsl_toupper(int c);

#ifdef _WIN32
  /**
      Translate MBCS to UTF-8.  Return a pointer to the translated
      text. ACHTUNG: Call fsl_mbcs_free() (not fsl_free()) to
      deallocate any memory used to store the returned pointer when
      done.
   */
  char * fsl_mbcs_to_utf8(char const * mbcs);

  /**
      Frees a string allocated from fsl_mbcs_to_utf8(). Results are undefined
      if mbcs was allocated using any other mechanism.
   */
  void fsl_mbcs_free(char * mbcs);
#endif
  /* _WIN32 */

  /**
      Deallocates the given memory, which must have been allocated
      from fsl_unicode_to_utf8(), fsl_utf8_to_unicode(), or any
      function which explicitly documents this function as being the
      proper finalizer for its returned memory.
   */
  void fsl_unicode_free(void *);


  /**
      Translate UTF-8 to Unicode for use in system calls. Returns a
      pointer to the translated text. The returned value must
      eventually be passed to fsl_unicode_free() to deallocate any
      memory used to store the returned pointer when done.

      This function exists only for Windows. On other platforms
      it behaves like fsl_strdup().

      The returned type is (wchar_t*) on Windows and (char*)
      everywhere else.
   */
  void *fsl_utf8_to_unicode(const char *zUtf8);

  /**
      Translates Unicode text into UTF-8.  Return a pointer to the
      translated text. Call fsl_unicode_free() to deallocate any
      memory used to store the returned pointer when done.

      This function exists only for Windows. On other platforms it
      behaves like fsl_strdup().
   */
  char *fsl_unicode_to_utf8(const void *zUnicode);

  /**
      Translate text from the OS's character set into UTF-8. Return a
      pointer to the translated text. Call fsl_filename_free() to
      deallocate any memory used to store the returned pointer when
      done.

      This function must not convert '\' to '/' on Windows/Cygwin, as
      it is used in places where we are not sure it's really filenames
      we are handling, e.g. fsl_getenv() or handling the argv
      arguments from main().

      On Windows, translate some characters in the in the range
      U+F001 - U+F07F (private use area) to ASCII. Cygwin sometimes
      generates such filenames. See:
      <http://cygwin.com/cygwin-ug-net/using-specialnames.html>
   */
  char *fsl_filename_to_utf8(const void *zFilename);

  /**
      Translate text from UTF-8 to the OS's filename character set.
      Return a pointer to the translated text. Call
      fsl_filename_free() to deallocate any memory used to store the
      returned pointer when done.

      On Windows, characters in the range U+0001 to U+0031 and the
      characters '"', '*', ':', '<', '>', '?' and '|' are invalid in
      filenames. Therefore, translate those to characters in the in the
      range U+F001 - U+F07F (private use area), so those characters
      never arrive in any Windows API. The filenames might look
      strange in Windows explorer, but in the cygwin shell everything
      looks as expected.

      See: <http://cygwin.com/cygwin-ug-net/using-specialnames.html>

      The returned type is (wchar_t*) on Windows and (char*)
      everywhere else.
   */
  void *fsl_utf8_to_filename(const char *zUtf8);


  /**
      Deallocate pOld, which must have been allocated by
      fsl_filename_to_utf8(), fsl_utf8_to_filename(), fsl_getenv(), or
      another routine which explicitly documents this function as
      being the proper finalizer for its returned memory.
   */
  void fsl_filename_free(void *pOld);

  /**
      Returns a (possible) copy of the environment variable with the
      given key, or NULL if no entry is found. The returned value must
      be passed to fsl_filename_free() to free it. ACHTUNG: DO NOT
      MODIFY the returned value - on Unix systems it is _not_ a
      copy. That interal API inconsistency "should" be resolved
      (==return a copy from here, but that means doing it everywhere)
      to avoid memory ownership problems later on.

      Why return a copy? Because native strings from at least one of
      the more widespread OSes often have to be converted to something
      portable and this requires allocation on such platforms, but
      not on Unix. For API transparency, that means all platforms get
      the copy(-like) behaviour.
   */
  char *fsl_getenv(const char *zName);

  /**
      Returns a positive value if zFilename is a directory, 0 if
      zFilename does not exist, or a negative value if zFilename
      exists but is something other than a directory. Results are
      undefined if zFilename is NULL.

      This function expects zFilename to be a well-formed path -
      it performs no normalization on it.
   */
  char fsl_dir_check(const char *zFilename);

  /**
      Deletes the given file from the filesystem. Returns 0 on
      success. If the component is a directory, this operation will
      fail unless the directory is empty. If zFilename refers to a
      symlink, the link (not its target) is removed.

      Results are undefined if zFilename is NULL.
   */
  int fsl_file_unlink(const char *zFilename);

  /**
      Sets the mtime (Unix epoch) for a file. Returns 0 on success,
      non-0 on error. If newMTime is less than 0 then the current
      time(2) is used. This routine does not create non-existent files
      (e.g. like a Unix "touch" command).
   */
  int fsl_file_mtime_set(const char *zFilename, fsl_time_t newMTime);

  /**
      Create the directory with the given name if it does not already
      exist. If forceFlag is true, delete any prior non-directory
      object with the same name.

      Return 0 on success, non-0 on error.

      If the directory already exists then 0 is returned, not an error
      (FSL_RC_ALREADY_EXISTS), because that simplifies usage. If
      another filesystem entry with this name exists and forceFlag is
      true then that entry is deleted before creating the directory,
      and this operation fails if deletion fails. If forceFlag is
      false and a non-directory entry already exists, FSL_RC_TYPE is
      returned.

      For recursively creating directories, use fsl_mkdir_for_file().

      Bug/corner case: if zFilename refers to a symlink to a
      non-existent directory, this function gets slightly confused,
      tries to make a dir with the symlink's name, and returns
      FSL_RC_ALREADY_EXISTS. How best to resolve that is not yet
      clear. The problem is that stat(2)ing the symlink says "nothing
      is there" (because the link points to a non-existing thing), so
      we move on to the underlying mkdir(), which then fails because
      the link exists with that name.
   */
  int fsl_mkdir(const char *zName, char forceFlag);

  /**
      A convenience form of fsl_mkdir() which can recursively create
      directories. If zName has a trailing slash then the last
      component is assumed to be a directory part, otherwise it is
      assumed to be a file part (and no directory is created for that
      part). zName may be either an absolute or relative path.

      Returns 0 on success (including if all directories already
      exist).  Returns FSL_RC_OOM if there is an allocation
      error. Returns FSL_RC_TYPE if one of the path components already
      exists and is not a directory. Returns FSL_RC_RANGE if
      zName is NULL or less than 2 bytes long.

      On systems which support symlinks, a link to a directory is
      considered to be a directory for purposes of this function.

      If forceFlag is true and a non-directory component is found in
      the filesystem where zName calls for a directory, that component
      is removed (and this function fails if removal fails).

      Examples:

      "/foo/bar" creates (if needed) /foo, but assumes "bar" is a file
      component. "/foo/bar/" creates /foo/bar. However "foo" will not
      create a directory - because the string has no path component,
      it is assumed to be a filename.

      Both "/foo/bar/my.doc" and "/foo/bar/" result in the directories
      /foo/bar.

   */
  int fsl_mkdir_for_file(char const *zName, char forceFlag);


  /**
      Uses fsl_getenv() to look for the environment variables
      (FOSSIL_USER, (Windows: USERNAME), (Unix: USER, LOGNAME)). If
      it finds one it returns a copy of that value, which must
      eventually be passed to fsl_free() to free it (NOT
      fsl_filename_free(), though fsl_getenv() requires that one). If
      it finds no match, or if copying the entry fails, it returns
      NULL.

      @see fsl_cx_user_set()
      @see fsl_cx_user_get()
   */
  char * fsl_guess_user_name();

  /**
      Tries to find the user's home directory. If found, 0 is
      returned, tgt's memory is _overwritten_ (not appended) with the
      path, and tgt->used is set to the path's string length.  (Design
      note: the overwrite behaviour is inconsistent with most of the
      API, but the implementation currently requires this.)

      If requireWriteAccess is true then the directory is checked for
      write access, and FSL_RC_ACCESS is returned if that check
      fails. For historical (possibly techinical?) reasons, this check
      is only performed on Unix platforms. On others this argument is
      ignored. When writing code on Windows, it may be necessary to
      assume that write access is necessary on non-Windows platform,
      and to pass 1 for the second argument even though it is ignored
      on Windows.

      On error non-0 is returned and tgt is updated with an error
      string OR (if the error was an allocation error while appending
      to the path or allocating MBCS strings for Windows), it returns
      FSL_RC_OOM and tgt "might" be updated with a partial path (up to
      the allocation error), and "might" be empty (if the allocation
      error happens early on).

      This routine does not canonicalize/transform the home directory
      path provided by the environment, other than to convert the
      string byte encoding on some platforms. i.e. if the environment
      says that the home directory is "../" then this function will
      return that value, possibly to the eventual disappointment of
      the caller.

      Result codes include:

      - FSL_RC_OK (0) means a home directory was found and tgt is
      populated with its path.

      - FSL_RC_NOT_FOUND means the home directory (platform-specific)
      could not be found.

      - FSL_RC_ACCESS if the home directory is not writable and
      requireWriteAccess is true. Unix platforms only -
      requireWriteAccess is ignored on others.

      - FSL_RC_TYPE if the home (as determined via inspection of the
      environment) is not a directory.

      - FSL_RC_OOM if a memory (re)allocation fails.
   */
  int fsl_find_home_dir( fsl_buffer * tgt, char requireWriteAccess );

  /**
      Values for use with the fsl_fstat::mode field.
   */
  enum fsl_fstat_type_t {
  /** Sentinel value for unknown/invalid filesystem entry types. */
  FSL_FSTAT_TYPE_UNKNOWN = 0,
  /** Indicates a directory filesystem entry. */
  FSL_FSTAT_TYPE_DIR,
  /** Indicates a non-directory, non-symlink filesystem entry.
      Because fossil's scope is limited to SCM work, it assumes that
      "special files" (sockets, etc.) are just files, and makes no
      special effort to handle them.
   */
  FSL_FSTAT_TYPE_FILE,
  /** Indicates a symlink filesystem entry. */
  FSL_FSTAT_TYPE_LINK
  };
  typedef enum fsl_fstat_type_t fsl_fstat_type_t;

  /**
      Bitmask values for use with the fsl_fstat::perms field.
   */
  enum fsl_fstat_perm_t {
  /**
      Sentinel value.
   */
  FSL_FSTAT_PERM_UNKNOWN = 0,
  /**
      The executable bit, as understood by Fossil. Fossil does not
      differentiate between different +x values for user/group/other.
   */
  FSL_FSTAT_PERM_EXE = 0x01
  };
  typedef enum fsl_fstat_perm_t fsl_fstat_perm_t;

  /**
      A simple wrapper around the stat(2) structure resp. _stat/_wstat
      (on Windows). It exposes only the aspects of stat(2) info which
      Fossil works with, and not any platform-/filesystem-specific
      details except the executable bit for the permissions mode and some
      handling of symlinks.
   */
  struct fsl_fstat {
    /**
        Indicates the type of filesystem object.
     */
    fsl_fstat_type_t type;
    /**
        The creation time stat'd file, in... well, the man pages
        (neither for Linux nor Windows) do not specify exactly what
        unit this is. Let's assume seconds since the start of the Unix
        Epoch.
     */
    fsl_time_t ctime;
    /**
        Last modification time.
     */
    fsl_time_t mtime;
    /**
        The size of the stat'd file, in bytes.
     */
    fsl_size_t size;
    /**
        Contains the filesystem entry's permissions as a bitmask of
        fsl_fstat_perm_t values. Note that only the executable bit for
        _files_ (not directories) is exposed here.
     */
    int perm;
  };
  /** Empty-initialized fsl_fstat structure. */
#define fsl_fstat_empty_m {FSL_FSTAT_TYPE_UNKNOWN,0,0,0,0}
  /** Empty-initialize fsl_fstat instance, intended for copy
      construction. */
  extern const fsl_fstat fsl_fstat_empty;

  /**
      Runs the OS's stat(2) equivalent to populate fst with
      information about the given file.

      Returns 0 on success, FSL_RC_MISUSE if zFilename is NULL, and
      FSL_RC_RANGE if zFilename starts with a NUL byte. Returns
      FSL_RC_NOT_FOUND if no filesystem entry is found for the given
      name. Returns FSL_RC_IO if the underlying stat() (or equivalent)
      fails for undetermined reasons inside the underlying
      stat()/_wstati64() call. Note that the fst parameter may be
      NULL, in which case the return value will be 0 if the name is
      stat-able, but will return no other information about it.

      The derefSymlinks argument is ignored on non-Unix platforms.  On
      Unix platforms, if derefSymlinks is non-0 then stat(2) is used,
      else lstat(2) (if available on the platform) is used. For most
      cases clients should pass non-0. They should only pass 0 if they
      need to differentiate between symlinks and files.

      The fsl_fstat_type_t family of flags can be used to determine
      the type of the filesystem object being stat()'d (file,
      directory, or symlink). It does apply any special logic for
      platform-specific oddities other than symlinks (e.g. character
      devices and such).
   */
  int fsl_stat(const char *zFilename, fsl_fstat * fst, char derefSymlinks );

  /**
      Create a new delta between the memory zIn and zOut.

      The delta is written into a preallocated buffer, zDelta, which
      must be at least 60 bytes longer than the target memory, zOut.
      The delta string will be NUL-terminated, but it might also
      contain embedded NUL characters if either the zSrc or zOut files
      are binary.

      On success this function returns 0 and the length of the delta
      string, in bytes, excluding the final NUL terminator character,
      is written to *deltaSize.

      Returns FSL_RC_MISUSE if any of the pointer arguments are NULL
      and FSL_RC_OOM if memory allocation fails during generation of
      the delta. Returns FSL_RC_RANGE if lenSrc or lenOut are "too
      big" (if they cause an overflow in the math).

      Output Format:

      The delta begins with a base64 number followed by a newline.
      This number is the number of bytes in the TARGET file.  Thus,
      given a delta file z, a program can compute the size of the
      output file simply by reading the first line and decoding the
      base-64 number found there.  The fsl_delta_applied_size()
      routine does exactly this.

      After the initial size number, the delta consists of a series of
      literal text segments and commands to copy from the SOURCE file.
      A copy command looks like this:

      (Achtung: extra backslashes are for Doxygen's benefit - not
      visible in the processsed docs.)

          NNN\@MMM,

      where NNN is the number of bytes to be copied and MMM is the
      offset into the source file of the first byte (both base-64).
      If NNN is 0 it means copy the rest of the input file.  Literal
      text is like this:

          NNN:TTTTT

      where NNN is the number of bytes of text (base-64) and TTTTT is
      the text.

      The last term is of the form

          NNN;

      In this case, NNN is a 32-bit bigendian checksum of the output
      file that can be used to verify that the delta applied
      correctly.  All numbers are in base-64.

      Pure text files generate a pure text delta.  Binary files
      generate a delta that may contain some binary data.

      Algorithm:

      The encoder first builds a hash table to help it find matching
      patterns in the source file.  16-byte chunks of the source file
      sampled at evenly spaced intervals are used to populate the hash
      table.

      Next we begin scanning the target file using a sliding 16-byte
      window.  The hash of the 16-byte window in the target is used to
      search for a matching section in the source file.  When a match
      is found, a copy command is added to the delta.  An effort is
      made to extend the matching section to regions that come before
      and after the 16-byte hash window.  A copy command is only
      issued if the result would use less space that just quoting the
      text literally. Literal text is added to the delta for sections
      that do not match or which can not be encoded efficiently using
      copy commands.

      @see fsl_delta_applied_size()
      @see fsl_delta_apply()
   */
  int fsl_delta_create( unsigned char const *zSrc, fsl_size_t lenSrc,
                        unsigned char const *zOut, fsl_size_t lenOut,
                        unsigned char *zDelta, fsl_size_t * deltaSize);

  /**
      Works identically to fsl_delta_create() but sends its output to
      the given output function. out(outState,...) may be called any
      number of times to emit delta output. Each time it is called it
      should append the new bytes to its output channel.

      The semantics of the return value and the first four arguments
      are identical to fsl_delta_create(), with these ammendments
      regarding the return value:

      - Returns FSL_RC_MISUSE if any of (zSrc, zOut, out) are NULL.

      - If out() returns non-0 at any time, delta generation is
      aborted and that code is returned.

      Example usage:

      @code
      int rc = fsl_delta_create( v1, v1len, v2, v2len,
                                 fsl_output_f_FILE, stdout);
      @endcode
   */
  int fsl_delta_create2( unsigned char const *zSrc, fsl_size_t lenSrc,
                         unsigned char const *zOut, fsl_size_t lenOut,
                         fsl_output_f out, void * outState);

  /**
      A fsl_delta_create() wrapper which uses the first two arguments
      as the original and "new" content versions to delta, and outputs
      the delta to the 3rd argument (overwriting any existing contents
      and re-using any memory it had allocated).

      If the output buffer (delta) is the same as src or newVers,
      FSL_RC_MISUSE is returned, and results are undefined if delta
      indirectly refers to the same buffer as either src or newVers.

      Returns 0 on success.
   */
  int fsl_buffer_delta_create( fsl_buffer const * src,
                               fsl_buffer const * newVers,
                               fsl_buffer * delta);

  /**
      Apply a delta created using fsl_delta_create().

      The output buffer must be big enough to hold the whole output
      file and a NUL terminator at the end. The
      fsl_delta_applied_size() routine can be used to determine that
      size.

      zSrc represents the original sources to apply the delta to.
      It must be at least lenSrc bytes of valid memory.

      zDelta holds the delta (created using fsl_delta_create()),
      and it must be lenDelta bytes long.

      On success this function returns 0 and writes the applied delta
      to zOut.

      Returns FSL_RC_MISUSE if any pointer argument is NULL. Returns
      FSL_RC_RANGE if lenSrc or lenDelta are "too big" (if they cause
      an overflow in the math). Invalid delta input can cause any of
      FSL_RC_RANGE, FSL_RC_DELTA_INVALID_TERMINATOR,
      FSL_RC_CHECKSUM_MISMATCH, FSL_RC_SIZE_MISMATCH, or
      FSL_RC_DELTA_INVALID_OPERATOR to be returned.

      Refer to the fsl_delta_create() documentation above for a
      description of the delta file format.

      @see fsl_delta_applied_size()
      @see fsl_delta_create()
      @see fsl_delta_apply2()
  */
  int fsl_delta_apply( unsigned char const *zSrc, fsl_size_t lenSrc,
                       unsigned char const *zDelta, fsl_size_t lenDelta,
                       unsigned char *zOut );

  /**
      Functionally identical to fsl_delta_apply() but any errors generated
      during application of the delta are described in more detail
      in pErr. If pErr is NULL this behaves exactly as documented for
      fsl_delta_apply().
   */
  int fsl_delta_apply2( unsigned char const *zSrc,
                        fsl_size_t lenSrc,
                        unsigned char const *zDelta,
                        fsl_size_t lenDelta,
                        unsigned char *zOut,
                        fsl_error * pErr);
  /*
     Calculates the size (in bytes) of the output from applying a the
     given delta. On success 0 is returned and *appliedSize will be
     updated with the amount of memory required for applying the
     delta. zDelta must point to lenDelta bytes of memory in the
     format emitted by fsl_delta_create(). It is legal for appliedSize
     to point to the same memory as the 2nd argument.

     Returns FSL_RC_MISUSE if any pointer argument is NULL. Returns
     FSL_RC_RANGE if lenDelta is too short to be a delta. Returns
     FSL_RC_DELTA_INVALID_TERMINATOR if the delta's encoded length
     is not properly terminated.

     This routine is provided so that an procedure that is able to
     call fsl_delta_apply() can learn how much space is required for
     the output and hence allocate nor more space that is really
     needed.

     TODO?: consolidate 2nd and 3rd parameters into one i/o parameter?

     @see fsl_delta_apply()
     @see fsl_delta_create()
  */
  int fsl_delta_applied_size(unsigned char const *zDelta,
                             fsl_size_t lenDelta,
                             fsl_size_t * appliedSize);

  /**
      "Fossilizes" the first len bytes of the given input string. If
      (len<0) then fsl_strlen(inp) is used to calculate its length.
      The output is appended to out, which is expanded as needed and
      out->used is updated accordingly.  Returns 0 on success,
      FSL_RC_MISUSE if !inp or !out. Returns 0 without side-effects if
      0==len or (!*inp && len<0). Returns FSL_RC_OOM if reservation of
      the output buffer fails (it is expanded, at most, one time by
      this function).

      Fossilization replaces the following bytes/sequences with the
      listed replacements:

      (Achtung: usage of doubled backslashes here it just to please
      doxygen - they will show up as single slashes in the processed
      output.)

      - Backslashes are doubled.

      - (\\n, \\r, \\v, \\t, \\f) are replaced with \\\\X, where X is the
      conventional encoding letter for that escape sequence.

      - Spaces are replaced with \\s.

      - Embedded NULs are replaced by \\0 (numeric 0, not character
      '0').
   */
  int fsl_bytes_fossilize( unsigned char const * inp, fsl_int_t len,
                           fsl_buffer * out );
  /**
      "Defossilizes" bytes encoded by fsl_bytes_fossilize() in-place.
      inp must be a string encoded by fsl_bytes_fossilize(), and the
      decoding processes stops at the first unescaped NUL terminator.
      It has no error conditions except for !inp or if inp is not
      NUL-terminated, both of which invoke in undefined behaviour.

      If resultLen is not NULL then *resultLen is set to the resulting string
      length.

   */
  void fsl_bytes_defossilize( unsigned char * inp, fsl_size_t * resultLen );

  /**
      Defossilizes the contents of b. Equivalent to:
      fsl_bytes_defossilize( b->mem, &b->used );
   */
  void fsl_buffer_defossilize( fsl_buffer * b );

  /**
     Return true (non-0) if the input string contains only valid base-16
     digits. If any invalid characters appear in the string, return
     0 (false).
  */
  char fsl_validate16(const char *zIn, fsl_size_t nIn);

  /**
     The input string is a base16 value.  Convert it into its canonical
     form.  This means that digits are all lower case and that conversions
     like "l"->"1" and "O"->"0" occur.
  */
  void fsl_canonical16(char *z, fsl_size_t n);

  /**
     Decode a N-character base-16 number into base-256.  N must be a 
     multiple of 2.  The output buffer must be at least N/2 characters
     in length. Returns 0 on success.
  */
  int fsl_decode16(const unsigned char *zIn, unsigned char *pOut, fsl_size_t N);

  /**
     Encode a N-digit base-256 in base-16. N is the byte length of pIn
     and zOut must be at least (N*2+1) bytes long (the extra is for a
     terminating NUL). Returns zero on success, FSL_RC_MISUSE if !pIn
     or !zOut.
  */
  int fsl_encode16(const unsigned char *pIn, unsigned char *zOut, fsl_size_t N);


  /**
      Tries to convert the value of errNo, which is assumed to come
      from the global errno, to a fsl_rc_t code. If it can, it returns
      something approximating the errno value, else it returns dflt.

      Example usage:

      @code
      FILE * f = fsl_fopen("...", "...");
      int rc = f ? 0 : fsl_errno_to_rc(errno, FSL_RC_IO);
      ...
      @endcode

      Why require the caller to pass in errno, instead of accessing it
      directly from this function? To avoid the the off-chance that
      something changes errno between the call and the conversion
      (whether or not that's possible is as yet undetermined). It can
      also be used by clients to map to explicit errno values to
      fsl_rc_t values, e.g. fsl_errno_to_rc(EROFS,-1) returns
      FSL_RC_ACCESS.

      A list of the errno-to-fossil conversions:

      - EINVAL: FSL_RC_MISUSE (could arguably be FSL_RC_RANGE, though)

      - ENOMEM: FSL_RC_OOM

      - EACCES, EBUSY, EPERM: FSL_RC_ACCESS

      - EISDIR: FSL_RC_TYPE

      - ENAMETOOLONG, ELOOP: FSL_RC_RANGE

      - ENOENT: FSL_RC_NOT_FOUND

      - EEXIST: FSL_RC_ALREADY_EXISTS

      - EIO: FSL_RC_IO

      Any other value for errNo causes dflt to be returned.

   */
  int fsl_errno_to_rc(int errNo, int dflt);

  /**
      Make the given string safe for HTML by converting every "<" into
      "&lt;", every ">" into "&gt;", every "&" into "&amp;", and
      encode " as &quot; so that it can appear as an argument to
      markup.

      The escaped output is send to out(oState,...).

      Returns 0 on success or if there is nothing to do (input has a
      length of 0). Returns FSL_RC_MISUSE if !out or !zIn. If out()
      returns a non-0 code then that value is returned to the caller.

      If n is negative, fsl_strlen() is used to calculate zIn's length.
   */
  int fsl_htmlize(fsl_output_f out, void * oState,
                  const char *zIn, fsl_int_t n);

  /**
      Functionally equivalent to fsl_htmlize() but optimized to perform
      only a single allocation.

      Returns 0 on success or if there is nothing to do (input has a
      length of 0). Returns FSL_RC_MISUSE if !p or !zIn, and
      FSL_RC_OOM on allocation error.

      If n is negative, fsl_strlen() is used to calculate zIn's length.
   */
  int fsl_htmlize_to_buffer(fsl_buffer *p, const char *zIn, fsl_int_t n);

  /**
      Equivalent to fsl_htmlize_to_buffer() but returns the result as a
      new string which must eventually be fsl_free()d by the caller.

      Returns NULL for invalidate arguments or allocation error.
   */
  char *fsl_htmlize_str(const char *zIn, fsl_int_t n);


  /**
      If c is a character Fossil likes to HTML-escape, assigns *xlate
      to its transformed form, else set it to NULL. Returns 1 for
      untransformed characters and the strlen of *xlate for others.
      Bytes returned via xlate are static and immutable.

      Results are undefined if xlate is NULL.
   */
  fsl_size_t fsl_htmlize_xlate(int c, char const ** xlate);

  /**
      Flags for use with text-diff generation APIs,
      e.g. fsl_diff_text().

      Maintenance reminder: these values are holy and must not be
      changed without also changing the corresponding code in
      fsl_diff.c.
   */
  enum fsl_diff_flag_t {
  /** Inline (not side-by-side) diff */
  FSL_DIFF_INLINE =       0x00,
  /** Ignore end-of-line whitespace */
  FSL_DIFF_IGNORE_EOLWS = 0x01,
  /** Generate a side-by-side diff */
  FSL_DIFF_SIDEBYSIDE =   0x02,
  /** Missing shown as empty files */
  FSL_DIFF_VERBOSE =      0x04,
  /** Show filenames only */
  FSL_DIFF_BRIEF =        0x08,
  /** Render for HTML. */
  FSL_DIFF_HTML =         0x10,
  /** Show line numbers. */
  FSL_DIFF_LINENO =       0x20,
  /** Warn about whitespace. */
  FSL_DIFF_WS_WARNING =   0x40,
  /** Suppress optimizations (debug). */
  FSL_DIFF_NOOPT =        0x0100,
  /** Invert the diff (debug). */
  FSL_DIFF_INVERT =       0x0200,
  /** Only display if not "too big." */
  FSL_DIFF_NOTTOOBIG =    0x0800
  };  

  /**
      Generates a textual diff from two text inputs and writes
      it to the given output function.

      pA and pB are the buffers to diff.

      contextLines is the number of lines of context to output. This
      parameter has a built-in limit of 2^16, and values larger than
      that get truncated. A value of 0 is legal, in which case no
      surrounding context is provided. A negative value translates to
      some unspecified default value.

      sbsWidth specifies the width (in characters) of the side-by-side
      columns. If sbsWidth is not 0 then this function behaves as if
      diffFlags contains the FSL_DIFF_SIDEBYSIDE flag. If sbsWidth is
      negative, OR if diffFlags explicitly contains
      FSL_DIFF_SIDEBYSIDE and sbsWidth is 0, then some default width
      is used. This parameter has a built-in limit of 255, and values
      larger than that get truncated to 255.

      diffFlags is a mask of fsl_diff_flag_t values. Not all of the
      fsl_diff_flag_t flags are yet [sup]ported.

      The output is sent to out(outState,...). If out() returns non-0
      during processing, processing stops and that result is returned
      to the caller of this function.

      Returns 0 on success, FSL_RC_OOM on allocation error,
      FSL_RC_MISUSE if any arguments are invalid, FSL_RC_TYPE if any
      of the content appears to be binary (contains embedded NUL
      bytes), FSL_RC_RANGE if some range is exceeded (e.g. the maximum
      number of input lines).

      None of (pA, pB, out) may be NULL.

      TODOs:

      - Add a predicate function for outputing only matching
      differences, analog to fossil(1)'s regex support (but more
      flexible).

      - Expose the raw diff-generation bits via the internal API
      to facilitate/enable the creation of custom diff formats.
   */
  int fsl_diff_text(fsl_buffer const *pA, fsl_buffer const *pB,
                    fsl_output_f out, void * outState,
                    short contextLines, short sbsWidth,
                    int diffFlags );

  /**
      Functionally equivalent to:

      @code:
      fsl_diff_text(pA, pB, fsl_output_f_buffer, pOut,
                    contextLines, sbsWidth, diffFlags);
      @endcode

      Except that it returns FSL_RC_MISUSE if !pOut.
   */
  int fsl_diff_text_to_buffer(fsl_buffer const *pA, fsl_buffer const *pB,
                              fsl_buffer *pOut, short contextLines,
                              short sbsWidth, int diffFlags );


  /**
      If zDate is an ISO8601-format string, optionally with a .NNN
      fractional suffix, then this function returns true and sets
      *pOut (if pOut is not NULL) to the corresponding Julian
      value. If zDate is not an ISO8601-format string then this
      returns false (0) and pOut is not modified.

      This function does NOT confirm that zDate ends with a NUL
      byte. i.e.  if passed a valid date string which has trailing
      bytes after it then those are simply ignored. This is so that it
      can be used to read subsets of larger strings.

      Achtung: this calculation may, due to voodoo-level
      floating-point behaviours, differ by a small fraction of a point
      (at the millisecond level) for a given input compared to other
      implementations (e.g. sqlite's strftime() _might_ differ by a
      millisecond or two or _might_ not). Thus this routine should not
      be used when 100% round-trip fidelity is required, but is close
      enough for routines which do not require 100% millisecond-level
      fidelity in time conversions.

      @see fsl_julian_to_iso8601()
   */
  char fsl_iso8601_to_julian( char const * zDate, fsl_double_t * pOut );

  /** 
      Converts the Julian Day J to an ISO8601 time string. If addMs is
      true then the string includes the '.NNN' fractional part, else
      it will not. This function writes (on success) either 20 or 24
      bytes (including the terminating NUL byte) to pOut, depending on
      the value of addMs, and it is up to the caller to ensure that
      pOut is at least that long.

      Returns true (non-0) on success and the only error conditions
      [it can catch] are if pOut is NULL, J is less than 0, or
      evaluates to a time value which does not fit in ISO8601
      (e.g. only years 0-9999 are supported).

      @see fsl_iso8601_to_julian()
   */
  char fsl_julian_to_iso8601( fsl_double_t J, char * pOut, char addMs );

  /**
      Returns the Julian Day time J value converted to a Unix Epoch
      timestamp. It assumes 86400 seconds per day and does not account
      for any sort leap seconds, leap years, leap frogs, or any other
      kind of leap, up to and including a leap of faith.
   */
  fsl_time_t fsl_julian_to_unix( fsl_double_t J );

  /**
      Performs a chdir() to the directory named by zChDir.
      On Unix systems, if bChroot is true then it attempts to
      chroot() after the chdir(). On Windows the second parameter
      is ignored.

      Returns 0 on success. On error it tries to convert the
      underlying errno to one of the FSL_RC_xxx values, falling
      back to FSL_RC_IO if it cannot figure out anything more
      specific.
   */
  int fsl_chdir(const char *zChDir, char bChroot);

  /**
      A strftime() implementation.

      dest must be valid memory at least destLen bytes long. The result
      will be written there.

      fmt must contain the format string. See the file fsl_strftime.c
      for the complete list of format specifiers and their descriptions.

      timeptr must be the time the caller wants to format.

      Returns 0 if any arguments are NULL.

      On success it returns the number of bytes written to dest, not
      counting the terminating NUL byte (which it also writes). It
      returns 0 on any error, and the client may need to distinguish
      between real errors and (destLen==0 or !*fmt), both of which could
      also look like errors.

      TODOs:

      - Refactor this to take a callback or a fsl_buffer, so that we can
      format arbitrarily long output.

      - Refactor it to return an integer error code.

      (i didn't write this implementation - it is derived from public domain
      sources dating back to the early 1990's.)
   */
  fsl_size_t fsl_strftime(char *dest, fsl_size_t destLen,
                          const char *format, const struct tm *timeptr);

  /**
     A convenience form of fsl_strftime() which takes its timestamp in
     the form of a Unix Epoch time. See fsl_strftime() for the
     semantics of the first 3 arguments and the return value. If
     convertToLocal is true then epochTime gets converted to local
     time (via, oddly enough, localtime(3)), otherwise gmtime(3) is
     used for the conversion.

     BUG: this function uses static state and is not thread-safe.
  */
  fsl_size_t fsl_strftime_unix(char * dest, fsl_size_t destLen, char const * format,
                               fsl_time_t epochTime, char convertToLocal);


  /**
      A convenience form of fsl_strftime() which assumes that the
      formatted string is of "some reasonable size" and appends its
      formatted representation to b. Returns 0 on success, non-0 on
      error. If any argument is NULL or !*format then FSL_RC_MISUSE is
      returned. FSL_RC_RANGE is returned if the underlying call to
      fsl_strftime() fails (which it will if the format string
      resolves to something "unususually long"). It returns FSL_RC_OOM
      if appending to b fails due to an allocation error.
   */
  int fsl_buffer_strftime(fsl_buffer * b, char const * format, const struct tm *timeptr);

  /**
      The "Path Finder" class is a utility class for searching the
      filesystem for files matching a set of common prefixes and/or
      suffixes (i.e. directories and file extensions).

      Example usage:

      @code
      fsl_pathfinder pf = fsl_pathfinder_empty;
      int rc;
      char const * found = NULL;
      rc = fsl_pathfinder_ext_add( &pf, ".doc" );
      if(rc) { ...error... }
      // The following error checks are elided for readability:
      rc = fsl_pathfinder_ext_add( &pf, ".txt" );
      rc = fsl_pathfinder_ext_add( &pf, ".wri" );
      rc = fsl_pathfinder_dir_add( &pf, "." );
      rc = fsl_pathfinder_dir_add( &pf, "/my/doc/dir" );
      rc = fsl_pathfinder_dir_add( &pf, "/other/doc/dir" );

      rc = fsl_pathfinder_search( &pf, "MyDoc", &found, NULL);
      if(0==rc){ assert(NULL!=found); }

      // Eventually clean up:
      fsl_pathfinder_clear(&pf);
      @endcode

      @see fsl_pathfinder_dir_add()
      @see fsl_pathfinder_ext_add()
      @see fsl_pathfinder_clear()
      @see fsl_pathfinder_search()
   */
  struct fsl_pathfinder {
    /**
        Holds the list of search extensions. Each entry
        is a (char *) owned by this object.
     */
    fsl_list ext;
    /**
        Holds the list of search directories. Each entry is a (char *)
        owned by this object.
     */
    fsl_list dirs;
    /**
        Used to build up a path string during fsl_pathfinder_search(),
        and holds the result of a successful search. We use a buffer,
        as opposed to a simple string, because (A) it simplifies the
        search implementation and (B) reduces allocations (it gets
        reused for each search).
     */
    fsl_buffer buf;
  };

  typedef struct fsl_pathfinder fsl_pathfinder;
  /**
      Initialized-with-defaults fsl_pathfinder instance, intended for
      const copy initialization.
   */
#define fsl_pathfinder_empty_m {fsl_list_empty_m/*ext*/,fsl_list_empty_m/*dirs*/,fsl_buffer_empty_m/*buf*/}

  /**
      Initialized-with-defaults fsl_pathfinder instance, intended for
      copy initialization.
   */
  extern const fsl_pathfinder fsl_pathfinder_empty;

  /**
      Frees all memory associated with pf, but does not free pf.
      Is a no-op if pf is NULL.
   */
  void fsl_pathfinder_clear(fsl_pathfinder * pf);

  /**
      Adds the given directory to pf's search path. Returns 0 on
      success, FSL_RC_MISUSE if !pf or !dir (dir _may_ be an empty
      string), FSL_RC_OOM if copying the string or adding it to the
      list fails.

      @see fsl_pathfinder_ext_add()
      @see fsl_pathfinder_search() 
   */
  int fsl_pathfinder_dir_add(fsl_pathfinder * pf, char const * dir);

  /**
      Adds the given directory to pf's search extensions. Returns 0 on
      success, FSL_RC_MISUSE if !pf or !dir (dir _may_ be an empty
      string), FSL_RC_OOM if copying the string or adding it to the
      list fails.

      Note that the client is responsible for adding a "." to the
      extension, if needed, as this API does not apply any special
      meaning to any characters in a search extension. e.g. "-journal"
      and "~" are both perfectly valid extensions for this purpose.

      @see fsl_pathfinder_dir_add()
      @see fsl_pathfinder_search() 
   */
  int fsl_pathfinder_ext_add(fsl_pathfinder * pf, char const * ext);

  /**
      Searches for a file whose name can be constructed by some
      combination of pf's directory/suffix list and the given base
      name.

      It searches for files in the following manner:

      If the 2nd parameter exists as-is in the filesystem, it is
      treated as a match, otherwise... Loop over all directories
      in pf->dirs. Create a path with DIR/base, or just base if
      the dir entry is empty (length of 0). Check for a match.
      If none is found, then... Loop over each extension in
      pf->ext, creating a path named DIR/baseEXT (note that it
      does not add any sort of separator between the base and the
      extensions, so "~" and "-foo" are legal extensions). Check
      for a match.

      On success (a readable filesystem entry is found):

      - It returns 0.

      - If pOut is not NULL then *pOut is set to the path it
      found. The bytes of the returned string are only valid until the
      next search operation on pf, so copy them if you need them.
      Note that the returned path is _not_ normalized via
      fsl_file_canonical_name() or similar, and it may very well
      return a relative path (if base or one of pf->dirs contains a
      relative path part).

      - If outLen is not NULL, *outLen will be set to the
      length of the returned string. 

      On error:

      - Returns FSL_RC_MISUSE if !pf, !base, !*base.

      - Returns FSL_RC_OOM on allocation error (it uses a buffer to
      hold its path combinations and return value).

      - Returns FSL_RC_NOT_FOUND if it finds no entry.

      The host platform's customary path separator is used to separate
      directory/file parts ('\\' on Windows and '/' everywhere else).

      Note that it _is_ legal for pOut and outLen to both be NULL, in
      which case a return of 0 signals that an entry was found, but
      the client has no way of knowing what path it might be (unless,
      of course, he relies on internal details of the fsl_pathfinder
      API, which he most certainly should not do).

      Tip: if the client wants to be certain that this function will
      not allocate memory, simply use fsl_buffer_reserve() on pf->buf
      to reserve the desired amount of space in advance. As long as
      the search paths never extend that length, this function will
      not need to allocate. (Until/unless the following TODO is
      implemented...)

      Potential TODO: use fsl_file_canonical_name() so that the search
      dirs themselves do not need to be entered using
      platform-specific separators. The main reason it's not done now
      is that it requires another allocation. The secondary reason is
      because it's sometimes useful to use relative paths in this
      context (based on usage in previous trees from which this code
      derives).

      @see fsl_pathfinder_dir_add()
      @see fsl_pathfinder_ext_add()
      @see fsl_pathfinder_clear()
   */
  int fsl_pathfinder_search(fsl_pathfinder * pf, char const * base,
                            char const ** pOut, fsl_size_t * outLen );


  /**
      Result codes for the fsl_confirmation_f() family of functions.
   */
  enum fsl_confirmation_t {
  /**
      Indicates there was some unrecoverable error during prompting.
      Should be used with care.
   */
  FSL_CONFIRM_ERROR = -1,
  /**
      Indicates that the answer to the question at hand is "yes" or "okay."
   */
  FSL_CONFIRM_YES = 0,
  /**
      Indicates that the answer to the question at hand is "yes" or
      "okay" and that the same answer should be assumed for any number
      of a series of similar questions. This is intended to have
      semantics similar to answering questions like "replace this
      file, cancel, or replace this and all up-coming files?"
   */
  FSL_CONFIRM_YES_ALL = 1,
  /**
      This is the converse of FSL_CONFIRM_YES.
   */
  FSL_CONFIRM_NO = 2,
  /**
      This is the converse of FSL_CONFIRM_YES_ALL.
   */
  FSL_CONFIRM_NO_ALL = 3
  };

  /**
      An interface for routines which answer a yes/no question, either
      via human interaction or programatic decisions.

      Conventions:

      The state parameter is arbitrary state needed for the confirmation,
      and its type is implementation-specified.

      msg is a message to present to the user (if any), and msgLen is
      its length in bytes.

      Must return one of the fsl_confirmation_t values to indicate
      the result of the question.
   */
  typedef int (*fsl_confirmation_f)(void * state, char const * msg, fsl_size_t msgLen);

  /**
      NOT YET IMPLEMENTED.

      A fsl_confirmation_f() implementation which reads from the opened FILE handle
      passed as the first argument.
   */
  typedef int fsl_confirmation_f_console_FILE(void * state, char const * msg, fsl_size_t msgLen);

  /**
      A fsl_confirmation_f() implementation which expects the state
      parameter to be a (int [const] *) which dereferences to one of
      the FSL_CONFIRM_xxx values, defaulting to FSL_CONFIRM_ERROR.
      This function simply returns that value on each call, ignoring
      its msg and msgLen parameters. It does not modify the state.
   */
  int fsl_confirmation_f_int(void * state, char const * msg, fsl_size_t msgLen);

  /**
      A helper type which wraps a fsl_confirmation_f() and its state
      into one object.
   */
  struct fsl_confirmer {
    /**
        State to be passed as the first argument to this->f().
     */
    void * state;
    /**
        A confirmation function.
     */
    fsl_confirmation_f f;
  };
  typedef struct fsl_confirmer fsl_confirmer;
  /**
      Initialized-with-defaults fsl_confirmer struct.
   */
#define fsl_confirmer_empty_m {NULL,NULL}
  /**
      Initialized-with-defaults fsl_confirmer struct.
   */
  extern const fsl_confirmer fsl_confirmer_empty;

  /**
      Creates a formatted message string using fsl_appendf(fmt,...) and passes
      that message to f(state,...). On success (message allocation succeeds)
      then the result of the call to f() is returned, otherwise 
   */
  int fsl_confirm( fsl_confirmation_f f, void * state, char const * fmt, ... );

  /**
      The va_list counterpart of fsl_confirm().
   */
  int fsl_confirmv( fsl_confirmation_f f, void * state, char const * fmt, va_list );


  /**
      A utility class for creating ZIP-format archives. All members
      are internal details and must not be mucked about with by the
      client.  See fsl_zip_file_add() for an example of how to use it.

      Note that it creates ZIP content in memory, as opposed to
      streaming it (it is not yet certain if abstractly streaming a
      ZIP is possible), so creating a ZIP file this way is exceedingly
      memory-hungry.

      @see fsl_zip_file_add()
      @see fsl_zip_timestamp_set_julian()
      @see fsl_zip_timestamp_set_unix()
      @see fsl_zip_end()
      @see fsl_zip_body()
      @see fsl_zip_finalize()
   */
  struct fsl_zip_writer {
    /**
        Number of entries (files + dirs) added to the zip file so far.
     */
    fsl_size_t entryCount;
    /**
        Current DOS-format time of the ZIP.
     */
    fsl_int32_t dosTime;
    /**
        Current DOS-format date of the ZIP.
     */
    fsl_int32_t dosDate;
    /**
        Current Unix Epoch time of the ZIP.
     */
    fsl_time_t unixTime;
    /**
        An artificial root directory which gets prefixed
        to all inserted filenames.
     */
    char * rootDir;
    /**
        The buffer for the table of contents.
     */
    fsl_buffer toc;
    /**
        The buffer for the ZIP file body.
     */
    fsl_buffer body;
    /**
        Internal scratchpad for ops which often allocate
        small buffers.
     */
    fsl_buffer scratch;
    /**
        The current list of directory entries (as (char *)).
     */
    fsl_list dirs;
  };
  typedef struct fsl_zip_writer fsl_zip_writer;

  /**
      An initialized-with-defaults fsl_zip_writer instance, intended
      for in-struct or const-copy initialization.
   */
#define fsl_zip_writer_empty_m {                \
    0/*entryCount*/,                            \
    0/*dosTime*/,                             \
    0/*dosDate*/,                             \
    0/*unixTime*/,                            \
    NULL/*rootDir*/,\
    fsl_buffer_empty_m/*toc*/,                \
    fsl_buffer_empty_m/*body*/,               \
    fsl_buffer_empty_m/*scratch*/,            \
    fsl_list_empty_m/*dirs*/                  \
    }

  /**
      An initialized-with-defaults fsl_zip_writer instance,
      intended for copy-initialization.
   */
  extern const fsl_zip_writer fsl_zip_writer_empty;

  /**
      Sets a virtual root directory in z, such that all files added
      with fsl_zip_file_add() will get this directory prefixed to
      it.

      If zRoot is NULL or empty then this clears the virtual root,
      otherwise is injects any directory levels it needs to into the
      being-generated ZIP. Note that zRoot may contain multiple levels
      of directories, e.g. "foo/bar/baz", but it must be legal for
      use in a ZIP file.

      This routine copies zRoot's bytes, so they may be transient.

      Returns 0 on success, FSL_RC_ERROR if !z, FSL_RC_OOM on
      allocation error. Returns FSL_RC_RANGE if zRoot is an absolute
      path or if zRoot cannot be normalized to a "simplified name" (as
      per fsl_is_simple_pathname(), with the note that this routine
      will pass a copy of zRoot through fsl_file_simplify_name()
      first).

      @see fsl_zip_finalize()
   */
  int fsl_zip_root_set(fsl_zip_writer * z, char const * zRoot );

  /**
      Adds a file or directory to the ZIP writer z. zFilename is the
      virtual name of the file or directory. If pContent is NULL then
      it is assumed that we are creating one or more directories,
      otherwise the ZIP's entry is populated from pContent. The
      permsFlag argument specifies the fossil-specific permission
      flags from the fsl_fs_perm_t enum, but currently ignores the
      permsFlag argument for directories. Not that this function
      creates directory entries for any files automatically, so there
      is rarely a need for client code to create them (unless they
      specifically want to ZIP an empty directory entry).

      Notes of potential interest:

      - The ZIP is created in memory, and thus creating ZIPs with this
      API is exceedingly memory-hungry.

      - The timestamp of any given file must be set separately from
      this call using fsl_zip_timestamp_set_unix() or
      fsl_zip_timestamp_set_julian(). That value is then used for
      subsequent file-adds until a new time is set.

      - If a root directory has been set using fsl_zip_root_set() then
      that name, plus '/' (if the root does not end with one) gets
      prepended to all files added via this routine.

      An example of the ZIP-generation process:

      @code
      int rc;
      fsl_zip_writer z = fsl_zip_writer_empty;
      fsl_buffer buf = fsl_buffer_empty;
      fsl_buffer const * zipBody;

      // ...fill the buf buffer (not shown here)...

      // Optionally set a virtual root dir for new files:
      rc = fsl_zip_root_set( &z, "myRootDir" ); // trailing slash is optional
      if(rc) { ... error ...; goto end; }

      // We must set a timestamp which will be used until we set another:
      fsl_zip_timestamp_set_unix( &z, time(NULL) );

      // Add a file:
      rc = fsl_zip_file_add( &z, "foo/bar.txt", &buf, FSL_FS_PERM_REGULAR );
      // Clean up our content:
      fsl_buffer_reset(&buf); // only needed if we want to re-use the buffer's memory
      if(rc) goto end;

      // ... add more files the same way (not shown) ...

      // Now "seal" the ZIP file:
      rc = fsl_zip_end( &z );
      if(rc) goto end;

      // Fetch the ZIP content:
      zipBody = fsl_zip_body( &z );
      // zipBody now points to zipBody->used bytes of ZIP file content
      // which can be sent to an arbitrary destination, e.g.:
      rc = fsl_buffer_to_filename( zipBody, "my.zip" );

      end:
      fsl_buffer_clear(&buf);
      // VERY important, once we're done with z:
      fsl_zip_finalize( &z );
      if(rc){...we had an error...}
      @endcode

      @see fsl_zip_timestamp_set_julian()
      @see fsl_zip_timestamp_set_unix()
      @see fsl_zip_end()
      @see fsl_zip_body()
      @see fsl_zip_finalize()
   */
  int fsl_zip_file_add( fsl_zip_writer * z, char const * zFilename,
                        fsl_buffer const * pContent, int permsFlag );

  /**
      Ends the ZIP-creation process, padding all buffers, writing all
      final required values, and freeing up most of the memory owned
      by z. After calling this, z->body contains the full generated
      ZIP file.

      Returns 0 on success. On error z's contents may still be
      partially intact (for debugging purposes) and z->body will not
      hold complete/valid ZIP file contents. Results are undefined if
      !z or z has not been properly initialized.

      The caller must eventually pass z to fsl_zip_finalize() to free
      up any remaining resources.

      @see fsl_zip_timestamp_set_julian()
      @see fsl_zip_timestamp_set_unix()
      @see fsl_zip_file_add()
      @see fsl_zip_body()
      @see fsl_zip_finalize()
   */
  int fsl_zip_end( fsl_zip_writer * z );

  /**
      Returns a pointer to z's ZIP content buffer. The contents
      are ONLY valid after fsl_zip_end() returns 0.

      @see fsl_zip_timestamp_set_julian()
      @see fsl_zip_timestamp_set_unix()
      @see fsl_zip_file_add()
      @see fsl_zip_end()
      @see fsl_zip_finalize()
   */
  fsl_buffer const * fsl_zip_body( fsl_zip_writer const * z );

  /**
      Frees all memory owned by z and resets it to a clean state, but
      does not free z. Any fsl_zip_writer instance which has been
      modified via the fsl_zip_xxx() family of functions MUST
      eventually be passed to this function to clean up any contents
      it might have accumulated during its life. After this returns,
      z is legal for re-use in creating a new ZIP archive.

      @see fsl_zip_timestamp_set_julian()
      @see fsl_zip_timestamp_set_unix()
      @see fsl_zip_file_add()
      @see fsl_zip_end()
      @see fsl_zip_body()
   */
  void fsl_zip_finalize(fsl_zip_writer * z);

  /**
      Set z's date and time from a Julian Day number. Results are
      undefined if !z. Results will be invalid if rDate is negative. The
      timestamp is applied to all fsl_zip_file_add() operations until it
      is re-set.

      @see fsl_zip_timestamp_set_unix()
      @see fsl_zip_file_add()
      @see fsl_zip_end()
      @see fsl_zip_body()
   */
  void fsl_zip_timestamp_set_julian(fsl_zip_writer *z, fsl_double_t rDate);

  /**
      Set z's date and time from a Unix Epoch time. Results are
      undefined if !z. Results will be invalid if rDate is negative. The
      timestamp is applied to all fsl_zip_file_add() operations until it
      is re-set.
   */
  void fsl_zip_timestamp_set_unix(fsl_zip_writer *z, fsl_time_t epochTime);

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