/*
 * $Id: interpreter.cxx,v 4.5 2004/05/23 23:13:53 hut66au Exp $
 *
 * Imview, the portable image analysis application
 * http://www.cmis.csiro.au/Hugues.Talbot/imview
 * ----------------------------------------------------------
 *
 *  Imview is an attempt to provide an image display application
 *  suitable for professional image analysis. It was started in
 *  1997 and is mostly the result of the efforts of Hugues Talbot,
 *  Image Analysis Project, CSIRO Mathematical and Information
 *  Sciences, with help from others (see the CREDITS files for
 *  more information)
 *
 *  Imview is Copyrighted (C) 1997-2001 by Hugues Talbot and was
 *  supported in parts by the Australian Commonwealth Science and 
 *  Industry Research Organisation. Please see the COPYRIGHT file 
 *  for full details. Imview also includes the contributions of 
 *  many others. Please see the CREDITS file for full details.
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *  
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *  
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
 * */

/*------------------------------------------------------------------------
 *
 *
 * A minuscule interpreter for Imview, accessed through the socket
 * interface.
 * The idea is to be able to control the GUI from the command line,
 * so that:
 *    1- remote control of imview from another application is easy
 *    2- linking two or more imviews is a possibility
 *    3- regression testing of imview is possible.
 *
 * Hugues Talbot	 1 Mar 2000
 *      
 *-----------------------------------------------------------------------*/

#include "imnmspc.hxx"
#include "imview.hxx"

#ifdef HAVE_PTHREADS // otherwise don't even bother

#include <string> // C++ ones
#include <FL/Fl.H>
#include <FL/Fl_Box.H>
#include <assert.h>


#include "imviewWindow.hxx"
#include "pointfile.hxx"
#include "imageIO.hxx"
#include "imageViewer.hxx"
#include "menubar.hxx"
#include "printPrefs.hxx"
#include "savePrefs.hxx"
#include "imageInfo.hxx"
#include "spectraBox.hxx"
#include "transferFunction.hxx"
#include "profileBox.hxx"
#include "userPrefs.hxx"
#include "progressInfo.hxx"
#include "bivHist.hxx"
#include "toolbar.hxx"
#include "FL/Fl_Help_Dialog.H"
#include "interpreter.hxx"
#include "imshared.hxx"

extern imview_server *is;
extern interpreter   *imview_interpreter;
extern linkmanager   *imview_linkmanager;
extern pointfile     *PtFileMngr;
extern imageViewer   *mainViewer;
extern imViewMenuBar *mainMenuBar;
extern imageIO       *IOBlackBox;
extern imviewWindow  *mainWindow;
extern bool           main_terminate;
extern char           appName[];

extern string         truestr, falsestr;

extern volatile bool syncDisplay;
extern volatile bool im_draw_finished;

extern Fl_Box         *titleBox ; // marker for when an image is present

// fluid-generated
extern printprefs     *printPrefsPanel;
extern saveprefs      *savePrefsPanel;
extern imageinfo      *imageInfoPanel;
extern transfer       *transferEditorPanel;
extern spectra        *spectraPanel;
extern profile        *profilePanel;
extern userprefs      *userprefsPanel;
extern bivhist        *bivHistPanel;
extern progress       *progressPanel;
extern Fl_Help_Dialog *help;
extern toolbar        *toolbarPanel;

// these are COMPULSERY definitions!!
//map<string,imgtype, Nocase> interpreter::tr_img;
//map<string,pixtype, Nocase> interpreter::tr_pix;



#ifndef WIN32
static const char *uservar = "USER";
#else
static const char *uservar = "USERNAME";
#endif

const char *ActionResult[] = {
    "500 Command failed\n",
    "501 Unknown command\n",
    "502 Unchanged\n",
    "503 Not moved\n",
    "504 Not sized\n",
    "505 Load failed\n",
    "506 Arghh!\n",
    "507 Not logged in\n",
    "508 No data\n",
    "509 Incorrect image type\n",
    "510 Incorrect pixel type\n",
    "511 Incomplete argument list\n",
    "512 No label\n",
    "513 Invalid command (", // note: no \n
    "514 No image present\n",
    "515 Not implemented yet\n",
    "516 Unknown dialog box\n",
    "517 Connection failed\n",
    "518 Socket exception", // no \n
    "519 Non-existent variable\n",
    "520 Pointfile failure\n",
    "000 OK", // NOTE: no \n
    NULL
};

enum ActionCode {
    CMD_FAILD = 0,
    CMD_UNKWN, //  1
    CMD_UNCHG, //  2 
    CMD_NOTMV, //  3
    CMD_NOTSZ, //  4 
    CMD_LFAIL, //  5
    CMD_AARGH, //  6
    CMD_NOLOG, //  7
    CMD_NODTA, //  8
    CMD_IMGNG, //  9
    CMD_PIXNG, // 10
    CMD_LOARG, // 11
    CMD_NOLBL, // 12
    CMD_INVAL, // 13
    CMD_NOIMG, // 14
    CMD_NOSYS, // 15
    CMD_NODLG, // 16
    CMD_CFAIL, // 17
    CMD_ESOCK, // 18
    CMD_NOVAR, // 19
    CMD_PTFIL, // 20
    CMD_OK,    // Leave that last...
    CMD_LAST
};

// NOTE:
// All functions (commands) must do the following:
// 1- return 0 in case of success or return a strictly 
//    positive integer in case of failure.
// 
// 2- Either in case of success or failure, functions can
//    return a result string. The last line in the result
//    must be 5xx in case of errors. The following codes
//    are reserved:
//
//    500 Command failed
//    501 Unknown command
//    000 OK
//
// `000 OK' is added automatically if the command completes
// sucessfully (return code = 0).
//
// Commands can also return a negative integer value if they don't
// want the evaluator to add the "000 OK" or "500 Command failed"
// strings to the result, but be warned that clients will often be
// parsing the above code as `end of response' for efficiency.
//

int complain::perform(vector<string> &l, string &result)
{
    result = ActionResult[CMD_UNKWN]; // This is a key word
    return -1; // special case for unknown command
}

int cmap::perform(vector<string> &l,	 /*  */
		  string &result)	 /*  */
{
    int retval;
    
    if ((l.size() > 0)  && (setClut(l[0].c_str()) == 0)) {
	retval = 0;
    } else if (l.size() == 0) { // revert to default LUT
	noclut_cb(0,0);
	retval = 0;
    } else {
	result = ActionResult[CMD_UNCHG];
	retval = 1;
    }

    return retval;
}


