[net.sources] dmdp

gwyn@brl-tgr.ARPA (Doug Gwyn <gwyn>) (01/03/86)

/*
	dmdp -- public-domain implementation of DMD printer program

	last edit:	86/01/03	D A Gwyn

	This code was produced from scratch to mimic the behavior of
	the real "dmdp" program as described in the DMD User Guide.
	The real "dmdp" program is distributed with Release 2.0 of
	the DMD software, which I do not yet have.  This emulation
	has a few shortcomings, but it serves our needs until the real
	"dmdp" program is ready.

	The only printer types supported at present are the Teletype
	5310 and 5320 (later versions).

	I haven't yet figured out how to intercept another layer's
	I/O; perhaps one needs the Release 2.0 layersys for this.
	However, Host mode does work for the "dmdp" layer itself.  It
	is also possible that this program will not work with firmware
	version 1.2 (8;7;3); I developed it under version 2.0 (8;7;5).

	If you have improvements, please mail them to: Gwyn@BRL.ARPA
*/
#ifndef lint
static char	SCCSid[] = "@(#)dmdp.c	1.1";
#endif

#define	MPXTERM	1			/* enable process control definitions */

#include	<blit.h>		/* pre-2.0 name for <dmd.h> */
#include	<font.h>
#include	<jerqproc.h>

#ifndef	Ishort				/* enabled for pre-2.0 DMD software */
static Bitmap	physical =		/* actual screen display bitmap */
	{
	(Word *)0x700000L,		/* DMD screen image base address */
	(XMAX + 31) / 32,		/* bitmap width in 32-bit words */
	0, 0, XMAX, YMAX		/* coords of full-screen rectangle */
	};
#endif

#undef psendchar			/* wrong in DMD Release 1.2 */
#define	psendchar(c)	Iint(192)(c)	/* print one character */

#ifndef PSEND				/* not defined in DMD Release 1.2 */
#define	PSEND	64			/* printer port resource */
#endif

typedef int	bool;
#define	false	0
#define	true	1

#define	MARGIN	2			/* space around image in layer */
#define	CHARHT	defont.height

static Bitmap	*mb;			/* bitmap for scan buffer */
static int	interlace;		/* interlace factor (normally 1 or 2) */
#define	MAX_INTERLACE	2
static int	w;			/* width of data for one byte */

/* printer-specific data: */

typedef struct
	{
	char	*pname;			/* name of printer */
	char	*pinit;			/* graphics initiation string */
	char	*pexit;			/* graphics termination string */
	char	*bol;			/* beginning-of-scan string */
	char	*eol;			/* end-of-scan string */
	int	gdelay;			/* time delay per raster scan (ticks) */
	int	ldelay;			/* time delay per line (ticks) */
	int	fdelay;			/* time delay per page (ticks) */
	short	rows;			/* rasters per scan (1 => horiz chunks) */
	short	dots;			/* width of scan */
	short	chunk;			/* dot bits per byte */
	bool	hifirst;		/* high-order bit leftmost or topmost */
	char	offset;			/* amount to add to binary byte */
	}	pinfo;			/* printer information */

static pinfo	pdata[] =
	{
#define	PT_TRANSPARENT	0		/* transparent attachment to layer */
	{	"transparent",
		"",
		"",
		"",
		"",
		0,
		0,
		0,
		0,
		0,
		0,
		true,
		0
	},
#define	PT_5310		1		/* Teletype 5310 */
	{	"5310",
		"\r\033P\0351q",
		"$\033\\\n",
		"",
		"-",
		60,			/* determined empirically */
		15,			/* manual says 4 lines/sec */
		90,			/* determined empirically */
		12,
		1194,
		6,
		false,
		0x3F
	},
#define	PT_5320		2		/* Teletype 5320 */
	{	"5320",
		"\r\033P\0351q",
		"$\033\\\n",
		"",
		"-",
		60,			/* determined empirically */
		15,			/* manual says 4 lines/sec */
		90,			/* determined empirically */
		12,
		1974,
		6,
		false,
		0x3F
	},
/* add info for your favorite printer just above this line and mail me a copy */
	};
#define	N_PTYPES	(sizeof pdata / sizeof pdata[0])

static int	ptype = PT_5320;	/* selected printer type */

