#include "microdot.h"
#include "asyncio.h"

#include "ogre.h"
#include "ogre_protos.h"

#include "md_rexx.h"

static void *brettmempool;

struct List brettlist;
BOOL didsortin;

struct TagItem undertags[]={ RT_Underscore, '_', TAG_DONE, NULL };

extern struct Library *LocaleBase;
extern ULONG checkprogramval;

extern int noreq;
ULONG __stdargs askreq( char *txt, char *button, ... )
{
	if( noreq )
		return( FALSE );

#ifndef MDV20
	if( !LocaleBase )
	{
		replacebigd( txt );
		replacebigd( button );
	}
#endif

	return( rtEZRequestA( txt, button, rinfo, (APTR)((long)(&button)+4), undertags ) );
}

#define NOSS 8
static char *schmeichelstrings[ NOSS ] = {
	"unvergleichlichen",
	"lieben",
	"liebreizenden",
	"umwerfenden",
	"grandiosen",
	"herrlichen",
	"graziösen",
	"unbeschreiblichen"
};

static char *schmeichel( void )
{
	return( schmeichelstrings[ (prefs.counter++) % NOSS ] );

}

static void doinfo(void)
{
	static struct TagItem tags[]={RTEZ_Flags,EZREQF_CENTERTEXT,TAG_DONE,0};
	int rc;
	//int isamosaic = (int)FindPort( "AMOSAIC.1" );
	char but[ 256 ];
	char *mdinfotext = 
#ifdef MDV20
	"(·) MicroDot020 (·)\nV" MDVER " (" __DATE__ ")\n\n"
#else
	"(·) MicroDot (·)\nV" MDVER " (" __DATE__ ")\n\n"
#endif
"Geschrieben von Oliver Wagner\n"
"<owagner@vapor.com>\n"
"© 1993-99, All Rights Reserved\n\n"
"WWW: http://www.vapor.com/microdot-1/\n"
"FTP: ftp.vapor.com, /pub/microdot-1/\n\n"
"%s\n\n"
"Benutzt \"reqtools.library\" © by Nico François\n"
"Benutzt \"ARexxBox\" © by Michael Balzer\n\n"
"%lD Bytes freier Speicher\n"
"Zeitverschiebung zur GMT: %lc%ld Stunde%s (%serzeit)\n\n"
"Gewidmet der %s Claudia.";

#ifndef MDV20
	if( !LocaleBase )
		replacebigd( mdinfotext );
#endif

	but[ 0 ] = 0;
	if( !registerinfo.serie )
		strcpy( but, "Registrieren|" );
	/*if( isamosaic )
		strcat( but, "Zur MicroDot-WWW-Page|" );*/
	strcat( but, "Weiter" );

	rc = rtEZRequest( mdinfotext,
		but,
		rinfo, tags,
		registerinfo.serie ? registerinfo.registriert_txt : "Unregistrierte Testversion",
		AvailMem(0),
		timezone_offset >= 0 ? '+' : '-',
		abs( timezone_offset / 3600 ),
		PLURALN( timezone_offset / 3600 ),
		( timezone_dst ) ? "Somm" : "Wint",
		schmeichel()
	 );

	switch( rc )
	{
		case 1:
			if( !registerinfo.serie )
			{
				doregisterform();
				return;
			}
		/*case 2:
			SystemTags( "rx \"address 'AMOSAIC.1'; 'show'; 'screen front'; 'activate'; 'jump url http://wade1.ab.umd.edu/support/microdot/'\"", TAG_DONE );
			break;*/
	}
}

static int __stdargs sortbcmpf(struct mbrett **s1,struct mbrett **s2)
{
	return(stricmp((*s1)->b.name,(*s2)->b.name));
}

static ULONG lastbrettcs;

static void calcbrettnamecrc( struct mbrett *mb )
{
	char tmp[ 128 ];

	strcpy( tmp, mb->b.name );
	strupr( tmp );

	mb->namecrc = crc32( tmp, strlen( tmp ) );
}

void sortbrettlist( void )
{
	int c, c2, c3;
	struct mbrett **array, *n;
	struct List tl, tl2;

	NewList( &tl );
	NewList( &tl2 );
	
	/* Move PM, Spool & Archiv to temporary list */
	for( c=0; c<4; c++ )
		AddTail( &tl, RemHead( &brettlist ) );

	c = GetNumEntries( &brettlist );
	if( c )
	{
		array = myAllocVec( 4 * c, 0 );
		if( !array )
		{
			displaybeep();
			return;
		}

		/* First, sort PM boards */
		c = 0;
		while((n=(struct mbrett*)RemHead(&brettlist)))
		{
			if( !( n->b.flags & BFLAG_EXTEND_PM ) ) 
			{
				AddHead( &tl2, n );
			}
			else
				array[ c++ ] = n;
		}
		if( c )
			qsort( array, c, 4, sortbcmpf);

		/* Sort Rest */

		c2 = 0;
		while((n=(struct mbrett*)RemHead(&tl2)))
			array[ c + c2++ ] = n;

		if( c2 )
			qsort( &array[ c ], c2, 4, sortbcmpf );

		c+= c2;
	}

	for( c2 = 0; c2 < 4; c2++ )
		AddTail( &brettlist, RemHead( &tl ) );

	if( c )
	{
		for( c2 = 0; c2<c; c2++ )
			AddTail( &brettlist, array[ c2 ] );
		myFreeVec( array );
	}

	makebrettview();
}

static void addmvnode(void *pool,struct List *l,char *txt)
{
	struct mvnode *n=LibAllocPooled(pool,sizeof(struct mvnode));
	n->txt=LibAllocPooled(pool,strlen(txt)+1);
	strcpy(n->txt,txt);
	AddTail(l,(struct Node*)n);
}

static void freemvlist(void *pool)
{
	LibDeletePool(pool);
}

static void procmapsmsg( char **msgbufp, int mode )
{
	if( isv37 )
	{
		char *mailbuf = *msgbufp;

		BPTR f = Open( "T:MD_MAPS.LST", MODE_NEWFILE );
		if( f )
		{
			int rc;
			Write( f, mailbuf, strlen( mailbuf ) );
			Close( f );
			execrexx( mode ? "MapsDel" : "MapsAdd" );
			if( ( rc = getfilelen( "T:MD_MAPS.LST" ) ) > 0 )
			{
				myFreeVec( mailbuf );
				mailbuf = myAllocVec( rc + 1, 0 );
				mailbuf[ rc ] = 0;
				f = Open( "T:MD_MAPS.LST", MODE_OLDFILE );
				Read( f, mailbuf, rc );
				Close( f );
				*msgbufp = mailbuf;
			}
			DeleteFile( "T:MD_MAPS.LST" );
		}
	}
}

static void domapsadd(int mode)
{
	FILE *f;
	struct mvnode *n;
	struct List mvlist;
	void *scs;
	char buff[256];
	struct IntuiMessage *mp,im;
	int done=0,mailsize=0;
	int markcount;
	void *mvpool;
	int rc;

	rtSetWaitPointer(w);

	f = fopen( "microdot.mapsblist", "r" );
	if( !f )
	{
		if( askreq( "Keine Brettliste vorhanden!\nBestellen Sie zuerst per MAPS eine Brettliste und\nwarten Sie, bis diese eingelesen wurde.", "Brettliste bestellen|Abbruch" ) )
		{
			sendmapsreq( 1 );
		}
		ClearPointer( w );
		return;
	}

	mvpool=LibCreatePool(MEMF_CLEAR,4096,2048);
	NewList(&mvlist);

	w->Flags |= WFLG_RMBTRAP;

	while((fgets(buff,256,f)))
	{
		clnl( buff );
		addmvnode( mvpool, &mvlist, buff );
	}
	fclose(f);

	scs=pushscs();
	setupscroller(&mvlist,3);
	displayscroller();

	preport(0,1,"Bretter per MAPS %sbestellen: Markieren Sie die gewünschten Bretter",(mode)?"ab":"");

	ClearPointer( w );

	while(!done)
	{
		while(!(mp=(struct IntuiMessage*)GetMsg(w->UserPort))) mWaitPort(w->UserPort);
		im=*mp;
		ReplyMsg(mp);

		switch(im.Class)
		{
			case IDCMP_REFRESHWINDOW:
				BeginRefresh( w );
				displayscroller();
				EndRefresh( w, TRUE );
				break;

			case IDCMP_CLOSEWINDOW:
				doaskquit();
				break;

			case MOUSEBUTTONS:
				rc = scrollerdoclick( &im );
				if( rc > 0 )
				{
					n=(struct mvnode*)GetEntry(&mvlist,getscrollercurrent());
					if(n) { n->sel^=~0; displaycurrentline(); }
				}
				else if( rc == -1 )
					done = 1;
				break;
			case MOUSEMOVE: scrollerdomove(&im); break;
			case GADGETDOWN: scrollerdogad(&im); break;
			case RAWKEY: if(scrollerdokey(&im)<0) switch(im.Code) {
					case 0x5f:	showguide("maps_add");
								break;

					case 0x5d:
					case 0x0f:
					case 0x43:
					case 0x44:
					case 0x45:	done=1; break;

				     }
				     break;

			case IDCMP_NEWSIZE:
				scrollerresize();
				break;
		}

		markcount=mailsize=0; n=(struct mvnode*)mvlist.lh_Head;
		while(n->n.mln_Succ)
		{
			if(n->sel)
			{
				mailsize += strlen( n->txt ) + strlen( prefs.maps_addprefix ) + strlen( prefs.maps_delprefix ) + 1;
				markcount++;
			}
			n=(struct mvnode*)n->n.mln_Succ;
		}
		preport(1,1,"%lD Bretter markiert.",markcount);

	}

	if( markcount )
	{
		char *mailbuf;
		if( !mode )
			rc = askreq("%lD Brett%s bestellen?","Bestellen|Bestellen & _Eintragen|Abbruch",markcount,markcount==1 ? "":"er");
		else
			rc = askreq("%lD Brett%s abbestellen?","Abbestellen|Abbruch",markcount,markcount==1 ? "":"er");

		if( rc )
		{
			mailbuf = myAllocVec( mailsize + 512,0);
			if(mailbuf)
			{
				strcpy( mailbuf, prefs.maps_msgprefix );

				n=(struct mvnode*)mvlist.lh_Head;
				while(n->n.mln_Succ)
				{
					if(n->sel)
					{
						strcpy( buff, &n->txt[ prefs.maps_list_boffs ] );
						skillfirstspace( buff );
						asprintf( mailbuf, "%s%s\n", ( mode ) ? prefs.maps_delprefix : prefs.maps_addprefix, buff );
						if( rc == 2 )
						{
							char tmpbname[ 128 ];

							strcpy( tmpbname, buff );
							if( (!ISRFC) && tmpbname[0] != '/' )
								strins( tmpbname, "/" );

							findbrett( tmpbname, -1 );
						}

					}
					n=(struct mvnode*)n->n.mln_Succ;
				}
				procmapsmsg( &mailbuf, mode );

				sendshortmail( prefs.mapsname,(mode)?prefs.maps_del:prefs.maps_add,
					mailbuf,NULL,0,0,1);

				myFreeVec(mailbuf);

				if( rc == 2 )
				{
					sortbrettlist();
				}
			}
		}
	}

	popscs(scs);
	freemvlist(mvpool);

	w->Flags &= ~WFLG_RMBTRAP;
}