int move::perform(vector<string> &l, string &result)
{
    int newx, newy;

    if (l.size() >= 2) {
	newx = atoi(l[0].c_str());
	newy = atoi(l[1].c_str());
	mainWindow->position(newx, newy);
	return 0;
    } else {
	result = ActionResult[CMD_NOTMV];
	return 1;
    }
}

int size::perform(vector<string> &l, string &result)
{
    int neww, newh;

    if (l.size() >= 2) {
	neww = atoi(l[0].c_str());
	newh = atoi(l[1].c_str());
	if (mainViewer) {
	    mainWindow->size(neww, newh);
	    mainViewer->resize();
	}
	return 0;
    } else {
	result = ActionResult[CMD_NOTSZ];
	return 1;
    }
}

int load::perform(vector<string> &l, string &result)
{
    if ((l.size() > 0) && (openimage(l[0].c_str(),0) == 0)) {
	if (syncDisplay)
	    im_draw_finished = false;
	return 0;
    } else {
	result = ActionResult[CMD_LFAIL];
	return 1;
    }
}

int quit::perform(vector<string> &l, string &result)
{
    result = "Bye!\n"; // do not change this command, this is a keyword.
    return -2; // telling the evaluator no to add "000 OK" to the command
}

int killme::perform(vector<string> &l, string &result)
{
    result = ActionResult[CMD_AARGH];
    main_terminate=true; // this will do it...
    return 0;
}

int interpreter_help::perform(vector<string> &l, string &result)
{
    result = \
	"help/?             : this help\n"
	"---- Connection management\n"
	"user <name>        : log in\n"
	"quit/bye/end/exit  : closes connection\n"
        "kill               : kills the server\n"
	"link <addr> <port> : link to another imview\n"
	"unlk <addr> <port> : unlink from another imview\n"
	"---- Upload images\n"
	"put <many args>    : puts an image for display\n"
#ifdef HAVE_SYSV_IPC
	"putm <many args>   : puts an image for display using shared memory (localhost only, SYSV IPC)\n"
#endif
#ifdef HAVE_POSIX_IPC
        "ppath              : return path to IPC resources\n"
	"putp <many args>   : puts an image for display using shared memory (localhost only, POSIX IPC)\n"
#endif
	"---- Pointfiles\n"
	"pf                 : prints pointfile\n"
        "stpf <file>        : sets the point file name\n"
        "ldpf <file>        : upload a pointfile\n"
        "svpf <file>        : save the pointfile\n"
        "clpf               : clears the pointfile\n"
	"---- Image management\n"
	"load <file>        : load the image from <file>\n"
	"cmap [name]        : apply colour map to current image, default if no name\n"
	"zoom               : zoom functions, described below:\n"
	" ... box x y w h   : zoom to specified box \n"
	" ... factor zf     : specify zoom factor (real number)\n"
	" ... default zf    : specify default zoom factor\n"
        "pan  x y           : pan to specified location, keeping zoom intact\n"
	"reset              : reset the image\n"
	"close [name]       : close an image (name optional)\n"
        "..... <overlay>    : close the overlay plane\n"
	"---- Window management\n"
	"move <X> <Y>       : move main window to new position\n"
	"size <W> <H>       : change the window dimensions\n"
	"raise              : raise/deiconize the main window to the top\n"
        "icon/hide          : iconize the application\n"
        "show|hide          : show or hide the main window\n"
        "      ... print    : show or hide the print image dialog\n"
        "      ... save     : show or hide the save image dialog\n"
        "      ... info     : show or hide the info image  dialog\n"
        "      ... transfer : show or hide the edit transfer  dialog\n"
        "      ... spectra  : show or hide the spectra display dialog\n"
        "      ... profile  : show or hide the profile display dialog\n"
        "      ... user     : show or hide the user preference dialog\n"
        "      ... bivhist  : show or hide the bivariate histogram dialog\n"
        "      ... progress : show or hide the image download progress dialog\n"
        "      ... help     : show or hide the online help dialog\n"
        "      ... toolbar  : show or hide the toolbar\n"
        "---- Variables\n"
        "set <var> <value> : set some variable\n"
        "get <variable>    : obtain a variable value\n"
        "list              : lists all the valid variables\n"
	;
    
    return 0;
}

//bool user::matches = false; // no one is authenticated by default

user::user(void)
{
    // find out who is running the show
    // this is a bit flaky, but should do for now. Proper security will
    // be for later.

    const char *p = getenv(uservar); // get the user name from the environment

    if (p) {
        string rawname = p;
        string::size_type   found = 0;
        // redundant whitespaces reduced to a single space
        // 1- replace all other whitespaces with space
        do {
            found = rawname.find_first_of("\t\r\n");
            if (found != string::npos) {
                dbgprintf("Space found in username %s at position %d; while npos=%d\n", rawname.c_str(), found, string::npos);
                rawname.replace(found,1," ");
            }
        } while (found != string::npos);
        // 2- replace pairs of spaces with a single one
        do {
            found = rawname.find("  "); // pair of spaces
            if (found != string::npos) {
                dbgprintf("Double space found in username %s\n", rawname.c_str());
                rawname.replace(found, 2, " ");  // a single space
            }
        } while (found != string::npos);

        server_user = rawname;
        dbgprintf("server_user = %s\n", server_user.c_str());
        assert(server_user.size() != 0); 
    } else
	server_user = ""; // empty string
    
    
    if (server_user.size() != 0)
	dbgprintf("User action constructor: user = %s\n", server_user.c_str());
    else
	errprintf("User unknown, env. variable %s not defined", uservar);
    
    return;
}

int user::perform(vector<string> &l, string &result)
{
    ostringstream osm, osnm;
    string        fullname;

    if (l.size() > 0) {
        // user names can have whitespaces in them
        osnm << l[0];
        for (unsigned int i = 1 ; i < l.size() ; ++i)
            osnm << " " << l[i];
        fullname = osnm.str();
        
	if (server_user.size() == 0) {
	    osm << "516 " << uservar
		<< "environment variable is not defined.\n";
	    return 3;
	} else if (fullname  == server_user) {
	    osm << "Welcome, " << fullname
#ifdef HAVE_SYSV_IPC
		<< ' ' << putm::unique_filename()  
#else
	        << ' '  << "no_shm"
#endif
		<< ' ' ; 
	    result = osm.str();
	    return 0;
	} else {
	    osm << "505 Go away, " << fullname << '\n';
	    result = osm.str();
	    return 1;
	}
    } else {
	result = "200 usage: user <username>\n";
	return 2;
    }
}