static bool	attached = false;	/* Host mode, attached to process I/O */
static bool	printing = false;	/* printer busy */
static bool	frozen = false;		/* control process suspended */

static struct Proc	*pp = 0;	/* layer being printed */
static int		pstate;		/* prior state of suspended process */
static Bitmap		*bb;		/* bitmap being printed */
static Rectangle	rr;		/* remaining rectangle to be printed */

static Texture16	printer =	/* printer icon */
	{
	0x01FF, 0x0201, 0x02FA, 0x0402,
	0x05F4, 0x0804, 0x0BEF, 0x100B,
	0x3FFD, 0x4009, 0xFFF1, 0x8011,
	0x8012, 0x8014, 0x8018, 0xFFF0
	};

static void	Attach(), Block(), Detach(), DoExit(), DoHost(), DoMain(),
		DoPCntl(), DoPrnt(), DoPrOn(), DoScreen(), DoSetUp(), Freeze(),
		Magnify(), Monitor(), PrintChar(), PrintStr(), ShowOff(), Tell(),
		Unblock(), Unfreeze();

main( argc, argv )
	int	argc;
	char	**argv;
	{
	/* process command line argument (printer type) */

	if ( argc > 1 )			/* user specified printer type */
		{
		for ( ptype = 0; ptype < N_PTYPES; ++ptype )
			if ( strcmp( argv[1], pdata[ptype].pname ) == 0 )
				break;

		if ( ptype == N_PTYPES )	/* not found */
			{
			Tell( "unknown printer type" );
			sleep( 300 );	/* allow time to read message */
			exit();
			}
		}
	/* else use default printer type */

	if ( pdata[ptype].rows > 1 )
		{			/* vertical chunks */
		interlace = pdata[ptype].rows / pdata[ptype].chunk;
		if ( interlace > MAX_INTERLACE )
			{
			Tell( "coding error: interlace" );
			sleep( 600 );	/* allow time to record message */
			exit();
			}
		w = 1;
		}
	else	{			/* horizontal chunks */
		interlace = 1;
		w = pdata[ptype].chunk;
		}

	request( MOUSE | ALARM | PSEND | RCV );

	(void)cursswitch( &printer );

	P->state |= RESHAPED;
	alarm( 1 );			/* force ShowOff */

	DoMain();

	/*NOTREACHED*/
	}

static void
DoMain()
	{
	static char	*maintext[] =
		{
		"Screen",
		"Host",
		"Exit",
		(char *)0
		};
	static Menu	main_menu = { maintext };
	register int	item;

	if ( ptype == PT_TRANSPARENT )
		main_menu.item = &maintext[1];	/* no Screen option */

	for ( ; ; )
		{
		Monitor( "Main Menu" );

		item = menuhit( &main_menu, 3 );
		if ( ptype == PT_TRANSPARENT && item >= 0 )
			++item;
		switch( item )
			{
		case 0:			/* Screen */
			DoScreen();				
			break;

		case 1:			/* Host */
			DoHost();
			break;

		case 2:			/* Exit */
			DoExit();
			break;
			}
		}
	}

static void
Monitor( s )				/* watch for mouse buttons and reshape */
	register char	*s;		/* state string */
	{
	Tell( s );

	for ( ; ; )
		{
		register int	got;

		got = wait( MOUSE | ALARM | RCV );

		if ( P->state & RESHAPED )
			{
			ShowOff( &printer );	/* display logo */
			Tell( s );

			P->state &= ~RESHAPED;
			}

		if ( got & MOUSE )
			{
			if ( bttn2() && !printing && ptype != PT_TRANSPARENT )
				{
				DoSetUp();	/* temporary diversion */

				Tell( s );
				}
			else if ( bttn3() )
				return;	/* something to do */
			}

		if ( got & RCV )	/* attached in Host mode, else junk mail */
			{
			register int	c = rcvchar();

			if ( attached && printing )
				PrintChar( c );	/* copy through */
			/* else discard */
			}

		if ( !(attached && printing) || (own() & RCV) == 0 )
			alarm( 60 );	/* periodically check for reshape */
		}
	}

static void
DoSetUp()
	{
	Tell( "Setup not implemented" );

	while ( bttn2() )		/* DEBUG */
		;			/* DEBUG */
	}

