#include "afni.h"
#include <sys/utsname.h>

/***********************************************************************
 Version notes:                                      30 Dec 2015 [rickr]

    NEW: AFNI_15.3.00
       - 15.3.00:
          - 2 digit year
          - 1 digit quarter (0-based)
          - 2 digit index (0-based index,incremented by build)
       - to be applied (by) Jan 1, 2016
       - stored in AFNI_version_base.txt
       - generated/updated by dist_src and checked into github
       * so this label represents an AFNI build version
         - it equates to a git commit object (committed to github by build)
         - people doing local builds will not see this change
            - local build versions and dates will separate over time

    OLD: AFNI_2011_12_21_1014 
       - such labels were done with Ctag in CVS
       - created Dec, 2004 - Dec, 2011 (though rarely after 2005)
       - as it labeled _every_ source file, it was dropped
       - label AFNI_2011_12_21_1014 lasted 4 years (until 30 Dec, 2015)

    pub/dist/AFNI.version:
       - file used by most AFNI versions (see AFNI_version_check())
          - except for those from June 2014 - Dec 2015
       - was generated by afni_vcheck, now by dist_src
       - first line for version, might have size limit for some sprintf
       - second line was used by very old AFNI versions
       - third line used for motd=

    pub/dist/AFNI.compile_date:
       - file used by AFNI versions from June 2014 - Dec 2015
       - see AFNI_compile_date_check()
       - updated in distall_MASTER (keep generating 2016 for ~old AFNI)

    AFNI_version_base.txt : updated by dist_src only
    AFNI_version.txt      : build target, install file for users/progs
    AFNI_version.h        : build target, code file for mrilib.h #include
       - AFNI_version is actual build target, so always makes new .h


    There are 3 types of checks going on now:
       - AFNI_version_check(): MOTD and afni GUI update comments
       - THD_check_AFNI_version(): command line progs update comments
       - MCW_popup_message_once(): afni GUI: situational comments
          - slowly growing? list, all seen on new accounts (or systems)
         
 ***********************************************************************
 */

#ifdef EXPECTED_AFNI_VER_STRLEN
#undef EXPECTED_AFNI_VER_STRLEN
#endif
#define EXPECTED_AFNI_VER_STRLEN 12

#define FAIL_MESSAGE(reason)                           \
 do{ fprintf(stderr,"\n"                               \
                    "\n** Version check disabled: %s", \
             reason ) ;                                \
     disabled = 1 ; } while(0)

static int disabled = 0 ;

#define STR_CHILD   "tcp:localhost:20279"
#define AFNI_HOST   "https://afni.nimh.nih.gov/pub/dist/"
#define VSIZE       1024

#define COMPILE_URL "https://afni.nimh.nih.gov/pub/dist/AFNI.compile_date"

static pid_t vc_child_pid = (pid_t)(-1) ;
static IOCHAN *vc_ioc     = NULL ;
static char *motd_old     = NULL ;  /* 29 Nov 2005 */
static char *motd_new     = NULL ;

#undef VERBOSE   /* print messages on failure? */

void AFNI_start_fetching_url( char *urlstring ) ;
static NI_element * AFNI_read_vctime(void);
static int AFNI_version_diff( char * s1, char * s2, int * diff );
static int AFNI_update_vctime( char * new_ver, char * new_motd );
static int vc_check_too_soon( void );

/*------------------------------------------------------------------------*/
/*!  This is only called from within the child process. */

static void vexit( int sig )
{
#ifdef VERBOSE
   static volatile int fff=0 ;
   if( fff ) _exit(1) ; else fff = 1 ;
   fprintf(stderr,"** Version Check: child fails to complete: %d **\n",sig);
#endif
   _exit(1);
}

/*------------------------------------------------------------------------*/
/*! This is called at main AFNI process exit,
    to destroy vc_ioc if it is still open.
--------------------------------------------------------------------------*/

static void vc_exit(void){ iochan_close(vc_ioc) ; }  /* 12 Dec 2002 */

/*------------------------------------------------------------------------*/
/*! Start the Web fetch to check the AFNI version in a child process.
    To complete things, call AFNI_version_check() somewhat later.
--------------------------------------------------------------------------*/

