/*
 * Name:		uniq.c
 * Author:		Oliver Kaufmann
 * 
 * created:		?
 * last modified:	10.6.1994
 * Version:		$VER: uniq 1.1
 * 
 * Doc:			uniq.doc
 * 
 */

#include <stdlib.h>
#include <stdio.h>
#include <dos/dos.h>
#include <functions.h>
#include <string.h>

void CloseAll(char *s);
char *makename(char *s, int blen, int elen, char **tb, char **tex, char *buffer);
char *modifyname(char *tb, int blen, int elen, long sernum, int origlen);
char *savestring(char *s);
struct tbl *feedtbl(char *s, struct tbl *firsttbl, int type);
struct tbl *lookup(char *t, struct tbl *firsttbl, struct tbl *excludetbl);
struct tbl *modifytbl(char *s, char *t, struct tbl *thistbl);
struct tbl *inserttbl(char *s, char *t, int type,  struct tbl *firsttbl);
struct tbl *uniq(struct tbl *thistbl, struct tbl *firsttbl,int blen, int elen);
struct tbl *makeuniq(struct tbl *firsttbl, int blen, int elen);
void uniqthislevel(struct tbl *thisdir, char *origpathname, char *newpathname);
void giveout(struct tbl *firsttbl, char *lformat, int allflag, int quoteflag);
struct tbl *freetbl(struct tbl *thistbl);


char ver[] = "$VER: uniq 1.1 (10.6.1994) © by Oliver Kaufmann";
char *usage = "uniq [<options>]\n\
          -d[<dir>] : use contents of <dir> as input, otherwise use stdin\n\
                 -a : print all names, even those which do not change\n\
                 -f : process files only, no dirs (requires -d option)\n\
                 -q : enclose output in quotes (rename \"%s\" \"%s\")\n\
                 -r : recurse sub-dirs (requires -d option)\n\
        -l<lformat> : use <lformat> for output (default rename %s %s)\n\
      -m[<mformat>] : enable & print <mformat> for dirs (default makedir %s)\n\
        -p<pattern> : pattern to apply (default #?)\n\
 -b<basenamelength> : max. length of the uniqified string (default 8)\n\
-e<extensionlength> : max. length of the uniqified extension (default 3)";


struct tbl {
	char *orig;
	char *new;
	int type;
	struct tbl *nexttbl;
};

struct MyAnchorPath {
	struct AChain	*ap_Base;	/* pointer to first anchor */
	struct AChain	*ap_Last;	/* pointer to last anchor */
	LONG	ap_BreakBits;		/* Bits we want to break on */
	LONG	ap_FoundBreak;		/* Bits we broke on. Also returns ERROR_BREAK */
	BYTE	ap_Flags;		/* New use for extra word. */
	BYTE	ap_Reserved;
	WORD	ap_Strlen;		/* This is what ap_Length used to be */
	struct	FileInfoBlock ap_Info;
	UBYTE	ap_Buf[1];		/* Buffer for path name, allocated by user */
};


#define BUFLEN 256
char pbuf[BUFLEN];
char str1[BUFLEN];
char str2[BUFLEN];

int level = 0;
int quoteflag = 0;
int mformflag = 0;
int allfilendirs = 1;
int allflag  = 0;
int recursflag = 0;
int blen=8;
int elen=3;
char *lformat = "rename %s %s";
char *mformat = "makedir %s";
char *pattern="#?";

void main(int argc, char *argv[])
{
   struct tbl localtbl;
   struct tbl *firsttbl=NULL;
   char lbuf[BUFLEN];
   char *dirname=NULL;

   char *s, *opt;
   int i, res;

   for(i=1; i<argc; i++) {
       if( *(opt=argv[i]) == '-') {
          switch(opt[1]) {
             case 'd' : dirname = &opt[2]; break;
             case 'a' : allflag      = 1 ; break;
             case 'r' : recursflag   = 1 ; break;
             case 'q' : quoteflag    = 1 ; break;
             case 'f' : allfilendirs = 0 ; break;
             case 'l' : lformat = &opt[2]; break;
             case 'p' : pattern = &opt[2]; break;
             case 'm' : mformflag    = 1;
                        if( opt[2] != 0 )
                           mformat = &opt[2];
                        break;
             case 'b' : if( (blen = atoi(&opt[2]) ) <= 0)
                           CloseAll("basenamelength must be > 0");
                        break;
             case 'e' : if( (elen = atoi(&opt[2])) < 0)
                           CloseAll("extensionlength must be >= 0");
                        break;
             default  : printf("unknown option: ignored\n");
                        break;
          }
       } else if( *opt == '?')
          CloseAll(usage);
   }

   if(dirname != NULL) {
      localtbl.orig = dirname;
      localtbl.new = "";
      localtbl.nexttbl = NULL;
      uniqthislevel(&localtbl,dirname,"");
   } else {
      if( ParsePattern(pattern, pbuf, BUFLEN) == -1)
         CloseAll("pattern too long");
      while( (res=scanf("%s",lbuf)) != EOF) {
         if(res==0)
            CloseAll("res = 0");
         if(MatchPattern(pbuf, lbuf))                      /* nur wenn pattern matched */
            firsttbl = feedtbl(lbuf, firsttbl, 0);
      }
      if(firsttbl != NULL)
         if( (firsttbl=makeuniq(firsttbl, blen, elen)) == NULL)
            CloseAll("unable to uniqify filetbl");
      giveout(firsttbl, lformat, allflag, quoteflag);
   }

   CloseAll(NULL);
}

