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

//
//	SMComp -- see SYMIRR.jot for docs
//   

#ifdef HEADER

typedef struct SMComp SMComp;
struct SMComp {
   char **inp1;			// Input string reference
   char **inp2;			// Input string reference
   char *out;			// Output string
   char *out2, *out3, *out4;	// Queued output strings
   SMTLin aa, bb;		// Input lines
   char *mode;			// Mode: 0 ending, else *mode gives the mode (see below)
   int lev;			// Level during directory copies/skips; must be 0 when not in use
};

// Modes:
//   0 in same directory, normal parallel scan
//   's' skip 'aa' directory (not including final 'X')
//   'S' skip 'bb' directory (not including final 'X')
//   'c' copy 'aa' directory (not including final 'X')
//   'C' copy 'bb' directory (not including final 'X')
//   'x' skip 'aa' directory closing 'X'
//   'X' skip 'bb' directory closing 'X'
//
// Using a string for the mode allows a sequence of modes to be
// applied before returning to mode 0 (which is the NUL at the end of
// the string).


#else

#include "all.h"


//
//	SMComp
//

SMComp *
smcomp(char **inp1, char **inp2) {
   SMComp *tt= ALLOC(SMComp);

   tt->inp1= inp1;
   tt->inp2= inp2;
   tt->aa.used= 1;
   tt->bb.used= 1;

   tt->mode= "";	// Mode 0

   return tt;
}

#define USED(pp) { pp->used= 1; }
#define RET(xx) { tt->out= xx; return 1; }
#define RET2(xx,yy) { tt->out= xx; tt->out2= yy; return 1; }
#define RET3(xx,yy,zz) { tt->out= xx; tt->out2= yy; tt->out3= zz; return 1; }
#define RET4(xx,yy,zz,aa) { tt->out= xx; tt->out2= yy; tt->out3= zz; tt->out4= aa; return 1; }
#define SWAP { SMTLin *tt; char *to; swap= !swap;  \
               tt= aa; aa= bb; bb= tt; \
               to= to_aa; to_aa= to_bb; to_bb= to; }

#define C_CP(src,dst)    	  StrDupF("%scp\t%s\t%s\t%s", to_##dst, src->mug, src->path, src->szt)
#define C_CHMUG(src,dst) 	  StrDupF("%schmug\t%s\t%s", to_##dst, src->mug, src->path)
#define C_MKDIR(src,dst) 	  StrDupF("%smkdir\t%s\t%s", to_##dst, src->mug, src->path)
#define C_RM(dst)                 StrDupF("%srm\t%s", to_##dst, dst->path)

// Commented-out versions
#define comm_C_CP(src,dst)    	  StrDupF("# %scp\t%s\t%s\t%s", to_##dst, src->mug, src->path, src->szt)
#define comm_C_CHMUG(src,dst) 	  StrDupF("# %schmug\t%s\t%s", to_##dst, src->mug, src->path)

#define C_CP_CLASH(src,dst)       StrDupF("%scp\t%s\t%s.%s\t%s", to_##dst, src->mug, src->path, clash, src->szt)
#define C_REN_CLASH(dst)          StrDupF("%smv\t%s\t%s.%s", to_##dst, dst->path, dst->path, clash)
#define C_REN_CLASH_DIR(dst)      StrDupF("%smv\t%.*s\t%s", to_##dst, strlen(dst->path)-strlen(clash)-1, dst->path, dst->path)

#define GEN_CLASH_EXT 	 sprintf(clash, "CLASH-%08X", hash_of_time())


