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

//
//      SMTLin -- unpacked tree-line or change-line representation
//
//
//	SMTLin *aa= ALLOC(SMTLin);
//	
// 	  Must start off zero'd
//
//	smtlin_decode(aa, &line);
//
//	  Decode a line into the structure; 'line' should be a pointer
//	  to a variable pointing to a StrDup'd string and this
//	  variable will be zero'd if the SMTLin structure takes over
//	  responsibility for freeing the string.
//
//	aa->*;		
//
// 	  Access structure members
//
//	aa->path;	
//
//	  For tree-lines, aa->path maintains the current pathname,
//	  which is base-relative, but always starts with a slash.
//	  After an 'X' or 'END', this contains a filename intended to
//	  sort higher than all other filenames in that directory
//	  ("\xFF").  For change-lines, aa->path is the path loaded
//	  from the file, with slashes instead of TABs.
//
//	aa->lev;
//
//	  Nesting level is by +0 for the 'D' starting a directory,
//	  then +1 for all files/directories inside, and also for the
//	  final 'X', before returning to +0 for the next item in the
//	  parent directory.  Level 0 is the top-level directory.
//
//	smtlin_addext(aa, ext);
//
//	  In certain cases we want to pretend that this directory name
//	  has an extra extension added (for clashes)
//
//	smtlin_del(aa);
//	free(aa);
//
//	  Structure may be used to process many lines.  Before finally
//	  freeing, clean up internal allocations with smtlin_del().
//
//
//	p= smtlin_out(aa, typ, ...);
//
//	  An SMTLin can also be used to keep track of the current
//	  output pathname (in aa->path) whilst outputting tree lines;
//	  returns a StrDup'd string.
//
//	cmp= pathcmp(p1, p2);
//
//	  Compare paths; result is -2,-1,0,1,2; see comments below
//


#ifdef HEADER

typedef struct SMTLin SMTLin;
struct SMTLin {
   int used;                    // Has this one been used yet?
   char *freeme;                // Pointer used to free line from memory
   int typ;                     // Char-const encoded type: 'D', 'F', 'X', 'END', or 0 for eof
   int flag;                    // Flag: one of [_nud] for: no-flag, 'n', 'u', 'd'
   int lev;			// D/X nesting level
   char *tf;			// Type+flag string
   char *mug;                   // Mode/user/group
   char *nam;                   // File-name/dir-name
   char *path;			// Full pathname of file/dir (base-relative) 
   char *szt;                   // Size/mtime
   time_t tim;                  // Mtime (or link hash for symlinks)
   char *apath;			// Allocated memory for path
   int ap_tail;			// Index of start of last segment in path
   int ap_max;			// Size of allocated memory for 'path'
};

#define isEOF(str) (str && str[0] == '\x1A')
#define SMEOF (smeof)

#else

#include "all.h"

// ^Z used as internal EOF marker; could be any string, but check isEOF() above
char smeof[]= "\x1A";

static void 
makespace(SMTLin *a, int len) {
   if (!a->apath) 
      a->apath= Alloc(a->ap_max= 1024);

   while (a->ap_tail + len > a->ap_max) {
      char *tmp= Alloc(a->ap_max *= 2);
      memcpy(tmp, a->apath, a->ap_tail);
      free(a->apath); a->apath= tmp;
   }
}

// Add to path (for entering directories)
void 
pathadd(SMTLin *a, char *nam) {
   int len= strlen(nam);
   makespace(a, len + 2);
   sprintf(a->apath + a->ap_tail, "/%s", nam);
   a->ap_tail += len+1;
   a->path= a->apath;
}   

// Append to path, but don't change ap_tail (for changing filename)
void 
pathapp(SMTLin *a, char *nam) {
   int len= strlen(nam);
   makespace(a, len + 2);
   sprintf(a->apath + a->ap_tail, "/%s", nam);
   a->path= a->apath;
}   

// Remove final pathname segment (for exiting directory), leaving a
// marker-file of "\xFF"
void 
pathdelM(SMTLin *a) {
   char *p, *q;
   makespace(a, 2);		// Get apath allocated if not already
   p= q= a->apath + a->ap_tail; 
   *p= 0;
   while (p > a->apath && *p != '/') p--;
   a->ap_tail= p-a->apath;
   strcpy(q, "/\xFF");
}

// Remove final pathname segment (for exiting directory)
void 
pathdel(SMTLin *a) {
   char *p;
   makespace(a, 2);		// Get apath allocated if not already
   p= a->apath + a->ap_tail; 
   *p= 0;
   while (p > a->apath && *p != '/') p--;
   *p= 0;
   a->ap_tail= p-a->apath;
}


//
//      Decode input line
//

