/*
 * xmail - X window system interface to the mail program
 *
 * Copyright 1990,1991,1992 by National Semiconductor Corporation
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose is hereby granted without fee, provided that
 * the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation, and that the name of National Semiconductor Corporation not
 * be used in advertising or publicity pertaining to distribution of the
 * software without specific, written prior permission.
 *
 * NATIONAL SEMICONDUCTOR CORPORATION MAKES NO REPRESENTATIONS ABOUT THE
 * SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE.  IT IS PROVIDED "AS IS"
 * WITHOUT EXPRESS OR IMPLIED WARRANTY.  NATIONAL SEMICONDUCTOR CORPORATION
 * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  IN NO
 * EVENT SHALL NATIONAL SEMICONDUCTOR CORPORATION BE LIABLE FOR ANY SPECIAL,
 * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
 *
 * The following software modules were created and are Copyrighted by National
 * Semiconductor Corporation:
 *
 * 1. initialize: and
 * 2. SetHints.
 *
 * Author:  Michael C. Wagnitz - National Semiconductor Corporation
 *
 */

#include "global.h"
#include "Mailwatch.h"			/* use as icon and watch for newmail */
#include <X11/Xaw/CommandP.h>
#include <X11/Xaw/TextP.h>
#include <X11/cursorfont.h>		/* use watch cursor for busy notify */
#include <X11/bitmaps/cross_weave>	/* background use in Newmail notify */
#include <pwd.h>			/* for getting user's home directory */
#ifdef linux
#include <sys/types.h>
#include <signal.h>
#endif

#ifndef	lint
static char what[] =
 "@(#)xmail.c 1.6 94/12/04 Copyright 1989,1990,1991,1992,1993,1994 National Semiconductor Corp.";
#endif

#define Offset(field) (XtOffset(XmailResources *, field))

static XtResource resrcs[] = {
    {"textFont", XtCFont, XtRFontStruct, sizeof (XFontStruct *),
	Offset(textFont), XtRString, XtDefaultFont},
    {"helpFont", XtCFont, XtRFontStruct, sizeof (XFontStruct *),
	Offset(helpFont), XtRString, XtDefaultFont},
    {"buttonFont", XtCFont, XtRFontStruct, sizeof (XFontStruct *),
	Offset(buttonFont), XtRString, XtDefaultFont},
    {"iconGeometry", "IconGeometry", XtRString, sizeof(char *),
        Offset(iconGeometry), XtRString, (caddr_t) NULL},
    {"mFileName", "MFileName", XtRString, sizeof(char *), 
	Offset(MFileName), XtRString, (caddr_t) NULL},
    {"editorCommand", "EditorCommand", XtRString, sizeof(char *), 
	Offset(editorCommand), XtRString, (caddr_t) NULL},
    {"bellRing", "BellRing", XtRBoolean, sizeof(Boolean), 
	Offset(bellRing), XtRImmediate, (caddr_t) True},
    {"expert", "Expert", XtRBoolean, sizeof(Boolean), 
	Offset(expert), XtRImmediate, (caddr_t) False},
    {"iconic", "Iconic", XtRBoolean, sizeof(Boolean), 
	Offset(iconic), XtRImmediate, (caddr_t) False},
    {"mailopt_n", "Mailopt_n", XtRBoolean, sizeof(Boolean), 
	Offset(mailopt_n), XtRImmediate, (caddr_t) False},
    {"mailopt_U", "Mailopt_U", XtRBoolean, sizeof(Boolean), 
	Offset(mailopt_U), XtRImmediate, (caddr_t) False},
    {"show_Last", "Show_Last", XtRBoolean, sizeof(Boolean), 
	Offset(Show_Last), XtRImmediate, (caddr_t) True},
    {"show_Info", "Show_Info", XtRBoolean, sizeof(Boolean), 
	Offset(Show_Info), XtRImmediate, (caddr_t) True},
    {"no_X_Hdr", "No_X_Hdr", XtRBoolean, sizeof(Boolean), 
	Offset(No_X_Hdr), XtRImmediate, (caddr_t) False},
    { XtNborderWidth, XtCBorderWidth, XtRInt, sizeof (int),
	Offset(borderWidth), XtRString, "1"},
};

