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

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

#ifdef HEADER

typedef struct SMXcomp SMXcomp;
struct SMXcomp {
   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"


//
//	SMXcomp
//

SMXcomp *
smxcomp(char **inp1, char **inp2) {
   SMXcomp *tt= ALLOC(SMXcomp);

   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_CP_CLASH(src,dst)       StrDupF("%scp\t%s\t%s.%s\t%s", to_##dst, src->mug, src->path, clash, src->szt)

#define C_RM(dst)        	  StrDupF("%srm\t%s", to_##dst, dst->path)
#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 
smxcomp_do(SMXcomp *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 (cmp == 0) {
         // Same name, both will be consumed
	 char comb[5], flags[3], ch;
	 char clash[20];
	 char *tr= 0;
	 char *fl= flags;

         USED(aa); USED(bb);
         if (aa->typ == 'F' && bb->typ == 'F') {
            if (str_ne(aa->szt, bb->szt)) 
	       tr= "FC";
	    else if (str_ne(aa->mug, bb->mug)) 
               tr= "FM";
	    else 
	       tr= "FI";
         } else if (aa->typ == 'D' && bb->typ == 'D') {
            if (str_ne(aa->mug, bb->mug)) 
	       tr= "DM";
	    else 
	       tr= "DI";
         } else if (aa->typ == 'D' && bb->typ == 'F') {
	    SWAP;
	    tr= "FD";
	 } else if (aa->typ == 'F' && bb->typ == 'D') {
	    tr= "FD";
         }
	 if (!tr) DIE;

	 // Do mapping to reduce options in select (see in docs: "How
	 // clashes and combinations of changes are handled")
	 sprintf(fl, "%c%c", aa->flag, bb->flag);
	 switch (enc_cc(fl)) {
	  case '__': fl= "nn"; break;
	  case 'uu': fl= "nn"; break;
	  case 'dd': return 1;		// No action
	  case '_n': fl= "_u"; break;
	  case 'dn': fl= "du"; break;
	  case 'nu': fl= "nn"; break;
	  case 'n_': fl= "u_"; break;
	  case 'nd': fl= "ud"; break;
	  case 'un': fl= "nn"; break;
	 }

	 // Choose action
	 sprintf(comb, "%s%s", tr, fl);
	 while (1) {
	    switch (enc_cc(comb)) {
	     case 'FCnn': // clash
	     case 'FMnn': // clash
		// The remote machine has the clash
		if (swap) SWAP;
		GEN_CLASH_EXT;
		RET4(StrDup("### CLASH"),
		     C_REN_CLASH(bb), 	 // Rename to clash file
		     C_CP_CLASH(bb, aa), // Copy clash to this side
		     C_CP(aa, bb)); 	 // Copy non-clash file over

	     case 'FC_u': // copy bb->aa
		RET(C_CP(bb, aa));
		
	     case 'FM_u': // chmug aa from bb's details
	     case 'DM_u': // chmug aa from bb's details
		// For DM??, enter the directories still in mode 0
		RET(C_CHMUG(bb, aa));

	     case 'FInn': // no action
	     case 'FI_u': // impossible, no action
	     case 'DInn': // no action
	     case 'DI_u': // impossible, no action
		// For DI??, enter the directories still in mode 0
		return 1;
		
	     case 'FCdu': // file on bb becomes a clash file on both
	     case 'FIdu': // file on bb becomes a clash file on both
	     case 'FMdu': // file on bb becomes a clash file on both
		GEN_CLASH_EXT;
		RET3(StrDup("### CLASH"),
		     C_REN_CLASH(bb),	  // Rename to clash file
		     C_CP_CLASH(bb, aa)); // Copy clash to other side
		
	     case 'FC_d': // delete aa
	     case 'FM_d': // delete aa
	     case 'FI_d': // delete aa
		RET(C_RM(aa));

	     case 'DM_d': // delete aa
	     case 'DI_d': // delete aa
		tt->mode= "sS";		// Skip contents of both directories
		RET(C_RM(aa));
		
	     case 'DMdu': // directory on bb becomes a clash directory on both
	     case 'DIdu': // directory on bb becomes a clash directory on both
		GEN_CLASH_EXT;
		smtlin_addext(bb, clash);
		tt->mode= swap ? "Sc" : "sC";	// Skip 'aa'; copy 'bb' to 'aa'
		RET3(StrDup("### CLASH"),
		     C_REN_CLASH_DIR(bb), // Rename to clash directory
		     C_MKDIR(bb, aa));	  // Create clash directory on this side
		
	     case 'DMnn': // clash
		// Directory on remote machine ('bb') becomes the
		// clash directory; we cheat slightly and only handle
		// 'bb' here.  We leave the 'aa' directory unhandled,
		// and let it be picked up afterwards.  It appears to
		// the code then as if it were a new directory to be
		// copied over -- exactly what we want to happen.
		if (swap) SWAP;
		aa->used= 0;		// Leave 'aa' directory for later
		GEN_CLASH_EXT;
		smtlin_addext(bb, clash);
		tt->mode= swap ? "cx" : "CX";	// Copy 'bb' to 'aa'
		RET3(StrDup("### CLASH"),
		     C_REN_CLASH_DIR(bb), // Rename to clash directory
		     C_MKDIR(bb, aa));	  // Create clash directory on other side
		
	     case 'FDnn': // clash
		if (swap) {
		   SWAP;
		   // File on 'bb' is the clash; 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
		}

		// The directory on 'bb' is the clash
		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

	     case 'FD_u': // copy bb->aa; new directory replaces file
		tt->mode= swap ? "cx" : "CX";
		RET2(C_RM(aa),		// Remove existing file
		     C_MKDIR(bb, aa));	// Create directory to fill

	     case 'FDdu': // copy bb->aa
		tt->mode= swap ? "cx" : "CX";
		RET(C_MKDIR(bb, aa));	// Create directory to fill
		
	     case 'FD_d': // impossible, treat as clash (copy aa->bb as clash file)
		GEN_CLASH_EXT;
		tt->mode= swap ? "sx" : "SX";
		RET3(StrDup("### CLASH"),
		     C_REN_CLASH(aa),
		     C_CP_CLASH(aa, bb));
		
	     case 'FDu_': // copy aa->bb; new file replaces directory
		tt->mode= swap ? "sx" : "SX";
		RET2(C_RM(bb),
		     C_CP(aa, bb));

	     case 'FDud': // copy aa->bb	
		tt->mode= swap ? "sx" : "SX";
		RET(C_CP(aa, bb));
		
	     case 'FDd_': // impossible, treat as clash (copy bb->aa as clash dir)
                GEN_CLASH_EXT;
                smtlin_addext(bb, clash);
                tt->mode= swap ? "cx" : "CX";
                RET3(StrDup("### CLASH"),
                     C_REN_CLASH_DIR(bb), // Rename to clash directory
                     C_MKDIR(bb, aa));    // Create clash directory on other side
 	    }
	    
	    // No match -- try swapping over aa<->bb to see if we get a match that way
	    if (swap) error("Impossible: no match for '%s' in smxcomp_do()", comb);
	    SWAP;
	    ch= comb[2]; comb[2]= comb[3]; comb[3]= ch;
	 }
      }
 
      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 or
      // file, unless it has a 'deleted' flag
      USED(aa);
      if (aa->typ == 'D') {
	 if (aa->flag == 'd') {
	    tt->mode= swap ? "SX" : "sx";
	    return 1;
 	 } else {
	    tt->mode= swap ? "CX" : "cx";
	    RET(C_MKDIR(aa, bb));
	 }
      }
      if (aa->typ == 'F') {
	 if (aa->flag == 'd')
	    return 1;
	 else 
	    RET(C_CP(aa, bb));
      }
      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 
smxcomp_del(SMXcomp *tt) {
   free(tt->out);
   free(tt->out2);
   free(tt->out3);
   free(tt->out4);
   smtlin_del(&tt->aa);
   smtlin_del(&tt->bb);
   free(tt);
}

#endif

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