
#include <utility/hooks.h>

#include "sumstl.h"

#include <proto/locale.h>

#include <stdio.h>
#include <ctype.h>

#ifdef __SASC
#include <dos.h>
#endif

#include "umsfilter.h"
#include "date.h"

/* SMAKE */


struct MyHook	{
    struct MinNode	h_MinNode;
    ULONG		(*h_Entry)();	/* assembler entry point	*/
    ULONG		(*h_SubEntry)();/* often HLL entry point	*/
    char	*h_Data;	/* owner specific		*/
};


// Version String
// --------------

static char VersionString[] = "$VER: sumsprint 2.8 "__AMIGADATE__;

static char UsageString[] = "\
     U=User       : user name.\n\
     P=Password   : user's password.\n\
    FI=Filter     : filter specification.\n\
    FO=Format     : format string (see documentation for details).\n\
  FILE=Formatfile : file that contains a format string.\n\
     O=SetOldFlag : if existing, read messages become old.\n\
     B=Backwards  : if existing, messages are scanned backwards.\n\
     S=Server     : server name.\n\n\
";


// Template
// --------

static char *TemplateString = "U=User/A,P=Password/A,FI=Filter/A,FO=Format,FILE=Formatfile/K,O=SetOldFlag/S,B=Backwards/S,S=Server/K";
enum opts {
	OPT_USER, OPT_PASSWORD, OPT_FILTER, OPT_FORMAT, OPT_FORMATFILE, OPT_SETOLDFLAG, OPT_BACKWARDS, OPT_SERVER,
	OPT_COUNT};
static LONG opts[OPT_COUNT];


// Globals
// -------

extern struct DosLibrary *DOSBase;

struct Library *UMSBase = NULL;
struct Library *LocaleBase = NULL;
struct Locale *Locale = NULL;
UMSAccount acc = NULL;
char *TextFields[UMSNUMFIELDS];
struct MyMessageInfo
{
	LONG      msgi_HeaderLength; // 0  (offsets for keyword table)
	LONG      msgi_TextLength;   // 1
	LONG      msgi_Date;         // 2
	UMSMsgNum msgi_ChainUp;      // 3
	UMSMsgNum msgi_ChainDn;      // 4
	UMSMsgNum msgi_ChainLt;      // 5
	UMSMsgNum msgi_ChainRt;      // 6
	UMSSet    msgi_GlobalStatus; // 7
	UMSSet    msgi_UserStatus;   // 8
	UMSSet    msgi_LoginStatus;  // 9
	UMSMsgNum msgi_HardLink;     // 10
	UMSMsgNum msgi_SoftLink;     // 11
	LONG      msgi_MsgNum;       // 12
} MessageInfo;


#define KW_STRING 0
#define KW_NUMBER 1
#define KW_DATE   2
#define KW_QTEXT  3
#define KW_FLAG   4