#undef Offset

static XrmOptionDescRec Opts[] = {
    {"-borderwidth",	".TopLevelShell.borderWidth",	XrmoptionSepArg,	(caddr_t) NULL},
    {"-buttonfont",	"*buttonFont",			XrmoptionSepArg,	(caddr_t) NULL},
    {"-buttonFont",	"*buttonFont",			XrmoptionSepArg,	(caddr_t) NULL},
    {"-bw",		".TopLevelShell.borderWidth",	XrmoptionSepArg,	(caddr_t) NULL},
    {"-editorcommand",	"*editorCommand",		XrmoptionSepArg,	(caddr_t) NULL},
    {"-editorCommand",	"*editorCommand",		XrmoptionSepArg,	(caddr_t) NULL},
    {"-e",		"*expert",			XrmoptionNoArg,		(caddr_t) "True"},
    {"-f",		"*MFileName",			XrmoptionSepArg,	(caddr_t) NULL},
    {"-fn",		"*Font",			XrmoptionSepArg,	(caddr_t) NULL},
    {"-font",		"*Font",			XrmoptionSepArg,	(caddr_t) NULL},
    {"-geometry",	".geometry",			XrmoptionSepArg,	(caddr_t) NULL},
    {"-h",		"*icon*useHost",		XrmoptionNoArg,		(caddr_t) "True"},
    {"-helpfont",	"*helpFont",			XrmoptionSepArg,	(caddr_t) NULL},
    {"-helpFont",	"*helpFont",			XrmoptionSepArg,	(caddr_t) NULL},
    {"-iconGeometry",	"*iconGeometry",		XrmoptionSepArg,	(caddr_t) NULL},
    {"-iconic",		"*iconic",			XrmoptionNoArg,		(caddr_t) "True"},
    {"-ls",		"*show_Last",			XrmoptionNoArg,		(caddr_t) "False"},
    {"-n",		"*mailopt_n",			XrmoptionNoArg,		(caddr_t) "True"},
    {"-nb",		"*bellRing",			XrmoptionNoArg,		(caddr_t) "False"},
    {"-noinfo",		"*show_Info",			XrmoptionNoArg,		(caddr_t) "False"},
    {"-nx",		"*no_X_Hdr",			XrmoptionNoArg,		(caddr_t) "True"},
    {"-rv",		"*icon*reverseVideo",		XrmoptionNoArg,		(caddr_t) "True"},
    {"-textfont",	"*textFont",			XrmoptionSepArg,	(caddr_t) NULL},
    {"-textFont",	"*textFont",			XrmoptionSepArg,	(caddr_t) NULL},
    {"-U",		"*mailopt_U",			XrmoptionNoArg,		(caddr_t) "True"},
    {"-u",		"*icon*useName",		XrmoptionNoArg,		(caddr_t) "True"},
    {"-xrm",		NULL,				XrmoptionResArg,	(caddr_t) NULL},
};


Atom		wmDeleteWindow;		/* for WM_DELETE_WINDOW property */
Boolean		In_Bogus_Mail_File;	/* in bogus folder when we're iconic */
Boolean		Waiting;		/* TRUE when we set the busy cursor */
Pixmap		hatch;			/* cross_weave, to indicate new mail */
Widget		toplevel; 		/* top level shell widget */
Window		WaitCursorWindow;

AliasRecPtr	*aliases = NULL;	/* array of mail alias strings */
helpText	*HelpList = NULL;	/* list of help text objects */
XmailResources 	XMail;			/* application resources of xmail */

int		mailargc;		/* counter passed to mail child	*/
int		RootWidth, RootHeight;
int		Highlighted;		/* state of 'Newmail' highlighting */

char		HOME[BUFSIZ];		/* home directory of user */
char		Command[BUFSIZ];	/* xmail command string */
char	 	InReply[BUFSIZ];	/* reply reference string */
char	 	tmpName[BUFSIZ];	/* message temporary filename */
char		otherBuf[BUFSIZ];	/* bucket for non-addressee names */
char		foldir[BUFSIZ];		/* name of the folder directory */
char		lastFolder[BUFSIZ];	/* previous folder's title-bar	*/
char		*mailargv[8];		/* array passed to mail child */