static void
DoScreen()
	{
	static char	*seltext[] =
		{
		"Select Layer",
		"Sweep Rectangle",
		"Whole Screen",
		"Main Menu",
		(char *)0
		};
	static Menu	sel_menu = { seltext };

	/* prepare scan buffer: */

	if ( (mb = balloc( Rect( 0, 0, pdata[ptype].dots, pdata[ptype].rows ) ))
	  == (Bitmap *)0
	   )	{
		Tell( "No Memory in DMD" );
		sleep( 300 );		/* allow time to read message */
		return;
		}

	for ( ; ; )
		{
		Monitor( "Select Print Area" );

		switch ( menuhit( &sel_menu, 3 ) )
			{
		case 0:			/* Select Layer */
			Tell( "Select Layer" );

			if ( (pp = debug()) == P )
				pp = (struct Proc *)0;

			(void)cursswitch( &printer );

			if ( pp == (struct Proc *)0 )
				break;

			bb = (Bitmap *)pp->layer;
			rr = bb->rect;

			Block();
			DoPrnt();
			Unblock();
			goto done;

		case 1:			/* Sweep Rectangle */
			Tell( "Sweep Rectangle" );

			bb = &physical;
			rr = getrect();

			if ( bttn12() )
				break;

			Freeze();	/* (too conservative) */
			DoPrnt();
			Unfreeze();
			goto done;

		case 2:			/* Whole Screen */
			bb = &physical;
			rr = Jrect;

			Freeze();
			DoPrnt();
			Unfreeze();
			goto done;

		case 3:			/* Main Menu */
    done:
			bfree( mb );
			return;
			}
		}
	}

static void
DoPrnt()				/* highlight rectangle and init print */
	{
	static char	*pontext[] =
		{
		"Print",
		"Main Menu",
		(char *)0
		};
	static Menu	pon_menu = { pontext };

	/* fudge to fit printer width, if necessary */

	if ( rr.corner.x - rr.origin.x > pdata[ptype].dots )
		rr.origin.x = rr.corner.x + pdata[ptype].dots;

	rectf( bb, rr, F_XOR );		/* highlight selected area */

	for ( ; ; )
		{
		Monitor( "Print Area Selected" );

		switch ( menuhit( &pon_menu, 3 ) )
			{
		case 0:			/* Print */
			DoPCntl();
			goto done;

		case 1:			/* Main Menu */
			goto done;
			}
		}

    done:
	rectf( bb, rr, F_XOR );		/* unhighlight remaining area */
	return;
	}

