/*
 *  FILE
 *	wbarg V1.0	by Anders Lindgren
 *
 *  DESCRIPTION
 *	Convert WB arguments into shell dito.
 *
 *	This programs looks for a tooltype of the name COMMAND, to which
 *	if adds the WB arguments, before executing the command as a shell
 *	process.
 *
 *	One area of use is to set the COMMAND tooltype to:
 *	  COMMAND=sys:rexxc/rx rexxscript
 *	to run the rexx script "rexxscript" with WB arguments. 
 *
 *  WARNING
 *	This is a HACK, it might stop to work on future OS-releases, if
 *	Commodore would change the path mechanism.
 *
 *	This program should work under 1.3, but it hasn't been tested. If
 *	it is runed under V39 it takes advantage of new features. (under 
 *	V37 AllocDosObject(DOS_CLI, ...) is buggy.)
 *
 *  LICENSE
 *      Copyright (C) 1993  Anders Lindgren
 *
 *	This program is free software; you can redistribute it and/or modify
 *	it under the terms of the GNU General Public License as published by
 *	the Free Software Foundation; either version 2 of the License,or
 *	(at your option) any later version.
 *
 *	This program is distributed in the hope that it will be useful,
 *	but WITHOUT ANY WARRANTY; without even the implied warranty of
 *	MERCHANTABILITY of FITNESS FOR A PARTICULAR PURPOSE. See the
 *	GNU General Public License for more details.
 *
 *	You should have received a copy of the GNU General Public License
 *	along with this program; if not, write to the Free Software
 *	Foundation, Inc., 675 Mass Ave, Cambridge, Ma 02139, USA.
 *
 *  HOW TO CONTACT ME:
 *	voice:   Scream  Anders Lindgren  LOUD
 *	email:   d91ali@csd.uu.se
 *	mail:    Anders Lindgren
 *	         Kantorsg. 2-331
 *	         S-754 24 Uppsala
 *	SUGA BBS:+46 (0)8 34 85 23
 *		 +46 (0)8 34 32 76  
 *
 *  TODO
 *	Define a STACK tooltype to set the stack size.
 *
 *  HISTORY
 *	09-Sep-93	Wrote the program.
 *	28-Sep-93	Added the add/free_cli functions
 *		        V1.0 Released
 */

#include <exec/types.h>

#include <exec/memory.h>
#include <exec/execbase.h>
#include <dos/dos.h>
#include <dos/dostags.h>
#include <workbench/startup.h>

#define __USE_SYSBASE

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

#include <stdio.h>
#include <strings.h>


#define CMDLINE 512		/* Length of the command line */


/* Forward references */

int  get_command(struct WBArg * wbarg, char buffer[], int maxlen);
void name_of_lock(BPTR lock, char buffer[], int maxlen);
void usage(void);
int  add_cli(void);
void free_cli(void);


/* The version string */
UBYTE *vers="\0$VER: wbarg 1.0 (" __DATE__ ")";

/* The (secret?) structure of path */

struct path {
    BPTR next;
    BPTR lock;
};


/* A combined cli-structure and BSTR:s used when KS < 39 */
struct mycli {
    struct CommandLineInterface cli; /* Must be the first entry in the structure! */
    UBYTE set_name[80];
    UBYTE command_name[80];
    UBYTE prompt[80];
    UBYTE command_file[80];
};


void
main(int argc, char *argv[])
{
    char commandline[CMDLINE];	/* The commandline to issue */
    char args[CMDLINE];

    struct WBStartup * argmsg;
    struct WBArg     * wbarg;

    BPTR olddir;
    BPTR lock;

    int found_cmd = FALSE;	/* Flag: TRUE when a COMMAND tooltype is found */
    int created_cli = FALSE;	/* Flag: TRUE when we created our own cli-structure */

    int i;

    /* If we haven't got a Cli-structure, create one */
    if (Cli() == NULL) {
	if (add_cli()) {
	    created_cli = TRUE;
	}
	else {
	    puts("wbarg: Error while creating Cli-structure and path\n");
	}

    }


    if (argc) {
	usage();		/* It's no point in running it from the shell */
    }
    else {
	argmsg = (struct WBStartup *)argv;
	wbarg = argmsg->sm_ArgList;

	args[0] = '\0';

	for (i=0; i<argmsg->sm_NumArgs; i++) {

	    olddir = -1;
	    if ((wbarg->wa_Lock) && (*wbarg->wa_Name)) {
		olddir = CurrentDir(wbarg->wa_Lock);
	    }

	    /* Get the command to execute */
	    if (get_command(wbarg, commandline, CMDLINE)) {
		found_cmd = TRUE;
	    }

	    /* Build all the arguments */
	    if ((i>0) && *wbarg->wa_Name) {
		if (lock = Lock(wbarg->wa_Name, ACCESS_READ)) {
		    strncat(args, " \"", CMDLINE);
		    name_of_lock(lock, args, CMDLINE);
		    strncat(args, "\"", CMDLINE);
		    UnLock(lock);
		}
	    }

	    if (olddir != -1) {
		CurrentDir(olddir);
	    }

	    wbarg++;	/* Use the next argument to the next loop iteration */
	}

	if (found_cmd) {
	    strncat(commandline, args, CMDLINE);
	    Execute(commandline, NULL, NULL);
	}
	else {
	    usage();		/* No COMMAND tooltype found */
	}
    }
    if (created_cli) {
	/* ((struct Process *) FindTask(NULL))->pr_CLI = NULL; */
	free_cli();
    }
}