/*
** @(#)mailoptions() - construct command line arguments for calling mail.
**                     Return the argument list for mail and new value of argc.
*/
int
mailoptions(void)
{
 XWMHints	*wm_hints;
 int		i;
 char		*cp;


 if ((cp = (char *)getenv("XMAILER")) == NULL)	/* first looks up env var */
      cp = DEFAULT_MAILER;

 wm_hints = XGetWMHints(XtDisplay(toplevel), XtWindow(toplevel));

 i = 0;
 mailargv[i++] = cp;		/* Argv[0] is the name of the program */
 mailargv[i++] = "-N";		/* no version or header info at start */
 if (XMail.mailopt_n)
    mailargv[i++] = "-n";	/* don't initialize from Mail.rc file */
 if (XMail.mailopt_U)
    mailargv[i++] = "-U";	/* Change uucp to Internet addresses */

 if (wm_hints->initial_state == IconicState) {	/* start iconic in bogus */
    FILE	*fp;

    (void) sprintf(InReply, "%s+", tmpName);
    if (fp = fopen(InReply, "w")) {	/* create our bogus mail file */
       (void) fprintf(fp, "\n");
       (void) fclose(fp);
       mailargv[i++] = "-f";		/* start in our bogus mail folder */
       mailargv[i++] = InReply;
       In_Bogus_Mail_File = True;
      }
   } else if (XMail.MFileName) {
	     mailargv[i++] = "-f";	/* start from mail folder MFileName */
	     mailargv[i++] = XMail.MFileName;
            }
 mailargv[i] = NULL;
 XFree((XWMHints *)wm_hints);
 return (i);
} /* mailoptions */


/*
** @(#)initialize() - establish xmail program defaults and setups
*/
void
initialize(void)
{
 long		buttonSpace, i;
 String		disp, file, str_type[20];
 Display	*dpy = XtDisplay(toplevel);
 XrmValue	value;
 struct passwd	*pw;


 if (XrmGetResource(XrmGetDatabase(dpy), "command.borderWidth", "Command.BorderWidth",
    str_type, &value)) {
    if ((i = atoi(value.addr)) > XMail.borderWidth)
       XMail.borderWidth = i;
   }

 if (XrmGetResource(XrmGetDatabase(dpy), "send.borderWidth", "Send.BorderWidth",
    str_type, &value)) {
    if ((i = atoi(value.addr)) > XMail.borderWidth)
       XMail.borderWidth = i;
   }

 lastFolder[0] = foldir[0] = '\0';

 (void) strcpy(Command, "Start");	/* let parser know we've started */

 (void) sprintf(tmpName, "/tmp/xmail%d", getpid());

 pw = getpwuid((uid_t) getuid());	/* get the user's home directory */
 (void) strcpy(HOME, pw->pw_dir);
/*
** TRAP any stupid attempt to set the border width of this application to less
** than the allowed minimum (0) or greater than an acceptable maximum.  This
** to prevent a really wierd reaction to a negative number for border width.
*/
 if (XMail.borderWidth < 0)
     XMail.borderWidth = 1;
 if (XMail.borderWidth > 6)
     XMail.borderWidth = 6;
/*
** Compute all window dimensions based on extents of the specified fonts.
** Make shell wide enough to hold eight buttons on a row.  Total button width
** includes width of text area plus width of separation plus width of border.
*/
 XMail.commandHSpace	= 10;
 XMail.commandVSpace	= 10;
 XMail.buttonHeight	= CHARHEIGHT(XMail.buttonFont) + (XMail.buttonFont->ascent / 2);
 XMail.buttonWidth	= figureWidth(XMail.buttonFont) * 9; /* ' NewMail ' */
       buttonSpace	= XMail.borderWidth * 2;
       buttonSpace	+= XMail.buttonWidth + XMail.commandHSpace;
 XMail.shellWidth	= (8 * buttonSpace) + 26;    /*  8 buttons per row */
 XMail.fileBoxWidth	= (4 * buttonSpace) + XMail.buttonWidth;
 XMail.helpHeight	= CHARHEIGHT(XMail.helpFont) * 15 + 5;
 XMail.helpWidth	= figureWidth(XMail.helpFont) * 62 + 5;
 XMail.helpX		= ((int)XMail.shellWidth - (int)XMail.helpWidth) / 2;
 XMail.helpY		= 70;
       buttonSpace	= CHARHEIGHT(XMail.textFont) + (XMail.textFont->ascent / 2);
 XMail.indexHeight	= 12 * buttonSpace;
 XMail.textHeight	= 23 * buttonSpace;
 XMail.commandHeight	= (XMail.buttonHeight*2) + (XMail.commandVSpace*3) +
			  (4 * XMail.borderWidth);
 XMail.menuX		= 15;
 XMail.menuY		= 7;
       RootWidth	= XDisplayWidth(XtDisplay(toplevel),
			  DefaultScreen(XtDisplay(toplevel))) - 1;
       RootHeight	= XDisplayHeight(XtDisplay(toplevel),
			   DefaultScreen(XtDisplay(toplevel))) - 1;
/*
** Ensure xmail shell height does not exceed the height of the root window.
*/
      buttonSpace	= (2 * XMail.buttonHeight) + XMail.indexHeight +
			  XMail.commandHeight + XMail.textHeight + 
			  (6 * XMail.borderWidth);

 for (i = 0, buttonSpace -= RootHeight; buttonSpace > 0; i++) {
     buttonSpace -= CHARHEIGHT(XMail.textFont);
     if (i % 2)
        XMail.indexHeight -= CHARHEIGHT(XMail.textFont);
     else
        XMail.textHeight -= CHARHEIGHT(XMail.textFont);
    }
/*
** If editorCommand resource exists, warn if it doesn't have two %s entries.
*/
 if (XMail.editorCommand)
    if (! (disp =  strchr(XMail.editorCommand, '%')) || *(disp + 1) != 's' ||
        ! (file = strrchr(XMail.editorCommand, '%')) || *(file + 1) != 's' ||
           disp == file) {
    XtWarning("xmail editorCommand resource improperly defined... ignoring");
    XtFree((String) XMail.editorCommand);
    XMail.editorCommand = NULL;
   }

 parser_init();
} /* initialize */