#define PATHNAMELEN 256
void uniqthislevel(struct tbl *thisdir, char *origpathname, char *newpathname)
{
   struct FileInfoBlock *fib;
   struct AnchorPath *ap;
   int result, emptyflag = 1;
   char origpath[PATHNAMELEN];               /* store full orig path */
   char newpath[PATHNAMELEN];                /* store full new  path */
   struct tbl *looptbl=NULL;
   struct tbl *firsttbl=NULL;
   BPTR lock, oldlock;

   if(thisdir == NULL) {
      return;
   }

   level += 1;

   if( (ap=(struct AnchorPath *)malloc(sizeof(struct MyAnchorPath)) ) == NULL)
      CloseAll("no memory for MyAnchorPath");

   memset(ap,0,sizeof(struct MyAnchorPath)-1);              /* crucial ! struct must be 0ed */
                                                            /* took me 2 h to find out */
   if( (lock=Lock(thisdir->orig,ACCESS_READ)) == 0)
       CloseAll("no lock on dir");
   oldlock = CurrentDir(lock);
   
   ap->ap_Strlen = 0;
   ap->ap_BreakBits = 0;
   ap->ap_FoundBreak = 0;

   result = MatchFirst(pattern, ap);                        /* pattern global ! */
   if ( result == 0 ) {
      do { 
         emptyflag = 0;
         fib = &ap->ap_Info;
         firsttbl = feedtbl(fib->fib_FileName, firsttbl, fib->fib_DirEntryType);
      } while ( !(result = MatchNext(ap)) );
   }

   if(result != ERROR_NO_MORE_ENTRIES)
      CloseAll("MatchFirst: other error");

    MatchEnd(ap);
    free(ap); 

   if(firsttbl != NULL) {
      if( (firsttbl=makeuniq(firsttbl, blen, elen)) == NULL)
         CloseAll("unable to uniqify filetbl");
   }

   looptbl = firsttbl;
   while (looptbl != NULL) {
      strcpy(origpath,origpathname);
      if( !AddPart(origpath, looptbl->orig, PATHNAMELEN) )
         CloseAll("path to long");
      strcpy(newpath, newpathname);
      if( !AddPart(newpath, looptbl->new, PATHNAMELEN) )
         CloseAll("path to long");

      if(mformflag && looptbl->type >= 0) {                 /* directory special output */
         *str1 = 0;
         if(quoteflag) strcat(str1,"\"");
         strcat(str1,newpath);
         if(quoteflag) strcat(str1,"\"");
         printf(mformat, str1);
         printf("\n");
      }
      if( allfilendirs || looptbl->type < 0) {              /* file output */
         if( allflag || strcmp(looptbl->orig, looptbl->new) ) {
            *str1 = 0;
            if(quoteflag) strcat(str1,"\"");
            strcat(str1,origpath);
            if(quoteflag) strcat(str1,"\"");
            *str2 = 0;
            if(quoteflag) strcat(str2,"\"");
            strcat(str2,newpath);
            if(quoteflag) strcat(str2,"\"");
            printf(lformat ,str1, str2);
            printf("\n");
         }
      }
      if(recursflag && looptbl->type >= 0) {
         uniqthislevel(looptbl, origpath, newpath);
      }
      
      looptbl = looptbl->nexttbl;
   }

   CurrentDir(oldlock);
   UnLock(lock);
   freetbl(firsttbl);					/* free memory */
   level -= 1;
}



struct tbl *makeuniq(struct tbl *firsttbl, int blen, int elen)
{
   struct tbl *thistbl;

   thistbl = firsttbl;
   while(thistbl != NULL) {
      if( uniq(thistbl, firsttbl, blen, elen) == NULL)
         return NULL;
      thistbl = thistbl->nexttbl;
   }
   return firsttbl;
}

struct tbl *uniq(struct tbl *thistbl, struct tbl *firsttbl,int blen, int elen)
{
   char lbuf[BUFLEN];
   char buffer[BUFLEN];

   char *t, *tb, *tex;
   char *s;