#if 0
static void donervsandro( void )
{
	char bet[ 64 ];
	int c, ml;
	char mail[ 256 ];

	srand( time( 0 ) );

	/* Bis zu 10 Leerzeilen */
	ml = rand() % 10;
	mail[ 0 ] = 0;
	for( c = 0; c < ml; c++ )
		strcat( mail, "\n" );

	ml = rand() % 30;
	mail[ 0 ] = 0;
	for( c = 0; c < ml; c++ )
		strcat( mail, " " );

	if( rand() & 1 )
		strcat( mail, "Wo bleibt der Guide?" );
	else
		strcat( mail, "Wo ist der Guide?" );
	ml = rand() % 7;
	for( c = 0; c < ml; c++ )
		strcat( mail, "\n" );

	ml = rand() % 60;
	for( c = 0; c < ml; c++ )
		bet[ c ] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01234567890 !"[ rand() % 64 ];
	bet[ c ] = 0;

	if( askreq( "Beschwerde über fehlenden MicroDot.guide\nan Sandro Paolini schicken?", "Verschicken|Abbruch" ) )
	{
		sendshortmail(
			"s_paolini@aworld.aworld.de",
			bet,
			mail,
			NULL,
			NULL,
			NULL,
			NULL			
		);
	}
}
#endif 

void doaskquit( void )
{
	rtSetWaitPointer(w);
	exit(0);
}

LONG procmenu( USHORT code, ULONG quals )
{
	struct MenuItem *mit;
	long itemnum = ITEMNUM( code );
	long menunum = MENUNUM( code );
	long subnum = SUBNUM( code );
	UBYTE *rvp;
	int rc;

	if(code==MENUNULL||itemnum==NOITEM) return(-1);
	mit=ItemAddress(w->MenuStrip,code);
	if(!mit) return(-1);
	rvp=(UBYTE*)(((long)mit)+sizeof(struct MenuItem));

	if( ( !menunum ) && ( itemnum == 9 ) )
	{
		showguide( "main" );
		return( -1 );
	}

	if( *rvp )
	{
		return((LONG)(rvp[1]|((rvp[0]==0xff)?0x100:0)));
	}
	else if(!menunum) switch(itemnum)
	{
		case 0:
		case 1:
		case 2:
		case 3:
			lockwindow();
			if( !dospool() )
			{
				if( !donetcall( itemnum ) )
				{
					delspool();
					dosortin();
					didsortin = 1;
					sortbrettlist();
				}
			}
			else
				askreq( "Fehler beim Spoolen", "Abbruch" );
			resetscroller();
			unlockwindow();
			break;
		case 4: lockwindow(); sortinhand(); didsortin=1; sortbrettlist(); resetscroller(); unlockwindow();break;
		case 6:
			if( wbwindow )
			{
				doterminal();
				resetscroller();
			}
			else
			{
				lockwindow();
				doterminal();
				unlockwindow();
			}
			break;
		/*case 4: lockwindow(); doanswermode(); unlockwindow(); break;*/
		case 8: doinfo(); displayscroller(); break;
		case 11: doiconify(); return( -1 );
		case 12: selectpubscreen(); return( -1 );
		case 14: doaskquit();
				break;
	}
	else if(menunum==3) switch(itemnum)
	{
		case 14:
			switch( subnum )
			{
				case 0: if(mit->Flags&CHECKED) prefs.flags|=MDF_SHOWREAL; else prefs.flags&=~MDF_SHOWREAL; break;
				case 1: if(mit->Flags&CHECKED) prefs.flags|=MDF_SHOWALL; else prefs.flags&=~MDF_SHOWALL; break;
				case 2: if(mit->Flags&CHECKED) prefs.flags|=MDF_SORTDATE; else prefs.flags&=~MDF_SORTDATE; break;
				case 3: if(mit->Flags&CHECKED) prefs.flags|=MDF_FILTERRE; else prefs.flags&=~MDF_FILTERRE; break;
				case 4: if(!(mit->Flags&CHECKED)) prefs.flags|=MDF_NOREADERSTYLES; else prefs.flags&=~MDF_NOREADERSTYLES; break;
			}
			break;

		case 22:
			doscrollerclipboard();
			break;
	}
	else if(menunum==4) switch( itemnum )
	{
		case 0: domapsadd( 0 ); makebrettview(); calcnumentries(); displayscroller(); break;
		case 1: domapsadd( 1 ); break;

		case 2:
			rc = askreq( "Ausführliche Brettliste?", "Ausführlich|_Kurz|Abbruch" );
			if( rc )
			{
				sendmapsreq( ( rc == 1 ) ? 1 : 0 );
				displayscroller();
				/*preport( 1,1, "Brettliste angefordert." );*/
			}
			break;
		case  3: sendmapsreq( 2 ); displayscroller(); /*preport(1,1,"HILFE zu MAPS angefordert.");*/ break;
		case  5: sendmapsreq( 3 ); displayscroller(); /*preport(1,1,"CLEARPM-Kommando verschickt.");*/ break;
		case  7: sendmapsreq( 4 ); displayscroller(); break;
		case 9: domailinglist();    displayscroller(); break;
		case 10: dobugreport();    displayscroller(); break;
		case 11: doregisterform(); displayscroller();	break;
	}
	else if(menunum==5) switch(itemnum)
		 {
		case 0:
			lockwindow();
			dolanguagecfg();
			unlockwindow();
			break;

		case 2: lockwindow(); dopointcfg();		unlockwindow(); break;
		case 3: lockwindow(); doserialcfg();	unlockwindow(); break;
		case 4: lockwindow(); dosysopmodereq(); unlockwindow(); break;
		case 5: lockwindow(); domapsprefs(); 	unlockwindow(); break;

		case 6: lockwindow(); donetcfg();		unlockwindow(); break;
		case 7: lockwindow(); doulwin(); 		displayscroller(); unlockwindow(); break;
		case 8: lockwindow(); douserkillwin();  unlockwindow(); break;

		case 10: lockwindow(); doexternprefs();  unlockwindow(); break;
		case 11: if( isv37 )
				{
					dofontprefs();
				}
				else
				{
					dofontprefs13();
					/*if( mit->Flags & CHECKED )
						prefs.flags |= MDF_INTERLACE;
					else
						prefs.flags &= ~MDF_INTERLACE;
					closegui();
					opengui();*/
				}
				return( -1 );
		
		case 12: lockwindow(); dobed2prefs(); displayscroller(); unlockwindow(); break;
		case 13: lockwindow(); dobedprefs(); unlockwindow(); setmenuescflag(); break;
		case 14: lockwindow(); dobed3prefs(); unlockwindow(); setmenuescflag(); break;

		case 16: if( mit->Flags & CHECKED )
					prefs.flags |= MDF_PASSWORD;
				 else
					prefs.flags &=~MDF_PASSWORD;
				 break;

		case 17: lockwindow(); dochangepassword(); unlockwindow(); break;

	}
	else if( menunum == 6 ) switch( itemnum )
	{
		case 0:
			lockwindow();
			pgp_prefs();
			unlockwindow();
			break;
		case 1:
			lockwindow();
			pgp_setpassphrase();
			unlockwindow();
			break;
		case 2:
			lockwindow();
			pgp_changepassphrase();
			unlockwindow();
			break;
		case 3:
			lockwindow();
			pgp_rereadownkey();
			unlockwindow();
			break;
		case 5:
			lockwindow();
			pgp_procincoming();
			unlockwindow();
			break;
		case 6:
			lockwindow();
			pgp_showkeys( FALSE );
			unlockwindow();
			break;
		case 7:
			lockwindow();
			pgp_showkeys( TRUE );
			unlockwindow();
			break;
		case 8:
			lockwindow();
			pgp_checkkeys();
			unlockwindow();
			break;
	}
	else if( menunum == 7 ) switch( itemnum )
	{
		case 0:
			lockwindow();
			dorexxshell();
			unlockwindow();
			break;

		case 2:
			dorexxcmd( 0, subnum );
			break;

		case 3:
			dorexxcmd( 1, subnum );
			break;

		case 4:
			dorexxcmd( 2, subnum );
			break;

	}
	return( -1 );
	//return( procmenu( mit->NextSelect, quals ) );
}