void AFNI_start_version_check(void)
{
   AFNI_start_fetching_url( AFNI_VERSION_URL ) ;
   return ;
}

void AFNI_start_compile_date_check(void)
{
   AFNI_start_fetching_url( COMPILE_URL ) ;
   return ;
}

/*------------------------------------------------------------------------*/

void AFNI_start_fetching_url( char *urlstring )
{
   pid_t child_pid ;

#ifdef CYGWIN  /* 18 Dec 2002 */
   FAIL_MESSAGE("not possible under Cygwin") ;
   return ;
#else

   /*-- decide if we are to do anything --*/

   if( ! AFNI_yesenv("AFNI_VERSION_CHECK") ){    /* never check */
     FAIL_MESSAGE("AFNI_VERSION_CHECK forbids") ;
     return ;
   }

#undef  VDELAY
#define VDELAY 1234567 /* about 2 weeks */
   /* check if we did this in the last VDELAY seconds */
   /* also, update global motd_old                    */
   if( vc_check_too_soon() ) {
      disabled = 1 ;
      return ;
   }

   /*-- OK, start the child process --*/

   child_pid = fork() ;
   if( child_pid == (pid_t)(-1) ){  /* bad */
     FAIL_MESSAGE("can't fork") ;
     return ;
   }

   /*---------------------------------------------------------*/
   if( child_pid > 0 ){                     /* I'm the parent */

     /*-- save PID of child for later use --*/

     vc_child_pid = child_pid ;

     /*-- open an IOCHAN to talk to child --*/

     vc_ioc = iochan_init( STR_CHILD , "accept" ) ;
     if( vc_ioc == NULL ){
       kill(child_pid,SIGTERM) ;            /* cf. Abraham and Isaac */
       vc_child_pid = (pid_t)(-1) ;
       FAIL_MESSAGE("can't open connection to child") ;
     } else {
       atexit( vc_exit ) ;                                       /* 12 Dec 2002 */
     }
     return ;

   /*---------------------------------------------------------*/
   } else {                                  /* I'm the child */
                                           /* (never returns) */
     int nbuf=0 , jj ;
     char *vbuf=NULL ;
     IOCHAN *ioc ;
     struct utsname ubuf ;
     char ua[512] ;

     iochan_enable_perror(0) ;   /* don't print TCP/IP error messages */
     signal( SIGTERM , vexit ) ; /* if parent kills us, call vexit()  */

     /*-- get information from the AFNI server --*/

#define USE_HTTP_10

#ifdef USE_HTTP_10
#  undef PCLAB
#  ifdef SHOWOFF
#    undef SHSH
#    undef SHSHSH
#    define SHSH(x)   #x
#    define SHSHSH(x) SHSH(x)
#    define PCLAB     SHSHSH(SHOWOFF)
#  else
#    define PCLAB     "Unknown"
#  endif
#endif

     /** 25 Mar 2005: send more info in the request header **/

#ifdef USE_HTTP_10
     ubuf.nodename[0] = ubuf.sysname[0] = ubuf.machine[0] = '\0' ;
     jj = uname( &ubuf ) ;
     if( jj >= 0 && ubuf.nodename[0] != '\0' )
       sprintf( ua ,
               "afni (avers='%s'; prec='%s' node='%s'; sys='%s'; mach='%s')" ,
                AVERZHN, PCLAB, ubuf.nodename, ubuf.sysname, ubuf.machine   ) ;
     else
       sprintf( ua , "afni (avers='%s'; prec='%s')" , AVERZHN , PCLAB ) ;

     set_HTTP_10( 1 ) ;
     set_HTTP_user_agent( ua ) ;
#else
     set_HTTP_10( 0 ) ;
#endif

     /* send the request */

     THD_death_setup( 34567 ) ;  /* die if 34.567 seconds passes away */

     nbuf = read_URL( urlstring , &vbuf ) ;  /* may take a while */

     set_HTTP_10( 0 ) ;

     /*-- if this failed, quit --*/

     if( nbuf <= 0 || vbuf == NULL || vbuf[0] == '\0' ) {
        /* block recheck until a new DELAY has passed    22 Mar 2016 [rickr] */
        /* - also so server is not flooded when there are issues             */
        AFNI_update_vctime(NULL, NULL);
        vexit(1);
     }

     /*-- talk to parent process thru IOCHAN --*/

     ioc = iochan_init( STR_CHILD , "create" ) ;
     if( ioc == NULL )                                  vexit(2);

     /*-- wait until ioc is ready for writing --*/

     jj = iochan_writecheck(ioc,-1) ;
     if( jj < 0 )                                       vexit(3);

     /*-- send the info in vbuf --*/

     iochan_sendall( ioc , vbuf , nbuf ) ;
     while( ! iochan_clearcheck(ioc,10) )  /* loop until cleared */
       AFNI_sleep(10) ;                   /* by parent process  */

     AFNI_sleep(10); /* a little extra napping, then death */
     _exit(0);
   }
#endif  /* not CYGWIN */
}

