/*///---------------------------------------------------------------------\\\*/
/*// Copyright (c)               (_)/=\~/_(_)         All rights reserved. \\*/
/*/  2005-2006                (_)  /=\  ~/_  (_)        Released under the  \*/
/*\  Jim Peters            (_)    /=\    ~/_    (_)             GNU GPL v2  /*/
/*\\ http://uazu.net    (_) ____ /=\ ____ ~/_ ____ (_)      http://fsf.org //*/
/*\\\---------------------------------------------------------------------///*/

//
//	MAIN
//

#ifdef HEADER

#define VERSION "2.0.8"

#ifndef DEBUG_ON
#define DEBUG_ON 1
#endif
#ifndef MEMDEBUG_ON
#define MEMDEBUG_ON 0
#endif

// DEBUG is for stuff that we want optimised away in the final version
#define DEBUG if (DEBUG_ON && o_debug > 1)

// MEMDEBUG is for memory checking
#define MEMDEBUG if (MEMDEBUG_ON)

#else

#include "all.h"

#define NL "\n"
char usage_text[]= 
"Symirr version " VERSION ": Copyright (c) 2005-2006 Jim Peters <http://uazu.net/>"
NL "Released under the GNU GPL v2.  Normal usage:"
NL "  symirr [<options>] <local-base-dir> <remote-base-dir>"
NL "  symirr [<options>] <local-base-dir> <remote-base-dir> <remote-connect-command>"
NL "Option summary:"
NL "  -a             # Automated mode; editor not invoked; messages only on error"
NL "  -av            # Verbose auto mode, messages as for non-auto operation"
NL "  -B             # Force a basic sync; ignore history info with this host"
NL "  -S or -M       # Force an asymmetric basic sync with local as slave or master"
NL "  -f or -ff etc  # Follow symbolic links in top-level dir (or to depth 2, etc)"
NL "  -b             # Backup deleted/overwritten files to a TGZ under _SYMIRR_BASE"
NL "  -x <pattern>   # Exclude files according to glob-style pattern"
NL "  -p <pattern>   # Prune (exclude) directories according to glob-style pattern"
NL "  -e <editor>    # Select editor (overriding $VISUAL, $EDITOR and defaults)"
NL "  -D -DD -T<num> -t <node>    # Debugging and special testing modes"
NL "Examples:"
NL "  symirr AA BB"
NL "  symirr AA BB ssh jim@sunrise symirr"
;

char node_test_usage[]= "Node-test usage:"
NL "  symirr [<options>] -t local-tree <idts-directory>"
NL "  symirr [<options>] -t local-xtree <base> <idts-directory>"
NL "  symirr [<options>] -t smtree <base>"
NL "  symirr [<options>] -t smcomp <tree1> <tree2>"
NL "  symirr [<options>] -t smxcomp <tree1> <tree2>"
NL "  symirr [<options>] -t smact <command-list> <base> <idts>"
NL "  symirr [<options>] -t smmapch <local-pc-changes>"
NL "  symirr [<options>] -t smmerge <tree> <mapch>"
NL "  symirr [<options>] -t smxtree <old-tree> <new-tree>"
NL "  symirr [<options>] -t smdiff <old-tree> <new-tree>"
NL "  symirr [<options>] -t smpatch <old-tree> <new-tree>";


// Globals
int o_local;		// Sanity check: set as soon as we're sure we're not doing a remote sync
int o_auto;		// Automated mode
int o_basic;		// Force basic sync? 0 no, 'B' yes/normal, 'S' yes/slave, 'M' yes/master
int o_debug;		// Debug output enabled
int o_follow;		// Depth of directories in which to follow symlinks
int o_test;		// Test mode
int o_backup;		// Backup deleted/overwritten files 
int o_verb;		// Output normal messages
char *o_editor;		// Editor override
char *base;		// Base directory name
char *tgz_pathname;	// Pathname to TGZ file
List *o_excl;		// Exclude list
List *o_prune;		// Prune list
int timeout= 60; 	// 60s timeout for remote connections
int no_xtreediff= 0;	// Disable xtreediff for testing
char *command;		// Full command-line, for printing only