/*
** @(#)SetHints() - Sets hatching, cursor, icon and size hints, and wm protocol
*/
SetHints(void)
{
 Cardinal		x, y;
 unsigned long		valuemask;
 Window			win = XtWindow(toplevel);
 CommandWidget		nm = (CommandWidget)XtNameToWidget(toplevel, "topBox.commandPanel.Newmail");
 Display		*dpy = XtDisplay(toplevel);
 Screen			*scn = XtScreen(toplevel);
 XSetWindowAttributes	attributes;
 XSizeHints		size_hints;
 XWMHints		wm_hints;
/*
** Hatch will be used to indicate new mail arrival on command & folder buttons
*/
 hatch	= XCreatePixmapFromBitmapData(dpy, XtScreen(toplevel)->root,
			cross_weave_bits, cross_weave_width, cross_weave_height,
			nm->label.foreground, nm->core.background_pixel,
			DefaultDepth(dpy, DefaultScreen(dpy)));
/*
** Notify the window manager about our icon window
*/
 wm_hints.input		= True;
 wm_hints.initial_state	= (XMail.iconic) ? IconicState : NormalState;
 wm_hints.flags		= InputHint | StateHint;

 if (XMail.iconGeometry) {
    ParseIconGeometry(XMail.iconGeometry, &x, &y);
    wm_hints.icon_x	= x;
    wm_hints.icon_y	= y;
    wm_hints.flags	|= IconPositionHint;
   }
 wm_hints.flags		|= IconWindowHint;
 wm_hints.icon_window	= XtWindow(XtNameToWidget(toplevel, "icon"));

 XSetWMHints(dpy, win, &wm_hints);
/*
** Set starting position and default geometry
*/
 if (! XGetNormalHints(dpy, win, &size_hints)) {
    size_hints.x = 56;
    size_hints.y = 56;			/* slightly off from top left corner */
   }

 size_hints.width	= XMail.shellWidth;
 size_hints.height	= (2 * XMail.buttonHeight) + XMail.indexHeight +
			  XMail.commandHeight + XMail.textHeight +
			  (6 * XMail.borderWidth);

 size_hints.min_width = size_hints.max_width = size_hints.width;
 size_hints.min_height = size_hints.max_height = size_hints.height;
 size_hints.flags = USPosition | USSize | PMinSize;

 XSetNormalHints(dpy, win, &size_hints);
/*
** Add a protocol flag indicating we wish to handle WM_DELETE_WINDOW requests
*/
 wmDeleteWindow = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
 XSetWMProtocols(dpy, win, &wmDeleteWindow, 1);
/*
** Create a WaitCursor window, ala FAQ to Ignore device events while
** the busy cursor is displayed.  The window will be as big as the
** display screen, and clipped by its own parent window, so we never
** have to worry about resizing.
*/
 valuemask = CWDontPropagate | CWCursor;

 attributes.do_not_propagate_mask =  (KeyPressMask | KeyReleaseMask |
                                   ButtonPressMask | ButtonReleaseMask |
                                   PointerMotionMask);

 attributes.cursor = XCreateFontCursor(dpy, XC_watch);

 WaitCursorWindow = XCreateWindow(dpy, win, 0, 0, WidthOfScreen(scn),
                          HeightOfScreen(scn), (unsigned int) 0, CopyFromParent,
                          InputOnly, CopyFromParent, valuemask, &attributes);
} /* SetHints */


