/*
 ReDev
 Un prodotto FreeWare di Romano Tenca (Anarkick) Fidonet 2:331/301.14
 Rinomina o rimuove i device del DOS 1.2/1.3

 Sotto Lattice 5.04:
 compilare con
 Lc -v -ms -cus -O
 linkare con
 Blink from lib:c.o ReDev.o to Redev lib lib:lc.lib SC SD ND DEFINE __main=__tinymain

*/

#include "exec/types.h"
#include "exec/memory.h"
#include "libraries/dosextens.h"
#include "libraries/filehandler.h"
#include "proto/exec.h"
#include "proto/dos.h"

typedef unsigned char	ubyte;
typedef unsigned short	uword;
typedef unsigned long	ulong;

typedef struct DosList		DOSLIST;	/* Contenuta in dosextens.h */
typedef struct DosInfo		DOSINFO;
typedef struct RootNode 	ROOTNODE;

#define strlen __builtin_strlen

#define CTOB(x) (BPTR)(((long)(x))>>2)	/* CPTR->BPTR */
#define BTOC(x) (void *)((x)<<2)		/* BPTR->CPTR */

void* dosalloc(ulong bytes);
void dosfree(ulong* ptr);
long cmpbstr(BSTR b1,BSTR b2);
DOSLIST* redev(BSTR oldname,BSTR newname, long remove);
void putstr(char *str);
long stripcolon(char *scanarg);

long stdout;

main(long argc,char *argv[])
{
BSTR oldname,newname;

stdout=Output();

if(argc>3 || argc<2)
	{
	putstr("ReDev 1.0 a FreeWare Production by Anarkick\nCopyright Romano Tenca 1991 Italy\nFidonet 2:331/301.14\nInternet romano.tenca@p14.f301.n331.z2.fidonet.org\nUsage: ReDev <oldname> <newname> | <deletename>\n");
	}
else
	{
	if(stripcolon(argv[1])) /* Elimina due punti e controlla nome */
		{
		putstr("Invalid name\n");
		return 20;
		}

	oldname=createbstr(argv[1]);
	if(oldname==NULL) return 20; /* Non c'e' memoria */

	if(argc==3) /* Rinomina */
		{
		if(stripcolon(argv[2]))  /* Elimina due punti e controlla nome */
			{
			putstr("Invalid name\n");
			return 20;
			}

		newname=createbstr(argv[2]);
		if(newname==NULL) /* Non c'e' memoria */
			{
			dosfree(BTOC(oldname));
			return 20;
			}
		if(redev(newname,0,0)) /* Controlla se esiste device */ 
			{
			putstr(argv[2]);
			putstr(" already exists\n");
			dosfree(BTOC(newname));
			}
		else if(redev(oldname,newname,0)==NULL) /* Rinomina device */
			{
			putstr(argv[1]);
			putstr(": not found\n");
			dosfree(BTOC(newname));
			}
		}
	else  /* Rimuove */
		{
		DOSLIST *ret=redev(oldname,0,1);
		putstr(argv[1]);
		if(ret==NULL) putstr(": not found\n");
		else if(ret!=(DOSLIST *)-1) putstr(": removed but in use\n");
		else putstr(": deleted\n");
		}
	dosfree(BTOC(oldname));
	}
}

/* DOSLIST* redev(BSTR oldname,BSTR newname, long remove)
* Cerca, rimuove o rinomina una DOSLIST di tipo device.
* Ritorna
*  0 se non trova la DOSLIST.
* -1 se la rimozione ha avuto successo       
* indirizzo DOSLIST negli altri casi
*/