static ULONG brettcount,newmailcount,mailcount,markcount,unreadcount;
void calcbcount(void)
{
	struct mbrett *n=(struct mbrett*)brettlist.lh_Head;

	brettcount=newmailcount=mailcount=markcount=unreadcount=0;

	while(n->n.ln_Succ)
	{
		brettcount++;
		newmailcount += n->b.newmails;
		mailcount += n->b.mails;
		unreadcount += n->b.unreadmails;
		if( n->n.ln_Type )
			markcount++;
		n = ( struct mbrett * ) n->n.ln_Succ;
	}

	preport(0,1,(markcount)?"BRETTAUSWAHL: Insgesamt %lD Brett%s, %lD markiert":"BRETTAUSWAHL: Insgesamt %lD Brett%s",brettcount,(brettcount==1)?"":"er",markcount);
	preport(1,1,"Insgesamt %lD Nachrichten, %lD neue, %lD ungelesene",mailcount,newmailcount,unreadcount);

	/* nur wenn markiert... */
	if( markcount )
		OnMenu( w, SHIFTITEM( 2 ) | SHIFTMENU( 1 ) );
	else
		OffMenu( w, SHIFTITEM( 2 ) | SHIFTMENU( 1 ) );
}

static void strcatnum(char *string,long num)
{
	asprintf( string, "_%ld", num );
}

void domenuhelp(char *menu,struct IntuiMessage *im)
{
	char buff[80];

	sprintf(buff,"menu_%s",menu);

	if(MENUNUM(im->Code)!=NOMENU && ITEMNUM(im->Code)!=NOITEM)
	{
		strcatnum(buff,MENUNUM(im->Code));
		strcatnum(buff,ITEMNUM(im->Code));
	}
	if(SUBNUM(im->Code)!=NOSUB) strcatnum(buff,SUBNUM(im->Code));

	showguide(buff);

}

static void dodelback( char *path )
{
	char buff[ 256 ];
	char *p;

	strcpy( buff, path );

	while( ( p = strrchr( buff, '/' ) ) )
	{
		*p = 0;
		if( !DeleteFile( buff ) )
			break;
	}
}

static void removeallmails( struct mbrett *mb )
{
	struct mindex *mix = loadbrettixquiet( mb );
	struct mmail *mm, *next;

	if( mix )
	{
		mm = ( struct mmail * ) mix->maillist.lh_Head;
		while( ( next = ( struct mmail * ) mm->n.mln_Succ ) )
		{
			if( !( mm->m.flags & MMF_ARCHIVE ) )
			{
				Remove( mm );
				mix->changed = 1;
			}
			mm = next;
		}
		savebrettix( mix, mb );
		freebrettix( mix );
		calcmcount_mb( mix, mb );
	}
}

static void dodelbrett( struct mbrett *mbrett, int mode )
{
	char tmp[ 256 ];

	if( mode && ( !stricmp( mbrett->b.name, prefs.username ) || 
				    !stricmp( mbrett->b.name, "/»Archiv" ) ||
				    !stricmp( mbrett->b.name, "/»Protokoll" ) ||
				    !stricmp( mbrett->b.name, "/»Spool" ) ) )

	{
		askreq( "Brett '%s' darf\nnicht gelöscht werden", "Abbruch", mbrett->b.name );
		return;
	}


	if( mode )
	{
		brettpfad( tmp, mbrett );
		closeixfile( mbrett );
		flushbrettix();
		if( strlen( mbrett->b.name ) > 1 )
		{
			delpathcontents( tmp, 0, 0 );
			dodelback( tmp );
		}
		Remove( mbrett );
	}
	else
	{
		removeallmails( mbrett );

		/*
		delpathcontents( tmp, ".Brettinfo", ".Origin" );
		mbrett->b.mails = mbrett->b.newmails = mbrett->b.unreadmails = 0;*/
		/*mbrett->nextfreemsg = 0;*/
	}
}

static void procmapsmsg( char **msgbufp, int mode );
static void dodelbretter( struct IntuiMessage *im )
{
	int dorem=ISSHIFT(im);
	struct mbrett *mbrett,*n;
	int rc;
	char *delmsg=0;

	calcbcount();
	if(!markcount)
	{
		mbrett=(struct mbrett*)GetEntry(&brettlist,getscrollercurrent());
		if(!mbrett) return;
		rc=askreq("%sBrett '%s' löschen?",dorem?"Löschen|Lö_schen & Abbestellen|Nur Abbes_tellen|Abbruch":"Löschen|Abbruch",(dorem)?"":"Inhalt von ",mbrett->b.name);
		if(!rc) return;
		lockwindow();
		if( rc >= 2)
			delmsg = myAllocVec( 1024, 0 );
		if( rc <= 2 )
			dodelbrett( mbrett, dorem );
		if(delmsg)
		{
			sprintf( delmsg, "%s%s%s\n", prefs.maps_msgprefix, prefs.maps_delprefix, ISRFC ? &mbrett->b.name[ 1 ] : mbrett->b.name );
			procmapsmsg( &delmsg, TRUE );
			sendshortmail(prefs.mapsname,prefs.maps_del,delmsg,0,0,0,1);
			myFreeVec( delmsg );
		}
		unlockwindow();
	}
	else
	{
		rc=askreq("%s%lD Bretter%s löschen?",dorem?"Löschen|Lö_schen & Abbestellen|Nur Abbes_tellen|Abbruch":"Löschen|Abbruch",(dorem)?"":"Inhalt von ",markcount,(dorem)?"":"n");
		if(!rc) return;
		lockwindow();
		if( rc>=2 )
			delmsg = myAllocVec( markcount * 128 + 512, 0 );
		if( delmsg )
		{
			strcpy( delmsg, prefs.maps_msgprefix );
		}
		mbrett=(struct mbrett*)brettlist.lh_Head;
		while(n=(struct mbrett*)mbrett->n.ln_Succ)
		{
			if(mbrett->n.ln_Type)
			{
				if( delmsg )
					asprintf( delmsg, "%s%s\n", prefs.maps_delprefix, ISRFC ? &mbrett->b.name[ 1 ] : mbrett->b.name );
				if( rc <= 2 )
					dodelbrett( mbrett, dorem );
			}
			mbrett->n.ln_Type = 0;
			mbrett = n;
		}
		if( delmsg )
		{
			if( delmsg[ 0 ] )
			{
				procmapsmsg( &delmsg, TRUE );
				sendshortmail( prefs.mapsname, prefs.maps_del, delmsg, 0, 0, 0, 1 );
			}
			myFreeVec( delmsg );
		}
		unlockwindow();
	}
	makebrettview();
	resetscroller();
}

void brettpfad(char *path,struct mbrett *mb)
{
	char *p;

	strcpy(path,prefs.datadir);
	p=mb->b.name;

	if(p[0]=='/') p++;
	if(p[0]=='»') p++;
	addpart(path,p);
}

/*void brettpfadfile(char *path,struct mbrett *mb,int num)
{
	char *p;
	char nb[16];

	sprintf(nb,"%ld",num);

	strcpy(path,prefs.datadir);
	p=mb->b.name;

	if(p[0]=='/') p++;
	if(p[0]=='»') p++;
	addpart(path,p);
	checkpath(path);
	addpart(path,nb);
}*/


static void doeditbrett(struct mbrett *mb)
{
	char path[256];
	int rc;
	long wasnotthere;

	brettpfad(path,mb);
	addpart(path,".origin");
	DeleteFile("T:mdtemp.origin");
	wasnotthere=copyfile(path,"T:mdtemp.origin");
	rc=dobrettprefs(mb,0,"Brettparameter ändern...");
	if(rc) {
		brettpfad(path,mb);
		checkpath(path);
		addpart(path,".origin");
		DeleteFile(path);
		copyfile("T:mdtemp.origin",path);
	}
	DeleteFile("T:mdtemp.origin");
}


