/* $Revision Header *** Header built automatically - do not edit! ***********
 *
 *	(C) Copyright 1993 by Rainer Koppler
 *
 *	Name .....: APCNames.c
 *	Created ..: Friday 5-Mar-93 19:31:41
 *	Revision .: 6
 *
 *	Date        Author                 Comment
 *	=========   ====================   ====================
 *	07-Dec-93   Rainer Koppler         enhanced encoding of long names
 *	23-Sep-93   Rainer Koppler         "S:APCNames.types" support added
 *	09-Sep-93   Rainer Koppler         Symantec C++ support added
 *	15-Jul-93   Rainer Koppler         MS-Word support added
 *	23-Mar-93   Rainer Koppler         Bug fix: multiple periods handled
 *      17-Mar-93   Rainer Koppler         Bug fix: disk entry only at root level
 *	05-Mar-93   Rainer Koppler         Created this file!
 *
 ****************************************************************************
 *
 *      Utility for creating original file names for MS-DOS file names
 *      from the APCINFO.DAT file created by the Mac tool AccessPC.
 *
 * $Revision Header ********************************************************/

#include "APCNames.h"
#include "Types.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#ifndef AMIGA
extern int strnicmp (char *, char *, int);
extern int stricmp (char *, char *);
#endif

/*-------------------------------------------------------------------
**  SetPattern/NextEntry are my own Amiga-specific pattern-matching
**   routines. I suppose there is no need for them for UNIX... :^)
**-------------------------------------------------------------------
*/

#ifdef AMIGA
#include "Wildcard.h"
#else
#ifndef SEEK_SET
#define	SEEK_SET	0
#define	SEEK_CUR	1
#define	SEEK_END	2
#endif
#ifndef EXIT_FAILURE
#define EXIT_FAILURE 1
#endif
static char *fnDummy;
static void  SetPattern (char *fileName)
{ 
  fnDummy = fileName;
}
static char *NextEntry (void)
{  
  char *fn = fnDummy;
  fnDummy = NULL;
  return fn;
}
#endif


/*-------------------------------------------------------------------
**  Each APCINFO.DAT file consists of several records denoting
**  disk or file entries. Each record has a length of 80 bytes.
**  The first record always denotes a disk entry, all subsequent
**  records are file entries.
**  After each 12 entries there are 64 bytes with undefined contents.
**-------------------------------------------------------------------
*/

#define APC_ENTRY_SIZE  sizeof(union APCEntry)
#define APC_PAD_SIZE    0x40

union APCEntry {

  UBYTE    ae_Data[80];

  struct DiskEntry {
    UBYTE  de_Length;        /*length of disk name*/
    UBYTE  de_Pad[79];       /*disk name and other data*/
  } de;

  struct FileEntry {
    UBYTE  fe_Length;        /*length of file name*/
    char   fe_Name[31];      /*file name characters*/
    ULONG  fe_Type;          /*Macintosh type ID*/
    ULONG  fe_Creator;       /*Macintosh creator ID*/
    UBYTE  fe_Pad1[23];      /*undocumented data*/
    UBYTE  fe_Flags;         /*file visible <=> 0 (?)*/
    char   fe_MSDOSName[8];  /*file name padded with blanks*/
    char   fe_MSDOSExt[3];   /*extension padded with blanks*/
    UBYTE  fe_Pad2[5];       /*always zero*/
  } fe;

};


/*----------------------------- global data ---------------------------------*/

static FILE *apcInfoDat;
static union APCEntry *entry,*tstEntry;
static char lngName[32],shtName[13],*sptr;

static const char versionStr[] = {
  "\0$VER: APCNames 1.2 by Rainer Koppler (7.12.93)"
};


/*---------------- disk entry for empty disk (view by name) -----------------*/

