/* cmd-inventory.c: file inventories
 *
 ****************************************************************
 * Copyright (C) 2002, 2003  Tom Lord
 *
 * See the file "COPYING" for further information about
 * the copyright and warranty status of this work.
 */


#include "config-options.h"
#include "hackerlab/cmd/main.h"
#include "hackerlab/vu/safe.h"
#include "hackerlab/char/pika-escaping-utils.h"
#include "tla/libarch/project-tree.h"
#include "tla/libarch/invent.h"
#include "tla/libarch/cmd.h"
#include "tla/libarch/cmd-inventory.h"

#include "hackerlab/os/errno-to-string.h"


static t_uchar * usage = "[options] [--] [dir]*";
static t_uchar * version_string = (cfg__std__package " from regexps.com\n"
                                   "\n"
                                   "Copyright 2002 Tom Lord\n"
                                   "\n"
                                   "This is free software; see the source for copying conditions.\n"
                                   "There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A\n"
                                   "PARTICULAR PURPOSE.\n"
                                   "\n"
                                   "Report bugs to " cfg__tla_bug_mail ".\n"
                                   "\n"
                                   cfg__std__release_id_string
                                   "\n");

#define OPTS(OP) \
  OP (opt_help_msg, "h", "help", 0, \
      "display help") \
  OP (opt_long_help, "H", 0, 0, \
      "Display a verbose help message and exit.") \
  OP (opt_version, "V", "version", 0, \
      "display version info\n") \
  OP (opt_source, "s", "source", 0, \
      "list source files") \
  OP (opt_precious, "p", "precious", 0, \
      "list precious files") \
  OP (opt_backups, "b", "backups", 0, \
      "list backup files") \
  OP (opt_junk, "j", "junk", 0, \
      "list junk files") \
  OP (opt_unrecognized, "u", "unrecognized", 0, \
      "list unrecognized files") \
  OP (opt_trees, "t", "trees", 0, \
      "list roots of nested trees\n") \
  OP (opt_directories, "d", "directories", 0, \
      "list only directories") \
  OP (opt_files, "f", "files", 0, \
      "list only non-directories") \
  OP (opt_both, "B", "both", 0, \
      "list both dirs and files") \
  OP (opt_kind, 0, "kind", 0, \
      "indicate file kinds\n") \
  OP (opt_all, 0, "all", 0, \
      "include arch control files") \
  OP (opt_nested, 0, "nested", 0, \
      "include nested trees\n") \
  OP (opt_ids, 0, "ids", 0, \
      "list with ids (source files only)") \
  OP (opt_untagged, 0, "untagged", 0, \
      "include files that are missing ids\n") \
  OP (opt_explicit, 0, "explicit", 0, \
      "use explicit file ids") \
  OP (opt_implicit, 0, "implicit", 0, \
      "permit implicit file ids") \
  OP (opt_tagline, 0, "tagline", 0, \
      "permit tagline file ids") \
  OP (opt_names, 0, "names", 0, \
      "use name-based file ids") \
  OP (opt_unescaped, 0, "unescaped", 0, \
      "show filenames in unescaped form")


t_uchar arch_cmd_inventory_help[] = ("inventory a source tree\n"
                                     "With no arguments, print a human-readable inventory report.\n"
                                     "\n"
                                     "With category options (--source etc) limit the report to just\n"
                                     "those files.  With no other options, the report includes all\n"
                                     "sections and files.\n"
                                     "\n"
                                     "The options -d, -f, and -B cancel each other.\n"
                                     "\n"
                                     "If a directory is precious, junk, or unrecognized, only the\n"
                                     "directory name itself is printed -- its contents are not\n"
                                     "searched.\n"
                                     "\n"
                                     "Each command option implies the corresponding category option\n"
                                     "(e.g. \"--source-command\" implies \"--source\").  The exit\n"
                                     "status of the command is ignored.\n");

enum options
{
  OPTS (OPT_ENUM)
};

static struct opt_desc opts[] =
{
  OPTS (OPT_DESC)
    {-1, 0, 0, 0, 0}
};


/* __STDC__ prototypes for static functions */
static void inventory_printer (const t_uchar * path,
                               struct stat * stat_buf,
                               enum arch_inventory_category category,
                               const t_uchar * id,
                               int has_source_name,
                               void * closure,
                               int escape_classes);



static int show_dirs = 0;
static int show_files = 1;
static int show_kind = 0;
static int show_ids;

static int n_categories = 0;