static void doeditbretter(void)
{
	struct mbrett *mbrett,*n;
	struct brett cmpbrett;
	struct brettinfo cmpbi;

	calcbcount();
	if( !markcount )
	{
		mbrett=(struct mbrett*)GetEntry(&brettlist,getscrollercurrent());
		doeditbrett(mbrett);
	}
	else
	{
		memset( &cmpbrett, 0, sizeof( cmpbrett ) );
		memset( &cmpbi, 0, sizeof( cmpbi ) );
		if( !dobrettwildreq( &cmpbrett, &cmpbi, markcount ) )
			return;
		mbrett=(struct mbrett*)brettlist.lh_Head;
		while(n=(struct mbrett*)mbrett->n.ln_Succ)
		{
			/* Brett ändern? */
			if(mbrett->n.ln_Type)
			{
				mbrett->n.ln_Type=0;

				if( cmpbrett.betreff[0] )
					strcpy( mbrett->b.betreff, cmpbrett.betreff );

				if( cmpbrett.days )
					mbrett->b.days = cmpbrett.days;

				if( cmpbrett.language )
					mbrett->b.language = cmpbrett.language - 1;

				if( cmpbrett.mails )
				{
					if( cmpbrett.flags & BFLAG_NOCRUNCH )
						mbrett->b.flags |= BFLAG_NOCRUNCH;
					else
						mbrett->b.flags &= ~BFLAG_NOCRUNCH;
				}

				if( cmpbi.autoexpfad[0] )
				{
					struct brettinfo *bi = getbrettinfo( mbrett );

					strcpy( bi->autoexpfad, cmpbi.autoexpfad );
					setbrettinfo( mbrett, bi );
				}

			}
			mbrett=n;
		}
	}
}

static void donewbrett( int pm )
{
	struct mbrett mb, *new;
	long rc;
	BPTR testlock;

	memset(&mb,0,sizeof(struct mbrett));
	if( pm )
		mb.b.flags = BFLAG_EXTEND_PM;
	mb.b.days = 14;

	DeleteFile( "T:mdtemp.origin" );
	rc = dobrettprefs( &mb, TRUE, pm ? "Neues PM-Brett... " : "Neues Brett..." );

	if(rc)
	{
		if( pm )
		{
			mb.b.name[ 78 - strlen( prefs.username ) ] = 0;
			strins( mb.b.name, prefs.username );
			mb.b.flags |= BFLAG_EXTEND_PM;
		}

		new = LibAllocPooled( brettmempool, sizeof( struct mbrett ) );
		*new = mb;

		AddTail( &brettlist, new );
		calcbrettnamecrc( new );

		testlock = Lock( "T:mdtemp.origin", SHARED_LOCK );
		if( testlock )
		{
			char path[ 256 ];

			UnLock( testlock );
			brettpfad( path, new );
			checkpath( path );
			addpart( path, ".origin" );
			copyfile( "T:mdtemp.origin", path );
			DeleteFile( "T:mdtemp.origin" );
		}
		sortbrettlist();
		updateprop();
		displayscroller();
	}
}

static void domsg( int firstnew )
{
	struct mbrett *mb=(struct mbrett*)GetEntry(&brettlist,getscrollercurrent());

	if(mb)
	{
		rtSetWaitPointer( w );
		execrexx( "Board" );
		doinhalt( mb, firstnew );
	}
}

static void markall( void )
{
	struct mbrett *mbrett=(struct mbrett*)brettlist.lh_Head;
	int c = 0;

	while( mbrett->n.ln_Succ )
	{
		if( ++c > 4 )
			mbrett->n.ln_Type = 0xff;
		mbrett = (struct mbrett *) mbrett->n.ln_Succ;
	}
}

static void markbyname( void )
{
	struct mbrett *mbrett=(struct mbrett*)brettlist.lh_Head;
	struct mbrett *curr = ( struct mbrett * ) GetEntry( &brettlist, getscrollercurrent() );
	char match[ 80 ], matchpat[ 88 ];
	int rc;

	if( curr )
		strcpy( match, curr->b.name );
	else
		match[ 0 ] = 0;	

	rc = rtGetString( match, 79, NULL, rinfo,
		RTGS_TextFmt, "Markieren nach Brettnamen\nMuster:",
		RTGS_Flags, GSREQF_CENTERTEXT,
		TAG_DONE
	);

	if( rc != TRUE )
		return;

	expandpattern( match, matchpat );

	while( mbrett->n.ln_Succ )
	{
		if( astcsma( mbrett->b.name, matchpat ) )
			mbrett->n.ln_Type = 0xff;
		mbrett = (struct mbrett *) mbrett->n.ln_Succ;
	}
}


void showguidegads( struct IntuiMessage *im, char *defguide )
{
	if( ( prefs.flags & MDF_GADGETS ) && ( im->MouseY >= gyoffs ) )
	{
		showguide( "fontprefs_16" );
	}
	else
	{
		showguide( defguide );
	}
}

// Datenbank scannen...

static void calcdbstatus( void )
{
	struct pindex *pixtab, *pix;
	ULONG dbfilecount = 0;
	ULONG dbfilemin = MAXINT, dbfilemax = 0;
	ULONG dbfileaccu = 0, dbfileixaccu = 0;
	ULONG dbfileixused = 0;
	ULONG dbmailcountused = 0;
	ULONG dbmailcountpack = 0, dbmailcountxpk = 0;
	struct FileInfoBlock *fib;
	BPTR lock, f;
	int cdblen, mc;
	char name[ 64 ];
	int c;

	ixc_flush();
	closeallixfiles();
	sortbrettlist();
	fib = allocfib();

	// Zuerst Puffer scannen

	pushdir( prefs.datadir );
	lock = Lock( "", SHARED_LOCK );
	Examine( lock, fib );

	while( ExNext( lock, fib ) )
	{
		if( !astcsma( fib->fib_FileName, "MD_#?.Index" ) )
			continue;

		dbfilecount++;
		dbfileixaccu += fib->fib_Size;

		if( fib->fib_Size < sizeof( struct pindex ) )
			continue;

		pixtab = myAllocVec( fib->fib_Size, 0 );
		if( !pixtab )
			continue;
		f = Open( fib->fib_FileName, MODE_OLDFILE );
		if( !f )
		{
			myFreeVec( pixtab );
			continue;
		}
		Read( f, pixtab, fib->fib_Size );
		Close( f );

		mc = fib->fib_Size / sizeof( struct pindex );

		strcpy( name, fib->fib_FileName );
		strcpy( strrchr( name, '.' ), ".Data" );
		cdblen = getfilelen( name );

		if( cdblen >= 0 )
		{
			dbfileaccu += cdblen;
			if( cdblen < dbfilemin )
				dbfilemin = cdblen;
			if( cdblen > dbfilemax )
				dbfilemax = cdblen;

			for( c = 0, pix = pixtab; c < mc; c++, pix++ )
			{
				if( ! ( pix->flags & PIF_KILLED ) )
				{
					dbfileixused += sizeof( struct pindex );
					dbmailcountused++;
					if( pix->flags & PIF_PACKED )
					{
						if( pix->flags & PIF_XPK )
							dbmailcountxpk++;
						else
							dbmailcountpack++;
					}
				}
			}
		}
		myFreeVec( pixtab );

	}
	freefib( fib );
	UnLock( lock );
	popdir();

	if( !dbfilecount || !dbfileaccu )
	{
		askreq( "Keine Daten vorhanden.", "Ok" );
		return;
	}

	askreq( 
"Datenbank-Status:\n\n"
"%lD Nachrichten insgesamt, %lD Referenzen (%ld.%ld%%).\n"
"%lD Nachrichten (%ld.%ld%%) intern, %lD (%ld.%ld%%) mit XPK gepackt.\n\n"
"Datenbank besteht aus %lD Dateien mit insgesamt %lD Bytes.\n"
"Größte Datei %lD Bytes, Kleinste %lD Bytes, ø %lD Bytes.\n\n"
"%lD Bytes Index, davon %lD benutzt (%ld.%ld%%)."
"%s%s",
		"Ok",
		dbmailcountused, mailcount, ( mailcount * 100 ) / dbmailcountused, ( ( mailcount * 1000 ) / dbmailcountused ) % 10, 
		dbmailcountpack, ( dbmailcountpack * 100 ) / dbmailcountused, ( ( dbmailcountpack * 1000 ) / dbmailcountused ) % 10,
		dbmailcountxpk, ( dbmailcountxpk * 100 ) / dbmailcountused, ( ( dbmailcountxpk * 1000 ) / dbmailcountused ) % 10,

		dbfilecount, dbfileaccu,
		dbfilemax, dbfilemin, dbfileaccu / dbfilecount,
		dbfileixaccu, dbfileixused, ( dbfileixused * 100 ) / dbfileixaccu, ( ( dbfileixused * 1000 ) / dbfileixaccu ) % 10,
		( mailcount < ( dbmailcountused / 2 ) ) ? "\n\nSie sollten die Datenbank reorganisieren!" : "",
		( ( dbfileaccu / dbfilecount ) < 65536 ) ? "\n\nSie sollten beim nächsten Reorganisieren\ndie Funktionen \"Zusammenfassen kleiner Files\" verwenden!" : ""
	);
}


/* Weiterschalten? */
extern int donextnewboard;