// sets the pointfile path
int set_pointfile::perform(vector<string> &l, string &result)
{
    PtFileMngr->setPtFileName((l[0]).c_str());

    return 0; // always succeeds
}

// sets the pointfile path and upload it
int load_pointfile::perform(vector<string> &l, string &result)
{
    PtFileMngr->setPtFileName((l[0]).c_str());
    PtFileMngr->readlist();
    mainViewer->redraw();

    return 0; // always succeeds
}

// sets the pointfile path and save it
int save_pointfile::perform(vector<string> &l, string &result)
{
    PtFileMngr->setPtFileName((l[0]).c_str());
    PtFileMngr->savelist();

    return 0; // always succeeds
}

// clears the pointfile
int clear_pointfile::perform(vector<string> &l, string &result)
{
    PtFileMngr->rmAllPoints();
    mainViewer->redraw();
    return 0; // always succeeds
}


// this is the command that always goes through if not logged in
int deny::perform(vector<string> &l, string &result)
{
    result = ActionResult[CMD_NOLOG];
    return 1; // always fail
}



// this function can be called both from the GUI AND
// from the image server.
IMAGE_HEADER *put::find_header(const char *name)
{
    vector<IMAGE_HEADER *>::iterator i;
    IMAGE_HEADER *result;

    result = 0;
    if (name != 0) {
	for (i = imh.begin() ; i != imh.end() ; i++) {
	    if (((*i)->label != 0) 
		&& (strcmp(name, (*i)->label) == 0)) { 
		srv_dbgprintf("Found the header associated with %s!\n", name);
		break;
	    }
	}
	if (i != imh.end())
	    result = *i;
    }

    return result;
}

// data coming from different-endian clients will have their data
// swapped.
inline unsigned int put::swapid(int id)
{
    uchar        *from, *to;
    int          out;

    from = (uchar *)&id;
    to   = (uchar *)&out;
    
    to[0] = from[3];
    to[1] = from[2];
    to[2] = from[1];
    to[3] = from[0];
    
    return out;
}

// this function can be called both from the GUI AND
// from the image server.
IMAGE_HEADER *put::find_header(unsigned int given_id)
{
    vector<IMAGE_HEADER *>::iterator i;
    
    for (i = imh.begin() ; i != imh.end() ; i++) {
	if (given_id == (*i)->unique_id) {
	    srv_dbgprintf("Found the header associated with %d!\n", given_id);
	    (*i)->needswap = false;
	    break;
	}  else if (swapid(given_id) == (*i)->unique_id) {
	    srv_dbgprintf("Found the header associated with %d, but swapped!\n", given_id);
	    (*i)->needswap = true;
	    break;
	}
    }
    if (i != imh.end())
	return *i;
    else
	return 0;
}

void put::delete_header(const char *fname)
{
    vector<IMAGE_HEADER *>::iterator i;
    IMAGE_HEADER   *myheader;

    if (fname != 0) {
	for (i = imh.begin() ; i != imh.end() ; i++) {
	    if ((*i)->label != 0) {
		if (strcmp(fname, (*i)->label) == 0 ) {
		    srv_dbgprintf("Found the header associated with %s for deletion...\n", fname);
		    myheader = *i;
		    delete_header(myheader);
		    // actually remove the label from the vector
		    imh.erase(i); // I'm not sure what would work
		    srv_dbgprintf("%s: all freed\n", fname);
		    break;
		}
	    }
	}
    }
}
 
void put::delete_header(unsigned int given_id)
{
    vector<IMAGE_HEADER *>::iterator i;
    IMAGE_HEADER   *myheader;

    if (given_id > 0) {
	for (i = imh.begin() ; i != imh.end() ; i++) {
	    if (given_id == (*i)->unique_id) {
		srv_dbgprintf("Found the header associated with %d for deletion!\n", given_id);
		myheader = *i;
		delete_header(myheader);
		// actually remove the label from the vector
		imh.erase(i); // I'm not sure what would work
		srv_dbgprintf("%d: all freed\n", given_id);
		break;
	    }
	}
    }
}

void put::delete_header(IMAGE_HEADER *myheader)
{
    int j;

    // delete the memory associated with it
    for (j = 0 ; j < myheader->nbc ; j++) {
	// free the buffp's
	free((myheader->comp[j]).buffp);
    }
    // delete the component headers
    delete[] myheader->comp;
    // free the raw data buffer.
    free(myheader->rawdata);
    // free the label (strduped)
    free(myheader->label);
    // delete the memory associated with the header
    delete(myheader);
    myheader = 0;

    return;
}