/*
** @(#)main() - main routine for the x11 interface to the mail utility
*/
main(int argc, String *argv)
{
 int	indx, value, aC;
 String	cp, *aV;

 static XtActionsRec actionTable[] = {
	{"CheckInsert",		(XtActionProc) CheckInsert},
	{"DeleteChar",		(XtActionProc) DeleteChar},
	{"DeleteLine",		(XtActionProc) DeleteLine},
	{"DeleteWord",		(XtActionProc) DeleteWord},
	{"DoCmd",		(XtActionProc) DoCmd},
	{"DoDone",		(XtActionProc) DoDone},
	{"DoNothing",		(XtActionProc) DoNothing},
	{"DoReply",		(XtActionProc) DoReply},
	{"DoSave",		(XtActionProc) DoSave},
	{"DoSelected",		(XtActionProc) DoSelected},
	{"Iconify",		(XtActionProc) Iconify},
	{"Folder",		(XtActionProc) Folder},
	{"MyNotify",		(XtActionProc) MyNotify},
	{"NextField",		(XtActionProc) NextField},
	{"PrintMsg",		(XtActionProc) PrintMsg},
	{"Quit",		(XtActionProc) Quit},
	{"SetAliases",		(XtActionProc) SetAliases},
	{"SetDirectory",	(XtActionProc) SetDirectory},
	{"SetFolders",		(XtActionProc) SetFolders},
	{"SetMenu",		(XtActionProc) SetMenu},
	{"SetPopup",		(XtActionProc) SetPopup},
	{"SetSelect",		(XtActionProc) SetSelect},
	{"ShowHelp",		(XtActionProc) ShowHelp},
        {NULL, NULL}
      };

#ifdef linux
/*
** Some distributions of Linux come with a window manager which leaves
** SIGCHLD ignored.  Since we use the state of SIGCHLD to verify an active
** delivery, we had better set it back to the default.
*/
  signal(SIGCHLD, SIG_DFL);
#endif

/*
** First, examine our command line arguments and Class resources for bogus
** values which would cause the application to operate incorrectly.  Drop
** command lines arguments and/or change the resource values before we
** initialize our widgets.
*/
 aV = (String *) XtMalloc((unsigned) (argc + 1) * sizeof(String *));
 aC = 0;

 for (value = indx = 0; indx < argc; indx++)
      value += strlen(argv[indx]) + 1;
 aV[aC] = (String) XtMalloc((unsigned) value);

 (void) strcpy(aV[aC++], *argv);
 aV[aC] = aV[aC - 1] + strlen(aV[aC - 1]) + 1;
 for (indx = 1; indx < argc; indx++)
     switch (argv[indx][0]) {		/* look for, -xrm *[Bb]orderWidth or */
         case '-':			/* -bw, w/ negative valued arguments */
              if ((argv[indx][1] != 'b'  || argv[indx][2] != 'w') &&
                  (argv[indx][1] != 'x'  || argv[indx][2] != 'r'  ||
                   argv[indx][3] != 'm') || (indx + 1) >= argc) {
                 (void) strcpy(aV[aC++], argv[indx]);
                 aV[aC] = aV[aC - 1] + strlen(aV[aC - 1]) + 1;
                }
              else if (sscanf(argv[indx+1], "%d", &value) == 1) {
                   if (value < 0)
                      indx++;		/* skip the value argument too */
                   else if (value < 7) {
                           (void) strcpy(aV[aC++], argv[indx]);
                           aV[aC] = aV[aC - 1] + strlen(aV[aC - 1]) + 1;
                          } else {
                           (void) strcpy(aV[aC++], argv[indx++]);
                           aV[aC] = aV[aC - 1] + strlen(aV[aC - 1]) + 1;
                           (void) strcpy(aV[aC++], "6");
                           aV[aC] = aV[aC - 1] + 2;
                          }
                }
              else if ((cp = strchr(argv[indx + 1], 'o')) == NULL ||
                       strncmp(cp, "orderWidth:", 11)     != 0    ||
                       sscanf(cp + 11, "%d", &value)      != 1) {
                      (void) strcpy(aV[aC++], argv[indx]);
                      aV[aC] = aV[aC - 1] + strlen(aV[aC - 1]) + 1;
                     }
              else if (value >= 0) {
                      if (value < 7) {
                         (void) strcpy(aV[aC++], argv[indx]);
                         aV[aC] = aV[aC - 1] + strlen(aV[aC - 1]) + 1;
                        } else {
                         (void) strcpy(aV[aC++], argv[indx++]);
                         aV[aC] = aV[aC - 1] + strlen(aV[aC - 1]) + 1;
                         (void) strcpy(aV[aC], argv[indx]);
                         cp = strchr(aV[aC], ':');
                         *++cp = '\0';
                         (void) strcat(aV[aC], " 6");	/* max allowed value */
                         aV[aC + 1] = aV[aC] + strlen(aV[aC]) + 1;
                         aC++;
                        }
                }
              else indx++;		/* skip the value argument too */
              break;			/* if negative, it is not included */
         default :
              (void) strcpy(aV[aC++], argv[indx]);
              aV[aC] = aV[aC - 1] + strlen(aV[aC - 1]) + 1;
              break;
        }
 aV[aC] = NULL;				/* array MUST end with a null */
/*
** Now, initialize our toplevel widget and parse the command line
*/
 toplevel = XtInitialize(*aV, "XMail", Opts, XtNumber(Opts), &aC, aV);
 if (aC > 1) {
    (void) fprintf(stderr,"Usage: %s [-toolkitoptions] [-xmailoptions]\n",*aV);
    exit(-1);
    /*NOTREACHED*/
   }

/*
** Next, add xmail specific as well as Athena text widget actions
*/
 XtAddActions(actionTable, XtNumber(actionTable));
 XtAddActions(_XawTextActionsTable, _XawTextActionsTableCount);

 XtGetApplicationResources(toplevel, &XMail, resrcs, XtNumber(resrcs), NULL, 0);

 XtOverrideTranslations(toplevel,
                     XtParseTranslationTable("<Message>WM_PROTOCOLS: Quit(q)"));

 initialize();				/* set window sizes based on font */
 CreateSubWindows(toplevel);		/* and build our widget hierarchy */
 XSync(XtDisplay(toplevel), False);	/* ensure everyone's on the same page */
 XtRealizeWidget(toplevel);		/* initialize widgets for our hints */
 SetHints();				/* tell WM about our icon and size */

 mailargc = mailoptions();		/* set up the mail pgm argv and argc */

 callMail(mailargv);			/* attempt to start Mail connection */

 SetCursor(WATCH);

 XtMainLoop();
 /* NOTREACHED */
} /* main */