/******************************************/

#ifdef VERBOSE
# define KAPUT(ss)                                          \
  do{ fprintf(stderr,"** Version Check fails: %s **\n",ss); \
      return 0; } while(0)
#else
# define KAPUT(ss) return 0
#endif

/*----------------------------------------------------------------------------*/
/*! compute version difference, store in 'diff'
    return 0 on success

    expect version strings of the form AFNI_YY.Q.II
       YY : 2 digit year
       Q  : 0-based quarter
       II : 0-based build index

    diff = number of quarters difference
           (positive means s1 is newer than s2)
-----------------------------------------------------------------------------*/
static int AFNI_version_diff(char * s1, char * s2, int * diff)
{
   int major1, minor1, major2, minor2, micro1, micro2;
   int rv1, rv2;

   if( !s1 || !s2 || !diff ) {
      fprintf(stderr,"AVD: bad version strings or diff ptr\n");
      return 1;
   }
   if( strlen(s1) < EXPECTED_AFNI_VER_STRLEN ||
       strlen(s2) < EXPECTED_AFNI_VER_STRLEN ) {
      fprintf(stderr,"AVD: short version strings, %.12s, %.12s\n", s1, s2);
      return 1;
   }

   /* assume a format like AFNI_16.1.17 */
   rv1 = sscanf(s1, "AFNI_%d.%d.%d", &major1, &minor1, &micro1);
   rv2 = sscanf(s2, "AFNI_%d.%d.%d", &major2, &minor2, &micro2);

   if( rv1 != 3 || rv2 != 3 ) {
      fprintf(stderr,"AVD: bad version conversion from %s, %s\n", s1, s2);
      return 1;
   }

   *diff = 4*major1 + minor1 - (4*major2 + minor2);

   return 0;
}


/*----------------------------------------------------------------------------*/
/*! Complete the version check by seeing if the child process
    has any data to report from the AFNI web site.

    - so local .afni.vctime has already been read, and it is time to
      check for updates (and rewrite local file in any case)

    - Returns 1 if the version at the AFNI site doesn't match the compiled-in
      version of this program;
    - Returns 0 if they match, or it can't tell.
    - Also prints stuff out.
    - 29 Nov 2005: also sets GLOBAL_motd string, maybe.
------------------------------------------------------------------------------*/