// this will get called when the transmission is
// finished. This will be called through the
// server thread.
int put::callback(void *buf, unsigned long nbbytes)
{
    int res = 0;

    srv_dbgprintf("Within the put callback, received %d bytes\n", nbbytes);
    
    if ((buf == 0) || (nbbytes <= HEADER_ID_SIZE)) {
	cerr << "Binary transmission failed!\n" ;
	res = 2;
    } else {
	// make up an image from the data we just received, but first
	// associate the buffer just received with a header
	IMAGE_HEADER   *myheader;
	unsigned int *idnb;
	ostringstream uniq;

	// look in all the headers
	idnb = (unsigned int *)buf;
	myheader = find_header(*idnb);
	
	if (myheader != 0) {
	    int j, ts, spp, smps;
	    unsigned long expected_size;

	    ts = spp = smps = 0;
	    // first things first: is this header being reused?
	    // if so, free the previous buffer.
	    if (myheader->rawdata != 0)
		free(myheader->rawdata);

	    // IMPORTANT: the server normally generates a magic number
	    // for the client to use, and header reuse in this fashion
	    // is not expected behaviour, but is possible.
	    
	    expected_size = HEADER_ID_SIZE ; // size of the header
	    for (j = 0 ; j < myheader->nbc ; j++) {
		// the multiplier
		ts = IOBlackBox->typeSize((myheader->comp[j]).pixt);
		spp = (myheader->comp[j]).spp;
		smps = \
		    (myheader->comp[j]).nx * \
		    (myheader->comp[j]).ny * \
		    (myheader->comp[j]).nz;
		
		expected_size += ts * spp * smps;
	    
	    }
	    srv_dbgprintf("We expected %d bytes\n", expected_size);
	    // header found
	    if (nbbytes == expected_size) {
		int   k;
		char *curp;

		// keeps the pointer to raw data, so it can be freed later
		myheader->rawdata = buf;
		
		// build an image!
		// I think it's OK if the buffer stays as it is. We only
		// need to make the diverse pointers point to the right
		// location
		curp = (char *)buf + HEADER_ID_SIZE; // start of the real data 
		for (j = 0 ; j < myheader->nbc ; j++) {
		    // allocate buffp
		    (myheader->comp[j]).buffp = (void **)malloc(spp * sizeof(void*));
		    for (k = 0 ; k < spp ; k++) {
			(myheader->comp[j]).buffp[k] = curp;
			curp += smps * ts; // size of a single spectrum of the given data
		    }
		}
		// So, everything went fine. When the GUI comes around to display
		// the current image, the previous one will have been deleted.
		// luckily we have its unique_id in the header.
		srv_dbgprintf("callback: closing previous header: %d \n", myheader->previous_id);
		delete_header(myheader->previous_id);
		// finally we need to post a message to the GUI,
		// saying an image has been transmitted.
		// constructs a string from a char * label
		const string uniqs=myheader->label;
		srv_dbgprintf("Ok so far. Asking the GUI to please diplay img %s\n", myheader->label);
		IOBlackBox->pleaseDisplay(uniqs);
		// note: we can't call the GUI directly as we are within
		// the server's thread.

                // if we need to synchronize, then we must wait for display at this stage.
                // otherwise future commands might be lost.
                if (syncDisplay) {
                    srv_dbgprintf("Blocking future commands until display completed\n");
                    im_draw_finished = false;
                }
	    } else {
		cerr << "Did not receive the expected number of bytes:\n"
		     << "Expected: " <<  expected_size << "\n"
		     << "Received: " <<  nbbytes << "\n";
		res = 3;
		
	    }
	} else {
	    cerr << "Could not match header with Id = " << *idnb << endl;
	}
    }

    // at any rate, we close the data channel
    // let's not do it, and see
    // is->close_data_channel();
    

    if (res != 0) {
	free(buf);
	buf = 0; // zap the buffer! Worthless!
    }
    
    return res;
}

// this static variables help us remember what headers
// have been created and which images are in the pipe (literally!)
// This is all necessary because there might be more than one
// image being transmitted at any one time.
vector<IMAGE_HEADER *> put::imh;
unsigned int          put::id_pool = (((unsigned int)'A') << 24) +
                                      (((unsigned int)'B') << 16) +
				      (((unsigned int)'C') << 8) +
				      ((unsigned int)'D');

// at last some real action!
// this opens a binary data connection so that an
// an image can go through it.
// The end of transmission will generate a callback.
int put::perform(vector <string> &l, string &result)
{
    int             port, i, j, ret, expected_size;
    char           *newlabel;
    size_t          exp_nb;
    ostringstream   sres, os;
    IMAGE_HEADER   *img, *previmg;

    
    if (l.size() > 3) {
	ret = 0;
    
	img = new IMAGE_HEADER;

	img->label = 0;     // this is important, as otherwise find_header will choke on an invalid pointer
	imh.push_back(img); // we need to remember this header.
	// fill the header
	newlabel = strdup(l[0].c_str());
	// find out if there is another header with the same
	// name in use already. If so it will have to be deleted
	// if the data transmission goes fine.
	dbgprintf("Looking for previous header with same label: %s\n", newlabel);
	previmg = find_header(newlabel); // might be 0

	if (previmg != 0) {
	    dbgprintf("Unique ID associated with this previous header: %d\n",
		      previmg->unique_id);
	    img->previous_id = previmg->unique_id;
	} else {
	    dbgprintf("No previous unique id found\n");
	    img->previous_id = 0;
	}
	
	img->label = newlabel;
	img->unique_id = id_pool++; // each header will have its own unique ID.
	img->nbc = strtol(l[1].c_str(),0,0);
	img->rawdata = 0 ; // nothing received yet

	exp_nb = 9 * img->nbc + 2; // expected number of arguments in the list
	if ((img->nbc > 0) && (exp_nb == l.size())) {
	    // create the suitable number of components
	    img->comp = new IMAGECOMP_HEADER[img->nbc];
	    for (i = 0, j = 2 ; i < img->nbc ; i++, j+=9) {
		(img->comp[i]).nx = atoi(l[j].c_str());   
		(img->comp[i]).ny = atoi(l[j+1].c_str()); 
		(img->comp[i]).nz = atoi(l[j+2].c_str()); 
		(img->comp[i]).ox = atoi(l[j+3].c_str());
		(img->comp[i]).oy = atoi(l[j+4].c_str());
		(img->comp[i]).oz = atoi(l[j+5].c_str());
		(img->comp[i]).imgt = imview_interpreter->translate_img(l[j+6]);
		(img->comp[i]).pixt = imview_interpreter->translate_pix(l[j+7]);
		(img->comp[i]).spp = atoi(l[j+8].c_str());
		(img->comp[i]).buffp = 0; // to be filled when we actually get some data.

		// check for each component:
		if (((img->comp[i]).nx * (img->comp[i]).ny * (img->comp[i]).nz) == 0) {
		    result = ActionResult[CMD_NODTA];
		    ret = 2;
		    break;
		} else if ((img->comp[i]).imgt == IM_ERROR) {
		    result = ActionResult[CMD_IMGNG];
		    ret = 3;
		    break;
		} else if ((img->comp[i]).pixt == IM_INVALID) {
		    result = ActionResult[CMD_PIXNG];
		    ret = 4;
		    break;
		}
	    }
	    // check that what we got was sensible
	    if (ret == 0) {
		int j, ts, spp, smps;

		// size of the raw data
		expected_size = HEADER_ID_SIZE ; // size of the header
		for (j = 0 ; j < img->nbc ; j++) {
		    // the multiplier
		    ts = IOBlackBox->typeSize((img->comp[j]).pixt);
		    spp = (img->comp[j]).spp;
		    smps = \
			(img->comp[j]).nx * \
			(img->comp[j]).ny * \
			(img->comp[j]).nz;
		    
		    expected_size += ts * spp * smps;
		}
		img->expected_size = expected_size;
		// new unsophisticated binary data channel
		port = is->open_data_channel(result);
		if (port > 0) {
		    sres << port << " " << img->unique_id << '\n';
		    // overwrite the result with the port number
		    result = sres.str();
		}
	    }
	} else {
	    os << "511 Put syntax error, expected " << exp_nb << " arguments, \n"
	       << "511 Got: " << l.size() << "\n";
	    result = os.str();
	    ret = 5;
	}

	if (ret > 0) {
	    imh.pop_back(); // forget about this header
	    free(img->label); // had been strduped
	    delete img; // not useful anymore
	}
    } else {
        result = ActionResult[CMD_LOARG]; // Short argument list
	ret = 10;
    }
    if (ret > 0)
	result += \
	    "511 \n"
	    "511 Syntax: put \"name\" nbcomp nx ny nz ox oy oz IMG_TYPE PIX_TYPE spp\n"
	    "511        with as many times (nx ny ... spp) as there are nbcomp\n"
	    "511 Syntax error";
    
    return ret;
}

