/*---------------------------------------------------------------------------*/
/* Command: SetSize							     */
/* Author: James E. Cooper Jr.						     */
/* Change History:							     */
/*  Date    Person	  Action					     */
/* -------  ------------- -----------------				     */
/* 14AUG91  Jim Cooper	  Initial Creation				     */
/*									     */
/* Notes:								     */
/*   2.0 specific program to set the size of a file.  Calls DOS SetFileSize  */
/*   routine.  Useful for hacking the ends off of XModem'd files, when you   */
/*   forgot to turn Auto-Chop on.					     */
/*---------------------------------------------------------------------------*/

#include <exec/types.h>
#include <exec/execbase.h>
#include <exec/memory.h>
#include <dos/dosextens.h>
#include <dos/rdargs.h>

#include <string.h>

#include <proto/exec.h>
#include <proto/dos.h>

#include "setsize_rev.h"

#define DOSLIB	"dos.library"
#define DOSVER	36L			    /* We require AT LEAST V36 of OS */

#define EXECBASE (*(struct ExecBase **)4)
#define THISPROC   ((struct Process *)(EXECBASE->ThisTask))
#define Result2(x) THISPROC->pr_Result2 = x


#define MSG_SETSIZE_FAILED "SetSize failed"

#define TEMPLATE  "FILE/A/M,SIZE/K/N,ALL/S" VERSTAG
#define OPT_FILE  0
#define OPT_SIZE  1
#define OPT_CTRLZ 2
#define OPT_ALL   3
#define OPT_COUNT 4

#define CTRL_Z '\x1A'