struct Keyword
{
	char *name;
	char *format;
	LONG type;
	LONG code;
} Keywords[] =
{
	{ "MsgText"      ,"%s",KW_STRING,UMSCODE_MsgText      },

	{ "FromName"     ,"%s",KW_STRING,UMSCODE_FromName     },
	{ "FromAddr"     ,"%s",KW_STRING,UMSCODE_FromAddr     },
	{ "ToName"       ,"%s",KW_STRING,UMSCODE_ToName       },
	{ "ToAddr"       ,"%s",KW_STRING,UMSCODE_ToAddr       },
	{ "MsgID"        ,"%s",KW_STRING,UMSCODE_MsgID        },
	{ "CreationDate" ,"%s",KW_STRING,UMSCODE_CreationDate },
	{ "ReceiveDate"  ,"%s",KW_STRING,UMSCODE_ReceiveDate  },
	{ "ReferID"      ,"%s",KW_STRING,UMSCODE_ReferID      },
	{ "Group"        ,"%s",KW_STRING,UMSCODE_Group        },
	{ "Subject"      ,"%s",KW_STRING,UMSCODE_Subject      },
	{ "Attributes"   ,"%s",KW_STRING,UMSCODE_Attributes   },
	{ "Comments"     ,"%s",KW_STRING,UMSCODE_Comments     },
	{ "Organization" ,"%s",KW_STRING,UMSCODE_Organization },
	{ "Distribution" ,"%s",KW_STRING,UMSCODE_Distribution },
	{ "Folder"       ,"%s",KW_STRING,UMSCODE_Folder       },
	{ "FidoID"       ,"%s",KW_STRING,UMSCODE_FidoID       },
	{ "MausID"       ,"%s",KW_STRING,UMSCODE_MausID       },
	{ "ReplyGroup"   ,"%s",KW_STRING,UMSCODE_ReplyGroup   },
	{ "ReplyName"    ,"%s",KW_STRING,UMSCODE_ReplyName    },
	{ "ReplyAddr"    ,"%s",KW_STRING,UMSCODE_ReplyAddr    },
	{ "LogicalToName","%s",KW_STRING,UMSCODE_LogicalToName},
	{ "LogicalToAddr","%s",KW_STRING,UMSCODE_LogicalToAddr},
	{ "RFCMsgNum"    ,"%s",KW_STRING,UMSCODE_RFCMsgNum    },

	{ "FidoText"     ,"%s",KW_STRING,UMSCODE_FidoText     },
	{ "ErrorText"    ,"%s",KW_STRING,UMSCODE_ErrorText    },
	{ "Newsreader"   ,"%s",KW_STRING,UMSCODE_Newsreader   },

	{ "RFCAttr"      ,"%s",KW_STRING,UMSCODE_RfcAttr      },
	{ "FTNAttr"      ,"%s",KW_STRING,UMSCODE_FtnAttr      },
	{ "ZerAttr"      ,"%s",KW_STRING,UMSCODE_ZerAttr      },
	{ "MausAttr"     ,"%s",KW_STRING,UMSCODE_MausAttr     },

	{ "CDate","%d-%b-%y %X",KW_DATE,UMSCODE_CreationDate },
	{ "IDate","%d-%b-%y %X",KW_DATE,UMSCODE_ReceiveDate  },

	{ "MsgNum"      ,"%ld"  ,KW_NUMBER,12 },
	{ "ChainUp"     ,"%ld"  ,KW_NUMBER, 3 },
	{ "ChainDown"   ,"%ld"  ,KW_NUMBER, 4 },
	{ "ChainLeft"   ,"%ld"  ,KW_NUMBER, 5 },
	{ "ChainRight"  ,"%ld"  ,KW_NUMBER, 6 },
	{ "HardLink"    ,"%ld"  ,KW_NUMBER,10 },
	{ "SoftLink"    ,"%ld"  ,KW_NUMBER,11 },

	{ "GlobalStatus","%08lx",KW_FLAG, 7 },
	{ "UserStatus"  ,"%08lx",KW_FLAG, 8 },
	{ "LoginStatus" ,"%08lx",KW_FLAG, 9 },

	{ "QuoteText","> ",KW_QTEXT,UMSCODE_MsgText },

	{ NULL,0,0 }
};

char *DefaultFormat = "\
Group..: <Group>\n\
From...: <FromName> (<FromAddr>)\n\
To.....: <ToName> (<ToAddr>)\n\
Subject: <Subject>\n\
\n\
<MsgText>\n\
";

static char filteradd[] = " AND readaccess=1 AND viewaccess=1";

#define FILTERADDSTR  filteradd
#define NULLFILTERSTR (&filteradd[5])
#define FILTERADDLEN  35


// Functions
// ---------

VOID PrintMessage(char *format);


// CTRL-C Stuff
// ------------

int brk(void)
{
	return(0);
}

#define ABORTED (SetSignal(0,0) & SIGBREAKF_CTRL_C)


// Main Function
// -------------

