/* **************************************************************************

Programma ..... Index.c - V1.0i
Autore ........ Edwin Hoogerbeets
Software ...... Aztec C V3.6a
Hardware ...... Amiga 512K, Kickstart V1.2/V1.3
Funzione ...... Riconoscere tipi di files
Sintassi ...... File nomefile [nomefile...]
Compilazione .. cc +L File, ln file -lc32

************************************************************************* */

#include <exec/types.h>
#include <exec/memory.h>
#include <ctype.h>
#include <libraries/dosextens.h>
#include <stdio.h>

#define toint(a) (int)((a) - '0')
#define BLOCKSIZE 484L              /* DImensione di almeno un blocco file */
#define FIBSIZE (long) sizeof(struct FileInfoBlock)

typedef struct asdf {
  int  length;
  char *name;
  char *pattern;
} pattern;

/* Maschere riconoscimento files binari con numero magico iniziale */
pattern bmagic[] = {
  4,"Amiga load file",                "\x00\x00\x03\xf3",
  4,"Amiga object file",              "\x00\x00\x03\xe7",
  2,"Amiga run-time library",         "\xec\x62",
  2,"Manx 3.6 run-time library",      "\x61\x6a",
  2,"Manx 3.6 object file",           "\x41\x4a",
  2,"Manx 3.4 object file",           "\x6a\x67",
  27,"TeX device independent output file",
                                      "\xf7\x02\x01\x83\x92\xc0"
                                      "\x1c\x3b\x00\x00\x00\x00"
                                      "\x03\xe8\x1b\x20\x54\x65"
                                      "\x58\x20\x6f\x75\x74\x70"
                                      "\x75\x74\x20",
  2,"Amiga icon .info file",          "\xe3\x10",
  4,"Amiga .info file",               "\xf3\x4c\x00\x12",
  2,"Amiga .font file",               "\x0f\x00",
  2,"SEA ARC compressed archive",     "\x1a\x08",
  NULL, NULL, NULL,
};

/* Per files ASCII inizianti con un numero magico */

pattern amagic[] = {
  2, "shell script file",             "#!",
  2, "ARexx script file",             "/*",
  4, "execute script file",           ".key",
  4, "execute script file",           ".bra",
  4, "execute script file",           ".ket",
  NULL, NULL, NULL,
};

/* Maschere di ricerca interna a files ASCII */
pattern asearch[] = {
  14,"LaTeX source code",            "\\documentstyle",
  6, "TeX source code",              "\n\\",
  4, "C source code",                "int ",
  5, "C source code",                "\n#inc",
  5, "C source code",                "\n#def",
  2, "C source code",                "{\n",
  21,"Modula II source code",        "IMPLEMENTATION MODULE",
  17,"Modula II source code",        "DEFINITION MODULE",
  5, "execute script file",          "\n.key",
  5, "execute script file",          "\n.bra",
  5, "execute script file",          "\n.ket",
  1, "yacc input file",              "\n%TOKEN",
  1, "yacc or lex input file",       "\n%%",
  1, "shell commands",               "\nalias",
  1, "shell commands",               "\nAlias",
  1, "shell commands",               "\nset",
  1, "commands text",                "\nrun",
  1, "commands text",                "\nRun",
  1, "uuencoded file",               "\nbegin ",
  NULL, NULL, NULL,
};

pattern IFFforms[] = {
  4,"IFF interleave bit map file",        "ILBM",
  4,"IFF Amiga compressed bit map files", "ACBM",
  4,"IFF anim format file",               "ANIM",
  4,"IFF instrument file",                "8SVX",
  4,"IFF simple music file",              "SMUS",
  NULL, NULL, NULL,
};

pattern compress = {
  2,"block compressed %d bit code data",  "\x1f\x9d",
};

pattern zoo = {
  4,"Zoo archive",                        "ZOO ",
};

int type(ty,name)
char *ty,*name;
{
  printf("%-16s %s\n",name,ty);
}

int memncmp(a,b,length)
char *a, *b;
int length;
{
  register int index;

  for ( index = 0; index < length; index++ ) {
    if ( a[index] != b[index] ) {
      return(a[index] > b[index] ? -1 : 1);
    }
  }

  return(0);
}

char *strrpbrk(str, charset)
register char *str, *charset;
{
  register char *temp;
  extern char *index();

  temp = str + strlen(str) - 1;

  while ( temp != (str - 1)  && !index(charset, *temp) )
    --temp;

  return( (temp != (str - 1)) ? temp : NULL);
}

char *basename(buf)
register char *buf;
{
  register char *foo = strrpbrk(buf,":/");

  return( foo ? (foo + 1) : buf );
}

usage(name)
char *name;
{
  printf("usage: %s file [file ...]\n",name);
  exit(-1);
}

extern char *AllocMem();
extern struct FileLock *Lock();