extern int scrollerenter;
void dobretter( void )
{
	struct IntuiMessage *mp,im;
	long rc;
	int oldflags;
	struct AppMessage *am;
	ULONG sig;
	int oldshowboardtree = prefs.showboardtree;

	if( donextnewboard )
	{
		struct mbrett *m;
		int nn = donextnewboard;

		rc = lastbrettcs & 0xffff;
		if( rc >= 0 && rc <= 2 )
			rc = 3;
		m =(struct mbrett*)GetEntry(&brettlist, rc + 1 );

		donextnewboard = FALSE;

		if( !m )
			m = ( struct mbrett * ) brettlist.lh_Head;

		while( m->n.ln_Succ )
		{
			if( m->b.newmails )
			{
				/*setscrollercurrent(GetEntryNum(&brettlist,m));
				showcurrentitem();
				lastbrettcs = getscrollerstate();*/
				lastbrettcs = GetEntryNum( &brettlist, m );
				setscrollercurrent_internal( lastbrettcs );
				if( lastbrettcs > 3 )
					lastbrettcs |= ( ( lastbrettcs - 3 ) << 16 );
				domsg( nn );
				return;
			}
			m=(struct mbrett*)m->n.ln_Succ;
		}
		displaybeep();
	}

	//setupscroller( &brettlist, 0 );
	setupscroller( &brettlist, checkprogramval );
	setscrollerstate( lastbrettcs );

	/* Menus umschalten */
	OnMenu( w, SHIFTMENU( 1 ) | SHIFTITEM( NOITEM ) );
	OffMenu( w, SHIFTMENU( 2 ) | SHIFTITEM( NOITEM ) );
	OffMenu( w, SHIFTMENU( 3 ) | SHIFTITEM( NOITEM ) );

	if( rexxsig )
	{
		OnMenu( w, FULLMENUNUM( 7, 3, NOSUB ) );
		OffMenu( w, FULLMENUNUM( 7, 4, NOSUB  ) );
	}

	calcbcount();

	oldflags = prefs.flags & MDF_GADGETS;

	dorexx( 0 );

	ClearPointer(w);

	for(;;)
	{
		if( scrollerenter )
		{
			scrollerenter = FALSE;
			domsg( FALSE );
			return;
		}

		if( !( mp = (struct IntuiMessage *) GetMsg( w->UserPort ) ) )
		{
			sig = mWait( appwindowsig | ( 1 << w->UserPort->mp_SigBit ) | rexxsig );

			if( sig & appwindowsig )
			{
				while( ( am = ( struct AppMessage *) GetMsg( appwindowmp ) ) )
				{
					if( am->am_NumArgs )
					{
						char name[ 256 ];
						struct WBArg *wba = am->am_ArgList;

						NameFromLock( wba->wa_Lock, name, 255 );
						AddPart( name, wba->wa_Name, 255 );
					
						lockwindow();
						DeleteFile(TMPMSGNAME);
						dosendwin("Neue Nachricht verschicken...","","",NULL,NULL, 2, 0,0,0,0,NULL, name, NULL, NULL );
						calcbcount();
						displayscroller();
						unlockwindow();
					}
					ReplyMsg( am );
				}
			}

			if( rexxsig && ( sig & rexxsig ) )
				dorexx( 0 );

			continue;
		}
		im = *mp;
		ReplyMsg( mp );

		switch(im.Class)
		{
			case IDCMP_REFRESHWINDOW:
				BeginRefresh( w );
				displayscroller();
				EndRefresh( w, TRUE );
				break;

			case IDCMP_CLOSEWINDOW:
				doaskquit();
				break;

			case IDCMP_NEWSIZE:
				scrollerresize();
				break;

			case MOUSEBUTTONS:
				rc = scrollerdoclick(&im);
				if( rc == 1 )
				{
					lastbrettcs = getscrollerstate();
					domsg( FALSE );
					return;
				}
				else if( rc == -2 )
				{
					im.Class		= RAWKEY;
					im.Code 		= 0x36;
					im.Qualifier 	= 0;
					goto rawkey;
				}
				calcbcount();
				break;
			case MOUSEMOVE:
				scrollerdomove(&im);
				break;
			case GADGETDOWN:
				scrollerdogad(&im);
				break;

			case GADGETUP:
				rc = scrollerdogad( &im );
				if( !rc )
					break;
				im.Class	 = RAWKEY;
				im.Code 	 = rc & 0xff;
				im.Qualifier = ( rc & 0x100 ) ? QSHIFT : 0;
				goto rawkey;
			case MENUPICK:
				rc = procmenu( im.Code, im.Qualifier );
				if( ( prefs.showboardtree != oldshowboardtree ) || ( oldflags != ( prefs.flags & MDF_GADGETS ) ) )
				{
					if( prefs.showboardtree != oldshowboardtree )
						makebrettview();
					rtSetWaitPointer( w );
					return;
				}
				if( rc < 0 )
				{
					calcbcount();
					break;
				}
				im.Code 	 = rc & 0xff;
				im.Qualifier = ( rc & 0x100 ) ? IEQUALIFIER_LSHIFT : 0;
rawkey:
					/* fallthrough */
			case RAWKEY:
					rc = scrollerdokey( &im );
					if( rc == 1 )
						calcbcount();
					else if( rc )
				      switch(im.Code)
					 {
					case 0x5f:
						showguidegads( &im, "brettauswahl" );
						break;

					case 0x18:	/* Brettindex wiederherstellen */
						if( ISSHIFT( &im ) )
						{
							lockwindow();
							restoremindex( (struct mbrett*) GetEntry( &brettlist, getscrollercurrent() ) );
							displayscroller();
							unlockwindow();
						}
						break;

					case 0x43: 
					case 0x44: lastbrettcs=getscrollerstate(); domsg( FALSE ); return;

					case 0x19:  lockwindow();
								if( ISSHIFT( &im ) )
								{
									donewbrett( TRUE );
								}
								else
								{
									doeditbretter();
									displayscroller();
								}
								calcbcount();
								unlockwindow();
								break;

					case 0x17: 	lockwindow();
								if( ISSHIFT( &im ) )
									doreorg();
								else
									setallold();
								displayscroller();
								calcbcount();
								unlockwindow();
								break;

					case 0x21:  // Datenbank-Status
						lockwindow();
						calcdbstatus();
						unlockwindow();
						break;

					case 0x5e: /* zehner-tastatur + */
					case 0x36: if(ISSHIFT(&im)) { lockwindow(); donewbrett( FALSE ); calcbcount(); unlockwindow(); break; }
						else {
							struct mbrett *m=(struct mbrett*)GetEntry(&brettlist,getscrollercurrent());
							if(!m) m=(struct mbrett*)brettlist.lh_Head;
							while(m->n.ln_Succ)
							{
								if(m->b.newmails)
								{
									setscrollercurrent(GetEntryNum(&brettlist,m));
									showcurrentitem();
									lastbrettcs=getscrollerstate();
									domsg( TRUE );
									return;
								}
								m=(struct mbrett*)m->n.ln_Succ;
							}
							displaybeep();
						}
						break;
							
					case 0x46:
						dodelbretter( &im );
						if( getscrollercurrent() >= GetNumEntries( &brettlist ) )
						{
							setscrollercurrent( GetNumEntries( &brettlist ) - 1 );
						}
						findtopnode();
						calcnumentries();
						calcbcount();
						showcurrentitem();
						updateprop();
						displayscroller();
						break;

					case 0x12:
							lockwindow();
							DeleteFile(TMPMSGNAME);
							{
								FILE *f = fopen( TMPMSGNAME, "w" );
								if( f )
								{
									fprintf( f, "\n\n\n\n" );
									doorigin( f, 0, NULL );
									fclose( f );
								}
							}
							dosendwin( "Neue Nachricht verschicken...", "", "", NULL, NULL, 0, 0,0,0,0,NULL, NULL, NULL, NULL );
							calcbcount();
							displayscroller();
							unlockwindow();
							break;

					case 0x37:
								if( ISSHIFT( &im ) )
									markall();
								else
									markbyname();
								calcbcount();
								displayscroller();
								break;

					case 0x4a:
					case 0x20: lockwindow(); docrunch((ISSHIFT(&im))?0:(struct mbrett*)GetEntry(&brettlist,getscrollercurrent()));
							calcbcount(); displayscroller(); unlockwindow(); break;

					case 0x27: lockwindow(); docrunchkillfile(); unlockwindow(); break;


					case 0x50:
					case 0x51:
					case 0x52:
					case 0x53:
					case 0x54:
						dorexxcmd( 0, ( im.Code - 0x50 ) + ( ISSHIFT( &im ) ? 5 : 0 ) );
						break;

					case 0x55:
					case 0x56:
					case 0x57:
					case 0x58:
					case 0x59:
						dorexxcmd( 1, ( im.Code - 0x55 ) + ( ISSHIFT( &im ) ? 5 : 0 ) );
						break;

				     }
				     break;

			case IDCMP_MENUHELP:
				domenuhelp("brett",&im);
				break;
		}

		if( didsortin )
		{
			updateprop();
			displayscroller();
			didsortin = 0;
		}
	}
}

static ULONG addbrett(char *name,char *betreff)
{
	struct mbrett *new = LibAllocPooled( brettmempool, sizeof( struct mbrett ) );

	if( !new )
		return( 0 );
	strcpy( new->b.name, name );
	strcpy( new->b.betreff, betreff );
	new->n.ln_Name = new->b.name;
	new->b.days = 14;
	calcbrettnamecrc( new );

	AddTail( &brettlist, new );

	return(-1);
}

