#ifndef CLIENT_WSENTITY_H
#define CLIENT_WSENTITY_H

/*
 * Copyright (c) 2002, The EROS Group, LLC and Johns Hopkins
 * University. All rights reserved.
 * 
 * This software was developed to support the EROS secure operating
 * system project (http://www.eros-os.org). The latest version of
 * the OpenCM software can be found at http://www.opencm.org.
 * 
 * Redistribution and use in source and binary forms, with or
 * without modification, are permitted provided that the following
 * conditions are met:
 * 
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 
 * 2. Redistributions in binary form must reproduce the above
 *    copyright notice, this list of conditions and the following
 *    disclaimer in the documentation and/or other materials
 *    provided with the distribution.
 * 
 * 3. Neither the name of the The EROS Group, LLC nor the name of
 *    Johns Hopkins University, nor the names of its contributors
 *    may be used to endorse or promote products derived from this
 *    software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

/* The following definitions represent the state of an entity in the
   Workspace since its last logical checkout.  ("Logical checkout"
   means one of the following commands was executed successfully:
   'checkout', 'update', or 'commit'.) Here is the specification for
   each status flag.  (The single character in parentheses for each
   status is what is displayed in brackets via the 'status' command.)

  NEF_RENAMED: ('N') Workspace object's current fsName is different
   than the fsName it had at its last logical checkout.  

  NEF_MODIFIED: ('M') The content of a Workspace object does not match
  the content when the object was last logically checked out.

  NEF_MERGED: ('J') One and only one of the following is true after
  executing merge:
 
  i)   The Workspace object didn't exist prior to calling 'merge' 
  ii)  The Workspace object was marked for deletion by the merge 
  iii) The Workspace object has been "touched" as a direct result of
  the 'merge' command, where "touched" means the content and/or the
  metadata changed.
  iv) The Workspace object contents and metadata are identical to its
  matching object in the mergespace.  (This requires the NEF_MERGED
  flag because we need to update the fact that this Workspace object
  has a new merge parent.)

  NEF_ADDED: ('A'): This Workspace object is a new object in this line
  of development and was added directly by the user.

  NEF_DELETED: ('D'): This Workspace object has been marked for
  deletion from this line of development.

  NEF_RENAMEALERT: ('!'): When merging, the Workspace object has a
  different fsName than both its matching commonspace object and
  mergespace object.

  NEF_CONDDEL: ('-'): The Workspace object has been marked for
  deletion, then a subsquent merge does NOT agree that this object 
  should be deleted.

  NEF_PERMS: ('P'): The executable bit of the Workspace object has
  been changed since that object was last logically checked out.

  NEF_MISSING: The actual file that the Workspace object represents
  does not exist.

  NEF_JADD: ('A'): This object was just added to the Workspace by a
  merge.  Notice that the same display character for NEF_ADDED is
  used.

  NEF_CONDADD:  obsolete

  NEF_UPDATED:  obsolete

Here are the "rules of engagement" (as of 08/02/02) for the statuses
above:

1) The NEF_MISSING flag can only be cleared by executing 'update' or
'revert'.  It's imperative that a user resolve NEF_MISSING flags
before doing anything else!  

2) If NEF_ADDED is set, the only other flag that can be set is
NEF_MISSING. There is no need to keep track of renames, changes in
exec bit, etc. on a file while its NEF_ADDED flag is set because the
object has not been uploaded to the repository yet.  Calling 'remove'
on an NEF_ADDED object removes it from the PendingChange but leaves
the actual file in place.

3) If NEF_ADDED and NEF_MISSING are both set, the user must
manually restore the file or remove it from the PendingChange.

4) A file can be NEF_RENAMED either by the user's explicitly changing
its fsName or as a result of a merge.

5) A file can be NEF_MODIFIED either by the user's explicitly
modifying the contents or as a result of a merge.

6) A file can be NEF_MERGED only as a result of a merge.

7) A file can be NEF_DELETED either by the user's explicitly marking
the file for deletion or as a result of a merge.

8) A file can be NEF_RENAMEALERT only as a result of a merge.  This
status cannot be cleared until the subsequent 'commit'. 

9) A file can be NEF_CONDDEL only as a result of a merge. The user
MUST resolve any NEF_CONDDEL flag before the subsequent 'commit' by
executing 'add' (to cancel the deletion of the file) or 'remove' (to
verify the deletion of the file).

10) A file can be NEF_PERMS either by the user's explicitly changing
the executable bit of the file or as a result of a merge operation's
changing the executable bit.  The general rule in the merge case is
that the executable bit of a Workspace file is set if the executable
bit of the mergespace object is set.  Otherwise, it is left in its
current state.  However, if NEF_PERMS is already set when a merge
occurs, the merge will not override the current Workspace exec bit
setting for that file.

11) A file can be NEF_JADD only as a result of a merge.  If no
modifications (to either content or metadata) are made prior to the
subsequent 'commit', the Workspace object for such a file is a
bit-for-bit copy of the mergespace object.  If modifications are made,
however, a new Workspace object is created whose parent is the
mergespace object and whose merge parent is <null>.  */

