/*	(set TAB size to 5)

	"touch" 37.2 written by Ralph Seichter, Copyright (c) 1992.

	Touching a file means setting its file date and time to the current
	system clock settings.  This  comes handy in combination with tools
	like "make" which naturally depend on proper date settings. I wrote
	my own version of "touch" just to see how it can be done.  :)

	Touching a nonexistent file will display an  error,  unless you use
	the switch CREATE. In the latter case, "touch" will create an empty
	file for you  (I was told that this is an important feature, thanks
	to <hoehle@post.inf-wiss.ivp.uni-konstanz.de> for the suggestion).

	DISCLAIMER:   You agree to use "touch" entirely at your own risk. I
	can not be held responsible for any damage caused by using "touch".
	I declare this program PUBLIC DOMAIN.  You  may do with it whatever
	you wish, as long as you remember the following conditions:

	o  The distribution archive must be left intact.  No file may be
	   changed without comment and absolutely no file may be deleted
	   from the archive, so everyone has the chance to recompile the
	   source (I don't want to slip you a virus).
	o  You must not gain any profit from giving "touch" to others.
	o  You must not include any of this code in commercial software.

	TEMPLATE:	touch PATTERN/A,CREATE/S,QUIET/S

	USAGE:	touch ram:foo CREATE QUIET
			touch sys:s/start???sequence
			touch #?.c QUIET

	COMMENTS:  Sorry, this was a quick shot which surely leaves much to
	be done for the future (like multiple patterns). But it does what I
	want it to do for me...  ;)
*/

#include	<dos/exall.h>
#include	<exec/memory.h>
#include	<proto/exec.h>
#include	<proto/dos.h>
#include	<stdio.h>
#include	<string.h>

#define	ARG_PATTERN	0
#define	ARG_CREATE	1
#define	ARG_QUIET		2
#define	ARG_TOTAL		3
#define	BUFSIZE		256



/* globals and prototypes */

STRPTR noMemory = "Not enough memory!\n";

struct TouchArgs {
	STRPTR Orig;			/* pointer to original pattern argument */
	UBYTE Pat[BUFSIZE];		/* pattern buffer */
	UBYTE Dir[BUFSIZE];		/* directory name buffer */
	UBYTE DirSep[2];		/* directory seperator string */
	BOOL Wild;			/* set if pattern contains wildcards */
	BOOL Quiet;			/* set for quiet mode */
	BOOL Create;			/* set for file creation mode */
	struct DateStamp DateStamp;
};

VOID findDir (struct TouchArgs *);
WORD main (WORD, STRPTR *);
VOID touch (struct TouchArgs *);
VOID touchFile (struct TouchArgs *, STRPTR);



/* the main program */

WORD main (WORD argc, STRPTR *argv)
{
	STRPTR version = "\0$VER: touch 37.2 (5.12.92) by Ralph Seichter";
	STRPTR template = "PATTERN/A,CREATE/S,QUIET/S";
	struct RDArgs *args, *rda;
	struct TouchArgs *ta;
	LONG taSize, array[ARG_TOTAL] = {0,0,0};

	printf ("\2331m%s\2330m\n", &version[7]);
	if (DOSBase->dl_lib.lib_Version >= 37) {
		if (rda = AllocDosObject (DOS_RDARGS, NULL)) {
			if (args = ReadArgs (template, array, rda)) {
				taSize = sizeof (struct TouchArgs);
				if (ta = AllocMem (taSize, MEMF_CLEAR)) {
					ta->Quiet = (array[ARG_QUIET] != 0);
					ta->Create = (array[ARG_CREATE] != 0);
					ta->Orig = (STRPTR)(array[ARG_PATTERN]);
					ta->Wild = ParsePatternNoCase (ta->Orig, ta->Pat, BUFSIZE);
					findDir (ta);
					touch (ta);
					FreeMem (ta, taSize);
				} else
					printf (noMemory);
				FreeArgs (args);
			} else
				printf ("Command template is %s.\n", template);
			FreeDosObject (DOS_RDARGS, rda);
		} else
			printf (noMemory);
	} else
		printf ("Requires dos.library V37\n");
	return (RETURN_OK);
}



/* seperate directory from pattern string */

VOID findDir (struct TouchArgs *ta)
{
	WORD i;
	STRPTR p = ta->Pat;

	for (i = strlen (p) - 1; i >= 0; --i) {
		switch (p[i]) {
			case ':':
				++i;
				stccpy (ta->Dir, p, i + 1);
				strcpy (p, &p[i]);
				i = -1;
				break;
			case '/':
				++i;
				*ta->DirSep = '/';
				stccpy (ta->Dir, p, i);
				strcpy (p, &p[i]);
				i = -1;
		}
	}
}



/* touch one single file */

VOID touchFile (struct TouchArgs *ta, STRPTR name)
{
	BPTR lock;
	struct DateStamp *ds = &ta->DateStamp;

	if (lock = Lock (name, SHARED_LOCK)) {
		UnLock (lock);
		/* file already exists, set new date */
		DateStamp (ds);
		if (SetFileDate (name, ds)) {
			if (!ta->Quiet)
				printf ("\"%s\" touched.\n", name);
		} else
			printf ("Can't touch \"%s\".\n", name);
	} else {
		if (ta->Create) {
			/* create empty file */
			if (lock = Open (name, MODE_NEWFILE)) {
				Close (lock);
				if (!ta->Quiet)
					printf ("\"%s\" created.\n", name);
			} else
				printf ("Can't create \"%s\".\n", name);
		} else
			printf ("Can't find \"%s\".\n", name);
	}
}



/* here's the touching stuff */

VOID touch (struct TouchArgs *ta)
{
	struct ExAllData *ead, *data;
	struct ExAllControl *eac;
	UBYTE name[BUFSIZE];
	LONG eadSize, err;
	BOOL more;
	BPTR lock;

	if (ta->Wild) {
		if (lock = Lock (ta->Dir, SHARED_LOCK)) {
			if (eac = AllocDosObject (DOS_EXALLCONTROL, NULL)) {
				eac->eac_MatchString = ta->Pat;
				eadSize = sizeof (struct ExAllData);
				if (ead = AllocMem (eadSize, MEMF_CLEAR)) {
					do {
						more = ExAll (lock, ead, eadSize, ED_NAME, eac);
						err = IoErr ();
						if (!more && err != ERROR_NO_MORE_ENTRIES) {
							printf ("ExAll failed!\n");
							break;
						}
						if (eac->eac_Entries != 0) {
							data = ead;
							while (data) {
								sprintf (name, "%s%s%s", ta->Dir, ta->DirSep, data->ed_Name);
								touchFile (ta, name);
								data = data->ed_Next;
							}
						}
					} while (more);
					FreeMem (ead, eadSize);
				} else
					printf (noMemory);
				FreeDosObject (DOS_EXALLCONTROL, eac);
			} else
				printf (noMemory);
			UnLock (lock);
		} else
			printf ("Can't lock directory \"%s\".\n", ta->Dir);
	} else
		touchFile (ta, ta->Orig);
}