int 
smcomp_do(SMComp *tt) {
   int swap= 0;
   int mode;
   char *to_aa= "<<";
   char *to_bb= ">>";
   SMTLin *aa= &tt->aa;
   SMTLin *bb= &tt->bb;

   if (tt->out) return 0;	// Nothing to do until our output has been cleared

   // Check for queued output waiting to go
   if (tt->out2) { tt->out= tt->out2; tt->out2= 0; return 1; }
   if (tt->out3) { tt->out= tt->out3; tt->out3= 0; return 1; }
   if (tt->out4) { tt->out= tt->out4; tt->out4= 0; return 1; }

   if (aa->used) {
      if (!*tt->inp1) return 0;	// Wait for input
      smtlin_decode(aa, tt->inp1);
   }

   if (bb->used) {
      if (!*tt->inp2) return 0;	// Wait for input
      smtlin_decode(bb, tt->inp2);
   }

   if (!tt->mode) {
      // Ending mode
      tt->out= StrDup(SMEOF);
      return 0;
   }
   if (aa->typ == 0) DIE;       // Unexpected EOF on 'aa'
   if (bb->typ == 0) DIE;       // Unexpected EOF on 'bb'

   mode= *tt->mode;
   if (mode == 0) {
      // Normal parallel operation
      int cmp;

      if (aa->typ == 'END' && bb->typ == 'END') {
	 USED(aa); USED(bb);
	 tt->mode= 0;
	 RET(StrDup("## END ##"));
      }
      
      if (aa->typ == 'X' && bb->typ == 'X') {
	 USED(aa); USED(bb);
	 return 1;
      }

      cmp= strcmp(aa->nam, bb->nam);
      if (o_basic == 'B') {
	 // Normal symmetrical basic sync

	 if (cmp == 0) {
	    // Same name, both will be consumed
	    char clash[20];
	    
	    USED(aa); USED(bb);
	    if (aa->typ == 'F' && bb->typ == 'F') {
	       if (str_ne(aa->szt, bb->szt)) {
		  if (starts_with(aa->mug, "SYMLNK/") ||
		      starts_with(bb->mug, "SYMLNK/")) {
		     // Can't select by mtime, so offer both possibilities
		     RET3(StrDup("### CHOOSE:"),
			  C_CP(aa, bb),
			  comm_C_CP(bb, aa));
		  }
		  if (aa->tim < bb->tim) SWAP;
		  RET(C_CP(aa, bb));
	       }
	       if (str_ne(aa->mug, bb->mug)) {
		  RET3(StrDup("### CHOOSE:"),
		       C_CHMUG(aa, bb),
		       comm_C_CHMUG(bb, aa));
	       }
	       return 1;
	    }
	    if (aa->typ == 'D' && bb->typ == 'D') {
	       if (str_ne(aa->mug, bb->mug)) {
		  RET3(StrDup("### CHOOSE:"),
		       C_CHMUG(aa, bb),
		       comm_C_CHMUG(bb, aa));
	       }
	       return 1;
	    }
	    if (aa->typ == 'D' && bb->typ == 'F') {
	       // Remote file becomes a clash file; cheat slightly and
	       // leave 'aa' directory to be handled on next loop
	       // through; it will appear as a new directory, which will
	       // result in it being copied over to 'bb'
	       aa->used= 0;
	       GEN_CLASH_EXT;
	       RET3(StrDup("### CLASH"),
		    C_REN_CLASH(bb),     // Rename to clash file
		    C_CP_CLASH(bb, aa)); // Copy clash file back
	    }
	    if (aa->typ == 'F' && bb->typ == 'D') {
	       // Remote directory becomes a clash directory
	       GEN_CLASH_EXT;
	       smtlin_addext(bb, clash);
	       tt->mode= "CX";   	      // Copy 'bb' to 'aa'
	       RET4(StrDup("### CLASH"),
		    C_REN_CLASH_DIR(bb), // Rename to clash directory
		    C_MKDIR(bb, aa),     // Create clash directory on this side
		    C_CP(aa, bb));       // Copy file the other way
	    }
	    DIE;
	 }
	 
	 if (cmp > 0) {
	    // 'bb' comes first in sort order, so swap and handle below
	    SWAP;
	 }
	 
	 // 'aa' comes first in sort order.  Copy over new directory/file
	 USED(aa);
	 if (aa->typ == 'D') {
	    tt->mode= swap ? "CX" : "cx";
	    RET(C_MKDIR(aa, bb));
	 }
	 if (aa->typ == 'F') {
	    RET(C_CP(aa, bb));
	 }
	 DIE;
      } else if (o_basic == 'S' || o_basic == 'M') {
	 // Asymmetrical basic sync, local as SLAVE or MASTER
	 if (o_basic == 'M') { cmp= -cmp; SWAP; }	// 'aa' is slave from now on

	 if (cmp == 0) {
	    // Same name, both will be consumed
	    USED(aa); USED(bb);
	    if (aa->typ == 'F' && bb->typ == 'F') {
	       if (str_ne(aa->szt, bb->szt)) {
		  if (!(starts_with(aa->mug, "SYMLNK/") ||
			starts_with(bb->mug, "SYMLNK/")) &&
		      aa->tim > bb->tim) {
		     RET2(StrDup("### WARNING: newer file on slave will be overwritten:"),
			  C_CP(bb, aa));
		  }
		  RET(C_CP(bb, aa));
	       }
	       if (str_ne(aa->mug, bb->mug)) {
		  RET(C_CHMUG(bb, aa));
	       }
	       return 1;
	    }
	    if (aa->typ == 'D' && bb->typ == 'D') {
	       if (str_ne(aa->mug, bb->mug)) {
		  RET(C_CHMUG(bb, aa));
	       }
	       return 1;
	    }
	    if (aa->typ == 'D' && bb->typ == 'F') {
	       // Overwrite local directory with remote file
	       tt->mode= swap ? "SX" : "sx";   	      // Skip 'aa'
	       RET2(C_RM(aa),
		    C_CP(bb, aa));
	    }
	    if (aa->typ == 'F' && bb->typ == 'D') {
	       // Remote directory overwrites local file
	       tt->mode= swap ? "cx" : "CX";   	      // Copy 'bb' to 'aa'
	       RET2(C_RM(aa),
		    C_MKDIR(bb, aa));    // Create directory on this side
	    }
	    DIE;
	 }
	 
	 // File/dir on 'bb' (master) not present on slave: copy over
	 if (cmp > 0) {
	    USED(bb);
	    if (bb->typ == 'D') {
	       tt->mode= swap ? "cx" : "CX";   	      // Copy 'bb' to 'aa'
	       RET(C_MKDIR(bb, aa));    // Create directory on this side
	    }
	    if (bb->typ == 'F') {
	       RET(C_CP(bb, aa));
	    }
	    DIE;
	 }
	 
	 // File/dir on 'aa' (slave) not present on master: delete it
	 USED(aa);
	 if (aa->typ == 'D') {
	    tt->mode= swap ? "SX" : "sx";
	    RET(C_RM(aa));
	 }
	 if (aa->typ == 'F') {
	    RET(C_RM(aa));
	 }
	 DIE;
      } 
      DIE;
   }

   if (isupper(mode)) {
      // We need to work on 'bb', so swap over
      SWAP;
      mode= tolower(mode);
   }

   // Note that both 's' and 'c' consume elements right up to BUT NOT
   // INCLUDING the 'X' that would close this directory.  If we are
   // working through a directory that occurs only on one machine, we
   // can then use mode 'x' to consume this 'X'.  Otherwise we can
   // drop back to mode 0 and consume the two 'X's in parallel.  

   if (mode == 'x') {
      // Swallow the final 'X'
      USED(aa);
      if (aa->typ != 'X') DIE;
      tt->mode++; return 1;
   }

   if (mode == 's') {
      // Skip 'aa' directory
      if (aa->typ == 'X' && tt->lev == 0) { tt->mode++; return 1; }
      USED(aa);
      if (aa->typ == 'D') tt->lev++;
      if (aa->typ == 'X') tt->lev--;
      return 1;
   }

   if (mode == 'c') {
      // Copy 'aa' directory to 'bb'
      if (aa->typ == 'X' && tt->lev == 0) { tt->mode++; return 1; }
      USED(aa); 
      if (aa->typ == 'F') {
	 RET(C_CP(aa, bb));
      }
      if (aa->typ == 'D') {
         tt->lev++;
	 RET(C_MKDIR(aa, bb));
      }
      if (aa->typ == 'X') {
         tt->lev--;
	 return 1;
      }
      DIE;
   }

   DIE;
}
   
void 
smcomp_del(SMComp *tt) {
   free(tt->out);
   free(tt->out2);
   free(tt->out3);
   free(tt->out4);
   smtlin_del(&tt->aa);
   smtlin_del(&tt->bb);
   free(tt);
}

#endif

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