int print_pointfile::perform(vector<string> &l, string &result)
{
    if (PtFileMngr) {
	PtFileMngr->savelist(result);
	return 0;
    } else {
	result = ActionResult[CMD_PTFIL];
	return 1;
    }
}

// one of the assumptions is that the label is only one word.
// this will always be true if only this function is called
// to set the label, as  whitespace is the separator for l.
int set_appname::perform(vector<string> &l, string &result)
{
    int  scanned;
    char tmp[100], realAppName[100];

    if (l.size() > 0) {
	if (appName[0] == '[') {
	    scanned = sscanf(appName, "%*s %s", realAppName);
	    if (scanned == 1) 
		sprintf(tmp, "[%s] %s", l[0].c_str(), realAppName);
	    else // didn't work, revert
		sprintf(tmp, "[%s] %s", l[0].c_str(), appName);
	} else {
	    sprintf(tmp, "[%s] %s", l[0].c_str(), appName);
	}
	strcpy(appName, tmp);
	mainWindow->label(appName);
	return 0;

    } else {
	result = ActionResult[CMD_NOLBL]; // No label
	return 1;
    }
}

int zoom::perform(vector<string> &l, string &result)
{
    int retval = 0;

    if ((l.size() > 0) && mainViewer) {
	if ((cmp_nocase(l[0],"box") == 0) && (l.size() > 4)){
	    int bx, by, bw, bh;
	    bx = atoi(l[1].c_str());
	    by = atoi(l[2].c_str());
	    bw = atoi(l[3].c_str());
	    bh = atoi(l[4].c_str());
	    if (syncDisplay)
		im_draw_finished = false;
            // call should not be forwarded to other instances of imview
	    mainViewer->zoomToBox(bx,by,bw,bh,false);
	} else if ((cmp_nocase(l[0],"factor") == 0) && (l.size() > 1)) {
	    double zf = atof(l[1].c_str());
	    if (zf > 0) 
                // call should not go to other imviews (last arg)
		mainViewer->zoomFactor(zf, true, false); // do redraw, call not from GUI
	} else if ((cmp_nocase(l[0],"default") == 0) && (l.size() > 1)) {
	    double zf = atof(l[1].c_str());
	    if (zf > 0) 
		mainViewer->setDefaultZoomFactor(zf, false);
	} else {
	    result = ActionResult[CMD_INVAL] + l[0] + ")\n"; // invalid command
	    retval = 1;
	}
    } else {
	result = "513 No change\n";
	retval = 1;
    }
    
    return retval;
}

int pan::perform(vector<string> &l, string &result)
{
    if ((l.size() == 2) && mainViewer) {
        int bx, by;
        bx = atoi(l[0].c_str());
        by = atoi(l[1].c_str());
        if (syncDisplay)
            im_draw_finished = false;
        mainViewer->pan(bx, by);
    }
    
    return 0;
}

int reset::perform(vector<string> &l, string &result)
{
    if (mainViewer) {
	unzoom_cb(0,0);
	mainViewer->resetDisplay(true,false); // do redraw but don't forward
	return 0;
    } else {
	result = ActionResult[CMD_NOIMG]; // no image present
	return 1;
    }
}

int close_image::perform(vector<string> &l, string &result)
{
    if (mainViewer) {
	if (l.size() == 0) {
	    close_cb(0,0); // close the currently diplayed image
	    return 0;
	} else if (cmp_nocase(l[0], OVERLAY_MARKER) == 0) {
            closeoverlay_cb(0,0);
            return 0;
        } else {
	    result = ActionResult[CMD_NOSYS]; // command not implemented
	    return 1;
	}
    } else {
	result = ActionResult[CMD_NOIMG]; // no image present
	return 1;
    }
}

int raise_window::perform(vector<string> &l, string &result)
{
    if (mainWindow)
	mainWindow->show();
    return 0; // Always succeed
}

int iconize_window::perform(vector<string> &l, string &result)
{
    if (mainWindow)
	mainWindow->iconize();
    return 0; // Always succeed
}


int hide_window::perform(vector<string> &l, string &result)
{
    if ((l.size() == 0) && mainWindow)
	mainWindow->iconize(); // hide() actually kills the main window
    else if (l.size() > 0) {
        if (cmp_nocase(l[0], "toolbar") == 0) {
            if (toolbarPanel) toolbarPanel->hide();
        } else if (cmp_nocase(l[0], "print") == 0) {
            if (printPrefsPanel) printPrefsPanel->hide();
        } else if (cmp_nocase(l[0], "save") == 0) {
            if (savePrefsPanel) savePrefsPanel->hide();
        } else if (cmp_nocase(l[0], "info") == 0) {
            if (imageInfoPanel) imageInfoPanel->hide();
        } else if (cmp_nocase(l[0], "transfer") == 0) {
            if (transferEditorPanel) transferEditorPanel->hide();
        } else if (cmp_nocase(l[0], "spectra") == 0) {
            if (spectraPanel) spectraPanel->hide();
        } else if (cmp_nocase(l[0], "profile") == 0) {
            if (profilePanel) profilePanel->hide();
        } else if (cmp_nocase(l[0], "user") == 0) {
            if (userprefsPanel) userprefsPanel->hide();
        } else if (cmp_nocase(l[0], "bivhist") == 0) {
            if (bivHistPanel) bivHistPanel->hide();
        } else if (cmp_nocase(l[0], "progress") == 0) {
            if (progressPanel) progressPanel->hide();
        } else if (cmp_nocase(l[0], "help") == 0) {
            if (help) help->hide();
        } else {
            result = ActionResult[CMD_NODLG]; // Unknown Dialog
            return 1;
        }
    }
    return 0; // Always succeed
}