int AFNI_version_check(void)
{
   int jj , nbuf=0 , vdiff=0 ;
   char *vbuf=NULL ;
   char vv[128]="none" ;
   char *sname , *vvbuf ;

#ifdef CYGWIN   /* 30 Jun 2005 [rickr] */

   return 0;

#else

   /* if something is rotten, then toss it out */
   /* (or AFNI_start_fetching_url() sets disabled if it is too soon) */
   if( GLOBAL_argopt.allow_rt || disabled ) return 0 ;   /* 27 Jan 2003 */

   if( vc_ioc == NULL || vc_child_pid == (pid_t)(-1) ) KAPUT("bad child state");

   jj = kill(vc_child_pid,0) ;                /* is child alive? */
   if( jj < 0 ){
     IOCHAN_CLOSE(vc_ioc); vc_child_pid=(pid_t)(-1);
     KAPUT("child not alive");
   }

   jj = iochan_readcheck( vc_ioc , 333 ) ;    /* is iochan open yet? */
   if( jj <= 0 ){
     IOCHAN_CLOSE(vc_ioc); kill(vc_child_pid,SIGTERM); vc_child_pid=(pid_t)(-1);
     KAPUT("connection to child gone bad");
   }

   /* if here, have data ready to read from child! */

   nbuf = 0 ;
   vbuf = AFMALL(char, VSIZE) ;
   while(1){
     jj = iochan_recv( vc_ioc , vbuf+nbuf , VSIZE-nbuf ) ;
     if( jj < 1 ) break ;
     nbuf += jj ;
     if( nbuf >= VSIZE-1 ) break ;
     jj = iochan_readcheck( vc_ioc , 5 ) ;
     if( jj < 1 ) break ;
   }

   /* now wait for child to kill itself */

   waitpid(vc_child_pid,NULL,WNOHANG); vc_child_pid = (pid_t)(-1);
   IOCHAN_CLOSE(vc_ioc);

   /* no data? */

   /* require enough (12 bytes) for e.g. AFNI_16.0.00 */
   if( nbuf < EXPECTED_AFNI_VER_STRLEN )
      { free(vbuf); vbuf = NULL; KAPUT("bad version data"); } /* unlikely */

   /* extract version and data/time strings from data */

   vvbuf = strstr(vbuf,"AFNI_") ;   /* 29 Nov 2005: scan for version string */
   if( vvbuf == NULL ) vvbuf = vbuf ;
   sscanf( vvbuf , "%127s" , vv );  /* get version string out of data */

   vvbuf = strstr(vbuf,"motd=") ;   /* 29 Nov 2005: motd string */
   if( vvbuf != NULL ){
     motd_new = (char *)calloc(sizeof(char),VSIZE) ;
     sscanf( vvbuf+5 , "%988s" , motd_new ) ;
     if( motd_new[0] == '\0' ){ free(motd_new); motd_new=NULL; }
   }

   free(vbuf) ;  /* done with the input data from the child */

   /* record the current time, so we don't check too often */
   /* move to separate function        22 Mar 2016 [rickr] */
   AFNI_update_vctime(vv, motd_new) ;

   /* 29 Nov 2005:
      compare motd strings (old and new)
      if different, save new one in GLOBAL_motd for display later */

   if( motd_new != NULL ){
     if( motd_old == NULL || strcmp(motd_new,motd_old) != 0 ){
       GLOBAL_motd = motd_new ;
     } else {
       free(motd_new) ; motd_new = NULL ;
     }
   }
   if( motd_old != NULL ){ free(motd_old); motd_old=NULL; }

   /* compare version strings */

   /* set vdiff = quarterly difference between web version and our version */
   if( AFNI_version_diff(vv, AVERZHN, &vdiff) ) return 0 ;

   /* if at least 2 quarters old (truncates to 3+ months), warn user */
   if( vdiff >= 2 ) {
      fprintf(stderr, "\n"
                   "****************************************************\n"
                   " This AFNI version is %d+ months old:\n"
                   "   Version ID   = %s\n"
                   " Latest version at %s\n"
                   "   Version ID   = %s\n"
                   "****************************************************\n"
                   " To disable future version checks:\n"
                   " set environment variable AFNI_VERSION_CHECK to NO\n"
                   "****************************************************\n",
          (vdiff-1)*3, AVERZHN, AFNI_HOST , vv ) ;

      return 1 ;
   }

   return 0 ;

#endif /* CYGWIN */
}