int 
main(int ac, char **av) {
   char ch, *p;
   List *rem_av= 0;
   char *rem_opt;
   char *rem_base;
   char *progname= av[0];
   int verb= 0;		// Temp flag
   int val;

   // Record command-line
   {
      List *lp= 0;
      int a;
      for (a= 0; a<ac; a++) list_add(&lp, StrDup(av[a]));
      command= list_string(&lp, 0, " ");
      list_free(&lp);
   }

   ac--, av++;

   // Setup various things
   umask(0);

   // Remote mode
   if (ac == 0) {
      if (isatty(0) || isatty(1)) usage();	// We should be on pipes
      do_remote();
      return 0;
   }

   // Local mode or testing mode operation
   o_local= 1;
   while (ac && av[0][0] == '-') {
      p= 1 + *av++; ac--;
      while ((ch= *p++)) switch (ch) {
       case 't': 
	  o_test++; break;
       case 'a': 
	  o_auto++; break;
       case 'v': 
	  verb++; break;
       case 'b': 
	  list_add(&rem_av, StrDup("-b"));
	  o_backup++; break;
       case 'B': 
       case 'S': 
       case 'M': 
	  if (o_basic) error("You are mixing your -B -S -M options; which do you want?");
	  o_basic= ch; break;
       case 'f':
	  list_add(&rem_av, StrDup("-f"));
	  o_follow++; break;
       case 'x':
       case 'p':
	  if (ac < 1) usage();
	  list_add((ch == 'x') ? &o_excl : &o_prune, StrDup(av[0]));
	  list_add(&rem_av, StrDup((ch == 'x') ? "-x" : "-p"));
	  list_add(&rem_av, StrDup(av[0]));
	  ac--, av++; break;
       case 'D': 
	  list_add(&rem_av, StrDup("-D"));
	  o_debug++; break;
       case 'e': 
	  if (!ac) usage();
	  o_editor= *av++; ac--;
	  break;
       case 'T':
	  val= atoi(p); p= "";
	  list_add(&rem_av, StrDupF("-T%d", val));
	  special_test_mode(val);
	  break;
       default:
	  usage();
      }
   }
   o_verb= o_auto ? verb : 1;
   
   // Testing mode
   if (o_test) {
      char *node;
      if (ac < 1) error("%s", node_test_usage);
      node= *av++; ac--;
      do_node_test(node, ac, av);
      return 0;
   }

   // Local mode
   if (ac < 2) usage();
   base= *av++; ac--; 
   rem_base= *av++; ac--;
   if ((p= ends_with(base, "/"))) *p= 0;
   if ((p= ends_with(rem_base, "/"))) *p= 0;

   // Prepare remote option/argument-list
   list_add(&rem_av, StrDup(rem_base));
   {	// Sanity check
      List *lp; 
      for (lp= rem_av; lp; lp= lp->nxt) if (strchr(lp->str, '\t')) 
	 error("Bad option/argument data contains TAB: %s", lp->str);
   }
   rem_opt= list_string(&rem_av, 0, "\t");
   list_free(&rem_av);

   if (ac == 0) {
      char *argv[2];
      argv[0]= progname;
      argv[1]= 0;
      do_local(argv, rem_opt);
   } else {
      do_local(av, rem_opt);
   }

   return 0;
}

// These for the test-suite
void 
special_test_mode(int val) {
   switch (val) {
    case 0:
       timeout= 5; break;
    case 1:
       no_xtreediff= 1; break;
    default:
       error("Bad -T special testing mode: %d", val);
   }	  
}

//
//	Node testing
//

#define PRINT(xx) { printf("%s\n", xx); free(xx); xx= 0; }