static void
DoPCntl()				/* printer control */
	{
	static char	*pctltext[] =
		{
		"Pause",
		"Main Menu",
		"Continue",
		(char *)0
		};
	static Menu	pctl_menu = { pctltext };

	printing = true;
	Tell( "Printing" );

	PrintStr( pdata[ptype].pinit );	/* print initiation string */

	for ( ; ; )
		{
		if ( P->state & RESHAPED )
			{
			ShowOff( &printer );	/* display logo */
			Tell( "Printing" );

			P->state &= ~RESHAPED;
			}

		if ( own() & MOUSE && bttn3() )
			{
    menu:
			switch ( menuhit( &pctl_menu, 3 ) )
				{
			case 0:		/* Pause */
				printing = false;	/* allows setup (???) */
				Monitor( "Pause" );
				goto menu;

			case 1:		/* Main Menu */
				goto done;

			case 2:		/* Continue */
				printing = true;
				Tell( "Printing" );
				break;
				}
			}

		/* The following is not elegant, but I was in a hurry. */

		if ( printing )
			{		/* print next scan */
			register int	i;	/* interlace offset */
			Rectangle	rs;	/* strip of screen image */

			rs.origin = rr.origin;
			rs.corner.x = rr.corner.x;
			rs.corner.y = rr.origin.y + pdata[ptype].rows;
			if ( rs.corner.y > rr.corner.y )
				rs.corner.y = rr.corner.y;

			/* restore from reverse video */

			rectf( bb, rs, F_XOR );

			/* clear background (margin) in strip buffer */

			rectf( mb, mb->rect, F_CLR );

			/* move image strip into strip buffer */

			bitblt( bb, rs, mb, mb->rect.origin, F_STORE );

			PrintStr( pdata[ptype].bol );

			for ( i = 0; i < interlace; ++i )
				{
				register short	nextx;
				short		lastx = rs.corner.x - rs.origin.x;
				short		lasty = rs.corner.y - rs.origin.y;

				for ( nextx = 0; nextx < lastx; nextx += w )
					{
					static union
						{
						Word	dummy[8 * MAX_INTERLACE];
								/* for alignment */
						char	c[8 * MAX_INTERLACE]
							 [sizeof(Word)];
								/* c[][0] data */
						}	u;	/* init 0 bits */
					static Bitmap	cb =
						{
						u.dummy,	/* base */
						1,		/* width */
						0, 0, 8, 8 * MAX_INTERLACE,
								/* rect */
						(char *)0	/* _null */
						};
					register char	d;	/* assemble bits */
					register int	j;	/* bit counter */

					/* pick up (interlaced) rasters for byte */

					d = 0;	/* init bit accumulator */

					if ( w > 1 )
						{	/* horizontal chunk */
						bitblt( mb, Rect( nextx, 0,
								  nextx + w, 1
								),
							&cb, Pt( 8 - w, 0 ),
							F_STORE
						      );

						if ( !pdata[ptype].hifirst )
							{	/* reverse bits */
							register int	e = 1 << w;

							for ( j = 0; j < w; ++j )
								{
								d <<= 1;
								e >>= 1;

								if ( (u.c[0][0] & e)
									!= 0
								   )
									d |= 1;
								}
							}
						}
					else	{	/* vertical chunk */

						/* fetch (offset) data chunk */

						bitblt( mb, Rect( nextx, i,
								  nextx + 1, lasty
								),
							&cb, Pt( 0, 0 ),
							F_STORE
						      );

						/* convert to printer bit pattern */

						if ( pdata[ptype].hifirst )
							for ( j = 0;
							      j <
								pdata[ptype].chunk;
							      ++j
							    )	{
								d <<= 1;

								if (
							u.c[j * interlace][0]
									!= 0
								   )
									d |= 1;
								}
						else	{
							j = pdata[ptype].chunk;
							while ( --j >= 0 )
								{
								d <<= 1;

								if (
							u.c[j * interlace][0]
									!= 0
								   )
									d |= 1;
								}
							}
						}

					PrintChar( d + pdata[ptype].offset );
					}

				PrintStr( pdata[ptype].eol );

				if ( frozen )
					nap( pdata[ptype].gdelay );
				else
					sleep( pdata[ptype].gdelay );
				}

			if ( (rr.origin.y = rs.corner.y) == rr.corner.y )
				{
    done:
				PrintStr( pdata[ptype].pexit );
					/* print termination string */

				printing = false;
				return;	/* no more printing, back to Main Menu */
				}
			}
		}
	}

static void
Block()					/* suspend layer process */
	{
	pstate = pp->state;		/* save state */
	pp->state &= ~(RUN | WAKEUP);
	}

static void
Unblock()				/* resume layer process */
	{
	pp->state |= pstate & (RUN | WAKEUP);
	}

static void
Freeze()				/* suspend control process */
	{
	frozen = true;
/*	setnorun( &proctab[CONTROL] );	*/
	}

static void
Unfreeze()				/* resume control process */
	{
	frozen = false;
/*	setrun( &proctab[CONTROL] );	*/
	}

static void
DoHost()
	{
	Tell( "Select Layer" );

	pp = debug();

	(void)cursswitch( &printer );
	if ( pp == (struct Proc *)0 )
		return;

	Attach();

	DoPrOn();

	Detach();

	return;
	}

static void
DoPrOn()
	{
	static char	*pontext[] =
		{
		"Printer On",		/* or "Printer Off" */
		"Main Menu",
		(char *)0
		};
	static Menu	pon_menu = { pontext };

	for ( ; ; )
		{
		Monitor( printing ? "Printer On" : "Printer Off" );
					/* actual printing done inside Monitor() */

		pontext[0] = printing ? "Printer Off" : "Printer On";
		switch ( menuhit( &pon_menu, 3 ) )
			{
		case 0:			/* Printer On/Off */
			if ( printing = !printing )
				/* flush all already-queued input */
				while ( own() & RCV )
					(void)rcvchar();
			break;

		case 1:			/* Main Menu */
			printing = false;
			return;
			}
		}
	}