/* Riconosce se si tratta di un file o di una directory */
filetype(myname,filename)
char *myname, *filename;
{
  struct FileLock *lock;
  struct FileInfoBlock *fib;

  if ( lock = Lock(filename,ACCESS_READ) ) {
    if ( fib = (struct FileInfoBlock *) AllocMem(FIBSIZE,MEMF_CLEAR) ) {
      Examine(lock,fib);

      dofile(myname,filename,fib);

      UnLock(lock);
      FreeMem(fib,FIBSIZE);

    } else {
      UnLock(lock);
      fprintf(stderr,"%s: not enough memory!\n",myname,filename);
      exit(-1);
    }
  } else {
    fprintf(stderr,"%s: could not access file %s\n",myname,filename);
  }
}

/* Scopre che tipo di nome file è */
int dofile(myname,filename,fib)
char *myname, *filename;
struct FileInfoBlock *fib;
{
  register char *f = &fib->fib_FileName[0];

  if ( fib->fib_DirEntryType > 0 ) {

    type("directory",f);

  } else if ( fib->fib_Size == 0 ) {

    type("empty",f);

  } else {
    char *buf;
    long filehandle;

    if ( !(filehandle = Open(filename,MODE_OLDFILE)) ) {
      fprintf(stderr,"%s: could not open file %s\n",myname,filename);
      return(0);
    }

    if ( !(buf = AllocMem(BLOCKSIZE+1,MEMF_PUBLIC)) ) {
      fprintf(stderr,"%s: not enough memory\n",myname);
      Close(filehandle);
      return(0);
    }

    if ( !Read(filehandle,buf,BLOCKSIZE) ) {
      fprintf(stderr,"%s: read error on file %s\n",myname,f);
      FreeMem(buf,BLOCKSIZE+1);
      Close(filehandle);
      return(0);
    }

    matchtype(myname,buf,f,fib);

    Close(filehandle);
    FreeMem(buf,BLOCKSIZE+1);
  }

  return(0);
}

int isasciifile(buf,len)
char *buf;
int len;
{
  register int index, flag = 1;

  for ( index = 0; index < len ; index++ ) {
    if ( !isprint(buf[index]) && !isspace(buf[index]) ) {
      flag = 0;
      break;
    }
  }

  return(flag);
}

matchtype(myname,buf,file, fib)
char *myname, *buf, *file;
struct FileInfoBlock *fib;
{
  register int index;
  int len = (fib->fib_Size < BLOCKSIZE) ? fib->fib_Size : BLOCKSIZE;

  if ( isasciifile(buf,len) ) {

    index = 0;

    /* Scopre numeri magici */
    while ( asearch[index].length ) {
      if ( search(asearch[index].pattern,buf,len) ) {
        type(asearch[index].name,file);
        return(0);
      }
      ++index;
    }

    index = 0;

    /* Verifica numeri magici */
    while ( amagic[index].length ) {
      if ( !memncmp(amagic[index].pattern,buf,amagic[index].length) ) {
        type(amagic[index].name,file);
        return(0);
      }
      ++index;
    }

    /* Verifica bit di scrittura */
    if ( fib->fib_Protection & (1<<6) ) {
      type("script file",file);
      return(0);
    }

    type("text",file);

  } else {

    index = 0;

    /* Verifica numeri magici */
    while ( bmagic[index].length ) {
      if ( !memncmp(bmagic[index].pattern,buf,bmagic[index].length) ) {
        type(bmagic[index].name,file);
        return(0);
      }
      ++index;
    }

    /* Verifica formati IFF, assumendo che vi sia un FORM iniziale */
    if ( !memncmp("FORM",buf,4) ) {
      char buffer[40];

      index = 0;
      buffer[0] = '\0';

      while ( IFFforms[index].length ) {
        if ( !memncmp(IFFforms[index].pattern,&buf[8],IFFforms[index].length) ) {
          type(IFFforms[index].name,file);
          return(0);
        }
        ++index;
      }
      sprintf(buffer,"IFF form %c%c%c%c",buf[8],buf[9],buf[10],buf[11]);
      type(buffer,file);
      return(0);
    }

    if ( !memncmp(compress.pattern,buf,compress.length) ) {
      char buffer[40];

      sprintf(buffer,compress.name,buf[2]&0x0f);
      type(buffer,file);
      return(0);
    }

    if ( !memncmp(zoo.pattern,buf,zoo.length) ) {

      /* Crea una stringa da archivio ZOO x.xx\0 per stampa */
      buf[16] = '\0';

      type(buf,file);
      return(0);
    }

    type("data",file);
  }
}

int search(pat, text, len)
char *pat, *text;
register int len;
{
  register int index = 0, patlen = strlen(pat);

  while ( memncmp(pat,&text[index],patlen) && index < len ) {
    index++;
  }

  return(index < len ? 1 : 0);
}

void main(argc,argv)
int argc; char **argv;
{
  register int index;
  register char *myname;

  /* if there are no arguments, take from the stdin */
  if ( argc <= 1 ) {
    usage(argv[0]);
  }

  myname = basename(argv[0]);

  for ( index = 1 ; index < argc ; index++ ) {
    filetype(myname,argv[index]);
  }
}

