/* $VER: SmartPrompt v1.0 (26.09.98) by Adrian Zurek
** :ts=4
**
** 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 or 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; see the file COPYING. If not, write to
** the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#define __USE_SYSBASE

#include <exec/types.h>

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

#include <exec/execbase.h>
#include <exec/memory.h>

#include <string.h>
#include <stdlib.h>



/************************************************************************/
/***************** these constants describe our program *****************/
/************************************************************************/
#define PROGRAM_NAME	"SmartPrompt"
#define PROGRAM_VERSION	" v1.0 "

#ifdef	__AMIGADATE__
#	define PROGRAM_DATE	__AMIGADATE__
#elif defined (__DATE__)
#	define PROGRAM_DATE	"(" __DATE__ ")"
#else
#	define PROGRAM_DATE	"(26.09.98)"
#endif
/************************************************************************/

/* NewPrompt's version string */
const char *Version="\0$VER: " PROGRAM_NAME PROGRAM_VERSION PROGRAM_DATE;


#define KICKVERSION		37

#define FMTSTR_MAX		160
#define OUTSTR_MAX		255

enum {OK,NO_KICK,NO_VAR};


/*** global variables ***/

extern struct ExecBase *SysBase;
struct muBase *muBase=NULL;
struct Library *UserGroupBase=NULL;

char FormatStr[FMTSTR_MAX+1];
char OutStr[OUTSTR_MAX+1]="\0";
char *FormatPtr=FormatStr;


/*** function prototypes (13 functions) ***/
void	cleanup( UBYTE );

void	stradd( char * );
void	getdevicename( char *, char * );
UBYTE	countdirs( char * );
UBYTE	getlimit( void );
void	getdir( void );
void	gettime( UBYTE );
void	gethostname( void );
void	getuser( void );
BOOL	checkroot( void );
void	openlibs( void );
void	process( UBYTE );

void	__main( char * );



/* function frees all(??) resources allocated by program,	*/
/* prints out operation status and exits. 					*/
void cleanup(UBYTE exception)
{
if (exception)
	if (exception==NO_KICK)
		Write(Output(),"This program needs Kickstart 2.04 (V37) to run!\n",49);
	else
		Printf("%s: No $SPROMPT variable found!\n",PROGRAM_NAME);

/* close libraries */
CloseLibrary((struct Library *)muBase);
CloseLibrary(UserGroupBase);

exit (exception);
}



void stradd(char *string)
{
UBYTE remaining=OUTSTR_MAX-strlen (OutStr);

if (remaining<strlen (string))
	string[remaining]='\0';

strcat (OutStr,string);
}

void getdevicename(char *volume,char *device)
{
struct DosList *dl;
struct FileLock *l;
BPTR lock;

if (lock=Lock(volume,ACCESS_READ))
{
	l=BADDR(lock);

	dl=LockDosList(LDF_DEVICES | LDF_READ);

	while (dl=NextDosEntry(dl,LDF_READ | LDF_DEVICES))
		if (dl->dol_Task==l->fl_Task)
		{
			strcat (device,(UBYTE *)BADDR(dl->dol_Name)+1);
			break;
		}

	UnLockDosList(LDF_DEVICES | LDF_READ);
	UnLock(lock);
}

if (!*device)
	strcat (device,volume);
}

/* function returns number of components of given pathname */
UBYTE countdirs(char *pathname)
{
char *ptr=pathname;
UBYTE cnt=1;

if (pathname[strlen (pathname)-1]!=':')
{
	do
		cnt++;
	while (ptr=strchr (++ptr,'/'));
}

return (cnt);
}

UBYTE getlimit(void)
{
char lim[3];
UBYTE x=0;

if (strlen (FormatPtr)>2)
{
	strncpy (lim,++FormatPtr,2); lim[2]='\0';
	FormatPtr++;
	x=(UBYTE)atoi (lim);
}

return (x);
}

void getdir(void)
{
char dirname[100],volume[30]="\0";
char *colon,*ptr;
BOOL lastpath=TRUE,ixstyle=FALSE,device=FALSE;
UBYTE limit=0,count;

FormatPtr++;
while (*FormatPtr!='}')
{
	if (*FormatPtr=='\0')
		return;

	switch (*FormatPtr)
	{
	case 'd':
		device=TRUE;
		break;
	case 'a':
		ixstyle=FALSE;
		break;
	case 'x':
		ixstyle=TRUE;
		break;
	case 'p':
		lastpath=TRUE;
		limit=getlimit();
		break;
	case 'P':
		lastpath=FALSE;
		limit=getlimit();
		break;
	}

	FormatPtr++;
}

GetCurrentDirName(dirname,99);

colon=strchr (dirname,':');

if (ixstyle)
	strcpy (volume,"/");		/* add "/" at the beginning */

if (device || ((ptr=strchr (dirname,' ')) && (ptr<colon)))
	getdevicename(dirname,volume);
else
	strncat (volume,dirname,colon-dirname);

if (!ixstyle)
	strcat (volume,":");

/* colon ptr now points on the char next to the ':' */
colon++;

if ((count=countdirs(dirname))==1)
	stradd(volume);
else if (lastpath)
{
	if ((!limit) || (limit>=count))
	{
		stradd(volume);
		if (ixstyle)
			stradd("/");
		stradd(colon);
	}
	else
	{
		ptr=colon-1;

		while (--count>limit)
			if (!(ptr=strchr (++ptr,'/')))
				break;

		stradd(++ptr);
	}
}
else
{
	UBYTE len=strlen (colon)+strlen (volume);

	if (ixstyle)
		len++;

	if (!limit)
		limit=255;

	stradd(volume);

	if (len>limit)
	{
		while (count-->(ixstyle ? 1 : 2))
			stradd("/");

		ptr=colon;
		while (*ptr++);			/* move to the end of string */
		while (*--ptr!='/');	/* and then look for '/' char starting from the end */
		stradd(++ptr);
	}
	else
	{
		if (ixstyle)
			stradd("/");

		stradd(colon);
	}
}
}