static void
Attach()				/* attach to process I/O */
	{
	if ( pp != P )
		{
		/* golly, I can't figure out how to tap the I/O stream */

		Tell( "Attach to own layer only" );
		sleep( 300 );		/* allow time to read message */
		}

	attached = true;
	}

static void
Detach()				/* detach from process I/O */
	{
	if ( pp != P )
		{
		/* I obviously can't figure out how to untap I/O, either */
		}

	attached = false;
	}

static void
DoExit()
	{
	static Texture16	sunset =
		{
		0x5006, 0xA819, 0x00A0, 0x04A0,
		0x049F, 0x12A4, 0x0808, 0x03E0,
		0x2412, 0x0808, 0x0808, 0x3FFF,
		0x3C1F, 0x7E7E, 0x783E, 0xFCFC,
		};

	(void)cursswitch( &sunset );

	P->state |= RESHAPED;
	alarm( 1 );			/* force display */

	for ( ; ; )
		{
		register int	got = wait( MOUSE | ALARM );

		if ( P->state & RESHAPED )
			{
			ShowOff( &sunset );	/* happy trails */
			Tell( "Exiting" );

			P->state &= ~RESHAPED;
			}

		if ( got & MOUSE && bttn123() )
			break;

		alarm( 60 );		/* periodically check for reshape */
		}

	while ( bttn3() )
		;

	if ( !bttn12() )
		exit();

	while ( bttn123() )
		;

	(void)cursswitch( &printer );
	ShowOff( &printer );
	Tell( "idle" );
	}

static void
ShowOff( tp )
	Texture16	*tp;		/* 16x16 template */
	{
	Point		fac;		/* magnification factor */
	Point		size;		/* magnified image size */
	register Bitmap	*b;		/* temporary source bitmap */

	rectf( &display, Drect, F_CLR );

	if ( (b = balloc( Rect( 0, 0, 16, 16 ) )) == (Bitmap *)0 )
		{
		string( &defont, "dmdp", &display, Pt( 3, 3 ), F_STORE );
		return;
		}

	texture16( b, b->rect, tp, F_STORE );

	size = sub( Drect.corner, Drect.origin );

	fac = sub( size, Pt( 2 * MARGIN, 3 * MARGIN + CHARHT ) );

	while ( fac.x > 2 * fac.y )
		fac.x /= 2;

	while ( fac.y > 2 * fac.x )
		fac.y /= 2;

	fac = div( fac, 16 );

	size = div( sub( size, mul( fac, 16 ) ), 2 );
	size.y -= (MARGIN + CHARHT) / 2;
	Magnify( b, b->rect, &display, add( Drect.origin, size ), fac );

	bfree( b );
	}

static void
Magnify( b, r, tb, p, fac )		/* adapted from "lens" */
	register Bitmap	*b, *tb;
	Rectangle	r;
	Point		p, fac;
	{
	register Bitmap	*stage;
	register int	i, shift;
	Point		d;
	Rectangle	s;

#if 0
	if ( fac.x < 1 || fac.y < 1 )	/* "can't happen" */
		return;
#endif

	d = sub( r.corner, r.origin );
	s.origin = p;
	s.corner = add( p, Pt( fac.x * d.x, fac.y * d.y ) );

	/* Copy source into origin of dest */

	bitblt( b, r, tb, p, F_STORE );

	/* Clear rest of dest */

	rectf( tb, Rect( s.origin.x + d.x, s.origin.y,
			 s.corner.x, s.corner.y
		       ),
	       F_CLR
	     );
	rectf( tb, Rect( s.origin.x, s.origin.y + d.y,
			 s.origin.x + d.x, s.corner.y
		       ),
	       F_CLR
	     );

	/* Now we expand in place */

	/* 1: expand horizontally */
	if ( fac.x > 1 )
		for( i = d.x - 1; i > 0; --i )
			{
			bitblt( tb, Rect( p.x + i, p.y, p.x + i + 1, p.y + d.y),
				tb, Pt( p.x + i * fac.x, p.y ), F_OR
			      );
			rectf( tb, Rect( p.x + i, p.y, p.x + i + 1, p.y + d.y ),
			       F_CLR
			     );
			}

	/* 2: smear horizontally */
	for( i = 1; i < fac.x; i *= 2 )
		{
		shift = min( i, fac.x - i );
		bitblt( tb, Rect( p.x, p.y, s.corner.x - shift, p.y + d.y ),
			tb, Pt( p.x + shift, p.y ), F_OR
		      );
		}

	/* 3: expand vertically */	
	if ( fac.y > 1 )
		for ( i = d.y - 1; i > 0; --i )
			{
			bitblt( tb, Rect( p.x, p.y + i, s.corner.x, p.y + i + 1 ),
				tb, Pt( p.x, p.y + i * fac.y ), F_OR
			      );
			rectf( tb, Rect( p.x, p.y + i, s.corner.x, p.y + i + 1 ),
			       F_CLR
			     );
			}

	/* 4: smear vertically */
	for ( i = 1; i < fac.y; i *= 2 )
		{
		shift = min( i, fac.y - i );
		bitblt( tb, Rect( p.x, p.y, s.corner.x, s.corner.y - shift ),
			tb, Pt( p.x, p.y + shift ), F_OR
		      );
		}	
	}