int show_window::perform(vector<string> &l, string &result)
{
    int retval = 0;
    
    if ((l.size() == 0) && mainWindow)
	mainWindow->show();
    else if (l.size() > 0) {
        if (cmp_nocase(l[0], "toolbar") == 0) {
            toolbar_cb(0,0);
        } else if (cmp_nocase(l[0], "print") == 0) {
            if (!titleBox)
                printprefs_cb(0,0);
            else {
                result = "520 No image shown\n";
                retval = 1;
            }
        } else if (cmp_nocase(l[0], "save") == 0) {
            if (!titleBox)
                save_cb(0,0);
            else {
                result = "520 No image shown\n";
                retval = 1;
            }
        } else if (cmp_nocase(l[0], "info") == 0) {
            imageinfo_cb(0,0);
        } else if (cmp_nocase(l[0], "transfer") == 0) {
            transfer_cb(0,0);
        } else if (cmp_nocase(l[0], "spectra") == 0) {
            showspectrum_cb(0,0);
        } else if (cmp_nocase(l[0], "profile") == 0) {
            showprofile_cb(0,0);
        } else if (cmp_nocase(l[0], "user") == 0) {
            userprefspanel_cb(0,0);
        } else if (cmp_nocase(l[0], "bivhist") == 0) {
            showbivhist_cb(0,0);
        } else if (cmp_nocase(l[0], "progress") == 0) {
            showprogress_cb(0,0);
        } else if (cmp_nocase(l[0], "help") == 0) {
            onlinehelp_cb(0,0);
        } else {
            result = ActionResult[CMD_NODLG]; // unknown dialog
            return 1;
        }
    }
    
    return retval;
}


int imlink::perform(vector<string> &l, string &result)
{
    int retval = 0;
    
    if (l.size() < 2) {
        result = ActionResult[CMD_LOARG]; // short argument list
        return 1;
    } 

    connection *imc = new connection;
        
    try {
        // create connection
        imc->address = l[0];
        imc->portnumber = atoi(l[1].c_str());
	dbgprintf("Making connection with imview on %s, port %d\n", 
		  (imc->address).c_str(), imc->portnumber);
        imc->stream = new TCPClientStream((imc->address).c_str(), imc->portnumber);
        // log in
        const char *p = getenv(uservar); // get the user name from the environment
        *(imc->stream) << "user " << p << endl;
        // get response
        string response;
        if (getline(*(imc->stream), response).good()) {
	    dbgprintf("Line is OK\n");
            if ((response.rfind(ActionResult[CMD_OK]) != string::npos)) {
		// all is good
		dbgprintf("Response is OK\n");
		// make a new link manager if we don't have one now
		if (!imview_linkmanager)
		    imview_linkmanager = new linkmanager;
		imview_linkmanager->save_connection(imc);
	    } else {
		dbgprintf("Response not OK\n");
		result = response + "\n" + ActionResult[CMD_CFAIL]; // connection failed
		retval = 1;
	    }
        } else {
            result = response + "\n" + ActionResult[CMD_CFAIL]; // connection failed
	    retval = 1;
        }
    }
    catch (const SocketRunTimeException &e) {
        result = ActionResult[CMD_ESOCK] + string(e.what()) + "\n"; // socket exception
        retval = 1;
    }
    
    if (retval > 0) {
	delete imc;
    }

    return retval;
}

int imunlink::perform(vector<string> &l, string &result)
{
    if (l.size() < 2) {
        result = ActionResult[CMD_LOARG]; // missing arguments
        return 1;
    } else {
	result = ActionResult[CMD_NOSYS]; // not implemented yet
	return 1;
    }

    // future work here...
    return 0;
}


int set_variable::perform(vector<string> &l, string &result)
{
    if (l.size() < 1) {
        result = ActionResult[CMD_LOARG]; // short argument list
        return 1;
    }else if (imview_interpreter->setVariable(l[0], l[1]) == 0) {
        return 0;
    } else {
        result = ActionResult[CMD_NOVAR]; // incorrect variable
        return 1;
    }
}

int get_variable::perform(vector<string> &l, string &result)
{
    if (imview_interpreter->getVariable(l[0], result) == 0) {
        return 0;
    } else {
        result = ActionResult[CMD_NOVAR]; // incorrect variable
        return 1;
    }
}

int list_variables::perform(vector<string> &l, string &result)
{
    result = imview_interpreter->listVariables();

    return 0;
}

int tokenize::perform(vector<string> &l, string &result)
{
    ostringstream osm;

    for (unsigned int i = 0 ; i < l.size() ; ++i)
        osm << l[i] << " ";
    
    result = osm.str();
    return 0;
}

// -------- The variables  ---------

extern bool useOffscreenBuff;

void useoffscreenbuf::set(const string &stringval)
{
    fromstr_convert(stringval, useOffscreenBuff);
}

string &useoffscreenbuf::get(void)
{
    return tostr_convert(useOffscreenBuff);
}

string useoffscreenbuf::list(void)
{
    ostringstream ost;
    ost << "useOffscreenBuff : <Bool> = " << tostr_convert(useOffscreenBuff) << '\n';
    return ost.str();
}

extern bool lutWraparound;

void lutwraparound::set(const string &stringval)
{
    fromstr_convert(stringval, lutWraparound);
    if (lutWraparound)
        mainMenuBar->setFlagsByName(WRAPLUT_ITEM, FL_PUP_BOX | FL_MENU_VALUE);
    else
        mainMenuBar->setFlagsByName(WRAPLUT_ITEM, FL_PUP_BOX);
    mainMenuBar->redraw();
    mainViewer->displayCurrentImage(); // stronger than just redisplay
}

string &lutwraparound::get(void)
{
    return tostr_convert(lutWraparound);
}

string lutwraparound::list(void)
{
    ostringstream ost;
    ost << "lutWraparound    : <Bool> = " << tostr_convert(lutWraparound) << '\n';
    return ost.str();
}


void syncdisplay::set(const string &stringval)
{
    bool mydisplay;

    fromstr_convert(stringval, mydisplay);
    syncDisplay = mydisplay;
    if (syncDisplay)
        mainMenuBar->setFlagsByName(SYNCDISP_ITEM, FL_PUP_BOX | FL_MENU_VALUE);
    else
        mainMenuBar->setFlagsByName(SYNCDISP_ITEM, FL_PUP_BOX);
    mainMenuBar->redraw();
}

string &syncdisplay::get(void)
{
    return tostr_convert(syncDisplay);
}

string syncdisplay::list(void)
{
    ostringstream ost;
    ost << "syncDisplay      : <Bool> = " << tostr_convert(syncDisplay) << '\n';
    return ost.str();
}