   long sernum;
   int origlen;

   s = thistbl->orig;
   makename(s, blen, elen, &tb, &tex, buffer);              /* name und extension */
   origlen = strlen(tb);                                    /* base name length */

   for(sernum = 0; ; sernum++) { 
      strcpy(lbuf,tb);
      strcat(lbuf,tex);
      if( lookup(lbuf, firsttbl, thistbl) == NULL )
         break;
      if( modifyname(tb, blen, elen, sernum, origlen) == 0)
         return NULL;
   }

   t = savestring(lbuf);

   modifytbl(s, t, thistbl);

   return thistbl;
}

struct tbl *inserttbl(char *s, char *t, int type,  struct tbl *firsttbl)
{
   struct tbl *thistbl;

   if( (thistbl=malloc(sizeof(struct tbl))) == NULL)
      CloseAll("no memory for tbl");

   thistbl->type = type;
   thistbl->orig = s;
   thistbl->new = t;
   thistbl->nexttbl = firsttbl;
   return thistbl;

}

struct tbl *modifytbl(char *s, char *t, struct tbl *thistbl)
{
   thistbl->orig = s;
   thistbl->new = t;
   return thistbl;
}

struct tbl *feedtbl(char *s, struct tbl *firsttbl, int type)
{
   char *m;

   m = savestring(s);
   return inserttbl(m, m, type, firsttbl);
}

char *savestring(char *s)
{
   char *mem;

   if( (mem=malloc(strlen(s)+1)) == NULL)
      CloseAll("no memory for string");

   strcpy(mem, s);
   return mem;
}

struct tbl *lookup(char *t, struct tbl *firsttbl, struct tbl *excludetbl)
{
   while(firsttbl != NULL) {
      if( (strcmp(t, firsttbl->new) == 0) && (firsttbl != excludetbl) )
         break;
      firsttbl = firsttbl->nexttbl;
   }
   return firsttbl;
}


char *modifyname(char *tb, int blen, int elen, long sernum, int origlen)
{
   char lbuf[BUFLEN];
   int sl, i, j, pos;

   sprintf(lbuf,"%x",sernum);
   if( (sl = strlen(lbuf)) > blen )      /* sernum zu groß */
      return NULL;

   pos = origlen;
   if( origlen + sl > blen )
      pos = blen-sl;
   strncpy(tb+pos, lbuf, sl);
   return tb;
}

char *makename(char *s, int blen, int elen, char **tb, char **tex, char *buffer)
{
   char *t;
   int n,m;
   int sl,i;

   *tb = buffer;

   for(n=0, t=s ; (*t != '\0') && (*t != '.') ; *t++, n++);

   strncpy(buffer, s, n);             /* es wird evtl mehr kopiert */

   while(n<blen)
      buffer[n++] = '\0';              /* auffüllen */

   buffer[blen] = '\0';               /* rest abschneiden */

   n = blen+1;                        /* ab hier extension */

   buffer[n] = '\0';                  /* default ist leer */

   *tex = &buffer[n];

   if(elen != 0 ) {
      
      for(i = sl = strlen(s)-1; sl >= 0; sl--)
         if(s[sl] == '.')
            break;
      if(s[sl] == '.') {
         buffer[n++] = '.';
         strncpy(&buffer[n], &s[sl+1], i-sl+1); /* 0 mitkopieren */
         n += i-sl;
      }

      buffer[n] = '\0';                  /* default ist leer */
      buffer[blen+elen+1+1]='\0';

   }
      
   return *tb;

}

void giveout(struct tbl *firsttbl, char *lformat, int allflag, int quoteflag)
{
   while (firsttbl != NULL) {
      if( allflag || strcmp(firsttbl->orig, firsttbl->new) ) {
         *str1 = 0;
         if(quoteflag) strcat(str1,"\"");
         strcat(str1,firsttbl->orig);
         if(quoteflag) strcat(str1,"\"");
         *str2 = 0;
         if(quoteflag) strcat(str2,"\"");
         strcat(str2,firsttbl->new);
         if(quoteflag) strcat(str2,"\"");
         printf(lformat, str1, str2);
         printf("\n");
      }
      firsttbl = firsttbl->nexttbl;
   }
}


struct tbl *freetbl(struct tbl *thistbl)
{
   struct tbl *tmptbl;
   
   while(thistbl != NULL) {
      if(thistbl->orig != NULL)
         free(thistbl->orig);
      if( (thistbl->new != NULL) && (thistbl->new != thistbl->orig) )
         free(thistbl->new);
      tmptbl = thistbl;
      thistbl = thistbl->nexttbl;
      free(tmptbl);
   }
   return NULL;
}

void CloseAll(char *s)
{
   if(s != NULL)
      printf("%s\n",s);
   exit(0);
}