/* try to update .afni.vctime with new version and motd strings */
/* (broken out from AFNI_version_check)     22 Mar 2016 [rickr] */
static int AFNI_update_vctime(char * new_ver, char * new_motd)
{
   char *home=getenv("HOME") , mname[VSIZE]="file:" , rhs[32];
   NI_element *nel ;
   NI_stream ns ;

   if( home != NULL ) strcat(mname,home) ;
   strcat(mname,"/.afni.vctime") ;

   /* first read the old one or make a new one */
   nel = AFNI_read_vctime();
   if ( ! nel ) nel = NI_new_data_element("AFNI_vctime",0);

   ns = NI_stream_open( mname , "w" ) ;
   if( ns == NULL ) return 1 ;

   sprintf(rhs,"%d",(int)time(NULL)) ;
   NI_set_attribute( nel , "version_check_time" , rhs ) ;
   if( new_ver && (strcmp(new_ver,"none") != 0 ) )         /* 27 Jan 2003 */
      NI_set_attribute( nel , "version_string" , AVERZHN ) ;
   if( new_motd != NULL )
      NI_set_attribute( nel , "motd" , new_motd ) ;         /* 29 Nov 2005 */
   NI_write_element( ns , nel , NI_TEXT_MODE ) ;
   NI_stream_close(ns) ;

   return 0 ;
}

/* read in .afni.vctime into NI_element    22 Mar 2016 [rickr] */
/* (broken out from AFNI_start_fetching_url)                   */
static NI_element * AFNI_read_vctime(void)
{
   char *home=getenv("HOME") , mname[VSIZE]="file:" ;
   NI_element *nel=NULL ;
   NI_stream   ns ;

   if( home != NULL ) strcat(mname,home) ;
   strcat(mname,"/.afni.vctime") ;
   ns = NI_stream_open( mname , "r" ) ;
   if( ns == NULL ) return NULL ;

   nel = NI_read_element(ns,22) ;
   NI_stream_close(ns) ;

   return nel;
}


/* do we have a valid check_time and is it too soon?                 */
/*    - warn on old check and version change                         */
/*    - possibly update motd_old                                     */
/* (broken out from AFNI_start_fetching_url)     22 Mar 2016 [rickr] */
static int vc_check_too_soon(void)
{
   NI_element *nel=NULL;
   char       *rhs;
   int         last_time, dtime;

   nel = AFNI_read_vctime() ;
   if( nel == NULL ) return 0;

   rhs = NI_get_attribute(nel , "version_check_time");
   if( rhs == NULL ) return 0;

   last_time = strtol(rhs,NULL,10);
   dtime = ((int)time(NULL)) - last_time;

   /* too soon to worry */
   if( dtime >= 0 && dtime < VDELAY ) {
      NI_free_element(nel);
      return 1;
   }

   /* warn on version change */
   rhs = NI_get_attribute(nel,"version_string") ;  /* 27 Jan 2003 */
   if( rhs != NULL && strcmp(rhs,AVERZHN) != 0 ){
      fprintf(stderr,
              "\n** Your AFNI version changed from %s to %s since last check\n",
              rhs , AVERZHN ) ;
   }

   /* possibly update global motd_old */
   rhs = NI_get_attribute(nel,"motd") ;            /* 29 Nov 2005 */
   if( rhs != NULL ) {
      if( motd_old ) free(motd_old);
      motd_old = strdup(rhs) ;
   }

   NI_free_element(nel);
   return 0;
}


/*----------------------------------------------------------------------------*/
/*! Complete the compile date check -- return number of days difference.
------------------------------------------------------------------------------*/

static char *monlist[12] =
  { "JAN","FEB","MAR","APR","MAY","JUN","JUL","AUG","SEP","OCT","NOV","DEC" } ;