int
arch_cmd_inventory (t_uchar * program_name, int argc, char * argv[])
{
  int o;
  struct opt_parsed * option;
  struct arch_inventory_options inv_options;
  int escape_classes = arch_escape_classes;

  option = 0;
  mem_set0 ((t_uchar *)&inv_options, sizeof (inv_options));

  while (1)
    {
      o = opt_standard (lim_use_must_malloc, &option, opts, &argc, argv,
                        program_name, usage, version_string, arch_cmd_inventory_help,
                        opt_help_msg, opt_long_help, opt_version);
      if (o == opt_none)
        break;
      switch (o)
        {
        default:
          safe_printfmt (2, "unhandled option `%s'\n", option->opt_string);
          panic ("internal error parsing arguments");

        usage_error:
          opt_usage (2, argv[0], program_name, usage, 1);
          exit (1);

        /* bogus_arg: */
          safe_printfmt (2, "ill-formed argument for `%s' (`%s')\n", option->opt_string, option->arg_string);
          goto usage_error;

        case opt_source:
          inv_options.categories |= arch_inventory_source;
          break;

        case opt_precious:
          inv_options.categories |= arch_inventory_precious;
          break;

        case opt_backups:
          inv_options.categories |= arch_inventory_backup;
          break;

        case opt_junk:
          inv_options.categories |= arch_inventory_junk;
          break;

        case opt_unrecognized:
          inv_options.categories |= arch_inventory_unrecognized;
          break;

        case opt_trees:
          inv_options.categories |= arch_inventory_tree;
          break;

        case opt_directories:
          show_dirs = 1;
          show_files = 0;
          break;

        case opt_files:
          show_dirs = 0;
          show_files = 1;
          break;

        case opt_both:
          show_dirs = 1;
          show_files = 1;
          break;

        case opt_kind:
          show_kind = 1;
          break;

        case opt_all:
          inv_options.include_excluded = 1;
          break;

        case opt_nested:
          inv_options.nested = 1;
          break;

        case opt_ids:
          inv_options.want_ids = 1;
          show_ids = 1;
          break;

        case opt_untagged:
          inv_options.treat_unrecognized_source_as_source = 1;
          break;

        case opt_explicit:
          inv_options.override_method = 1;
          inv_options.method = arch_explicit_id_tagging;
          break;

        case opt_implicit:
          inv_options.override_method = 1;
          inv_options.method = arch_implicit_id_tagging;
          break;

        case opt_tagline:
          inv_options.override_method = 1;
          inv_options.method = arch_tagline_id_tagging;
          break;

        case opt_names:
          inv_options.override_method = 1;
          inv_options.method = arch_names_id_tagging;
          break;

	case opt_unescaped:
          escape_classes = 0;
          break;
        }
    }

  n_categories = 0;
  if (inv_options.categories & arch_inventory_source)
    ++n_categories;

  if (inv_options.categories & arch_inventory_precious)
    ++n_categories;

  if (inv_options.categories & arch_inventory_backup)
    ++n_categories;

  if (inv_options.categories & arch_inventory_junk)
    ++n_categories;

  if (inv_options.categories & arch_inventory_tree)
    ++n_categories;

  if (inv_options.categories & arch_inventory_unrecognized)
    ++n_categories;

  if (!n_categories)
    {
      n_categories = 6;
      inv_options.categories = arch_inventory_source | arch_inventory_precious | arch_inventory_backup | arch_inventory_tree | arch_inventory_unrecognized;
    }

  if (argc == 1)
    {
      argv[1] = ".";
      ++argc;
    }

  {
    int x;
    int errn;
    int re_error;

    for (x = 1; x < argc; ++x)
      {
        t_uchar * tree_root;
        char * bad_file;
        errn = 0;
        re_error = 0;

        if ( vu_access(&errn, argv[x], X_OK | F_OK ) == -1) 
          {
            safe_printfmt (2, "inventory: Cannot access directory %s\n%s\n", argv[x], errno_to_string(errn));
            exit (2);
          }
        tree_root = arch_tree_root (0, argv[x], 0);
        arch_get_inventory_naming_conventions (&inv_options, tree_root);

        bad_file = 0;
        arch_inventory_traversal (&inv_options, argv[x], inventory_printer, 0, escape_classes);
        arch_free_inventory_naming_conventions (&inv_options);
        lim_free (0, tree_root);
      }
  }
  return 0;
}



static void
inventory_printer (const t_uchar * path,
                   struct stat * stat_buf,
                   enum arch_inventory_category category,
                   const t_uchar * id,
                   int has_source_name,
                   void * closure,
                   int escape_classes)
{
  if ((category != arch_inventory_tree) && !show_dirs && S_ISDIR (stat_buf->st_mode))
    return;

  if (!show_files && !S_ISDIR (stat_buf->st_mode))
    return;

  if (n_categories > 1)
    {
      t_uchar * quest = (has_source_name ? "?" : " ");

      switch (category)
        {
        case arch_inventory_source:
          safe_printfmt (1, "S  ");
          break;
        case arch_inventory_precious:
          safe_printfmt (1, "P%s ", quest);
          break;
        case arch_inventory_backup:
          safe_printfmt (1, "B%s ", quest);
          break;
        case arch_inventory_junk:
          safe_printfmt (1, "J%s ", quest);
          break;
        case arch_inventory_tree:
          safe_printfmt (1, "T%s ", quest);
          break;
        case arch_inventory_unrecognized:
          safe_printfmt (1, "U%s ", quest);
          break;
        case arch_inventory_excludes:
          break;
        }
    }

  if (show_kind)
    {
      if (S_ISDIR (stat_buf->st_mode))
        safe_printfmt (1, "d ");
      else if (S_ISLNK (stat_buf->st_mode))
        safe_printfmt (1, "> ");
      else
        safe_printfmt (1, "r ");
    }

  if (path[0] == '.' && path[1] == '/')
    path += 2;

  {
    t_uchar * item;
    item = pika_save_escape_iso8859_1 (0, 0, escape_classes, path);

    safe_printfmt (1, "%s%s%s\n", item, (show_ids ? "\t" : ""), (show_ids ? (id ? (char *)id : "???") : ""));

    lim_free (0, item);
  }
}



/* tag: Tom Lord Wed May 14 10:52:12 2003 (srcfind.c)
 */