static void newinit(void)
{
	struct mbrett *mapsbrett;
	struct brettinfo bi = { "", "", "MAPS", "ABS", "MAPS" };
	char buffer[ 80 ];

	NewList( &brettlist );
	addbrett( prefs.username, "Persönliche Nachrichten" );
	addbrett( "/»SPOOL", "Ausgehende Nachrichten");
	addbrett( "/»PROTOKOLL", "Netcall-Protokolle");
	addbrett( "/»ARCHIV", "Archivierte Nachrichten");

	strcpy( buffer, prefs.username );
	strcat( buffer, "/MAPS" );
	addbrett( buffer, "MAPS-Nachrichten" );

	mapsbrett = ( struct mbrett * ) GetEntry( &brettlist, 4 );
	if( mapsbrett )
	{
		mapsbrett->b.flags |= BFLAG_EXTEND_PM;
		setbrettinfo( mapsbrett, &bi );
	}
}

static void findbrettbetreffinmapslist( char *bn, char *to )
{
	char buffer[ 256 ];
	char *p, *p2;
	FILE *f;
	int c = prefs.maps_list_offs, d;

	if( *bn == '/' )
		bn++;

	pushhomedir();
	f = fopen( "microdot.mapsblist" , "r" );
	popdir();
	if( !f )
		return;
	fsetbuf( f, 4096 );

	while( ( fgets( buffer, 256, f ) ) )
	{
		clnl( buffer );
		d = strlen( buffer );
		if( c >= d )
			continue;
		p = &buffer[ prefs.maps_list_boffs ];
		
		if( *p == '/' )
			p++;
		p2 = p;
		while( *p2 && !isspace( *p2 ) )
			p2++;
		if( !p2[0] )
			continue;
		*p2++ = 0;
		if( !stricmp( bn, p ) )
		{
			if( !c )
			{
				strncpy( to, stpblk( p2 ), 31 );
			}
			else
				strncpy( to, &buffer[ c ], 31 );
			break; 
		}
	}
	fclose( f );
}

struct mbrett *findpmbrett( struct hlist *hlist )
{
	struct header *hl;
	int cmplen;
	struct mbrett *mb = ( struct mbrett * ) GetEntry( &brettlist, 4 );
	char *p;
	char buffer[ 256 ];

	while( mb->n.ln_Succ && ( mb->b.flags & BFLAG_EXTEND_PM ) )
	{
		struct brettinfo *bi = getbrettinfo( mb );

		cmplen = strlen( bi->expm_data );

		if( cmplen && bi->expm_header[ 0 ] )
		{
			strcpy( buffer, bi->expm_header );
			p = strtok( buffer, "|, " );

			while( p )
			{
				hl = hl_findheader( hlist, p );

				while( hl )
				{
					if( !strnicmp( hl->data, bi->expm_data, cmplen ) || astcsma( hl->data, bi->expm_data ) )
						return( mb );
					hl = hl_findnextheader( hl );
				}
				p = strtok( NULL, "|, " );
			}
		}

		/* Keine Header-Übereinstimmung; vielleicht EMP: ? */
		cmplen = strlen( bi->antwortan );
		if( cmplen )
		{
			hl = hl_findheader( hlist, "EMP" );

			while( hl )
			{
				if( !strnicmp( hl->data, bi->antwortan, cmplen ) )
					return( mb );
				hl = hl_findnextheader( hl );
			}
		}

		mb = mb->n.ln_Succ;
	}

	/* Kein spezielles PM-Brett gefunden */

	return( ( struct mbrett * ) brettlist.lh_Head );
}

struct mbrett *findbrett(char *name,int create)
{
	struct mbrett *mb=(struct mbrett*)brettlist.lh_Head;
	char tmp[ 128 ];
	ULONG crc;

	if( !name )
		return( mb );

	stccpy( tmp, name, sizeof( tmp ) );
	strupr( tmp );
	crc = crc32( tmp, strlen( tmp ) );

	while( mb->n.ln_Succ )
	{
		if( ( mb->namecrc == crc ) && !stricmp( name, mb->b.name ) )
			return( mb );
		mb=(struct mbrett*)mb->n.ln_Succ;
	}
	if( create && !strstr( name, "//" ) )
	{
		char betreff[40];
		sprintf( betreff, "Eingetragen %s", dat2str( time( 0 ) ) );

		findbrettbetreffinmapslist( name, betreff );

		addbrett( name, betreff );
		mb = findbrett( name, FALSE );
		if( mb )
			makembrettlastwrite( mb );
		return( mb );
	}
	return(0);
}

static int slcmp( char *s1, char *s2 )
{
	int c = 0, d = 0;
	int sc = '/';
	int sc2 = ( prefs.mode == MDM_RFC ) ? '.' : '/';

	if( *s1 == sc || *s1 == sc2 )
		s1++;
	if( *s2 == sc || *s2 == sc2  )
		s2++;

	while( *s1 && *s2 && ( *s1 == *s2 ) )
	{
		s1++;
		s2++;
		c++;
	}

	if( *s1 == sc || *s1 == sc2 )
		s1--;
	if( *s2 == sc || *s2 == sc2 )
		s2--;

	while( ( c > 0 ) )
	{
		if( *s1 == sc || *s1 == sc2 )
			d++;
		s1--;
		c--;
	}

	return( d );
}

static char *bnind( char *bn, int ind )
{
	int sc = '/';
	int sc2 = ( prefs.mode == MDM_RFC ) ? '.' : '/';

	while( *bn && ind )
	{
		if( *bn == sc || *bn == sc2 )
			ind--;
		bn++;
	}
	if( *bn )
		return( bn );
	else
		return( NULL );
}

static void convbn( char *bn )
{
	char *bn2 = bn;

	while( ( bn = strchr( bn, '/' ) ) )
		*bn++ = 3;

	if( prefs.mode == MDM_RFC )
	{
		while( ( bn2 = strchr( bn2, '.' ) ) )
			*bn2++ = 3;
	}
}

static struct mbrett * attachbl( struct mbrett *n, int indent, char *indentspace )
{
	char *bn = n->b.name;
	int cmp;
	char *p;
	int offsl;

	memset( indentspace, 32, 127 );

	if( *bn == '/' || ( prefs.mode == MDM_RFC && *bn == '.' ) )
		bn++;

	/* Aktuellen Eintrag erzeugen */
	p = bnind( bn, indent );
	if( p )
	{
		offsl = ( p - bn ) + 1;
		strcpy( &indentspace[ offsl ], p );
	}
	else
	{
		strcpy( indentspace, bn );
	}
	/*Printf("ACTUAL: %ld, %s\n", offsl, p );*/
	strncpy( n->viewname, indentspace, 80 );
	convbn( n->viewname );

	n = (struct mbrett *) n->n.ln_Succ;
	while( n->n.ln_Succ )
	{
		/* Übereinstimmung mit nächstem Eintrag? */
		cmp = slcmp( bn, n->b.name );
		if( cmp <= indent )
			break;
		n = attachbl( n, cmp, indentspace );

	}
	return( n );
}

static void rollbv( struct mbrett *n )
{
	char cmp[ 84 ];
	UBYTE *p;
	int c;

	cmp[ 0 ] = cmp[ 83 ] = 0;

	while( n->n.ln_Pred )
	{
		for( c = 0; n->viewname[ c ] && cmp[ c ]; c++ )
		{
			if( ( n->viewname[ c ] == 3 ) && ( cmp[ c ] == 2 ) )
				n->viewname[ c ] = 6;

		}

		p = strchr( n->viewname, 0 );
		while( p >= n->viewname && *p != 32 )
			p--;
		memcpy( n->viewname, cmp, min( strlen( cmp ), p - n->viewname + 1 ) );

		if( *p == 32 )
			*p = 4;
		else if( *p == 2 )
			*p = 1;

		strcpy( cmp, n->viewname );

		p = cmp;
		while( *p )
		{
			if( *p == 4 || *p == 1 )
				*p = 2;
			else if( *p == 3 || *p > 5 )
				*p = 32;

			p++;
		}

		n = ( struct mbrett * ) n->n.ln_Pred;
	}
}

#define BLR_ID 1

int bl_maxname, bl_maxcomment;

static void calcblmaxes( void )
{
    struct mbrett *n = (struct mbrett *) brettlist.lh_Head;

	bl_maxname = bl_maxcomment = 0;

	while( n->n.ln_Succ )
	{
		bl_maxname = max( strlen( n->viewname ), bl_maxname );
		bl_maxcomment = max( strlen( n->b.betreff ), bl_maxcomment );
		n = (struct mbrett *) n->n.ln_Succ;
	}
}

void makebrettview( void )
{
    struct mbrett *n = (struct mbrett *) brettlist.lh_Head;
	char indentspace[ 128 ];

    if( !prefs.showboardtree )
    {
		while( n->n.ln_Succ )
		{
			strcpy( n->viewname, n->b.name );
			n = (struct mbrett *) n->n.ln_Succ;
		}
		calcblmaxes();
		return;
    }
	/* Brettbaum aufbauen */
	indentspace[ 127 ] = 0;
	while( n->n.ln_Succ )
	{
		n = attachbl( n, 0, indentspace );
	}
	rollbv( ( struct mbrett * ) brettlist.lh_TailPred );
	(( struct mbrett * ) brettlist.lh_Head)->viewname[0] = 7;
	calcblmaxes();
}