static ULONG de_Empty[] = {
  0x0A526169, 0x6E657220, 0x31001600, 0x0002A002,
  0x02000000, 0x00000000, 0x00000000, 0x00000001,
  0x002E0008, 0x00EA019C, 0x01000070, 0x02480200,
  0xFFF8FFF0, 0x00000000, 0x00008000, 0x00000002,
  0x7FA95F49, 0x6E736967, 0x6E696100, 0x00000000
};

/*------------------------- universal file entry ----------------------------*/

static ULONG fe_Default[] = {
  0x00000000, 0x00000000, 0x00000000, 0x00000000,
  0x000D0D90, 0x0000000F, 0x000000E5, 0x000D0D90,
  0x00000000, 0x00000000, 0x0100FFFF, 0xFFFF0000, /* TYPE, CREATOR, ... */
  0x00000000, 0x00000000, 0x00000000, 0x00000000,
  0x00000000, 0x00000000, 0x00000000, 0x00000000
};


/*---------------------------------------------------------------------------*/

static void Pas2CStr (UBYTE *ps, char *cs)
{
 UBYTE len = *ps;

 strncpy(cs,(char *)ps+1,len);
 *(cs+len) = '\0';
}

/*---------------------------------------------------------------------------*/

static void C2PasStr (char *cs, UBYTE *ps)
{
 UBYTE len = strlen(cs);

 strcpy((char *)ps+1,cs);
 *ps = len;
}

/*---------------------------------------------------------------------------*/

static void Long2Short (char *fileName)
{
  char *dotPtr,*lptr,ch,orgCh;
  struct TCInfo *tci;
  int i,stubIdx,state;
  LONG fp;

/*--- copy entry template to "entry" and insert TYPE/CREATOR ---*/
  memcpy(entry,fe_Default,APC_ENTRY_SIZE);
  tci = TCInfoFor(fileName);
  entry->fe.fe_Type = tci->tci_Type;
  entry->fe.fe_Creator =tci->tci_Creator;
/*--- insert original file name ---*/
  C2PasStr(fileName,(UBYTE *)entry);
/*--- 1. encode original file name to "shtName" (MS-DOS format) ---*/
  for(i=0,sptr=shtName,lptr=fileName; i<8 && *lptr!='\0' && *lptr!='.';
      sptr++,lptr++,i++)
    *sptr = toupper(*lptr);
  stubIdx = lptr-fileName; /* where the stub character would be placed */
  if(stubIdx == 8) stubIdx--;
  dotPtr = strchr(fileName,'.');
  if(dotPtr) {
    for(; i<8; i++,sptr++) *sptr = ' ';
    for(lptr=dotPtr+1; i<11 && *lptr!='\0'; lptr++)
      if(*lptr != '.') {
        *sptr = toupper(*lptr);
        sptr++;
        i++;
      }
  }
  for(; i<11; i++,sptr++) *sptr = ' ';
/*--- 2. seek existing entry with same MS-DOS name & modify new name if so ---*/
  fseek(apcInfoDat,0L,SEEK_SET);
  state = 0;
  orgCh = shtName[stubIdx];
  while(fread(tstEntry,APC_ENTRY_SIZE,1,apcInfoDat) > 0) {
    if(!strncmp(tstEntry->fe.fe_MSDOSName,shtName,11)) {
      do {
        if(state == 0)
          ch = '&';
        else if(state>=1 && state<=10)
          ch = '0'+state-1;
        else if(state>=11 && state<=36)
          ch = 'A'+state-11;
        else {
          printf("*** Encoding possibilities exceeded. Try to rename some files.\n");
          exit(EXIT_FAILURE);
        }
        state++;
      } while(ch == orgCh);
      shtName[stubIdx] = ch;
    }
    fp = ftell(apcInfoDat);
    if(fp % 0x0400 == 0x03C0)
      fread(tstEntry,APC_PAD_SIZE,1,apcInfoDat);
  }
/*--- 3. insert MS-DOS file name into entry & prepare "shtName" for rename ---*/
  for(i=0,sptr=shtName,lptr=entry->fe.fe_MSDOSName; i<11; i++,sptr++,lptr++) 
    *lptr = *sptr;
  for(i=0,sptr=shtName,lptr=entry->fe.fe_MSDOSName; i<11; i++) {
    if(i==8 && dotPtr && *(dotPtr+1)) {
      *sptr = '.';
      sptr++;
    }
    if(*lptr != ' ') {
      *sptr = *lptr;
      sptr++;
    }
    lptr++;
  }
  *sptr = 0;
}