int main(int argc,char *argv[])
{
	char *filter;
	char *format = DefaultFormat;
	char *language=NULL;
	int erg = RETURN_ERROR, umserr;
	int nr=0;
	int oldtag = UMSTAG_RNoUpdate;
	int direction = 1;
	int fi;
	struct RDArgs *args_ptr;

	onbreak(brk);

	if (argc<2 || *argv[1] == '?')
	{
		fprintf(stderr,"\33[1m%s\33[0m, written by Stefan Stuntz, Public Domain.\n\nTemplate: %s\n%s",&VersionString[6],TemplateString,UsageString);
	}

	{
		int i;

		for (i=0; i<OPT_COUNT; i++)
		opts[i]=NULL;
	}

	if (args_ptr = ReadArgs(TemplateString, opts, NULL))
	{
		if (opts[OPT_FORMATFILE])
		{
			FILE *f;
			int len;
			if (f=fopen((char *)opts[OPT_FORMATFILE],"rb"))
			{
				fseek(f,0,2);
				len=ftell(f);
				fseek(f,0,0);
				if (!(len>0 && (format=calloc(1,len+1)) && fread(format,1,len,f)==len))
					format = DefaultFormat;
				fclose(f);
			}
			else
			{
				fprintf(stderr,"\7Format file \"%s\" not found.\n",argv[4]+5);
				FreeArgs(args_ptr);
				return(20);
			}
		}
		else
			if (opts[OPT_FORMAT])
				format = (char *)opts[OPT_FORMAT];

		if (opts[OPT_SETOLDFLAG])
			oldtag    = TAG_IGNORE;

		if (opts[OPT_BACKWARDS])
			direction = -1;

		if (!strnicmp(format,"LANGUAGE: ",10))
		{
			language = format+10;
			if (!(format=strchr(language,'\n')))
			{
				fprintf(stderr,"\7Invalid language spec in format file.");
				FreeArgs(args_ptr);
				return(20);
			}
			*format++=0;
		}

		if (!(filter = malloc(strlen(argv[3])+FILTERADDLEN)))
		{
			fprintf(stderr,"\7Out of memory.\n");
			FreeArgs(args_ptr);
			return(20);
		}

		if (opts[OPT_FILTER] && *(char *)opts[OPT_FILTER])
		{
			strcpy(filter,(char *)opts[OPT_FILTER]);
			strcat(filter,FILTERADDSTR);
		}
		else
		{
			strcpy(filter,NULLFILTERSTR);
		}

		if (LocaleBase = OpenLibrary("locale.library",38))
			Locale = OpenLocale(language);

		if (UMSBase = OpenLibrary(UMSNAME,UMSVERSION))
		{
			if (acc = UMSRLogin((char *)opts[OPT_SERVER],(char *)opts[OPT_USER],(char *)opts[OPT_PASSWORD]))
			{
				if (!(fi = UmsFilterExpression(filter,acc,0,0,1)))
				{
					erg = RETURN_OK;
					while (nr = UMSSearchTags(acc,
					            UMSTAG_SearchLast     ,nr,
					            UMSTAG_SearchLocal    ,TRUE,
					            UMSTAG_SearchMask     ,1,
					            UMSTAG_SearchMatch    ,1,
									UMSTAG_SearchDirection,direction,
					            TAG_DONE))
					{
						if (ABORTED)
						{
							fprintf(stderr,"\7*** User break.\n");
							erg = RETURN_WARN;
							break;
						}

						if (umserr=UMSReadMsgTags(acc,
						    UMSTAG_RMsgNum    ,nr,
						    UMSTAG_RMsgInfo   ,&MessageInfo,
						    UMSTAG_RTextFields,TextFields,
						    UMSTAG_RReadAll   ,TRUE,
						    oldtag            ,TRUE,
						    TAG_DONE))
						{
							if (!TextFields[UMSCODE_Group] || !*TextFields[UMSCODE_Group])
	                     TextFields[UMSCODE_Group]="Netmail";
							MessageInfo.msgi_MsgNum = nr;
							PrintMessage(format);
							UMSFreeMsg(acc,nr);
						}
						else
						{
							fprintf(stderr,"UMS-Error %d: %s\n",UMSErrNum(acc),UMSErrTxt(acc));
							erg = RETURN_WARN;
							/* kludge for bad behavior of UMSSearchTags() */
							if ( umserr=UMSERR_ServerTerminated ) {
								erg = RETURN_ERROR;
								break;
							}
						}
					}
				}
				else printf("\7Expression Error %ld\n",fi);

				UMSLogout(acc);
			}
			else fprintf(stderr,"\7UMS-Login failed.\n");

			CloseLibrary(UMSBase);
		}
		else fprintf(stderr,"\7Could not open ums.library V10.\n");

		if (LocaleBase)
		{
			if (Locale) CloseLocale(Locale);
			CloseLibrary(LocaleBase);
		}

		FreeArgs(args_ptr);
	}
	else
	{
		PrintFault(IoErr(), NULL);
		return(RETURN_ERROR);
	}

	return(erg);
}


struct Keyword *FindKeyword(char *name)
{
	struct Keyword *k;

	for (k=Keywords;k->name;k++)
	{
		if (!stricmp(name,k->name)) return(k);
	}
	return(NULL);
}


#ifdef _DCC
VOID DatePutCharFunc(__A0 struct MyHook *h, __A1 UBYTE c, __A2 struct Locale *loc)
{
	*h->h_Data++=c;
}
#else
VOID __asm DatePutCharFunc(register __a0 struct MyHook *h,register __a1 UBYTE c,register __a2 struct Locale *loc)
{
	*h->h_Data++ = c;
}
#endif