static void blscandir( struct ogwin *ogw, struct brett *b, FILE *f, char *name )
{
	char tmp[ 256 ];
	BPTR lock;
	struct FileInfoBlock *fib;

	strcpy( tmp, prefs.datadir );
	addpart( tmp, name );
	ogreSetStringValue( ogw, BLR_ID, tmp );

	lock = Lock( tmp, SHARED_LOCK );
	if( !lock )
		return;

	fib = allocfib();
	if( !fib )
	{
		UnLock( lock );
		return;
	}

	if( !Examine( lock, fib ) )
	{
		UnLock( lock );
		freefib( fib );
		return;
	}

	/* Gut, nun Liste durchgehen. Falls .index, dann Brett
       anhängen, falls Dir, dann weitersuchen */

	while( ExNext( lock, fib ) )
	{
		if( !stricmp( fib->fib_FileName, ".mindex" ) )
		{
			if( !stricmp( name, "PROTOKOLL" ) ||
				!stricmp( name, "ARCHIV" ) ||
				!stricmp( name, "SPOOL" ) ||
				!stricmp( name, prefs.username ) )
			
			{
				continue;
			}

			tmp[ 0 ] = '/';
			strcpy( &tmp[1], name );
			strupr( tmp );
			strcpy( b->name, tmp );
			strcpy( b->betreff, "<gerettet>" );
			findbrettbetreffinmapslist( tmp, b->betreff );
			fwrite( b, sizeof( *b ), 1, f );

		}
		else if( fib->fib_DirEntryType > 0 )
		{
			strcpy( tmp, name );
			addpart( tmp, fib->fib_FileName );
			blscandir( ogw, b, f, tmp );
		}
	}

	UnLock( lock );
	freefib( fib );
}



static void doblrecover( void )
{
	FILE *f;
	struct ogwin *ogw;
	struct brett b;

	f = fopen( "microdot.boardix", "w" );

	if( !f )
	{
		displaybeep();
		return;
	}

	memset( &b, 0, sizeof( b ) );
	b.flags |= BFLAG_HASBINFO;
	strcpy( b.name, prefs.username );
	strcpy( b.betreff, "Persönliche Nachrichten" );
	fwrite( &b, sizeof( b ), 1, f );
	strcpy( b.betreff, "Ausgehende Nachrichten" );
	strcpy( b.name, "/»SPOOL" );
	fwrite( &b, sizeof( b ), 1, f );
	strcpy( b.betreff, "Netcall-Protokolle" );
	strcpy( b.name, "/»PROTOKOLL" );
	fwrite( &b, sizeof( b ), 1, f );
	strcpy( b.betreff, "Archivierte Nachrichten" );
	strcpy( b.name, "/»ARCHIV" );
	fwrite( &b, sizeof( b ), 1, f );

	ogw = ogreInitWindow( scr, 0, 0, "Brettlistenrestauration" );
	ogreAddGroup( ogw, 0, OGFRAME_NONE, NULL );
	ogreAddText( ogw, 0, "Untersuche:", BLR_ID, "", 44, 1 );
	if( !ogreOpenWindow( ogw ) )
		return;

	blscandir( ogw, &b, f, "" );
	fclose( f );

	ogreExitWindow( ogw );
}

/* MicroDot-1.x-Daten wandeln... */

//#define copytilleof(f1,f2) {while(((ch=fgetc(f1))!=EOF))fputc(ch,f2);}

static void copytilleof( BPTR f1, BPTR f2 )
{
	char buffer[ 1024 ];
	int rc;

	while( ( rc = Read( f1, buffer, 1024 ) ) > 0 ) 
		Write( f2, buffer, rc );
}

static void copy1xbrett( int puffer, struct brett *b )
{
	struct mbrett mb;
	char path[ 256 ];
	int mc = 0;
	struct pindex pix;
	struct mail newmail;
	struct oldmail oldmail;
	BPTR oldix;
	BPTR newix;
	BPTR puf;
	BPTR pufix;
	BPTR md;
	int ml;

	mb.b = *b;
	brettpfad( path, &mb );
	addpart( path, ".index" );
	oldix = Open( path, MODE_OLDFILE );
	if( !oldix )
		return;

	brettpfad( path, &mb );
	addpart( path, ".MIndex" );
	newix = Open( path, MODE_NEWFILE );
	if( !newix )
		return;

	sprintf( path, "%sMD_%lx.Data", prefs.datadir, puffer );
	puf = Open( path, MODE_NEWFILE );
	if( !puf )
		return;

	sprintf( path, "%sMD_%lx.Index", prefs.datadir, puffer );
	pufix = Open( path, MODE_NEWFILE );
	if( !pufix )
		return;

	/* Alten index überspringen */
	Seek( oldix, sizeof( struct index ), OFFSET_BEGINNING );

	brettpfad( path, &mb );
	pushdir( path );

	/* Nachrichten durchgehen */
	while( ( Read( oldix, &oldmail, sizeof( oldmail ) ) ) == sizeof( oldmail ) )
	{
		memset( &newmail, 0, sizeof( newmail ) );
		memset( &pix, 0, sizeof( pix ) );

		sprintf( path, "%ld", mc );
		ml = getfilelen( path );
		md = Open( path, MODE_OLDFILE );
		if( !md )
			break;

		Read( md, &pix.headersize, 4 );
		pix.offset = Seek( puf, 0, OFFSET_CURRENT );
		pix.datasize = ml - pix.headersize;
		pix.headersize -= 8;
		Seek( md, 4, OFFSET_CURRENT );

		copytilleof( md, puf );
		Close( md );

		DeleteFile( path );

		Write( pufix, &pix, sizeof( pix ) );

		/* Mail-Struktur erstellen */
		newmail.time = oldmail.time;
		newmail.pufferID = puffer;
		newmail.mailID = mc++;
		newmail.flags = oldmail.flags;
		newmail.maillen = pix.datasize;
		newmail.midcrc = oldmail.midcrc;
		newmail.bezcrc = oldmail.bezcrc;
		newmail.incomtime = oldmail.incomtime;
		strcpy( newmail.betreff, oldmail.betreff );
		strcpy( newmail.absender, oldmail.absender);
		strcpy( newmail.absenderreal, oldmail.absenderreal );

		Write( newix, &newmail, sizeof( newmail ) );
	}

	popdir();

	Close( oldix );
	Close( newix );
	Close( puf );
	Close( pufix );

	brettpfad( path, &mb );
	addpart( path, ".index" );

	DeleteFile( path );

}

#define FPS(x) Write(outwin,x,strlen(x))

static void convertmd1( void )
{
	BPTR outwin;
	BPTR f1, f2;
	ULONG headoffs;
	struct brett newbrett;
	struct oldbrett oldbrett;
	int puffer = 0;
	char buffer[ 128 ];

	prefs.pointxfer = 0;

	outwin = Open( "RAW:////Konvertiere MicroDot-1.6-Konfiguration", MODE_NEWFILE );

	/* Maps-Liste */
	f1 = Open( "microdot.mbl", MODE_OLDFILE );
	if( f1 )
	{
		FPS( "MAPS-Brettliste...\n" );
		f2 = Open( "MicroDot.mapsblist", MODE_NEWFILE );
		Read( f1, &headoffs, 4 );
		Seek( f1, headoffs, OFFSET_BEGINNING );
		copytilleof( f1, f2 );
		Close( f2 );
		Close( f1 );
		DeleteFile( "microdot.mbl" );
	}

	f1 = Open( "microdot.bix", MODE_OLDFILE );
	if( !f1 )
		return;

	f2 = Open( "microdot.boardix", MODE_NEWFILE );
	if( !f2 )
		return;

	FPS( "Konvertiere Brettdaten...\n" );

	/* Bretter... */
	while( ( Read( f1, &oldbrett, sizeof( oldbrett ) ) ) == sizeof( oldbrett ) )
	{
		memset( &newbrett, 0, sizeof( newbrett ) );

		sprintf( buffer, "Brett %s nach MD_%lx.Data\n", oldbrett.name, puffer );
		FPS( buffer );

		/* Neue Brettstruktur */
		strcpy( newbrett.name, oldbrett.name );
		strcpy( newbrett.betreff, oldbrett.betreff );
		newbrett.flags = oldbrett.flags;
		newbrett.unreadmails = oldbrett.unreadmails;
		newbrett.mails = oldbrett.mails;
		newbrett.newmails = oldbrett.newmails;
		newbrett.days = oldbrett.days;
		Write( f2, &newbrett, sizeof( newbrett ) );

		/* Inhalt... */
		copy1xbrett( puffer++, &newbrett );
	}

	FPS( "Konvertierung abgeschlossen\n" );

	Close( f1 );
	Close( f2 );

	DeleteFile( "microdot.bix.bak" );
	Rename( "microdot.bix", "microdot.bix.bak" );
	
	Delay( 350 );

	Close( outwin );

}