// controls whether the image always fill the window
void imagewindowfit::set(const string &stringval)
{
    int maybenextmode;
    fromstr_convert(stringval, maybenextmode);
    switch (maybenextmode) {
    case IMV_DISPLAY_WINDOW_FIT_IMG:
	mainMenuBar->setFlagsByName(WIN_FIT_IMG_ITEM, FL_MENU_RADIO | FL_MENU_VALUE);
	mainViewer->setdisplaymode(static_cast<displaymode>(maybenextmode));
	break;
    case IMV_DISPLAY_IMG_FIT_WINDOW:
	mainMenuBar->setFlagsByName(IMG_FIT_WIN_ITEM, FL_MENU_RADIO | FL_MENU_VALUE);
        mainViewer->setdisplaymode(static_cast<displaymode>(maybenextmode));
	break;
    case IMV_DISPLAY_DECOUPLE_WIN_IMG:
	mainMenuBar->setFlagsByName(DECOUPLE_IMG_WIN_ITEM, FL_MENU_RADIO | FL_MENU_VALUE);
        mainViewer->setdisplaymode(static_cast<displaymode>(maybenextmode));
	break;
    default:
	dbgprintf("Diplay mode given (%d) is incorrect\n", maybenextmode);
	break; // don't change the display mode
    }
    mainMenuBar->redraw();
}

string &imagewindowfit::get(void)
{
    return tostr_convert(mainViewer->getdisplaymode());
}

string imagewindowfit::list(void)
{
    ostringstream ost;
    ost << "displaymode      : <Int>  = " << tostr_convert(mainViewer->getdisplaymode()) << '\n';
    return ost.str();
}

// list existing established connections
string &connectionlist::get(void)
{
    static string res;

    if (imview_linkmanager)
	res = imview_linkmanager->print_connections();
    else
	res = "No connection"; // no connection -> no link manager (at least at init)

    return res;
}

string connectionlist::list(void)
{
    ostringstream ost;
    int nb;

    if (imview_linkmanager)
	nb = imview_linkmanager->nb_connections();
    else 
	nb = 0;

    ost << "connectionList   : <string> = (" << nb ;

    if (nb > 1) 
	ost << " connections) \n";
    else
	ost << " connection) \n";

    return ost.str();
}

// -- conversion helper functions

// we can't have this function return a bool because C++
// won't overload it unless bool is in the argument list...
void fromstr_convert(const string &strval, bool &value)
{
    if (cmp_nocase(strval, "true") == 0)
        value = true;
    else
        value = false;

    return;
}

string &tostr_convert(bool value)
{
    if (value)
        return truestr;
    else
        return falsestr;
}

void fromstr_convert(const string &strval, int &value)
{
    value = atoi(strval.c_str());
    return;
}

string &tostr_convert(int value)
{
    ostringstream result;
    static string mystr;

    //result.seekp(0); // rewind
    result << value;

    mystr = result.str();
    return mystr;
}

// -------- The link manager ---------

string linkmanager::print_connections(void)
{
    ostringstream result;

    result << "Live connections:\n";
    for (connectionIterator i = otherimview.begin() ; i != otherimview.end() ; ++i) {
	result << "  Host: " << (*i)->address << " port : " << (*i)->portnumber << endl;
    }

    return result.str();
}

int linkmanager::getresponse(TCPClientStream *stream)
{
    int   retval = 0;
    bool  run;
    string message;

    // get response
    while (run = getline(*stream, message).good())  {
	if (message.rfind("000 OK") != string::npos) {
	    dbgprintf("Response from stream: OK\n");
	    retval = 0;
	    break;
	} else if ((message.rfind("500 Command failed") != string::npos)
		   || (message.rfind("501 Unknown command") != string::npos)) {
	    dbgprintf("Response from stream:\n%s\n", message.c_str());
	    retval = 2;
	    break;
	}
    }
    if (!run)
	retval = 3; // server down

    return retval;
}

// duplicate current zoom box to all imview clients
int linkmanager::zoombox(int x, int y, int w, int h)
{
    int retval = 0; 

    if (duplicate_action[ActionZoom]) {
	for (connectionIterator i = otherimview.begin() ; i != otherimview.end() ; ++i) {
	    *((*i)->stream) << "zoom box " 
			    << x << " "
			    << y << " "
			    << w << " "
			    << h << endl;
	    retval = getresponse((*i)->stream);
	}
    } else {
	retval = 1;
    }    

    return retval;
}

int linkmanager::zoomfactor(double zf)
{
    int retval = 0;

    if (duplicate_action[ActionZoom]) {
	for (connectionIterator i = otherimview.begin() ; i != otherimview.end() ; ++i) {
	    *((*i)->stream) << "zoom factor " << zf << endl;
	    retval = getresponse((*i)->stream);
	}
    } else 
	retval = 1;

    return retval;
}

int linkmanager::setDefaultZoomFactor(double f)
{
    int retval = 0;

    if (duplicate_action[ActionZoom]) {
	for (connectionIterator i = otherimview.begin() ; i != otherimview.end() ; ++i) {
	    *((*i)->stream) << "zoom default " << f << endl;
	    retval = getresponse((*i)->stream);
	}
    } else 
	retval = 1;

    return retval;
}

int linkmanager::resetDisplay(void)
{
    int retval = 0;

    if (duplicate_action[ActionZoom]) {
	for (connectionIterator i = otherimview.begin() ; i != otherimview.end() ; ++i) {
	    *((*i)->stream) << "reset" << endl;
	    retval = getresponse((*i)->stream);
	}
    } else 
	retval = 1;

    return retval;
}

// -------- The interpreter ---------