VOID PrintMessage(char *format)
{
	static char buf[50];
	char *c = format;
	char *ob,*cb,*fmt;
	int len;
	struct Keyword *k;

	while (*c)
	{
		if (*c=='<')
		{
			ob=c;
			if (!(cb=strchr(ob+1,'>'))) break;
			c=cb+1;
			if ((len=cb-ob-1)>49) break;
			strncpy(buf,ob+1,len); buf[len]=0;
			if (fmt=strchr(buf,',')) *fmt++=0;
			if (k=FindKeyword(buf))
			{
				if (!fmt) fmt=k->format;
				if (k->type==KW_STRING)
				{
					printf(fmt,TextFields[k->code] ? TextFields[k->code] : "");
				}
				else if (k->type==KW_NUMBER)
				{
					printf(fmt,((LONG *)&MessageInfo)[k->code]);
				}
				else if (k->type==KW_FLAG)
				{
					if ( *fmt!='%' )
					{
						char	*scan,*cfstart=fmt,output[34],*pptr;
						ULONG	p,flags=((LONG *)&MessageInfo)[k->code];

						if ( pptr=strchr(fmt,'%') ) 
						{
							scan=pptr-1;
							fmt=pptr;
						} 
						else 
						{
							scan=fmt+strlen(fmt)-1;
							fmt="%s";
						}

						pptr=output;
						p=1;
						while ( scan>=cfstart ) 
						{
							if ( *scan!='-' ) 
							{
								if ( flags&p ) 
								{
									*pptr++=toupper(*scan);
								} 
								else 
								{
									if ( isupper(*scan) )
										*pptr++='-';
								}
							}
							scan--;
							p<<=1;
						}
						*pptr='\0';
						printf(fmt,output);
					}
					else
					{
						printf(fmt,((LONG *)&MessageInfo)[k->code]);
					}
				}
				else if (k->type==KW_DATE && Locale)
				{
					LONG secs;
					char *dstr = TextFields[UMSCODE_CreationDate] ? TextFields[UMSCODE_CreationDate] : "";

					if (k->code == UMSCODE_CreationDate)
					{
						secs = DateToSeconds(dstr);
					}
					else
					{
						if (!(secs = MessageInfo.msgi_Date)) secs=1;
					}

					if (secs)
					{
						struct DateStamp ds;
						static char buf[256];
						struct MyHook hook = {{0,0},(APTR)DatePutCharFunc,NULL,buf };

						ds.ds_Days   = secs/(24*60*60);
						ds.ds_Minute = (secs%(24*60*60))/60;
						ds.ds_Tick   = (secs%60)*50;

						FormatDate(Locale,fmt,&ds,&hook);
						printf("%s",buf);
					}
					else
						printf("%s",dstr);
				}
				else if (k->type==KW_QTEXT)
				{
					char *str = TextFields[k->code] ? TextFields[k->code] : "";
					int pos=0,npos;

					while (*str)
					{
						if (pos==0) printf("%s",fmt);

						switch (*str)
						{
							case '\r':	putchar('\n');
											pos=0;
											if (*++str=='\n') str++;
											break;

							case '\n':	putchar('\n');
											pos=0;
											if (*++str=='\r') str++;
											break;

							case '\t':  npos = pos + 8 - (pos%8);
											while (pos!=npos) { putchar(' '); pos++; }
											str++;
											if (++pos>79) { putchar('\n'); pos=0; }
											break;

							default:		putchar(*str);
											str++;
											if (++pos>79) { putchar('\n'); pos=0; }
											break;
						}
					}
				}
			}
			else
				fprintf(stderr,"\7Unknown Keyword: %s\n",buf);
		}
		else if (*c=='\\')
		{
			if (!(*++c)) break;
			switch (*c)
			{
				case '\^': putchar('^');  break;
				case '\\': putchar('\\'); break;
				case 'n' : putchar('\n'); break;
				case 'r' : putchar('\r'); break;
				case 't' : putchar('\t'); break;
				case 'f' : putchar('\f'); break;
				case '<' : putchar('<');  break;
				case '>' : putchar('>');  break;
				case 'e' : putchar(27);   break;
				default  : putchar(*c);   break;
			}
			c++;
		}
		else if (*c=='^')
		{
			if (!(*++c)) break;
			putchar(*c & (~64));
			c++;
		}
		else
		{
			putchar(*c);
			c++;
		}
	}
}