void loadbretter( void )
{
	struct AsyncFile *f;
	BPTR f2;
	char path[256];
	struct mbrett *n, *mb;
	struct brett b;
	struct brettinfo *binfo;
	int ml;
	struct List templist;

	NewList( &brettlist );
	NewList( &templist );
	brettmempool = LibCreatePool( MEMF_CLEAR, sizeof( struct mbrett ) * 32, sizeof( struct mbrett ) * 8 );

retry:
	ml = getfilelen( "microdot.boardix" );
	f = OpenAsync( "microdot.boardix", MODE_READ, sizeof( struct brett ) * 128 );
	if( !f || ml < sizeof( struct brett ) )
	{
		if( f )
			CloseAsync( f );

		/* Vielleicht altinstallation? */
		if( exists( "microdot.bix" ) )
		{
			ml = askreq( "Möchten Sie ihre MicroDot 1.0/1.6-Installation\nin das neue Datenbankformat umwandeln lassen?\n\nACHTUNG!\nDie Wandlung kann nicht mehr rückgängig gemacht werden!",
				"Umwandeln|Abbruch" );

			if( ml )
			{
				convertmd1();
				goto retry;
			}
			exit( 0 );
		}


		/* Brettliste kaputt? */

		if( prefs.flags2 & MDF2_PREFSLOADED )
		{
			if( ( ml = getfilelen( "microdot.boardix.bak" ) ) > ( int ) sizeof( struct brett ) )
			{
				ml = askreq( "Die Brettliste \"microdot.boardix\" ist defekt.\nEs existiert aber eine wahrscheinlich\nintakte Sicherheitskopie der Liste mit %lD Einträgen.\nSoll die Sicherheitskopie installiert werden?",
					"Ja|Nein",
					ml / sizeof( struct brett )
				);

				if( ml )
				{
					Rename( "microdot.boardix", "microdot.boardix.error" );
					DeleteFile( "microdot.boardix" );
					Rename( "microdot.boardix.bak", "microdot.boardix" );
					goto retry;
				}
			}

			/* Nun gut, Recovery versuchen? */
			ml = askreq( "Die Brettliste \"microdot.boardix\" ist defekt.\nSoll eine Restauration versucht werden?",
				"Rettungsversuch|Ignorieren"
			);
			if( ml )
			{
				Rename( "microdot.boardix", "microdot.boardix.error" );
				DeleteFile( "microdot.boardix" );
				doblrecover();
				goto retry;
			}
		}

		newinit();
		return;
	}
	mb = NULL;
	while( ( ReadAsync( f, &b, sizeof( struct brett ) ) == sizeof( struct brett ) ) )
	{
		if( !*( stpblk( b.name ) ) )
			continue;

		n = LibAllocPooled( brettmempool, sizeof( struct mbrett ) );
		if( !n )
		{
			displaybeep();
			break;
		}
		n->b = b;
		calcbrettnamecrc( n );
		if( b.flags & BFLAG_HASBINFO )
		{
			brettpfad( path, n );
			addpart( path, ".Brettinfo" );
			f2 = Open( path, MODE_OLDFILE );
			if( f2 )
			{
				binfo = LibAllocPooled( brettmempool, sizeof( struct brettinfo ) );
				if( !binfo )
				{
					displaybeep();
					break;
				}
				Read( f2, binfo, sizeof( struct brettinfo ) );
				Close( f2 );
				n->brettinfo = binfo;
			}
		}

		makembrettlastwrite( n );

		/* Hm... */
		if( b.flags & BFLAG_EXTEND_PM )
		{
			AddTail( &templist, n );
		}
		else
		{
			if( n->b.name[ 0 ] == '/' && n->b.name[ 1 ] == '»' )
				Insert( &brettlist, n, mb );
			else
				AddTail( &brettlist, n );
		}
		mb = n;

	}

	mb = GetEntry( &brettlist, 3 );

	while( n = RemTail( &templist ) )
		Insert( &brettlist, n, mb );

	CloseAsync( f );
}

void freebretter(void)
{
	struct mbrett *n;

	if( !brettlist.lh_Head )
		return;

	while( ( n = ( struct mbrett * ) RemHead( &brettlist ) ) )
	{
		if( n->ixfile )
			CloseAsync( n->ixfile );
	}

	if( brettmempool )
		LibDeletePool( brettmempool );

}

static void checkixcache( void )
{
	struct mbrett *mb = ( struct mbrett * ) brettlist.lh_Head;
	int cachesize = 0;

	while( mb->n.ln_Succ )
	{
		if( mb->ixfile )
			cachesize++;
		mb = ( struct mbrett * ) mb->n.ln_Succ;
	}

	if( ( cachesize > 32 ) || ( cachesize > 4 && AvailMem( MEMF_LARGEST | MEMF_FAST ) < 128000 ) )
	{
		mb = ( struct mbrett * ) brettlist.lh_Head;
		while( mb->n.ln_Succ && ( cachesize > 5 ) )
		{
			if( mb->ixfile )
			{
				CloseAsync( mb->ixfile );
				mb->ixfile = NULL;
				cachesize--;
			}
			mb = ( struct mbrett * ) mb->n.ln_Succ;
		}
	}
}


struct AsyncFile *getixfile( struct mbrett *mb, int rw )
{
	char buf[ 256 ];

	if( mb->rw == MODE_WRITE )
		mb->rw = 42;

	if( mb->ixfile && ( ( mb->rw == rw ) ) )
	{
		if( rw == MODE_READ )
			SeekAsync( mb->ixfile, 0, MODE_START );
		return( mb->ixfile );
	}

	if( mb->ixfile )
		CloseAsync( mb->ixfile );
	mb->ixfile = NULL;
	checkixcache();

	mb->rw = rw;

	brettpfad( buf, mb );
	checkpath( buf );
	addpart( buf, ".MIndex" );

	mb->ixfile = OpenAsync( buf, rw, sizeof( struct mail ) * 32 );

	return( mb->ixfile );
}

void closeixfile( struct mbrett *mb )
{
	if( mb->ixfile )
	{
		CloseAsync( mb->ixfile );
		if( mb->rw )
		{
			char buf[ 256 ];

			brettpfad( buf, mb );
			checkpath( buf );
			addpart( buf, ".MIndex" );

			if( !getfilelen( buf ) )
				DeleteFile( buf );
		}

		mb->ixfile = NULL;
	}
}

void closeallixfiles( void )
{
	struct mbrett *mb = ( struct mbrett * ) brettlist.lh_Head;

	while( mb->n.ln_Succ )
	{
		if( mb->ixfile )
		{
			CloseAsync( mb->ixfile );
			mb->ixfile = NULL;
		}

		mb = ( struct mbrett * ) mb->n.ln_Succ;
	}
}

void closeallwriteixfiles( void )
{
	struct mbrett *mb = ( struct mbrett * ) brettlist.lh_Head;

	while( mb->n.ln_Succ )
	{
		if( mb->rw && mb->ixfile )
		{
			CloseAsync( mb->ixfile );
			mb->ixfile = NULL;
		}

		mb = ( struct mbrett * ) mb->n.ln_Succ;
	}
}


void savebretter(void)
{
	struct AsyncFile *f;
	BPTR f2;
	struct mbrett *mb;
	char path[256];
	int rc;

	mb=(struct mbrett*)brettlist.lh_Head;
	if(!mb || IsListEmpty(&brettlist))
		return;
	pushhomedir();
	DeleteFile( "microdot.boardix.bak" );
	Rename( "microdot.boardix", "microdot.boardix.bak" );
	f = OpenAsync( "microdot.boardix", MODE_WRITE, sizeof( struct brett ) * 64 );
	popdir();
	if( !f )
	{
		rc = IoErr();
		askreq("Kann Brettliste nicht\nabspeichern (%ld)\n%s","Abbruch",rc, errorstring( rc ) );
		return;
	}
	while( mb->n.ln_Succ )
	{
		if( mb->ixfile )
		{
			CloseAsync( mb->ixfile );
			mb->ixfile = NULL;
		}

		if( mb->brettinfo )
		{
			brettpfad(path,mb);
			checkpath(path);
			addpart(path,".Brettinfo");
			f2=Open(path,MODE_NEWFILE);
			if(f2)
			{
				Write(f2,mb->brettinfo,sizeof(struct brettinfo));
				Close(f2);
			}
			else displaybeep();
			mb->b.flags|=BFLAG_HASBINFO;
		}
		else
			mb->b.flags &= ~BFLAG_HASBINFO;

		WriteAsync(f,&mb->b,sizeof(struct brett));

		mb=(struct mbrett*)mb->n.ln_Succ;
	}
	CloseAsync( f );
}


struct brettinfo *getbrettinfo(struct mbrett *mb)
{
	static struct brettinfo fakebi={0};

/*	Printf("getbrettinfo mb %lx\n",mb);*/

	if(mb->brettinfo) return(mb->brettinfo);
	else return( &fakebi );

}

void setbrettinfo(struct mbrett *mb,struct brettinfo *bi)
{
	if( !bi->diskussionin[0] && !bi->autoexpfad[0] && !( mb->b.flags & BFLAG_EXTEND_PM ) )
	{
		if( mb->brettinfo )
		{
			char path[256];
			/*FreeMem(mb->brettinfo,sizeof(struct brettinfo));*/
			mb->brettinfo = 0;
			brettpfad(path,mb);
			addpart(path,".Brettinfo");
			DeleteFile(path);
		}
		return;
	}
	if( !mb->brettinfo )
		mb->brettinfo = LibAllocPooled( brettmempool, sizeof(struct brettinfo) );
	*mb->brettinfo=*bi;
}

/*static int tstmail(struct mbrett *b,int m)
{
	BPTR lck;
	char path[256];
	
	brettpfadfile( path, b, m );
	lck = Lock( path, SHARED_LOCK );
	if( lck )
		UnLock( lck );
	return( ( int ) lck );
}
*/

/*int getbrettnextfree(struct mbrett *mb)
{
	return( -1 );
}
*/

void makembrettlastwrite( struct mbrett *mb )
{
	char x[ 6 ];

	utunpk( mb->b.lastwrite, x );
	sprintf( mb->lastwritedate, "%02ld.%02ld", x[ 2 ], x[ 1 ] );

}