int AFNI_compile_date_check(void)
{
   int jj , nbuf=0 , day_diff=0 ;
   char *vbuf=NULL , *qbuf ;
   int  new_day    =-666    , old_day     =-666    ;
   int  new_year   =-666    , old_year    =-666    ;
   char new_mon[8] = "Zork" , old_mon[8]  = "Zork" ;
   int  new_imm             , old_imm           ;

#ifdef CYGWIN   /* 30 Jun 2005 [rickr] */

   return 0;

#else

   /* if something is rotten, then toss it out */

   if( GLOBAL_argopt.allow_rt || disabled ) return 0 ;   /* 27 Jan 2003 */

   if( vc_ioc == NULL || vc_child_pid == (pid_t)(-1) ) KAPUT("bad child state");

   jj = kill(vc_child_pid,0) ;                /* is child alive? */
   if( jj < 0 ){
     IOCHAN_CLOSE(vc_ioc); vc_child_pid=(pid_t)(-1);
     KAPUT("child not alive");
   }

   jj = iochan_readcheck( vc_ioc , 333 ) ;    /* is iochan open yet? */
   if( jj <= 0 ){
     IOCHAN_CLOSE(vc_ioc); kill(vc_child_pid,SIGTERM); vc_child_pid=(pid_t)(-1);
     KAPUT("connection to child gone bad");
   }

   /* if here, have data ready to read from child! */

   nbuf = 0 ;
   vbuf = AFMALL(char, VSIZE) ;
   while(1){
     jj = iochan_recv( vc_ioc , vbuf+nbuf , VSIZE-nbuf ) ;
     if( jj < 1 ) break ;
     nbuf += jj ;
     if( nbuf >= VSIZE-1 ) break ;
     jj = iochan_readcheck( vc_ioc , 5 ) ;
     if( jj < 1 ) break ;
   }

   /* now wait for child to kill itself */

   waitpid(vc_child_pid,NULL,WNOHANG); vc_child_pid = (pid_t)(-1);
   IOCHAN_CLOSE(vc_ioc);

   /* no data? */

   if( nbuf < 11 ){ free(vbuf); vbuf = NULL; KAPUT("bad compile date data"); } /* unlikely */

   /* extract date info from string [e.g., "16 Jun 2014"] */

   qbuf = strcasestr(vbuf,"Content-Length:") ;
   if( qbuf != NULL ){
     char *zbuf = strstr(qbuf,"\r\n\r\n") ;
     if( zbuf != NULL ) qbuf = zbuf + 4 ;
   } else {
     qbuf = vbuf ;
   }

   sscanf( qbuf     , "%d %s %d" , &new_day ,  new_mon , &new_year ) ;
   sscanf( __DATE__ , "%s %d %d" ,  old_mon , &old_day , &old_year ) ;  /* day/mon invert */

   free(vbuf) ;  /* done with the input data from the child */

   /* bad results? */

   if( new_day < 0 || new_mon[0] == 'Z' || new_year < 0 ) return 0 ;
   if( old_day < 0 || old_mon[0] == 'Z' || old_year < 0 ) return 0 ;

   /* compare dates */

   for( jj=0 ; jj < 12 ; jj++ ) if( strcasecmp(new_mon,monlist[jj]) == 0 ) break ;
   if( jj < 12 ) new_imm = jj ; else return 0 ;

   for( jj=0 ; jj < 12 ; jj++ ) if( strcasecmp(old_mon,monlist[jj]) == 0 ) break ;
   if( jj < 12 ) old_imm = jj ; else return 0 ;

   day_diff = (new_year-old_year)*365 + (new_imm-old_imm)*31 + (new_day-old_day) ;

   /* record the current time, so we don't check too often */

   { char *home=getenv("HOME") , mname[VSIZE]="file:" ;
     NI_stream ns ;
     if( home != NULL ) strcat(mname,home) ;
     strcat(mname,"/.afni.vctime") ;
     ns = NI_stream_open( mname , "w" ) ;
     if( ns != NULL ){
       NI_element *nel=NI_new_data_element("AFNI_vctime",0); char rhs[32];
       sprintf(rhs,"%d",(int)time(NULL)) ;
       NI_set_attribute( nel , "version_check_time" , rhs ) ;
       NI_write_element( ns , nel , NI_TEXT_MODE ) ;
       NI_stream_close(ns) ;
     }
   }

   return day_diff ;

#endif /* CYGWIN */
}

/*----------------------------------------------------------------------*/
/***---------------- 20 Nov 2003: auto-download stuff ----------------***/

#ifdef SHOWOFF
# undef SHSH
# undef SHSHSH
# define SHSH(x)   #x
# define SHSHSH(x) SHSH(x)

# define SNAME "AFNI_UPDATER"   /* script file name */