/*---------------------------------------------------------------------------*/

static void Short2Long (void)
{
  int i;
  char *ptr;

  if(entry->fe.fe_Length > 31) {
    *shtName = '\0'; *lngName = '\0';
    return;
  }
  Pas2CStr((UBYTE *)entry,lngName);
  sptr = shtName;
  ptr = entry->fe.fe_MSDOSName;
  for(i=0; i<8 && *ptr!=' '; sptr++,ptr++,i++)
    *sptr = *ptr;
  ptr = entry->fe.fe_MSDOSExt;
  if(*ptr != ' ') {
    *sptr = '.';
    sptr++;
  }
  for(i=0; i<3 && *ptr!=' '; sptr++,ptr++,i++)
    *sptr=*ptr;
  *sptr = '\0';
}

/*---------------------------------------------------------------------------*/

static BOOL EntryExistsFor (char *fileName)
{
  BOOL fnd = FALSE;
  LONG fp;

  fseek(apcInfoDat,0L,SEEK_SET);
  while(!fnd && fread(entry,APC_ENTRY_SIZE,1,apcInfoDat) > 0) {
    Pas2CStr((UBYTE *)entry,lngName);
    if(!(fnd = !stricmp(fileName,lngName))) {
      fp = ftell(apcInfoDat);
      if(fp % 0x0400 == 0x03C0)
        fread(entry,APC_PAD_SIZE,1,apcInfoDat);
    }
  }
  return(fnd);
}

/*---------------------------------------------------------------------------*/