DOSLIST* redev(BSTR oldname,BSTR newname, long remove)
{
DOSINFO *di;
DOSLIST *dol; /* L'indirizzo della DOSLIST */
BPTR *doladd; /* L'indirizzo del puntatore BCPL alla DOSLIST */
DOSLIST *ret=0; /* Ritorna 0 se non trova DOSLIST */

/* trova indirizzo di DOSINFO */
di=BTOC(((ROOTNODE *)DOSBase->dl_Root)->rn_Info);
Forbid();

/* trova DOSLIST */
for(doladd=&di->di_DevInfo, dol=BTOC(di->di_DevInfo);
	dol;
	doladd=&dol->dol_Next, dol=BTOC(dol->dol_Next))
	{
	if(dol->dol_Type==DLT_DEVICE && cmpbstr(oldname,dol->dol_Name))
		{
		char* dosname=BTOC(dol->dol_Name);
		char* handlername=BTOC(dol->dol_misc.dol_handler.dol_Handler);
		ret=dol; /* Trovato */
		if(newname) /* Rinomina */
			{
			if(TypeOfMem(dosname)) dosfree((long*)dosname);
			dol->dol_Name=newname;
			}
		else if(remove) /* Rimuove */
			{
			/* Elimino dalla lista del DOS */
			*doladd=dol->dol_Next;
			if(!dol->dol_Task && !dol->dol_misc.dol_handler.dol_SegList)
				{
				/* Libero le risorse */
				if(TypeOfMem(dosname)) dosfree((long*)dosname);
				if(TypeOfMem(handlername)) dosfree((long*)handlername);
				dosfree((long*)dol);
				ret=(DOSLIST *)-1;
				}
			}
		break;
		}
	}
Permit();
return ret;
}

/* BSTR createbstr(ubyte *cstr)
* A partire dalla CSTR iniziale crea una BSTR che termina con uno zero,
* allocando la memoria necessaria. Ritorna 0 se non c'e' memoria.
*/

BSTR createbstr(ubyte *cstr)
{
register int len=strlen(cstr);
register int alloc=(len+5) & ~03;
register char *str,*ret;

str=dosalloc(alloc);
if(str==NULL) return NULL;
ret=str;
*str++=len;
while(*str++=*cstr++);
return(CTOB(ret));
}

/* void putstr(char *str)
* Invia una stringa verso stdout senza newline.
*/

void putstr(char *str)
{
long len =strlen(str);
Write(stdout,str,len);
}

/* long stripcolon(char *scanarg)
* Trasforma il primo ':' in uno zero. Ritorna 1 in caso di errore.
*/

long stripcolon(char *scanarg)
{
register char ch,*scan=scanarg;
for(;ch=*scan;scan++)
	{
	if(ch==':')
		{
		*scan++=0;
		if(*scan) return 1;
		break;
		}
	if(ch=='/')	return 1;
	}
return 0;
}

/* void *dosalloc(ulong bytes)
 * Alloca memoria seguendo le convenzioni DOS. Ritorna 0 se non c'e'
 * memoria. Ritorna CPTR al primo long utile (a -4 c'e' la lunghezza
 * in byte).
*/

void *dosalloc(ulong bytes)
{
register ULONG *ptr;

bytes += 4;
ptr = (ulong *)AllocMem(bytes, MEMF_PUBLIC|MEMF_CLEAR);
if(ptr)
	{
	*ptr = bytes;
	return(ptr+1);
	}
return NULL;
}

/* void dosfree(ulong *ptr)
 * Libera memoria DOS (con a -4 la lunghezza in byte), accetta CPTR.
*/

void dosfree(ulong *ptr)
{
--ptr;
FreeMem(ptr, *ptr);
}

/* long cmpbstr(BSTR b1, BSTR b2)
* Confronta due BSTR, non tiene conto delle maiuscole.
*/

long cmpbstr(BSTR b1, BSTR b2)
{
register ubyte *c1,*c2;
register short l;

c1=BTOC(b1);
c2=BTOC(b2);

l=*c1++;
if(l==*c2++)
	{
	while (--l>=0)
		{
		if ((c1[l]|0x20) != (c2[l]|0x20)) return 0;
		}
	return 1;
	}
return 0;
}

void MemCleanup(){}
