/****************************************************************/
/* "ArcLead" - Copyright (hah!) 1990 by Richard Lawrence        */
/*                                                              */
/* Don't forget! Call the Hallucination! 703-425-5824, thousands*/
/* of files, huge networked conferences, technical info, more!  */
/*                                                              */
/* You may use, abuse, distribute, smell, and otherwise look at */
/* this source code as you see fit.                             */
/*   Purpose: To ease the task of arc'ing (or lharc'ing, or     */
/* whatever you want to call it) a bunch of files in a common   */
/* directory that are in several parts but have a common root.  */
/* For instance, you might have bunch of picture files: each one*/
/* has a picture and an icon. It's a pain to type all those arc */
/* command lines seperately...so I whipped this up.             */
/*                                                              */
/*   Format: ArcLead SRC: DST: EXT command                      */
/*                                                              */
/* Where:                                                       */
/*        SRC: is the source device/directory                   */
/*        DST: is the destination device/directory              */
/*        EXT is the arc extension                              */
/*        command is the arc command line of the arc utility    */
/*                                                              */
/* Note: It is assumed you want a "." before your arc extender. */
/* ArcLead   will provide it.                                   */
/*                                                              */
/* Here's an example:                                           */
/* 1> ArcLead   df1: ram: lzh lharc a                           */
/*                                                              */
/* This would compile a disk full of pictures, with their icons */
/* (and anything else that started with the same root) into a   */
/* bunch of seperate .lzh files in RAM:                         */
/*                                                              */
/* Revision history:                                            */
/*  03/08/90 - Thought up and hacked out program in early       */
/*             morning. Should have been studying.              */
/*  03/09/90 - Fixed bug so that no duplicates in resulting     */
/*             script will be found.                            */
/*  03/10/90 - Spring break begins.                             */
/*  03/11/90 - Started extensive rewrite that ends on 3/12/90   */
/*             allowed program to launch archiver directly,     */
/*             improved root selection logic, and other stuff.  */
/*  03/??/90 - Made little odd and ends improvements. Removed   */
/*             problem with locked directories. Tested out.     */
/*  03/19/90 - Replaced forkv() with Execute(). Executable size */
/*             went from 7280 to 4304. Hmmmmm.                  */
/*  03/19/90 - Dropped Lattice Ctrl-C processing and replaced   */
/*  03/20/90 - Replaced Ctrl-C handling, again. Followed Rich   */
/*             Krehbiels hint on using SIGBREAKF_CTRL_C.        */
/*             Added hard coded processing for Zoo's abberant   */
/*             wildcards (which are now transparent to the user */
/*             under ArcLead, just use as you would normally).  */
/*             The same night, just because I'm morbig, I worked*/
/*             for hours to get ARC working correctly. Result is*/
/*             that ArcLead can use Arc to archive any AmigaDOS */
/*             file regardless of length of filename.           */
/*                                                              */
/* I compiled thus: LC -cfist -Lt -O -v ArcLead  .c             */
/****************************************************************/



/* There's nothing mysterious about Proto functions. Just include the   */
/* the .h files that contain Amiga commands you are using and you'll be */
/* all set. Executable size goes down and compatibility goes up.        */

#include <string.h>
#include <exec/types.h>
#include <proto/dos.h>
#include <proto/exec.h>
#include <exec/memory.h>
#include <dos.h>
#include <libraries/dosextens.h>

int CXBRK(VOID) {return(0);} /*Disables Lattice Cntrl-C routine*/

#define MAXLINES 5000