/*
 *  FUNCTION
 *	get_command
 *
 *  DESCRIPTION
 *	Find a icon with the COMMAND tooltype set.
 */

int
get_command(struct WBArg * wbarg, char buffer[], int maxlen)
{
    struct DiskObject * dobj;
    char ** toolarray;
    char * s = NULL;

    if ((*wbarg->wa_Name) && (dobj = GetDiskObject(wbarg->wa_Name))) {
	toolarray = (char **)dobj->do_ToolTypes;

	if (s = (char *)FindToolType(toolarray, "COMMAND")) {
	    s[maxlen-1] = '\0';	/* Make sure the string is nullterminated */
	    strncpy(buffer, s, maxlen-1);
	}
	FreeDiskObject(dobj);
    }
    return(s == NULL ? FALSE : TRUE);
}


/*
 *  FUNCTION
 *	name_of_lock
 *
 *  DESCRIPTION
 *  	Create the filename from the lock supplied.
 *  	NOTE: The name is ADDED to the string in the buffer.
 */

void
name_of_lock(BPTR lock, char buffer[], int maxlen)
{
    BPTR newlock;
		/* Static to save space on the stack during recursion */
    static __aligned struct FileInfoBlock fib; 
    int root;

    if (newlock = ParentDir(lock)) {
	name_of_lock(newlock, buffer, maxlen);
	UnLock(newlock);
	root = FALSE;
    }
    else {
	root = TRUE;
    }

    if (Examine(lock, & fib)) {
	strncat(buffer, fib.fib_FileName, maxlen);
	if (fib.fib_DirEntryType > 0) {
	    strncat(buffer, root ? ":" : "/", maxlen);
	}
    }
}
    

/*
 *  FUNCTION
 *	usage
 *
 *  DESCRIPTION
 *	Write a small information text.
 */

void
usage(void)
{
    puts("WBARG  A program which converts workbench");
    puts("       arguments to command line dito.");
    puts("Usage: Create a project icon and set"); 
    puts("       the tooltype named COMMAND to the");
    puts("       command you would like to run");
    puts("Example: COMMAND=sys:rexxc/rx someprogram");
}


/*
 *  FUNCTION
 *	add_cli
 *
 *  DESCRIPTION
 *	Attaches a CommandLineInterface-structure to the current (WB-started)
 *	process and copies the path from the Workbench task.
 *
 *	Return non-FALSE on success;
 */

int
add_cli()
{
    struct CommandLineInterface * cli = NULL;
    struct mycli * mycli;
    struct Process * wb;
    BPTR p;			/* The path structure to copy from */
    struct path * newp;		/* The new path structure */
    BPTR * prev;

    /*
     * Under 1.3 AllocDosObject does not exists, under <39 the DOS_CLI option is buggy 
     */
    if (SysBase->LibNode.lib_Version < 39) {
	if (mycli = (struct mycli *)AllocMem(sizeof(struct mycli), MEMF_CLEAR|MEMF_PUBLIC)) {
	    cli = & mycli->cli;
	    mycli->cli.cli_FailLevel   = 10;
	    mycli->cli.cli_Background  = DOSTRUE;
	    mycli->cli.cli_SetName     = MKBADDR(& mycli->set_name);
	    mycli->cli.cli_CommandName = MKBADDR(& mycli->command_name);
	    mycli->cli.cli_Prompt      = MKBADDR(& mycli->prompt);
	    mycli->cli.cli_CommandFile = MKBADDR(& mycli->command_file);
	}
    }
    else {
	cli = (struct CommandLineInterface *)AllocDosObjectTags(DOS_CLI, TAG_DONE);
    }
    if (cli) {
	cli->cli_DefaultStack = 4096;
	((struct Process *) FindTask(NULL))->pr_CLI = MKBADDR(cli);

	if (wb = (struct Process *)FindTask("Workbench")) {
	    prev = & cli->cli_CommandDir;
	
	    p = ((struct CommandLineInterface *)BADDR(wb->pr_CLI))->cli_CommandDir;
	    while(p){
		if (newp = AllocMem(sizeof(struct path), MEMF_PUBLIC)) {
		    newp->lock = DupLock(((struct path *)BADDR(p))->lock);
		
		    * prev = MKBADDR(newp);
		    prev = & newp->next;
		}
		else {
		    * prev = NULL;
		    free_cli();
		    return(FALSE);
		}
		p = ((struct path *)BADDR(p))->next;
	    }
	    * prev = NULL;	/* Terminate the linked list */
	}
    	return(TRUE);		/* OK */
    }
    return(FALSE);		/* No cli structure allocated */
}


/*
 *  FUNCTION
 *	free_cli
 *
 *  DESCRIPTION
 *	Undo the work of add_cli
 */

void
free_cli()
{
    struct CommandLineInterface * cli;
    struct path * p;
    struct path * p_next;

    if (cli = Cli()) {
	p = (struct path *)BADDR(cli->cli_CommandDir);
	cli->cli_CommandDir = NULL;

	while (p) {
	    p_next = (struct path *)BADDR(p->next);
	    if (p->lock) {
		UnLock(p->lock);
	    }
	    FreeMem((void *)p, sizeof(struct path));
	    p = p_next;
	}
	if (SysBase->LibNode.lib_Version < 39) {
	    FreeMem((void *)cli, sizeof(struct mycli));
	}
	else {
	    FreeDosObject(DOS_CLI, (void *)cli);
	}

	((struct Process *) FindTask(NULL))->pr_CLI = NULL;
    }
}