void 
do_node_test(char *node, int ac, char **av) {
   if (str_eq(node, "local-tree")) {
      char *p;
      if (ac < 1) error("Usage: symirr -t local-tree <idts-directory>");
      p= strchr(av[0], 0); if (p > av[0] && p[-1] == '/') *--p= 0;

      {
	 SMLoad *ld1= smload(StrF("%s/local-pc-tree.gz", av[0]));
	 SMLoad *ld2= smload_sorted(StrF("%s/local-pc-changes", av[0]));
	 SMMapch *map= smmapch(&ld2->out);
	 SMMerge *mrg= smmerge(&ld1->out, &map->out);
	 while (1) {
	    smload_do(ld1);
	    smload_do(ld2);
	    smmapch_do(map);
	    smmerge_do(mrg);
	    if (isEOF(mrg->out)) break;
	    if (mrg->out) { PRINT(mrg->out); }
	 }
	 smload_del(ld1);
	 smload_del(ld2);
	 smmapch_del(map);
	 smmerge_del(mrg);
	 return;
      } 
   }
   if (str_eq(node, "local-xtree")) {
      char *p;
      if (ac < 2) error("Usage: symirr -t local-xtree <base> <idts-directory>");
      p= strchr(av[0], 0); if (p > av[0] && p[-1] == '/') *--p= 0;
      p= strchr(av[1], 0); if (p > av[1] && p[-1] == '/') *--p= 0;

      {
	 SMLoad *ld1= smload(StrF("%s/local-pc-tree.gz", av[1]));
	 SMLoad *ld2= smload_sorted(StrF("%s/local-pc-changes", av[1]));
	 SMMapch *map= smmapch(&ld2->out);
	 SMMerge *mrg= smmerge(&ld1->out, &map->out);
	 SMTree *tr= smtree(av[0]);
	 SMXtree *xtr= smxtree(&mrg->out, &tr->out);
	 while (1) {
	    smload_do(ld1);
	    smload_do(ld2);
	    smmapch_do(map);
	    smmerge_do(mrg);
	    smtree_do(tr);
	    smxtree_do(xtr);
	    if (isEOF(xtr->out)) break;
	    if (xtr->out) { PRINT(xtr->out); }
	 }
	 smload_del(ld1);
	 smload_del(ld2);
	 smmapch_del(map);
	 smmerge_del(mrg);
	 smtree_del(tr);
	 smxtree_del(xtr);
	 return;
      } 
   }
   if (str_eq(node, "smtree")) {
      SMTree *tt;
      if (ac < 1) error("Usage: symirr -t smtree <base>");
      tt= smtree(av[0]);
      while (1) {
	 smtree_do(tt);
	 if (isEOF(tt->out)) break;
	 if (tt->out) { PRINT(tt->out); }
      }
      smtree_del(tt);
      return;
   }
   if (str_eq(node, "smcomp")) {
      SMLoad *i0, *i1;
      SMComp *tt;
      if (ac < 2) error("Usage: symirr -t smcomp <tree1> <tree2>");
      i0= smload(av[0]);
      i1= smload(av[1]);
      tt= smcomp(&i0->out, &i1->out);
      while (1) {
	 smload_do(i0);
	 smload_do(i1);
	 smcomp_do(tt);
	 if (isEOF(tt->out)) break;
	 if (tt->out) { PRINT(tt->out); }
      }
      return;
   }
   if (str_eq(node, "smxcomp")) {
      SMLoad *i0, *i1;
      SMXcomp *tt;
      if (ac < 2) error("Usage: symirr -t smxcomp <tree1> <tree2>");
      i0= smload(av[0]);
      i1= smload(av[1]);
      tt= smxcomp(&i0->out, &i1->out);
      while (1) {
	 smload_do(i0);
	 smload_do(i1);
	 smxcomp_do(tt);
	 if (isEOF(tt->out)) break;
	 if (tt->out) { PRINT(tt->out); }
      }
      return;
   }
   if (str_eq(node, "smact")) {
      error("NYI: Usage: symirr -t smact <command-list> <base> <idts>");
   }
   if (str_eq(node, "smmapch")) {
      SMLoad *ii;
      SMMapch *tt;
      if (ac < 1) error("Usage: symirr -t smmapch <local-pc-changes>");
      ii= smload_sorted(av[0]);
      tt= smmapch(&ii->out);
      while (1) {
	 smload_do(ii);
	 smmapch_do(tt);
	 if (isEOF(tt->out)) break;
	 if (tt->out) { PRINT(tt->out); }
      }
      return;
   }
   if (str_eq(node, "smmerge")) {
      SMLoad *i0, *i1;
      SMMerge *tt;
      if (ac < 2) error("Usage: symirr -t smmerge <tree> <mapch>");
      i0= smload(av[0]);
      i1= smload(av[1]);
      tt= smmerge(&i0->out, &i1->out);
      while (1) {
	 smload_do(i0);
	 smload_do(i1);
	 smmerge_do(tt);
	 if (isEOF(tt->out)) break;
	 if (tt->out) { PRINT(tt->out); }
      }
      return;
   }
   if (str_eq(node, "smxtree")) {
      SMLoad *i0, *i1;
      SMXtree *tt;
      if (ac < 2) error("Usage: symirr -t smxtree <old-tree> <new-tree>");
      i0= smload(av[0]);
      i1= smload(av[1]);
      tt= smxtree(&i0->out, &i1->out);
      while (1) {
	 smload_do(i0);
	 smload_do(i1);
	 smxtree_do(tt);
	 if (isEOF(tt->out)) break;
	 if (tt->out) { PRINT(tt->out); }
      }
      return;
   }
   if (str_eq(node, "smdiff")) {
      SMLoad *i0, *i1;
      SMDiff *tt;
      if (ac < 2) error("Usage: symirr -t smdiff <old-tree> <new-tree>");
      i0= smload(av[0]);
      i1= smload(av[1]);
      tt= smdiff(&i0->out, &i1->out);
      while (1) {
	 smload_do(i0);
	 smload_do(i1);
	 smdiff_do(tt);
	 if (isEOF(tt->out)) break;
	 if (tt->out) { PRINT(tt->out); }
      }
      return;
   }
   if (str_eq(node, "smpatch")) {
      SMLoad *i0, *i1;
      SMPatch *tt;
      if (ac < 2) error("Usage: symirr -t smpatch <old-tree> <new-tree>");
      i0= smload(av[0]);
      i1= smload(av[1]);
      tt= smpatch(&i0->out, &i1->out);
      while (1) {
	 smload_do(i0);
	 smload_do(i1);
	 smpatch_do(tt);
	 if (isEOF(tt->out)) break;
	 if (tt->out) { PRINT(tt->out); }
      }
      return;
   }
   error("%s\nNode-name not known: %s", node_test_usage, node);
}

#endif

/*///---------------------------------------------------------------------\\\*/
/*// )`( )()/)(`/\`//\`          (_)/=\~/_(_)           '/\\'/\')(\()( )'( \\*/
/*/ ()(')()\)(/`/\\ `         (_)  /=\  ~/_  (_)          ' //\'\)(/()(`)() \*/
/*\  )`()( )(/'            (_)    /=\    ~/_    (_)             `\)( )()'(  /*/
/*\\  ()()(`)(/         (_) ____ /=\ ____ ~/_ ____ (_)          \)(')()()  //*/
/*\\\---------------------------------------------------------------------///*/