char * AFNI_make_update_script(void)
{
   char *pg_ftp , *pg_afni , *pg_gzip , *pg_tar ;
   char hbuf[4096], fname[128], adir[4096], *cpt, cwd[4096] ;
   FILE *fp ;
   static char sname[4096] ;

   pg_ftp  = THD_find_executable("ftp" ); if( pg_ftp  == NULL ) return NULL;
   pg_afni = THD_find_executable("afni"); if( pg_afni == NULL ) return NULL;
   pg_gzip = THD_find_executable("gzip");
      if( pg_gzip == NULL ) pg_gzip = THD_find_executable("pigz") ;
      if( pg_gzip == NULL ) return NULL;
   pg_tar  = THD_find_executable("tar" ); if( pg_tar  == NULL ) return NULL;

   strcpy(adir,pg_afni) ;                /* extract name of AFNI directory */
   cpt = THD_trailname(adir,0) ;
   *cpt = '\0' ;
   if( strlen(adir) <= 0 ) return NULL ; /* no AFNI directory? */

   strcpy( cwd , adir ) ;                /* try to write a test file there */
   strcat( cwd , "afni_qadgop" ) ;
   fp = fopen( cwd , "a" ) ;
   if( fp == NULL ) return NULL ;        /* can't write to AFNI directory? */
   fclose(fp) ; remove(cwd) ;

   getcwd( cwd , 4096 ) ;   /* get current directory for later use */
   chdir( adir ) ;          /* switch to AFNI directory for this work */

   /* write a script to get and install the binary archive via FTP */

   gethostname( hbuf , 4096 ) ;
   strcpy( fname , SHSHSH(SHOWOFF) ) ; strcat( fname , ".tgz" ) ;
   fp = fopen( SNAME , "w" ) ; if( fp == NULL ){ chdir(cwd); return NULL; }
   fprintf( fp ,
            "#!/bin/sh\n"
            "echo '++ FTP-ing %s from afni.nimh.nih.gov'\n"
            "%s -n afni.nimh.nih.gov << EEEEE\n"   /* FTP to get file */
            "user anonymous AFNI_UPDATER@%s\n"
            "binary\n"
            "passive\n"
            "cd tgz\n"
            "get %s\n"
            "bye\n"
            "EEEEE\n"
            "echo '++ Unpacking %s'\n"
            "%s -dc %s | %s xf -\n"      /* uncompress and untar .tgz file */
            "/bin/rm -f %s\n"            /* delete .tgz file */
            "echo '++ Moving files'\n"
            "/bin/mv -f %s/* .\n"        /* move untar-ed files up to here */
            "/bin/rm -rf %s\n"           /* remove any directory leftovers */
            "echo '++ Finished'\n" ,
            fname ,                 /* filename to get (for 'FTP-ing' echo) */
            pg_ftp ,                /* FTP program */
            hbuf ,                  /* hostname, for FTP password */
            fname ,                 /* filename to get (for FTP) */
            fname ,                 /* ditto (for 'Unpacking' echo) */
            pg_gzip, fname, pg_tar, /* GZIP program, filename, TAR program */
            fname ,                 /* filename to delete */
            SHSHSH(SHOWOFF) ,       /* directory to copy up */
            SHSHSH(SHOWOFF)         /* directory to delete */
          ) ;
   fclose( fp ) ;
   chmod( SNAME , S_IRUSR | S_IWUSR | S_IXUSR ) ; /* mark as executable */

   /* get back to current working directory, then leave */

   chdir(cwd) ;
   sprintf(sname,"%s%s",adir,SNAME) ; return sname ;
}

#else /* undefined SHOWOFF */
char * AFNI_make_update_script(void){ return NULL; }
#endif /* SHOWOFF */

/*----------------------------------------------------------------------*/
#define MOTD_fails ERROR_message("Can't connect to AFNI server!\a\n")

/*----------------------------------------------------------------------*/
/*! Display the AFNI message of the day.  [29 Nov 2005]
------------------------------------------------------------------------*/