enum {TIME,DATE,WEEKDAY};

void gettime(UBYTE type)
{
struct DateTime dtime;
char timestr[LEN_DATSTRING+1];

DateStamp(&dtime.dat_Stamp);

dtime.dat_Format=FORMAT_DOS;
dtime.dat_Flags=0;
dtime.dat_StrDay=(type==WEEKDAY) ? timestr : NULL;
dtime.dat_StrDate=(type==DATE) ? timestr : NULL;
dtime.dat_StrTime=(type==TIME) ? timestr : NULL;

DateToStr(&dtime);

stradd(timestr);
}

void gethostname(void)
{
char host[13];
char *ptr;

if (GetVar("hostname",host,13,GVF_GLOBAL_ONLY)!=-1)
{
	if (ptr=strchr (host,'.'))
		*ptr='\0';

	stradd(host);
}
else
	stradd("AmigaOS");
}

void getuser(void)
{
char userid[muUSERIDSIZE];

strcpy (userid,"nobody");

if (muBase)
{
	struct muUserInfo *info;
	UWORD uid=muGetTaskOwner(NULL)>>16;

	if (info=muAllocUserInfo())
	{
		info->uid=uid;
		if (muGetUserInfo(info,muKeyType_uid))
			strcpy (userid,info->UserID);

		muFreeUserInfo(info);
	}
}
else if (UserGroupBase)
{
	uid_t uid=getuid();
	struct passwd *pw;

	if (pw=getpwuid(uid))
		strcpy (userid,pw->pw_name);
}

stradd(userid);
}

BOOL checkroot(void)
{
if (muBase)
{
	if (muROOT_UID==(muGetTaskOwner(NULL)>>16))
		return (TRUE);
}
else if (UserGroupBase)
{
	if (!getuid())
		return (TRUE);
}

return (FALSE);
}

void openlibs(void)
{
struct Library *lib;
BOOL found_mufs=FALSE,found_ug=FALSE;

Forbid();

lib=(struct Library *)SysBase->LibList.lh_Head;

do
{
	if (!found_mufs)
		if (!strcmp (lib->lib_Node.ln_Name,MULTIUSERNAME))
		{
			found_mufs=TRUE;
			break;
		}

	if (!found_ug)
		if (!strcmp (lib->lib_Node.ln_Name,"usergroup.library"))
			found_ug=TRUE;

	lib=((lib==(struct Library *)SysBase->LibList.lh_TailPred) ? NULL :
		(struct Library *)lib->lib_Node.ln_Succ);
}
while (lib);

Permit();

if (found_mufs)
	muBase=(struct muBase *)OpenLibrary(MULTIUSERNAME,MULTIUSERVERSION);
else if (found_ug)
	UserGroupBase=OpenLibrary("usergroup.library",1);
}

enum {GLOBAL_,ROOT_DO,ROOT_IGNORE};

void process(UBYTE type)
{
while (*++FormatPtr!='\0')
{
	if (*FormatPtr=='%')
	{
		if (type!=ROOT_IGNORE)
		{
			if (*++FormatPtr=='\0')
				break;

			switch (*FormatPtr)
			{
			case 'p':
				getdir();
				break;
			case '%':
				stradd("%");
				break;
			case 'u':
				getuser();
				break;
			case 'h':
				gethostname();
				break;
			case 'q':
				stradd("\"");
				break;
			case 'b':
				stradd("{");
				break;
			case 'B':
				stradd("}");
				break;
			case 'R':
				if (type==GLOBAL_)
					process(checkroot() ? ROOT_DO : ROOT_IGNORE);
				break;
			case 'r':
				if (type==GLOBAL_)
					process(checkroot() ? ROOT_IGNORE : ROOT_DO);
				break;
			case 't':
				gettime(TIME);
				break;
			case 'd':
				gettime(DATE);
				break;
			case 'w':
				gettime(WEEKDAY);
				break;
			}
		}
	}
	else
	{
		char ch[2]=" ";
		BOOL write2=TRUE;

		if (type!=GLOBAL_)
			if (*FormatPtr=='{')
				write2=FALSE;
			else if (*FormatPtr=='}')
				return;
			else if (type==ROOT_IGNORE)
				write2=FALSE;

		if (write2)
		{
			*ch=*FormatPtr;
			stradd(ch);
		}
	}
}
}


void __stdargs __main(char *line)
{
extern struct WBStartup *_WBenchMsg;

/* get Kickstart version */
if (SysBase->LibNode.lib_Version<KICKVERSION)
	cleanup(NO_KICK);

if (_WBenchMsg)
{
	ReplyMsg((struct Message *)_WBenchMsg);
	cleanup(OK);
}

if (GetVar("SPROMPT",FormatStr,FMTSTR_MAX,NULL)==-1)
	cleanup(NO_VAR);

openlibs();

FormatPtr=FormatStr-1;

process(GLOBAL_);

PutStr(OutStr);

cleanup(OK);
}

/*
%u - userid
%h - hostname
%R{} - root string
%r{} - non-root string
%% - '%' char
%q - '"' char
%b - '{' char
%B - '}' char
%p{} - path:
	a - Amiga style pathes (default)
	x - UNIX style pathes
	d - device names instead of volume names
	pxx - partial path style no. 1: "path" shows only "xx" lasts components of path (0 prints all and is the default)
	Pxx - partial path style no. 2: "work:////path" and is limited to "xx" number of chars
%t - time in format hh:mm
%d - date
%w - weekday
*/