main (int argc, char *argv[])
{
  int cnt,fnCnt,total,cIdx;
  char *fn,cmdBuf[80];
  LONG fp;
  BOOL err = FALSE,cpyFiles = FALSE;

  printf("\n»»APCNames«« - AccessPC renaming utility v1.2 for UNIX\n\n");

  if(--argc > 0) {
    err = argc==1 && strcmp(argv[1],"-x") ;
    if(!err) err = argc==2 && strncmp(argv[1],"-c",2);
    if(!err) err = argc>2 && strncmp(argv[1],"-c",2) && (strcmp(argv[1],"-x") &&
      strncmp(argv[2],"-c",2));
    cpyFiles = !strcmp(argv[1],"-x");
  }

  if(err) {
    printf("USAGE: %s [ -x ][ -c[<config>] <file> { <file> }]\n",argv[0]);
    exit(EXIT_FAILURE);
  }

  if(!(entry=malloc(APC_ENTRY_SIZE*2))) {
    printf("*** No memory for entry blocks.\n");
    exit(EXIT_FAILURE);
  }
  tstEntry = entry+APC_ENTRY_SIZE;

  fnCnt = 0;
  total = 0;

/*--- transform long names to short names and create/update APCINFO file ---*/
  if(argc > 1) {
    cIdx = strcmp(argv[1],"-x") ? 1 : 2;
    if(!(apcInfoDat=fopen("APCINFO.DAT","a+"))) {
      printf("*** Cannot open APCINFO.DAT for update.\n");
      exit(EXIT_FAILURE);
    }
  /*--- if new file then create (empty) disk entry at beginning ---*/
    fseek(apcInfoDat,0L,SEEK_END);
    if(ftell(apcInfoDat) == 0)
      fwrite(de_Empty,1,APC_ENTRY_SIZE,apcInfoDat);
  /*--- configuration file specified ? ---*/
    if(strlen(argv[cIdx]) > 2)
      ReadTypes(argv[cIdx]+2);
    else
      ReadTypes(DEFAULT_CONFIG);
  /*--- process file patterns ---*/
    for(cnt=cIdx+1; cnt<=argc; cnt++) {
      SetPattern(argv[cnt]);
      while(fn = NextEntry()) {
      /*--- lookup APCINFO.DAT for existing entry ---*/
        if(!EntryExistsFor(fn)) {
          fseek(apcInfoDat,0L,SEEK_END);
          fp = ftell(apcInfoDat);
          if(fp % 0x0400 == 0x03C0) {
            memset(tstEntry,0,APC_PAD_SIZE);
            fwrite(tstEntry,APC_PAD_SIZE,1,apcInfoDat);
          }       
          Long2Short(fn);
          fwrite(entry,APC_ENTRY_SIZE,1,apcInfoDat);
        }
        else {
          printf("\"%s\" already exists, ",fn);
          Short2Long();
        } 
        if(cpyFiles) {
          printf("writing \"%s\" as \"%s\" to DOS disk...",fn,shtName);
          sprintf(cmdBuf,"mwrite %s %s",fn,shtName);
          if(!system(cmdBuf)) {
            printf("ok\n");
            fnCnt++;
          }
          else
            printf("failed\n");
        }
        else {
          printf("renaming \"%s\" to \"%s\"...",fn,shtName);
          if(!rename(fn,shtName)) {
            printf("ok\n");
            fnCnt++;
          }
          else
            printf("failed\n");
        }
        total++;
      }
    }
    fclose(apcInfoDat);
    printf("\n%d file(s) of %d renamed successfully.\n",fnCnt,total);
    if(cpyFiles && system("mwrite APCINFO.DAT APCINFO.DAT"))
      printf("*** Cannot write APCINFO.DAT to DOS disk.\n");
  }
/*--- get contents of APCINFO file and transform short names to long names ---*/
  else {
    if(cpyFiles && system("mread APCINFO.DAT APCINFO.DAT"))
      printf("*** Cannot access APCINFO.DAT on DOS disk.\n");
    if(!(apcInfoDat=fopen("APCINFO.DAT","r"))) {
      printf("*** Cannot open APCINFO.DAT for input.\n");
      exit(EXIT_FAILURE);
    }
    cnt = 0;
    while(fread(entry,APC_ENTRY_SIZE,1,apcInfoDat) > 0) {
    /*--- D I S K  E N T R Y ---*/
      if(entry->fe.fe_MSDOSName[1] == '©') {
        Pas2CStr((UBYTE *)entry,lngName);
        printf("Disk name: %s\n\n",lngName);
      }
    /*--- F I L E  E N T R Y ---*/
      else {
      /*--- create MS-DOS name and original name of entry data ---*/
        Short2Long();
      /*--- rename only if valid file entry ---*/
        if(entry->fe.fe_Type == TYPE_TEXT || entry->fe.fe_Type == TYPE_WDBN) {
          if(cpyFiles) {
            printf("reading \"%s\" as \"%s\" from DOS disk...",lngName,shtName);
            sprintf(cmdBuf,"mread %s %s",shtName,lngName);
            if(!system(cmdBuf)) {
              printf("ok\n");
              fnCnt++;
            }
            else
              printf("failed\n");
          }
          else {
            printf("renaming \"%s\" to \"%s\"...",shtName,lngName);
            if(!rename(shtName,lngName)) {
              printf("ok\n");
              fnCnt++;
            }
            else
              printf("failed\n");
          }
          total++;
        }
        else
          printf("ignoring \"%s\" (\"%s\")\n",shtName,lngName);
      }
      cnt++;
    /*--- skip 64 bytes after each 12 records ---*/
      if(cnt == 12) {
        fread(entry,APC_PAD_SIZE,1,apcInfoDat);
        cnt = 0;
      }
    }
    fclose(apcInfoDat);
    printf("\n%d file(s) of %d renamed successfully.\n",fnCnt,total);
  }
  FreeTypes();

  free(entry);
}