void AFNI_display_motd( Widget w )
{
   int nbuf ;
   char *buf=NULL , url[VSIZE] ;

ENTRY("AFNI_display_motd") ;

   set_HTTP_10( 0 ) ;

   if( GLOBAL_motd == NULL || *GLOBAL_motd == '\0' ){ /* fetch motd name */
     char *vbuf=NULL , *vvbuf ;                       /* from AFNI server */
     nbuf = read_URL( AFNI_VERSION_URL , &vbuf ) ;
     if( nbuf <= 0 || vbuf == NULL ){ MOTD_fails; EXRETURN; }
     vvbuf = strstr(vbuf,"motd=") ;
     if( vvbuf == NULL ){ free(vbuf); MOTD_fails; EXRETURN; }
     GLOBAL_motd = (char *)calloc(sizeof(char),VSIZE) ;
     sscanf( vvbuf+5 , "%988s" , GLOBAL_motd ) ; free(vbuf) ;
     if( GLOBAL_motd[0] == '\0' ){
       free(GLOBAL_motd); GLOBAL_motd=NULL; MOTD_fails; EXRETURN;
     }
   }

   sprintf(url,"%s%.988s",AFNI_HOST,GLOBAL_motd) ;
/** INFO_message("MOTD URL = '%s'",url) ; **/
   nbuf = read_URL( url , &buf ) ;
   if( nbuf > 0 && buf != NULL ){
     char *msg = malloc(sizeof(char)*(nbuf+2048)) ;
     sprintf(msg,
     "\n"
     "         *********** Current AFNI Message of the Day **********\n\n"
     "   [cf. %s ]\n\n"
     "   [cf. menu item Define Datamode -> Misc -> Message of the Day ]\n\n"
     " ====================================================================\n\n"
     "%s\n"
     , url , buf );

     if( w != NULL ){
       MCW_textwin_setbig(1) ;  /* 29 Apr 2009 */
       (void) new_MCW_textwin( w , msg , TEXT_READONLY );
     } else {
       fputs(msg,stderr) ;
     }

     free(msg) ; free(buf) ;
   } else {
     MOTD_fails ;
   }

   EXRETURN ;
}

/*----------------------------------------------------------------------*/
/*! Display the AFNI historical documents [05 Mar 2008]
------------------------------------------------------------------------*/

void AFNI_display_hist( Widget w )
{
#define NBUF 1024
   static char *cmd=NULL ;
   char buf[NBUF+1] , *all=NULL ;
   int nbuf         , nall=0 ;
   FILE *fp ;

   /*-- get the path to the command to run --*/

   if( cmd == NULL ){
     char *pg = THD_find_executable("afni_history") ;
     if( pg == NULL || *pg == '\0' ){
       (void)MCW_popup_message( w ,
                                  " \n"
                                  " Can't find afni_history \n"
                                  " program in your PATH!!! \n" ,
                                MCW_USER_KILL | MCW_TIMER_KILL ) ;
       XtSetSensitive(w,False) ; return ;
     }
     cmd = (char *)calloc( sizeof(char) , (64+strlen(pg)) ) ;
     sprintf(cmd,"%s -reverse",pg) ;
   }

   /*-- open a pipe to read from the command --*/

   fp = popen( cmd , "r" );
   if( fp == NULL ){
     (void)MCW_popup_message( w ,
                              " \n"
                              " Can't run afni_history\n"
                              " program for some reason!\n" ,
                                MCW_USER_KILL | MCW_TIMER_KILL ) ;
     return ;
   }

   /*-- read the first bunch of data fromt the pipe --*/

   nbuf = fread( buf , 1 , NBUF , fp ) ;
   if( nbuf < 16 || *buf == '\0' ){
     (void)MCW_popup_message( w ,
                              " \n"
                              " afni_history program\n"
                              " fails to give output!\n" ,
                              MCW_USER_KILL | MCW_TIMER_KILL ) ;
     return ;
   }

   /*-- store this initial string in 'all' --*/

   buf[nbuf] = '\0' ; all = strdup(buf) ; nall = strlen(all) ;

   /*-- loop: read buffer, copy into 'all', until nothing left to read --*/

   do{
     nbuf = fread( buf , 1 , NBUF , fp ) ;
     if( nbuf <= 0 ){ pclose(fp); break; }  /* read failed ==> done */
     buf[nbuf] = '\0' ;
     all = realloc( all , nall+nbuf+2 ) ;
     strcat(all,buf) ; nall = strlen(all) ;
   } while(1) ;

   /*-- display results in a window, and exeunt omnes --*/

   (void)new_MCW_textwin( w , all , 1 ) ;

   free(all) ; return ;
}