/****************************************************************/
/* Pretty standard stuff here. All global,saves space,headaches,*/
/* and helps when you're being lazy.                            */
/* lock         : a BPTR to the lock that AmigaDOS returns      */
/* scriptfile   : BPTR to file opened to contain scripts for ARC*/
/* task         : struct TASK pointer for Ctrl-C check          */
/* nextfile     : a struct FILEINFO, used for next file in dir  */
/* i,j,k,l      : Integer loop variables and assorted           */
/* nlines       : Number of directory files in SRC:             */
/* use_arc      : True/False for whether utility chosen is ARC  */
/* ptrn[]       : Source directory/path with pattern match      */
/* cmd_ln[]     : Most often holds the line to be Executed      */
/* rt_file      : Most often the "root" file for comparison     */
/* foo_file     : Used when renaming (for arc) to foob??        */
/* real_name    : When renaming for Arc, the real name of foob  */
/* cmd_ln2     : Holds the original command line               */
/* ext[]        : the extender to be added to resultant arcs    */
/* *list        : Pointer to the allocated area for next filenam*/
/* held[]       : Root name "held" for comparison to following  */
/* arc_line     : The arc-utility command and parameters        */
/* *lineptr[]   : Array of pointers to file names in SRC:       */
/****************************************************************/

BPTR    lock,scriptfile;
struct  Task            *task;
struct  FileInfoBlock   *nextfile;
int     i,j,k,l,num_ren=1,nlines=0,use_arc;
char    ptrn[80],cmd_ln[256],rt_file[256],foo_file[256],real_name[256],cmd_ln2[256],
        ext[10]=".",*list,held[256],src[256],arc_line[256],foo2[256],
        *lineptr[MAXLINES],ch;


/* By routing all exits though quit, I'm sure that I'm freeing  */
/* the proper amount of memory. */

void quit(int i,char *message)
{
    Write(Output(),message,(long) strlen(message));
    if (nextfile)
        FreeMem(nextfile,sizeof(struct FileInfoBlock));
    for(j=0;j<nlines;j++)
        FreeMem(lineptr[j],strlen(lineptr[j]));
    exit(i);
}

/* This little routine is used when ArcLead is fronting for     */
/* ARC. It just take an integer, FRED, and adds it on to the end*/
/* of the string "foob" contained in foo_file. Thus, when       */
/* passed a 10, parse returns foob10 in foo_file                */


void parse(int fred)
{
    strcpy(foo2,"foob");
    for(k=1;k<fred+1;k=k*10)
    {
        l=strlen(foo2);
        foo2[l]=fred/k+48;                               
        foo2[l+1]=0;
    }
    strcpy(foo_file,src);
    strcat(foo_file,foo2);
    strcpy(real_name,src);
}


/************************  MAIN  ****************************/