interpreter::interpreter()
{
#ifdef HAVE_SYSV_IPC
    // this must be called before initializing any putm instance
    putm::setPathToEmpty();
#endif

    // associate tokens by hand
    tokens["quit"]  = new quit;
    tokens["bye"]   = new quit;
    tokens["end"]   = new quit;
    tokens["exit"]  = new quit; // I hope that's enough ways to quit!
    tokens["kill"]  = new killme; // this is not the same thing: will terminate imview
    tokens["help"]  = new interpreter_help;
    tokens["?"]     = new interpreter_help;
    tokens["pf"]    = new print_pointfile;
    tokens["user"]  = new user;
    tokens["deny"]  = new deny;
    tokens["put"]   = new put;
    tokens["putm"]  = new putm;
    tokens["ppath"] = new ppath;
    tokens["putp"]  = new putp;
    tokens["labl"]  = new set_appname;
    tokens["stpf"]  = new set_pointfile;
    tokens["ldpf"]  = new load_pointfile;
    tokens["svpf"]  = new save_pointfile;
    tokens["clpf"]  = new clear_pointfile;
    tokens["load"]  = new load;
    tokens["move"]  = new move;
    tokens["size"]  = new size;
    tokens["cmap"]  = new cmap;
    tokens["zoom"]  = new zoom;
    tokens["pan"]   = new pan;
    tokens["reset"] = new reset;
    tokens["close"] = new close_image;
    tokens["raise"] = new raise_window;
    tokens["icon"]  = new iconize_window;
    tokens["hide"]  = new hide_window;
    tokens["show"]  = new show_window;
    tokens["set"]   = new set_variable; 
    tokens["get"]   = new get_variable;
    tokens["list"]  = new list_variables;
    tokens["toks"]  = new tokenize;
    tokens["link"]  = new imlink;
    tokens["unlk"]  = new imunlink;

    // variables
    varnames["useoffscreenbuff"] = new useoffscreenbuf;
    varnames["lutwraparound"]    = new lutwraparound;
    varnames["syncdisplay"]      = new syncdisplay;
    varnames["displaymode"]     = new imagewindowfit;
    varnames["connectionlist"]   = new connectionlist; // read-only var
    
    // associate img type with string
    tr_img["IM_ERROR"]      = IM_ERROR;
    tr_img["IM_UNSPEC"]     = IM_UNSPEC;
    tr_img["IM_SINGLE"]     = IM_SINGLE;
    tr_img["IM_SPECTRUM"]   = IM_SPECTRUM;
    tr_img["IM_MULTI"]      = IM_SPECTRUM; // at this stage, this is what we have to assume
    tr_img["IM_RGB"]        = IM_RGB;
    tr_img["IM_HLS"]        = IM_HLS;

    // associate pix type with string
    tr_pix["IM_BINARY"]     = IM_BINARY;
    tr_pix["IM_CHAR"]       = IM_CHAR;
    tr_pix["IM_INT1"]       = IM_INT1;
    tr_pix["IM_UINT1"]      = IM_UINT1;
    tr_pix["IM_SHORT"]      = IM_SHORT;
    tr_pix["IM_INT2"]       = IM_INT2;
    tr_pix["IM_UINT2"]      = IM_UINT2;
    tr_pix["IM_INT"]        = IM_INT;
    tr_pix["IM_INT4"]       = IM_INT4;
    tr_pix["IM_UINT4"]      = IM_UINT4;
    tr_pix["IM_INT8"]       = IM_INT8;
    tr_pix["IM_UINT8"]      = IM_UINT8;
    tr_pix["IM_FLOAT"]      = IM_FLOAT;
    tr_pix["IM_DOUBLE"]     = IM_DOUBLE;

    
    return;
}

interpreter::~interpreter()
{
    // delete all the newed stuff
    map<string,action*,Nocase>::iterator p;

    dbgprintf("Deleting all tokens\n");
    for (p = tokens.begin() ; p != tokens.end() ; p++)
	delete p->second;

    tokens.clear();

    // the rest is not so important
    tr_img.clear();
    tr_pix.clear();

    // get rid of connections to other imviews
    if (imview_linkmanager)
	delete imview_linkmanager;
    
    return;
}

// evaluate with arguments in a list (optional)
void interpreter::evaluate(string &s, vector<string> &args, string &result)
{
    AMI    p;
    int    res;
    
    if ((p = tokens.find(s)) != tokens.end()) {
	action *any = p->second;
	res = any->perform(args, result);
	if (res == 0)
	    result += ActionResult[CMD_OK] + string("\n"); // keyword!
	else if (res > 0)
	    result += ActionResult[CMD_FAILD]; /// this is a keyword
    } else {
	ourcomplaint.perform(args,result);
    }
}

// The parser is called through the GUI via manage_command
void interpreter::parseCommand(string &command, string &result)
{
    string::size_type    pos, startpos;
    string               firstarg, nextarg, reststr;
    vector<string>       restarg(100);
    int                  i;

    // if command starts with white space, tough
    pos = command.find_first_of(" \r\t\n");  // whitespaces
    if (pos == 0) {
	result = ""; // command still needs to be set up
	return ;  // commands starting with a white space are ignored.
    }

    firstarg = command.substr(0,pos);
    dbgprintf("  interpreter: first argument = (%s)\n", firstarg.c_str());
    // rest of the arguments in a list, '\r' or '\n' indicates end of command
    i = 0;
    while ((pos != string::npos) && (command[pos] != '\r') && (command[pos] != '\n')) {
	startpos = command.find_first_not_of(" \t", pos); // skip to non-whitespace
	if (command[startpos] == '"') { // identify strings
	    pos = command.find_first_of("\"\r\n", startpos+1);
	    if (command[pos] != '"') {
		nextarg = command.substr(startpos+1, pos-startpos); // no trailing \"
	    } else {
		nextarg = command.substr(startpos+1, pos-startpos-1); // we don't want the \"
		pos++; // go past the last \"
	    }
	} else {
	    pos = command.find_first_of(" \r\t\n", startpos);  // whitespaces
	    if (pos == startpos)
		break; // we have hit the end of the command, and it's all spaces.
	    nextarg = command.substr(startpos, pos-startpos);
	}
	dbgprintf("  interpreter: next argument = (%s)\n", nextarg.c_str());
	restarg[i++] = nextarg; // we should be using a push_back here
    }
    // OK, this is due to MINGW32 crapping out on push_back right here. Why?
    restarg.erase(restarg.begin()+i, restarg.end()); // This is weird, right?
    dbgprintf("Ready to evaluate\n");
    evaluate(firstarg, restarg, result);
 

    return;
}

int interpreter::setVariable(const string &aVar, const string &value)
{
    map<string,variable*,Nocase>::iterator p = varnames.find(aVar);

    if (p != varnames.end()) {
        variable *somevar = p->second;
	somevar->set(value);
    } else {
        return 1;
    }
    return 0;
}

int interpreter::getVariable(const string &aVar, string &value)
{
    map<string,variable*,Nocase>::iterator p = varnames.find(aVar);
    if (p != varnames.end()) {
        variable *somevar = p->second;
        value = somevar->get() + "\n";
    } else
        return 1;
    
    return 0;
}

string &interpreter::listVariables(void)
{
    static string result;
    map<string,variable*,Nocase>::iterator p;

    result = "";
    for (p = varnames.begin() ; p != varnames.end() ; p++) {
        variable *somevar = p->second;
        result += somevar->list();
    }

    return result;
}


#endif // HAVE_PTHREADS