int cmd_setsize(void)
{
   struct Library *SysBase = (*((struct Library **) 4));
   struct DosLibrary *DOSBase;
   long temprc, rc, i, size;
   long opts[OPT_COUNT];
   struct RDargs *rdargs;
   struct AnchorPath __aligned ua;
   BPTR oldlock, curlock, fh;
   char fname[32];
   char *curarg, **argptr, buf;

   /*------------------------------------------------------------------------*/
   /* Set up the default return code.					     */
   /*------------------------------------------------------------------------*/
   rc = RETURN_FAIL;

   /*------------------------------------------------------------------------*/
   /* And open the dos library for our use.				     */
   /*------------------------------------------------------------------------*/
   if ((DOSBase = (struct DosLibrary *)OpenLibrary(DOSLIB, DOSVER)))
   {
      /*---------------------------------------------------------------------*/
      /* Clear the options array, just to be on the safe side...	     */
      /*---------------------------------------------------------------------*/
      memset((char *)opts, 0, sizeof(opts));

      /*---------------------------------------------------------------------*/
      /* Parse the command line.					     */
      /*---------------------------------------------------------------------*/
      rdargs = ReadArgs(TEMPLATE, opts, NULL);

      /*---------------------------------------------------------------------*/
      /* If there was an error parsing, print an error message and get out!  */
      /*---------------------------------------------------------------------*/
      if (rdargs == NULL)
      {
	 PrintFault(IoErr(), NULL);
      }
      else
      {
	 /*------------------------------------------------------------------*/
	 /* Now we have to set up the pattern match structure.		     */
	 /*------------------------------------------------------------------*/
	 /* Set the flag which tells the pattern matcher to decode wild      */
	 /* cards.							     */
	 /*------------------------------------------------------------------*/
	 ua.ap_Flags = APF_DOWILD;

	 /*------------------------------------------------------------------*/
	 /* Tell the matcher to stop if the user presses Ctrl-C.	     */
	 /*------------------------------------------------------------------*/
	 ua.ap_BreakBits = SIGBREAKF_CTRL_C;

	 /*------------------------------------------------------------------*/
	 /* Initialize for MultiArgs handling...			     */
	 /*------------------------------------------------------------------*/
	 argptr = (char **)opts[OPT_FILE];

	 /*------------------------------------------------------------------*/
	 /* The following while loop handles the MultiArgs spec.	     */
	 /*------------------------------------------------------------------*/
	 while (curarg = *argptr++)
	 {
	    /*---------------------------------------------------------------*/
	    /* Clear our UserAnchor structure to all zeros before going to   */
	    /* the next step.						     */
	    /*---------------------------------------------------------------*/
	    memset(&ua, 0, sizeof(struct AnchorPath));

	    /*---------------------------------------------------------------*/
	    /* Set up a default message leader. 			     */
	    /*---------------------------------------------------------------*/
	    strcpy(fname, MSG_SETSIZE_FAILED);

	    /*---------------------------------------------------------------*/
	    /* Finally, call the matcher.				     */
	    /*---------------------------------------------------------------*/
	    MatchFirst(curarg, &ua);
	    temprc = IoErr();

	    /*---------------------------------------------------------------*/
	    /* Process all matches returned.  If 'MatchFirst()' returned an  */
	    /* error, the 'while()' let's us skip all this and fall through  */
	    /* the exit code.						     */
	    /*---------------------------------------------------------------*/
	    while (temprc == 0)
	    {
	       strcpy(fname, ua.ap_Info.fib_FileName);

	       if (ua.ap_Info.fib_DirEntryType > 0 && opts[OPT_ALL])
	       {
		  /*---------------------------------------------------------*/
		  /* The flag APF_DIDDIR tells us that we are 'backing out'  */
		  /* of a subdirectory... in other words, we have processed  */
		  /* all sub-files of that directory and we are moving back  */
		  /* to its parent.  If this flag is set, we need to clear   */
		  /* it.						     */
		  /*---------------------------------------------------------*/
		  if (!(ua.ap_Flags & APF_DIDDIR))
		  {
		     /*------------------------------------------------------*/
		     /* If we are resizing ALL files, tell the matcher to    */
		     /* enter this directory.				     */
		     /*------------------------------------------------------*/
		     ua.ap_Flags |= APF_DODIR;
		  }
		  ua.ap_Flags &= ~APF_DIDDIR;
	       }

	       /*------------------------------------------------------------*/
	       /* If we have an error OTHER than that we are out of matches, */
	       /* we need to exit IMMEDIATELY!				     */
	       /*------------------------------------------------------------*/
	       if (temprc && (temprc != ERROR_NO_MORE_ENTRIES))
	       {
		  break;
	       }

	       /*------------------------------------------------------------*/
	       /* Set us to the directory this file lives in.		     */
	       /*------------------------------------------------------------*/
	       curlock = DupLock(ua.ap_Current->an_Lock);
	       oldlock = CurrentDir(curlock);

	       /*------------------------------------------------------------*/
	       /* We must open the file for SetFileSize() to work.           */
	       /*------------------------------------------------------------*/
	       if ((fh = Open(fname, MODE_OLDFILE)) == NULL)
	       {
		  temprc = IoErr();
		  CurrentDir(oldlock);
		  UnLock(curlock);
		  break;
	       }

	       /*------------------------------------------------------------*/
	       /* Now, we're ready to set the new size.  If the user didn't  */
	       /* give us a SIZE, we will assume he simply wants us to strip */
	       /* ^Z's from the end of the file.                             */
	       /*------------------------------------------------------------*/
	       if (opts[OPT_SIZE] == NULL)
	       {
		  /*---------------------------------------------------------*/
		  /* Find the beginning of the ^Z padding at the end of the  */
		  /* file.						     */
		  /*---------------------------------------------------------*/
		  Seek(fh, 0, OFFSET_END);
		  Seek(fh, -1, OFFSET_CURRENT);
		  while (Read(fh, &buf, 1))
		  {
		     if (buf != CTRL_Z)
		     {
			break;
		     }
		     Seek(fh, -2, OFFSET_CURRENT);
		  }
		  size = Seek(fh, 0, OFFSET_CURRENT);
	       }
	       else
	       {
		  /*---------------------------------------------------------*/
		  /* O.K., so they gave us a size, let's use it!             */
		  /*---------------------------------------------------------*/
		  size = *((long *)(opts[OPT_SIZE]));
	       }

	       /*------------------------------------------------------------*/
	       /* Tell DOS to set the size.				     */
	       /*------------------------------------------------------------*/
	       if ((i = SetFileSize(fh, size, OFFSET_BEGINNING)) == -1)
	       {
		  temprc = IoErr();
	       }

	       /*------------------------------------------------------------*/
	       /* Close the file (whether it worked or not!).                */
	       /*------------------------------------------------------------*/
	       Close(fh);

	       /*------------------------------------------------------------*/
	       /* Now change the directory back to where it was when we got  */
	       /* started.						     */
	       /*------------------------------------------------------------*/
	       CurrentDir(oldlock);
	       UnLock(curlock);

	       if (i == -1)
	       {
		  /*---------------------------------------------------------*/
		  /* If 'SetFileSize()' returned an error, we have to get    */
		  /* out and tell the user about it!			     */
		  /*---------------------------------------------------------*/
		  break;
	       }

	       /*------------------------------------------------------------*/
	       /* Now get the next file which matches the given pattern.     */
	       /*------------------------------------------------------------*/
	       MatchNext(&ua);
	       temprc = IoErr();
	    }

	    /*---------------------------------------------------------------*/
	    /* If the only error we had was that we ran out of files to      */
	    /* resize, we are doing fine!  Set the return code so the user   */
	    /* knows everything went well.				     */
	    /*---------------------------------------------------------------*/
	    if (temprc == ERROR_NO_MORE_ENTRIES)
	    {
	       rc = RETURN_OK;
	    }
	    else
	    {
	       PrintFault(temprc,fname);

	       /*------------------------------------------------------------*/
	       /* If the user hit Ctrl-C, we have to warn him that he is     */
	       /* being rude!						     */
	       /*------------------------------------------------------------*/
	       if (temprc == ERROR_BREAK)
	       {
		  rc = RETURN_WARN;
	       }
	       else
	       {
		  /*---------------------------------------------------------*/
		  /* If there was any other error, FAIL!!!		     */
		  /*---------------------------------------------------------*/
		  rc = RETURN_FAIL;
	       }
	    }
	    /*---------------------------------------------------------------*/
	    /* We have to clean up after ourselves.			     */
	    /*---------------------------------------------------------------*/
	    MatchEnd(&ua);
	 }
	 FreeArgs(rdargs);
      }
      CloseLibrary((struct Library *)DOSBase);
   }
   else
   {
      /*---------------------------------------------------------------------*/
      /* They tried to run us under the wrong version of the OS.  We need to */
      /* exit gracefully, yet still let them know what went wrong...	     */
      /*---------------------------------------------------------------------*/
      Result2(ERROR_INVALID_RESIDENT_LIBRARY);
   }
   return(rc);
}
