E          *I,         /* input image */
	       const char     *name,      /* identifier */
	       const char     *user,      /* optional user name (NULL default: current user) */
	       const char     *hostname,  /* hostname (default localhost if NULL) */
	       short           port)      /* connection port (suitable default if 0) */
{
/**
   Puts an image to the imview server for display                    
                                                                      
   RETURN VALUE:                                                     
   0 if success. Error code > 0 if not.                              
                                                                      
   DESCRIPTION:                                                      
   This function puts an arbitrary image to imview. It uses          
   a TCP/IP protocol to connect and upload to an existing imview     
   server.                                                           
                                                                      
   NOTE:                                                             
   The upload will be known to imview through its name. If           
   two uploads are made with the same name, the later one            
   to complete will overwrite the earlier one. Uploads               
   with different names are retained (of course they can             
   be deleted manually by the imview user).                          
                                                                      
   HISTORY:                                                          
   Written by Hugues Talbot         9 Mar 2000                       
                                                                      
   TESTS:                                                            
                                                                      
   REFERENCES:                                                       
                                                                      
   KEYWORDS:                                                         
   display, socket, tcp/ip.                                          
**/
    void          *buff;
    wsize_t         buflen, rec, tbs = 0;
    int            c, fd, bfd, res;
    char           FnName[100];
    short          binport;
    unsigned       magic_number;
    char           magic[HEADER_ID_SIZE];
    int            nbc;
    char           final_name[ANSWER_MAX_SIZE];
    char           put_command[8*ANSWER_MAX_SIZE];
    char           subcommand[ANSWER_MAX_SIZE], *cmdres;
    char           username[40], *u;
    char           login_command[50], login_result[50], sync_filename[50];
    char           delayed_answer[ANSWER_MAX_SIZE];
    int            nbscanned;
    ipctype        use_shm;
    int            nb_da;
    int            nx, ny, nz, ox, oy, oz;
    pixtype        imp;
    const char    *imgt, *pixt;
    struct timeval start_prog, start_opensocket, start_login; 
    struct timeval start_put, start_upload, end_upload, before_close, end_prog;
    struct timezone dummy;
    double         time0, time1, time2, time3, time4, time5, time6, time7;
    double         dt0, dt1, dt2, dt3, dt4, dt5, dt6, dt7, dt8;

    gettimeofday(&start_prog, &dummy);
    sprintf(FnName,"imviewput2(%s,%s,%d)", name, hostname, port);
    res = 0; /* all peachy */
    
    if (!I || !name) {
	imexception("%s: requires an image and a name\n", FnName);
	return 1;
    }
    /* NOTES:
    ** The server decides on the integer identifier
    ** names decide on the removal policy
    */
    
    /* Make sure we are saving a valid image */
    if (imgetimgtype(I) == IM_BADIMAGE) {
	imexception("%s: bad image\n", FnName);
	return 2;
    }

    nbc = imgetnumcomp(I);
    
    /* some people don't set IM_MULTI correctly */
    if (imgetimgtype(I) == IM_MULTI) {
	if (nbc > 1) {
	    /* check whether the image is a true multicomponent image */
	    nx = imgetnx(I, 0);
	    ny = imgetny(I, 0);
	    nz = imgetnz(I, 0);
	    ox = imgetfirstx(I, 0);
	    oy = imgetfirsty(I, 0);
	    oz = imgetfirstz(I, 0);
	    imp = imgetpixtype(I, 0);
	    for (c = 1 ; c < nbc ; c ++) {
		if (   (imgetnx(I,c) != nx)
		       || (imgetny(I,c) != ny)
		       || (imgetnz(I,c) != nz)
		       || (imgetfirstx(I,c) != ox)
		       || (imgetfirsty(I,c) != oy)
		       || (imgetfirstz(I,c) != oz)
		       || (imgetpixtype(I, c) != imp)) {
		    if (c > 2) {
			imexception(
			    "%s: warning, cannot cope with true multi-component images yet.\n"
			    "    Only the first %d components will be sent\n", c+1);
			nbc = c+1;
			break;
		    } else {
			imexception(
			    "%s: warning, cannot cope with true multi-component images yet.\n"
			    "    Only the first component will be sent\n");
			nbc = 1;
			break;
		    }
		}
	    }
	    if (c>=nbc)
		imexception("%s: warning, IM_MULTI should be reserved for true multi-component images\n");
		
	} else
	    imexception("%s: warning, IM_MULTI should be reserved for images with more than one component\n");
    }

    /* 4D images not yet supported either */
    if (imgetnt(I,0) != 1) {
	imexception("%s: imview can't cope with 4D images yet\n", FnName);
	return 4;
    }

    /* get the user name */
    username[0] = '\0'; /* empty user field */
    if ((user == NULL) || (user[0] == '\0')) {
	if ((u = getenv(USRID)) != NULL) {
	    strcpy(username, u);
	}
    } else {
	strcpy(username, user);
    }

    if (username[0] == '\0') {
	imexception("%s: no user name specified and no USER environment variable\n");
	return 5;
    }

    gettimeofday(&start_opensocket, &dummy);
    LIARdebug("Step 1: open socket");
    /* step 1: open a socket to the imview command server */
    if (port == 0) {
	port = DEFAULTPORT;
    }

    if ((hostname == 0) || (hostname[0] == '\0'))
	hostname = DEFAULTHOST;

    if (!force_socket && (strcmp(hostname, "localhost") == 0)) {
	use_shm = use_shm_default;
    } else {
	use_shm = SHM_NONE;
    }
    
    /* radical solution for now */
#if !defined(HAVE_SYSV_IPC) && !defined(HAVE_POSIX_IPC)
    use_shm = SHM_NONE;
#endif
    
    fd = imview_open_connection(hostname, port);
    if (fd < 0) {
	imexception("%s: cannot open connection, %s\n",
		    FnName, strerror(errno));
	return 6;
    }

    gettimeofday(&start_login, &dummy);
    LIARdebug("Step 2: log in");
    /* step 2: log in */
    sprintf(login_command, "user %s", username);
    strcat(login_command, EOC);
    
    /* flush the input from any previous answer. They would confuse the issue */
    /* imview_flush_input(fd); */
    cmdres = imview_command(fd, login_command); 

    if (cmdres == NULL) {
	imexception("%s: login command didn't work\n", FnName);
	imview_close_connection(fd);
	return 7;
    }

    LIARdebug("Login command result: %s", cmdres);
    nbscanned = sscanf(cmdres, "Welcome, %s %s", login_result, sync_filename);
    if ((nbscanned != 2) || (strcmp(login_result, username) != 0)) {
	imexception("%s: login failed\n", FnName);
	imview_close_connection(fd);
	return 8;
    }


    /* step 2: send a PUT command */
    gettimeofday(&start_put, &dummy);
    LIARdebug("Sending Put command");
    final_name[0] = '"';
    strncpy(final_name+1, name, ANSWER_MAX_SIZE-100);
    strcat(final_name, " <memory>\""); /* to identify the data coming from the client */
    sprintf(put_command,
	    "%s %s %d ",
            putcmd[use_shm],
	    final_name,
	    1);

    /* for the time being we only support SPECTRUM-type image (all nx, etc the same) */
    c = 0;
    nx = imgetnx(I,c);
    ny = imgetny(I,c);
    nz = imgetnz(I,c);
    ox = imgetfirstx(I,c);
    oy = imgetfirsty(I,c);
    oz = imgetfirstz(I,c);
    imgt = imgetimgtypestr(I);
    pixt = imgetpixtypestr(I,c);
    
    sprintf(subcommand,"%d %d %d %d %d %d %s %s %d ",
	    nx, ny, nz,
	    ox, oy, oz,
	    imgt, pixt, nbc);
    strcat(put_command, subcommand);

    strcat(put_command, EOC); /* end of command */

    /* flush the input from any previous answer. They would confuse the issue */
    /* imview_flush_input(fd); no need I think*/

    /* send the actual command */
    if (use_shm == SHM_SYSV) {
	imview_command_noanswer(fd, put_command);
	if (imview_sysv_ipc_setup(sync_filename) == 0) {
	    gettimeofday(&start_upload, &dummy);
	    for (c = 0 ; c < nbc ; c++) {
		buff = imgetbuff(I, c);
		buflen = imgetnx(I,c) * imgetny(I,c) * imgetnz(I,c) * imgetnt(I,c);
		tbs = buflen * imgetpixsize(I,c);
		rec = imview_sysv_shm_sendata(buff, tbs);
		if (rec != tbs ) {
		    imexception("Incomplete transfer, sent: %d, wanted to send: %d\n", rec, tbs);
		    res = 7;
		    break;
		}
	    }
	    gettimeofday(&end_upload, &dummy);
	    /* release access to shared memory */
	    imview_sysv_ipc_wind_down();
	    nb_da=imview_get_delayed_response(fd, delayed_answer); /* I don't know that this is necessary */
	    if (nb_da < 0)
		imexception("Transfer probably failed\n");
	    else
		LIARdebug("Transfer response: %s", delayed_answer);
	} else {
	    imexception("SYSV IPC setup failed, closing connection\n");
	}
    } else if (use_shm == SHM_POSIX) {
        char *ppath_answer = imview_command(fd, "ppath" EOC);
        /* find the path to the resources */
        sscanf(ppath_answer, "%s ", ipc_base_path);
        LIARdebug("ipc_base_path = (%s)", ipc_base_path);
        
        imview_command_noanswer(fd, put_command);
        
	if (imview_px_ipc_setup(ipc_base_path) == 0) {
	    gettimeofday(&start_upload, &dummy);
	    for (c = 0 ; c < nbc ; c++) {
		buff = imgetbuff(I, c);
		buflen = imgetnx(I,c) * imgetny(I,c) * imgetnz(I,c) * imgetnt(I,c);
		tbs = buflen * imgetpixsize(I,c);
		rec = imview_px_shm_sendata(buff, tbs);
		if (rec != tbs ) {
		    imexception("Incomplete transfer, sent: %d, wanted to send: %d\n", rec, tbs);
		    res = 7;
		    break;
		}
	    }
	    gettimeofday(&end_upload, &dummy);
	    /* release access to shared memory */
	    imview_px_ipc_wind_down();
	    nb_da=imview_get_delayed_response(fd, delayed_answer); /* I don't know that this is necessary */
	    if (nb_da < 0)
		imexception("Transfer probably failed\n");
	    else
		LIARdebug("Transfer response: %s", delayed_answer);
	} else {
	    imexception("POSIX IPC setup failed, closing connection\n");
	}
    } else {
	cmdres = imview_command(fd, put_command); 
	
	if (cmdres == NULL) {
	    imexception("%s: sent command didn't work\n", FnName);
	    imview_close_connection(fd);
	    return 8;
	}

	LIARdebug("Got result: %s", cmdres);
	/* check the result. It should contain a port number and a magic number */
	sscanf(cmdres, "%hd %u", &binport, &magic_number); /* match a shord and an unsigned */
	memset(magic, 0, HEADER_ID_SIZE);
	memcpy(magic, &magic_number, sizeof(unsigned int)); /* the rest of the header is padded with 0s */

	if (binport > 0 && magic > 0) {
	    /* step 3: everything is peachy, send the binary data now */
	    LIARdebug("OK, sending binary data now");
	    /* 3a: open binary port connection (same host) */
	    if ((bfd = imview_open_binconn(hostname, binport)) > 0) {
		/* 3b: send back the magic number */
		gettimeofday(&start_upload, &dummy);
		if (imview_send_data(bfd, (char *)(&magic), HEADER_ID_SIZE, 0) == HEADER_ID_SIZE) {
		    /* 3c: send each buffer in turn */
		    for (c = 0 ; c < nbc ; c++) {
			LIARdebug("Sending buffer #%d", c);
			buff = imgetbuff(I, c);
			buflen = imgetnx(I,c) * imgetny(I,c) * imgetnz(I,c) * imgetnt(I,c);
			tbs = buflen * imgetpixsize(I,c);
			rec = imview_send_data(bfd,
					       (char *)buff,
					       tbs,
					       (c == (nbc-1))); /* check for end of transmission */
			if (rec != tbs ) {
			    imexception("Incomplete transfer, sent: %d, wanted to send: %d\n", rec, tbs);
			    res = 7;
			    break;
			}
		    }
		    gettimeofday(&end_upload, &dummy);
		    /* close the binary connection */
		    LIARdebug("Closing binary connection");
		    imview_close_binconn(bfd);
		}
	    }
	}
    }
    gettimeofday(&before_close,&dummy);
    /* close connection gracefully */
    LIARdebug("Closing command connection");
    imview_close_connection(fd);
    gettimeofday(&end_prog,&dummy);

    /* compute timings */
    time0 = start_prog.tv_sec + start_prog.tv_usec/1e6;
    time1 = start_opensocket.tv_sec + start_opensocket.tv_usec/1e6;
    dt0 = time1 - time0;
    time2 = start_login.tv_sec + start_login.tv_usec/1e6;
    dt1 = time2 - time1;
    time3 = start_put.tv_sec + start_put.tv_usec/1e6;
    dt2 = time3 - time2;
    time4 = start_upload.tv_sec + start_upload.tv_usec/1e6;
    dt3 = time4 - time3;
    time5 = end_upload.tv_sec + end_upload.tv_usec/1e6;
    dt4 = time5 - time4;
    time6 = before_close.tv_sec + before_close.tv_usec/1e6;
    dt5 = time6-time5;
    time7 = end_prog.tv_sec + end_prog.tv_usec/1e6;
    dt6 = time7-time6;
    dt7 = time6-time3; 
    dt8 = time7-time0;

    
    LIARdebug("\n"
	      "Timings: start to opening of socket  : %6.4f sec\n"
	      "         opening of socket to login  : %6.4f sec\n"
	      "         login to put                : %6.4f sec\n"
	      "         put to beginning of upload  : %6.4f sec\n"
	      "         begining of upload to end   : %6.4f sec\n"
	      "         end of upload to close conn : %6.4f sec\n"
	      "         close conn to exit          : %6.4f sec\n"
	      "-------------------------------------           \n"
	      "         put to close conn           : %6.4f sec\n"
	      "         start to exit               : %6.4f sec\n",
	      dt0, dt1, dt2, dt3, dt4, dt5, dt6, dt7, dt8);

    if ((dt2> 0) && (dt1 > 0))
	LIARdebug("\n"
		  "Throughput:             upload only : %10.3f kB/s\n"
		  "                       steady state : %10.3f kB/s\n"
		  "                     whole process  : %10.3f kB/s\n",
		  (tbs*nbc)/(dt4*1024), (tbs*nbc)/(dt7*1024), (tbs*nbc)/(dt8*1024));

    return res;
}