void main(argc,argv)
 int  argc;
 char *argv[];
{
    use_arc=(!(strcmp(argv[4],"arc")) || !(strcmp(argv[4],"ARC")));

/* Copy the arguments from the fourth position of the command   */
/* line on into arc_line, because they are just the name of the */
/* arc utility and its parms. */

    for(j=0; argc>j+4 ; j++)
    {
        strcat(arc_line,argv[j+4]);
        strcat(arc_line," ");
    }
    strcat(arc_line,argv[2]);
    if(arc_line[strlen(arc_line)-1]>64)
        strcat(arc_line,"/");

/* There MUST BE at least 5 arguments. Source directory,         */
/* destination, extension, arc-command (plus the one for the     */
/* program name itself. Hopefully, you all are entering things   */
/* where they are supposed to be because I don't check. But the  */
/* program plays dead gracefully in case of any errors. I need   */
/* to know which task I am to poll for SIGBREAKF_CTRL_C          */
        
    task=FindTask(0L);
    if(argc<5)
        quit(1,"\
\033[42m\033[31m\
 USAGE: ArcLead   SRC: DST: EXT  Arc-Command <parameters>          \
\033[m\n\033[42m\
 ABORT: <CTRL>'c'\033[33m\
 Source Destination Arcfile-extension Arc-command \
\033[m\n\033[42m\033[31m\
 Version 2.0 \xA9 1990 Richard Lawrence - freely distributable        \
\033[m\n\n");

/* This whole section (mainly, the IF) is to handle wildcards    */
/* passed with SRC:. The problem is that I need to find two      */
/* things out when SRC: is more than just a directory: 1) What   */
/* pattern match is 2) what the drive/directory is. First step is*/
/* to move the specified SRC: into a temporary buffer. Then,     */
/* check for a wildcard at the end - either ? or * is you've got */
/* ARP. If we DO have a wildcard, then I track backwards from the*/
/* end of SRC: until I find either a / or :, indicating a drive  */
/* or directory. Once I've found that, I copy the drive/directory*/
/* path to SRC (need this to be directory or drive for Lock() )  */
/* and then save the pattern in PTRN. Note that if you use ARP   */
/* wildcards I carry them over - I don't support them here, but  */
/* the archive program itself might.                             */
          
    strcpy(cmd_ln,argv[1]);
    if( (cmd_ln[strlen(cmd_ln)-1]==63) || (cmd_ln[strlen(cmd_ln)-1]==42) )
    {
        for(i=strlen(cmd_ln);!( cmd_ln[i]==58 || i==0 || cmd_ln[i]==47 );i--);
            if(i)
            {
                strncpy(src,cmd_ln,i+1);
                strcpy(ptrn,&cmd_ln[i+1]);
            }
            else
                quit(1,"You MUST specifiy a drive for SRC:\n");
    }
    else
    {
        strcpy(src,cmd_ln);
        if(src[strlen(src)-1]>64)
            strcat(src,"/");
        strcpy(ptrn,"#?");
    }
    strcat(ext,argv[3]);

/* AllocMem guarantees word alignment of memory segments. Use it */
/* when you are in doubt, because it can be a major headache when*/
/* AmigaDos wants a word aligned structure and you give it       */
/* something allocated with malloc(). Works _sometimes_!         */

    if(!(nextfile=(struct FILEINFO *)AllocMem(sizeof(struct FileInfoBlock),MEMF_CLEAR|MEMF_PUBLIC)))
        quit(1,"Not enough memory\n");
    lock=Lock(src,ACCESS_READ);
    if(!lock) 
        quit(1,"Can't find or lock SRC:\n");
    if(Examine(lock,nextfile))
    {

/* Do loop simply retrieves all the file names in SRC: and      */
/* stores them in areas pointed to by lineptr[]. STRSRT() sorts */
/* the array of pointers (important to the root selector logic) */
/* There are three types of files we don't want: directories,   */
/* files that don't match the specified pattern, and foob files */
/* (because then we'd have foob99 in lineptr[])                 */

        do
        {
            if(  !(nextfile->fib_DirEntryType>0) && astcsma(nextfile->fib_FileName,ptrn) && !(astcsma(nextfile->fib_FileName,"foob#?")) )
                if(nlines>=MAXLINES || (list=(char *)AllocMem(strlen(nextfile->fib_FileName),MEMF_CLEAR|MEMF_PUBLIC))==NULL)
                    quit(1,"Not enough memory\n");
                else
                {
                    strcpy(list,nextfile->fib_FileName);
                    lineptr[nlines++]=list;
                }
        } while(ExNext(lock,nextfile));
        UnLock(lock);
        strsrt(lineptr,nlines-1);
        strcpy(held,".info");
        for(i=0; i<nlines; i++)
        {
            if(task->tc_SigRecvd & SIGBREAKF_CTRL_C)
                quit(1,"***User aborted via Ctrl-C\n");

/* Here's how this logic works: Get the next file name, skip it */
/* if it's an ".info" file (covers Disk.info, .info, and any    */
/* icons, since they'll be included with the archive later).    */
/* Then search for the first "." in the file (ASCII value 46) or*/
/* for the end of the filename, whichever comes first. Compare  */
/* the resultant truncated "root" to the held value, and if they*/
/* are different, archive everything in SRC: with the new root  */
/* and make the new root the held value.                        */

            strcpy(rt_file,lineptr[i]);
            for(j=1; ((j<strlen(rt_file)+1) && (rt_file[j]!=46)); j++);
            rt_file[j]=0;

/* The next line checks to see if we have a new "root" filename.*/
/* If we do, then we must build a command line that will archive*/
/* everything that starts with that root, and then copy the root*/
/* into the HELD string for subsequent comparisons.             */

            if( !(astcsma(rt_file,held)) && strcmp(rt_file,".info") )
            {
                strcpy(cmd_ln,arc_line);
                strcat(cmd_ln,rt_file);
                strcat(cmd_ln,ext);
                strcat(cmd_ln," ");
                strcat(cmd_ln,src);

/* Here's where the trouble starts. ARC is a major headache: it */
/* will only accept files of up to 12 characters. Instead of    */
/* picking only those files illegal to ARC, I choose to change  */
/* the names of ALL files to belong in the resulting archive.   */
/* Mostly so people wouldn't think the other files where just   */
/* data files or unnecessary. ArcLead puts together a small     */
/* script that will change the names back to the correct ones   */
/* and automagically includes it in the archive. If you move the*/
/* included handy-dandy icon and script, foob99, to the SRC:  */
/* directory, when the archive is unarched it will even have an */
/* icon for the script to change names back. Isn't that special.*/

                strcpy(held,rt_file);
                strcat(held,"#?");
                if(use_arc)
                {

/* Given:a new root file in rt_file, and the directory list in  */
/* lineptr where the new files start at lineptr[i], this routine*/
/* will check all the file names that match the rt_file pattern */
/* and rename them to a foob?? file. Then it just makes foob    */
/* the root name for archive purposes, and all the foobs get    */
/* sucked up into the resulting arc. Voila.                     */

                    for(j=i; astcsma(lineptr[j],held)  ;j++)
                    {
                        parse(num_ren);
                        strcat(real_name,lineptr[j]);
                        Rename(real_name,foo_file);
                        num_ren++;
                    }
                    strcpy(rt_file,"foob");
                }
                strcpy(cmd_ln2,cmd_ln);
                strcat(cmd_ln2,rt_file);
                if(!(strcmp(argv[4],"ZOO")) || !(strcmp(argv[4],"zoo")))
                    strcat(cmd_ln2,"*");
                else
                    strcat(cmd_ln2,"#?");
                Execute(cmd_ln2,0,0);
                    
/* Ok, NOW we have to get those foob's BACK to the original   */
/* file names. We know we have an offset supplied, i, and we    */
/* know that num_ren is the number of files renamed. So we can  */
/* loop from 1 to num_ren and look in lineptr[] (remembering to */
/* add the offset i) for the original file names, and change    */
/* them back. At the same time, I open the "Execute.me"script to*/
/* be included in the archive, since it will contain the SAME   */
/* commands to set file names back (minus the pathways)*/ 

                if(use_arc)
                {
                    strcpy(rt_file,src);
                    strcat(rt_file,"Execute.me");
                    scriptfile=Open(rt_file,MODE_NEWFILE);
                    if(!scriptfile)
                        quit(1,"I/O error - disk full??\n");
                    for(j=1;j<num_ren;j++)
                    {
                        parse(j);
                        strcat(real_name,lineptr[i+j-1]);
                        Rename(foo_file,real_name);
                        strcpy(cmd_ln2,"rename ");
                        strcat(cmd_ln2,foo2);
                        strcat(cmd_ln2," ");
                        strcat(cmd_ln2,lineptr[i+j-1]);
                        strcat(cmd_ln2,"\n");
                        Write(scriptfile,cmd_ln2,strlen(cmd_ln2));
                     }
                     Close(scriptfile);
                     strcat(cmd_ln,"Execute.me");

/* Ok, we've built the script file, now include it in the same  */
/* archive with it's foobs.                                     */

                     Execute(cmd_ln,0,0);
                     DeleteFile(rt_file);
                     num_ren=1;
                }
            }
        }
    }
    else
        quit(1,"Error reading SRC:\n");
    quit(0,"ArcLead is finished\n\n");
}