static void
Tell( s )				/* display message line */
	char	*s;			/* message */
	{
	rectf( &display, Rect( Drect.origin.x, Drect.corner.y - MARGIN - CHARHT,
			       Drect.corner.x, Drect.corner.y
			     ),
	       F_CLR
	     );

	(void)string( &defont, s, &display, Pt( Drect.origin.x + MARGIN,
						Drect.corner.y - MARGIN - CHARHT
					      ),
		      F_OR
		    );
	}

static void
PrintStr( s )
	register char	*s;
	{
	for ( ; *s != '\0'; ++s )
		PrintChar( *s );
	}

static void
PrintChar( c )
	register int	c;
	{
	static char	parity[128] =	/* odd-parity bits for 7-bit data */
		{
		0x80,	0x00,	0x00,	0x80,	0x00,	0x80,	0x80,	0x00,
		0x00,	0x80,	0x80,	0x00,	0x80,	0x00,	0x00,	0x80,
		0x00,	0x80,	0x80,	0x00,	0x80,	0x00,	0x00,	0x80,
		0x80,	0x00,	0x00,	0x80,	0x00,	0x80,	0x80,	0x00,
		0x00,	0x80,	0x80,	0x00,	0x80,	0x00,	0x00,	0x80,
		0x80,	0x00,	0x00,	0x80,	0x00,	0x80,	0x80,	0x00,
		0x80,	0x00,	0x00,	0x80,	0x00,	0x80,	0x80,	0x00,
		0x00,	0x80,	0x80,	0x00,	0x80,	0x00,	0x00,	0x80,
		0x00,	0x80,	0x80,	0x00,	0x80,	0x00,	0x00,	0x80,
		0x80,	0x00,	0x00,	0x80,	0x00,	0x80,	0x80,	0x00,
		0x80,	0x00,	0x00,	0x80,	0x00,	0x80,	0x80,	0x00,
		0x00,	0x80,	0x80,	0x00,	0x80,	0x00,	0x00,	0x80,
		0x80,	0x00,	0x00,	0x80,	0x00,	0x80,	0x80,	0x00,
		0x00,	0x80,	0x80,	0x00,	0x80,	0x00,	0x00,	0x80,
		0x00,	0x80,	0x80,	0x00,	0x80,	0x00,	0x00,	0x80,
		0x80,	0x00,	0x00,	0x80,	0x00,	0x80,	0x80,	0x00
		};

	if ( ptype != PT_TRANSPARENT )
		{
		c &= 0x7F;

		if ( attached )		/* interpret control codes */
			{
			static int	col = 0;

			switch ( c )
				{
			case '\r':
			case '\f':
				col = 0;
				break;

			case '\n':
			case '\v':
			case '\007':	/* BEL */
				break;

			case '\b':
				if ( col > 0 )
					--col;
				break;

			case '\t':
				do
					PrintChar( ' ' );
				while ( col % 8 != 0 );
				return;

			default:
				if ( c < ' ' )
					return;
				break;
				}
			}

		c |= parity[c];		/* make odd parity */
		}

	while ( psendchar( c ) == 0 )
		;

	if ( ptype != PT_TRANSPARENT && attached )	/* handle delays */
		switch ( c & 0x7F )
			{
		case '\v':
		case '\f':
			sleep( pdata[ptype].fdelay );
			break;

		case '\n':
			sleep( pdata[ptype].ldelay );
			break;
			}
	}