void
smtlin_decode(SMTLin *a, char **strp) {
   char *str, *p;
   char *typ;
   char *flag;
   int ch;

   // Update the level from the previous line
   switch (a->typ) {
    case 'D': a->lev++; break;
    case 'X': a->lev--; break;
   }

   // Clear the structure
   free(a->freeme);
   a->freeme= 0;
   a->used= 0;
   a->typ= 0;
   a->flag= '_';
   a->mug= a->nam= a->szt= a->tf= flag= "";
   a->tim= 0;

   // EOF, don't consume input, return a->typ == 0
   if (isEOF(*strp)) return;

   // Copy value from variable
   a->freeme= str= p= *strp; *strp= 0;
   if (!str) DIE;

   // Take a temp copy to scratch to use for error messages
   char *tmp= StrT(str, 0);

   a->tf= typ= p;
   if ((p= strchr(p, '\t'))) {
      *p++= 0; a->mug= p;
      if ((p= strchr(p, '\t'))) {
         *p++= 0; a->nam= p;
         if ((p= strchr(p, '\t'))) {
            *p++= 0; a->szt= p;
         }
      }
   }

   a->typ= enc_cc(typ);
   switch (ch= enc_cc(typ+1)) {
    case 'n':
    case 'u':
    case 'd':
       a->flag= ch;
       a->typ= typ[0];
   }

   if (!(a->typ == 'F' ||
	 a->typ == 'D' ||
	 a->typ == 'X' ||
	 a->typ == 'END'))
      error("Bad type on line:\n  %s", tmp);

   if (a->typ == 'F') {
      if (!(a->mug[0] && 
	    a->nam[0] && 
	    a->szt[0]))
         error("Bad file tree-line:\n  %s", tmp);
   }
   if (a->typ == 'D') {
      if (!(a->mug[0] && 
	    a->nam[0]))
         error("Bad directory tree-line:\n  %s", tmp);
   }

   if ((p= strchr(a->szt, '/'))) {
      char *end;
      p++; if (*p == '#') p++;
      a->tim= strtol(p, &end, 16);
      if (*end) 
	 error("Bad modification time '%s' on tree-line:\n  %s", p, tmp);
   }

   // Update path
   if (a->typ == 'F') {
      pathapp(a, a->nam);
   } else if (a->typ == 'D') {
      pathadd(a, a->nam);
   } else if (a->typ == 'X' || a->typ == 'END') {
      pathdelM(a);
      a->nam= "\xFF";
   }
   a->path= a->apath;
}

void 
smtlin_addext(SMTLin *a, char *ext) {
   int len= strlen(ext);
   makespace(a, len + 2);
   sprintf(a->apath + a->ap_tail, ".%s", ext);
   a->ap_tail += len+1;
   a->path= a->apath;
}   

void
smtlin_del(SMTLin *a) {
   free(a->freeme);
   free(a->apath);
}

char *
smtlin_out(SMTLin *a, char *typ, ...) {
   va_list(ap);
   va_start(ap, typ);

   if (str_eq(typ, "F")) {
      char *mug= va_arg(ap, char*);
      char *nam= va_arg(ap, char*);
      char *szt= va_arg(ap, char*);
      return StrDupT(typ, mug, nam, szt, 0);
   }
   if (str_eq(typ, "D")) {
      char *mug= va_arg(ap, char*);
      char *nam= va_arg(ap, char*);
      pathadd(a, nam);
      return StrDupT(typ, mug, nam, 0);
   }
   if (str_eq(typ, "X")) {
      pathdelM(a);
      return StrDupT(typ, 0);
   }
   if (str_eq(typ, "END")) {
      return StrDupT(typ, 0);
   }
   DIE;
}
   

//////// This code not in use
//  // Compare paths, making sure that '/' compares before all other
//  // characters.  Return value is:
//  //   -2 first before second, different directory
//  //   -1 first before second, same directory
//  //   0 same path
//  //   1 second before first, same directory
//  //   2 second before first, different directory
//  
//  int 
//  pathcmp(char *p1, char *p2) {
//     unsigned char *p, *q;
//     int a, b, cmp;
//  
//     p= p1; q= p2;
//     while (1) {
//        if ((a= *p++) == '/') a= 1;	// Make '/' sort before all other characters except NUL
//        if ((b= *q++) == '/') b= 1;
//        if (a == b) {
//  	 if (a) continue;
//  	 break;
//        }
//        cmp= (a < b) ? -1 : 1;
//        p--; q--;
//        while ((a= *p++)) if (a == '/') return cmp * 2;
//        while ((b= *q++)) if (b == '/') return cmp * 2;
//        return cmp;
//     }
//     return 0;
//  }

#endif

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