#define NEF_RENAMED    0x001u	/* fsname was changed (either by merge
                                   or user */
#define NEF_MODIFIED   0x002u	/* contents of file was changed since
                                   last checkout (either by merge or
                                   user) */
#define NEF_MERGED     0x004u	/* file contents and/or metadata
                                   changed by 'merge' */
#define NEF_UPDATED    0x008u	/* obsolete */
#define NEF_ADDED      0x010u	/* user added this file to his workspace */
#define NEF_DELETED    0x020u	/* file marked for deletion (either by
                                   merge or user) */
#define NEF_RENAMEALERT 0x040u	/* workspace current fsname does not
                                   match either the fsname in the
                                   commonspace OR the fsname in the
                                   mergespace (after a 'merge') */
#define NEF_CONDADD    0x080u	/* obsolete */
#define NEF_CONDDEL    0x100u	/* usr deleted file without committing
                                   and then did 'merge' which didn't
                                   agree that this file should be
                                   deleted. */
#define NEF_PERMS      0x200u   /* permissions change (either by merge
                                   or user) */
#define NEF_MISSING    0x400u   /* file in workspace has disappeared. */
#define NEF_JADD       0x800u   /* merge added this file */

/* Following is not yet being used: */
#define NEF_MODTIME    0x1000u  /* modification time changed */

#include <opencm.h>

struct WsEntity {
  Serializable ser;

  const Entity *old;			/* state of the entity at checkout */

  /* state of the world currently reflected by Workspace contents */
  uint32_t      flags;
  const char   *cur_fsName;
  unsigned char cur_entityType;
  uint32_t      cur_entityPerms;
  const char   *cur_mergeParent;
  const char   *cur_mergeParentChange;

  /* These are recomputed from the workspace by
     wsentity_RecomputeStatus(). These are serialized, but you cannot
     believe them unless you have recently recomputed them via
     wsentity_RecomputeStatus(). */
  const char   *lk_contentTrueName;
  uint32_t      lk_length;

  /* Last known modification time. This is used to avoid running the
     expensive SHA-1 check in wsentity_RecomputeStatus(). If the mod
     time and the length match the previously recorded mod time and
     length, we assume that the content has not changed. Use
     the --force-hash command line option to override this
     optimization. */
  time_t        lk_modTime;

  /* Family NID (not serialized). This is either the value of
   * old->familyNid if old is non-NULL or a randomly assigned
   * value. The latter case applies only to newly-added files. It
   * would simplify matters slightly to serialize this, but only
   * slightly, and it isn't worth breaking the schema to achieve such
   * a minor advantage. */
  const char *familyNID;
  tnhash_t    famNidHash;	/* not serialized */

  /* We don't bother keeping modtime, since that needs to be
     recomputed on every pass in any case. */
} ;

/* const char * wsentity_status(WsEntity *); */

void wsentity_ReportStatus(WsEntity *);

/* For adding new files: */
WsEntity* wsentity_addFromFile(PendingChange *pc,
			       const char *fsNm, unsigned char eType);

/* For building from an existing entity: */
WsEntity *wsentity_fromEntity(const Entity *);

void wsentity_RecomputeStatus(WsEntity *);

int wsentity_UploadTo(WsEntity *, Repository *r, 
		      Mutable *m,
		      CommitInfo *ci,
		      const char *change_name);

WsEntity *wsentity_ShallowCopy(const WsEntity *);

void wsentity_dodiff(const WsEntity *wse, Repository *r, 
		     const char *branchURI);

int wse_fam_cmp(const WsEntity *wse1, const WsEntity *wse2);

#endif /* CLIENT_WSENTITY_H */

