/* COPYRIGHT 1999-2003 Fred Hutchinson Cancer Research Center

      LAMA_search.c
  Processes LAMA_search.sh form
  Executes the following programs which must be in the same directory:
	LAMA
	htmlize-blimps.pl
	htmlize-LAMA
	/blocks/icons/cyrcashLogo.gif
	/blocks-bin/cyrcaLama.pl
	/blocks/tmp/

   WARNING! This is not a very robust piece of code.  If the data in the 
            entry.val field overflows the allowed space it will be truncated
	    by the following entry and have the following entry.name appended 
	    to the truncation.
	    Because of this the sequence needs to be the last entry in the
	    html form because there is no entry following it to truncate 
	    the sequence data.
*/
/* 5/02/95  Modified chmod calls to give proper permissions to log files. 
	    Wouldn't compile: had to remove calls to "remove_trailing_
	    whitespace(); also a problem with Buffer[], changed to buf[] JGH
   5/ 6/95  Modified .cs file to search mats.dat instead of blocks.dat
   7/11/95  Added "umask 006" to prevent general read on wwwuser files
	    Added more "chmod"s for .cs, seq, .out
   7/19/95  Changed the error message when the page is not accessed by a post.
            Netscape does this sometimes when backing up to a page.
   9/ 5/95  Changed blimps search to search a blocks db using pseudo-counts.
	    Added option to search the Prints database in blocks format.
   9/23/95  Fixed problem introduced 9/5 with blimps options.
   9/25/95  Changed log file entry to indicate blocks or prints
  12/ 2/95  Changed SMALL_BUFF_LENGTH to LARGE_BUFF_LENGTH everywhere
	    blksort_output, etc. was getting corrupted ...
  12/ 3/95  Changed to look for environment variables BLOCKS_HOME &
            BLOCKS_EMAIL before using values here.
 May/ 8/96  Changed the database searched when prints is specified to 
            actually be prints (it was small test database until now).
   8/22/96  Log to the general file.
   3/22/97  Changed location of prints db, JGH.
   4/ 2/97  Changed location of blocks db. JGH
  10/29/97  Recompiled - unknown problem with version of 6/28/97?
   11/15/97 Moved home directory definitions to homdir.h
   3/ 6/98  Added "nice" to execution of LAMA
   12/22/98 Removed all hard-coded file names; assumes execution is from
	    bin/ sub-directory. Added blocks+ searching option.
    6/20/99 Execute htmlize-LAMA instead of htmlink_LAMA-out.
    23/8/00 Execute CYRCA and add icon linking to results if there are any.
    5/21/03 Stop logging
    8/17/03 Don't execute CYRCA if user blocks
===========================================================================*/
/* blimps stuff */
#define EXTERN

#include <blocksprogs.h>

typedef struct {
    char *name;
    char *val;
} entry;

/* from util.c */
char *makeword(char *line, char stop);
char *fmakeword(FILE *f, char stop, int *len);
char x2c(char *what);
void unescape_url(char *url);
void plustospace(char *str);
 
entry entries[10000];
 
/*	Assumes execution is from the bin/ directory  */
#define HOME ".."
#define BIN_SUBDIR   "."
#define TMP_SUBDIR   "../tmp"
#define LOG_SUBDIR   "../log"
#define BLOCKS_SUBDIR "../data-blocks"
#define PRINTS_SUBDIR "../data-prints"
#define BLPLUS_SUBDIR "../data-blplus"
#define BLOCKS_EMAIL "blocks@fhcrc.org"
#define WWW_ADDRESS "blocks"
#define LAMA_HELP_PAGE "../help/LAMA_help.html"
#define LAMA_HELP_ICON "?"

#define MAX_BLOCKS 10

#define CYRCA_ZSCORE "5.6"
#define CYRCA_LOGO "/blocks/icons/cyrcashLogo.gif"
#define CYRCA "./cyrcaLama.pl"

char LAMA[LARGE_BUFF_LENGTH];
char LAMA_queue[LARGE_BUFF_LENGTH];
char add_queue_entry[LARGE_BUFF_LENGTH];
char extblock_stdout[LARGE_BUFF_LENGTH];
char Qblock_file[LARGE_BUFF_LENGTH];
char log_dir[LARGE_BUFF_LENGTH];
char log_file[LARGE_BUFF_LENGTH];
char error_file[LARGE_BUFF_LENGTH];
char LAMA_output[LARGE_BUFF_LENGTH];
char cyrcaOutputFile[LARGE_BUFF_LENGTH];
char database[LARGE_BUFF_LENGTH];		/* searched database of blocks */
char blocksdbase[LARGE_BUFF_LENGTH];		/* Blocks Database*/
char printsdbase[LARGE_BUFF_LENGTH];		/* Prints blocks db */
char blplusdbase[LARGE_BUFF_LENGTH];		/* Blocks+ db */
char targetdbase[LARGE_BUFF_LENGTH];		/* user target blocks db */
char mail_file[LARGE_BUFF_LENGTH];
char email_addr[LARGE_BUFF_LENGTH];
char buf[LARGE_BUFF_LENGTH];

int pid;

entry *Targetblock_Ptr = NULL;
entry *Qblock_Ptr = NULL;
entry *Title_Ptr = NULL;
entry *Address_Ptr = NULL;



int Debug_Level = 0;
float Score_Cutoff_Level = 0;


Boolean Mail_Flag = FALSE;
Boolean Prints_Flag = FALSE;
Boolean Blplus_Flag = FALSE;
Boolean No_DB_Flag = FALSE;
 
/*FILE *jgh;
*/

void read_startup_info()
{
  int i;
  char *script, *ptr;
  FILE *logdate, *curr_year_month;

  ptr = getenv("BLOCKS_EMAIL");
  if (ptr != NULL) sprintf(email_addr, "%s", ptr);
  else  sprintf(email_addr, "%s", BLOCKS_EMAIL);
/*fprintf(jgh, "%s %s\n", ptr, email_addr);
*/

  pid = getpid();
  curr_year_month = popen("date '+%y%m'", "r");
  logdate = popen("date \"+%y%m%d\"", "r");

  sprintf(LAMA, "%s/LAMA", BIN_SUBDIR);
  sprintf(add_queue_entry, "%s/add_queue_entry.pl", BIN_SUBDIR);
  sprintf(LAMA_queue, "%s/LAMA_queue", BIN_SUBDIR);
  sprintf(extblock_stdout, "%s/extblock_stdout", BIN_SUBDIR);
  sprintf(blocksdbase, "%s/blocks.dat", BLOCKS_SUBDIR);
  sprintf(printsdbase, "%s/prints.dat", PRINTS_SUBDIR);
  sprintf(blplusdbase, "%s/blocks.dat", BLPLUS_SUBDIR);
/* '_tmp' string added to Qblock and target names so they won't be removed and be available for making logos */
  sprintf(targetdbase, "%s/%d_tmp.dat", TMP_SUBDIR, pid);
  sprintf(Qblock_file, "%s/%d_tmp.blk", TMP_SUBDIR, pid); 
  fgets(buf, LARGE_BUFF_LENGTH, curr_year_month);
  remove_trailing_whitespace(buf);
  sprintf(log_dir, "%s/%s", LOG_SUBDIR, buf);
  fgets(buf, LARGE_BUFF_LENGTH, logdate);
  remove_trailing_whitespace(buf);
  sprintf(log_file, "%s/%s", log_dir, buf);
  sprintf(mail_file, "%s/%d.mail", TMP_SUBDIR, pid);
  sprintf(LAMA_output, "%s/%d.out", TMP_SUBDIR, pid);

  pclose(curr_year_month);
  pclose(logdate);
}   /*  end of read_startup_info */


int parse()
{
/* parse the input
   while parsing
    set the email flag if there is an address, remember which entry it is in.
    set the pointer to the sequence 
*/

  register int i,num_entries=0;
  char *entry_string;
  int cl;

  /* check that there is something to use. */
  entry_string = getenv("QUERY_STRING");
/*  if (entry_string != NULL) fprintf(jgh, "%s", entry_string);
*/

/*
  if (entry_string == NULL) 
     {
     printf("No query information to decode.\n");
     exit(1);
     }
*/

  /* parse the query string into individual entries */

  cl = atoi(getenv("CONTENT_LENGTH"));

  for(i=0;cl && (!feof(stdin));i++) 
     {
     num_entries=i;
     entries[i].val = fmakeword(stdin,'&', &cl);
     plustospace(entries[i].val);
     unescape_url(entries[i].val);
     entries[i].name = makeword(entries[i].val,'=');

/*    fprintf(jgh, "%s %s\n", entries[i].name, entries[i].val);
*/

     if (!strncmp(entries[i].name, "User_target_block", 14)) 
        {
	Targetblock_Ptr = &entries[i]; 
        }


     if (!strncmp(entries[i].name, "User_query_block", 16)) 
        {
        Qblock_Ptr = &entries[i];
        }

     if (!strncmp(entries[i].name, "debug_level", 16)) 
        {
	  Debug_Level = atoi(entries[i].val);
        }

     if (!strncmp(entries[i].name, "score_cutoff_level", 16)) 
        {
	  Score_Cutoff_Level = atof(entries[i].val);
        }

     else if (!strncmp(entries[i].name, "address", 7)) 
        {
        Address_Ptr = &entries[i];
        if ( (int) strlen(entries[i].val) > 0) 
           {
  	   Mail_Flag = TRUE;
           }
        }

     else if (!strncmp(entries[i].name, "database", 8)) 
       {
	 if (!strncmp(entries[i].val, "prints-db", 9))
           {
	     strncpy(database, printsdbase,LARGE_BUFF_LENGTH) ;
	     Prints_Flag = TRUE;
           }
	 else if (!strncmp(entries[i].val, "blplus-db", 9))
           {
	     strncpy(database, blplusdbase,LARGE_BUFF_LENGTH) ;
	     Blplus_Flag = TRUE;
           }
	 else if (!strcmp(entries[i].val, "no-db")) 
	   {
	     strncpy(database, targetdbase,LARGE_BUFF_LENGTH) ;
	     No_DB_Flag = TRUE;
	   }
	 else 
	   { /* assume the blocks database by default */
	     strncpy(database, blocksdbase, LARGE_BUFF_LENGTH) ;
	   }
       }
    }


  return num_entries;
} /*  end of parse */


void clean_temp_files()
{
  sprintf(buf, "rm -f %s/%d.*", TMP_SUBDIR, pid); 
  system(buf);
}


void write_Qblock()
{
  FILE  *bfp, *efp ;
  Block *block ;
  int   blocks_num ;


  /* check that a block was specified */
  if (Qblock_Ptr->val[0] == NULL) 
    {
      printf("<H1>Search Error</H1>\n");
      printf("You need to enter a block to search with by writing or pasting a block in the query block window.<P>\n");
      exit(0);
    }
  

  /* open error_file so that error messages from read_a_block can be examined*/
  sprintf(error_file, "%s/%d.LAMA_Qblock_errors", TMP_SUBDIR, pid);
  set_error_file_name(error_file) ;     



  /* if block was pasted/written - */
  if (Qblock_Ptr->val[0] != NULL)
    {
      
      /* open up the block file to write to */
      bfp = fopen(Qblock_file, "w");
      
      fprintf(bfp, "%s\n",Qblock_Ptr->val);

/* add "//" to the file end. 
   This is done to over come the fact that read_a_block does not detect if the 
   end of block signal (//) is missing and the program crashes later. */
      fprintf(bfp, "//\n");

      /* close and reopen block file in a read mode */
      fclose(bfp);
      bfp = fopen(Qblock_file, "r");
      
      
      blocks_num = 0 ;
      
      /* read-in blocks to check their format and content */
      while ((block = read_a_block(bfp)) != NULL)
        {
	  blocks_num++ ;
	  
	  /* try and open error file to see if there were any errors */
	  if ((efp = fopen(error_file, "r")) != NULL) 
	    {
	      printf("<H1>Search Error</H1>\n");
	      printf("There seems to be an error in the format of query block number %d:<br>", blocks_num);
	      
	      printf("<pre>\n");
	      while((buf[0]=getc(efp)) != EOF) putchar(buf[0]) ; 
	      printf("</pre>") ;
	      
	      fclose(efp) ;
	      
	      clean_temp_files() ;
	      
	      exit(1);
	    }
	  
        }
      
      if (blocks_num <= 0) 
	{
	  printf("<H1>Search Error</H1>\n");
	  printf("No blocks were read from the query.\n");
	  clean_temp_files() ;
	  exit(1);
	}

      if (blocks_num > MAX_BLOCKS) 
	{
	  printf("<H1>Search Error</H1>\n");
	  printf("Sorry, you can not compare more than %d query blocks in each run.\n", 
                 MAX_BLOCKS);
	  clean_temp_files() ;
	  exit(1);
	}
      
      
      fclose(bfp);
    } /* end if (Qblock_Ptr->val[0] == NULL) */
  
  
  if (No_DB_Flag)
    {
      /* open file, write data from target into the temp target file */
      bfp = fopen(targetdbase, "w");
      fprintf(bfp, "%s\n",Targetblock_Ptr->val);

/* add "//" to the file end. 
   This is done to over come the fact that read_a_block does not detect if the 
   end of block signal (//) is missing and the program crashes later. */
      fprintf(bfp, "//\n");

      fclose(bfp);
      
      /* check that the DB is of the right format first (see above) */
      bfp = fopen(targetdbase, "r");
      
      blocks_num = 0 ;
      
      /* read-in blocks to check their format and content */
      while ((block = read_a_block(bfp)) != NULL) 
	{
	  blocks_num++ ;
	  
	  /* try and open error file to see if there were any errors */
	  if ((efp = fopen(error_file, "r")) != NULL) 
	    {
	      printf("<H1>Search Error</H1>\n");
	      printf("There seems to be an error in the format of target block number %d:<br>", blocks_num);
	      
	      printf("<pre>\n");
	      while((buf[0]=getc(efp)) != EOF) putchar(buf[0]) ; 
	      printf("</pre>") ;
	      
	      fclose(efp) ;
	      
	      clean_temp_files() ;
	      
	      exit(1);
	    }
	}
      
      fclose(bfp);
      
      if (blocks_num <= 0) 
	{
	  printf("<H1>Search Error</H1>\n");
	  printf("No blocks were read from the target data.\n");
	  clean_temp_files() ;
	  exit(1);
	}

      if (blocks_num > MAX_BLOCKS) 
	{
	  printf("<H1>Search Error</H1>\n");
	  printf("Sorry, you can not compare more than %d target blocks in each run.\n", 
                 MAX_BLOCKS);
	  clean_temp_files() ;
	  exit(1);
	}

    } /* end if a target DB */
}


 

/*
+Run search
-
if submitted in the background, check the load level before running
  if have waited for more than half an hour?, then submit.
run LAMA and keep track of where the output goes

*/
void run_search()
{
  FILE *dfp;
  char datein[LARGE_BUFF_LENGTH];
  char dateout[LARGE_BUFF_LENGTH];
  char db[80];

  dfp = popen("date", "r");
  remove_trailing_whitespace(fgets(datein, LARGE_BUFF_LENGTH, dfp));
  pclose(dfp);

  sprintf(buf, "/usr/bin/nice -5 %s %s,%s %s 0 %d 0 %f > /dev/null 2>&1",
	  LAMA, Qblock_file, database, LAMA_output, Debug_Level, 
	  Score_Cutoff_Level);

  if (system(buf)) {
    printf("<H1>Search Error</H1>\n");
    printf("An error occured during the search.\n");
    printf("Please try your search again at a later time.\n");
    printf("If it fails again contact the maintainer of these pages\n");
    printf("and describe what caused the problem with an example.<P>\n");

    clean_temp_files() ;

    exit(0);
  }

  dfp = popen("date", "r");
  remove_trailing_whitespace(fgets(dateout, LARGE_BUFF_LENGTH, dfp));
  pclose(dfp);

  /* OK, I know you can do this with system calls, but this was easier. */
  /* make the path if needed (and make sure of the protection) */
/*
  sprintf(buf, "mkdir -p %s", log_dir);
  system(buf);
  sprintf(buf, "chmod -f 775 %s", log_dir);
  system(buf);
*/

  /* enter the data into the log file (and make sure of the protection) */
/*
  if (Prints_Flag) {
    strcpy(db, "Prints");
  }
  if (Blplus_Flag) {
    strcpy(db, "Blplus");
  }
  else if (No_DB_Flag) {
    strcpy(db, "user_target");
  }
  else {
    strcpy(db, "Blocks");
  }
  sprintf(buf, 
	  "echo \"%s\tLAMA %s: `grep \'Probe Size\' %s`\t%s\t%s\" >> %s",
	  WWW_ADDRESS, db, LAMA_output, datein, dateout, log_file);

  system(buf);
  sprintf(buf, "chmod -f 664 %s", log_file);
  system(buf);
*/

}  /*  end of run_search */


void convert_LAMA_output()
{
  FILE *outf;
/** either **/
  FILE *fp;

  if ((outf = fopen(LAMA_output, "r")) == NULL)
     {
     printf("Error opening LAMA output file.\n") ;

     clean_temp_files() ;

     exit(1) ;
     }

/*
  printf("<pre>\n");
  while (!feof(outf) && fgets(buf, LARGE_BUFF_LENGTH, outf) != NULL)
     printf(buf) ; 
  printf("</pre>\n"); */

  fclose(outf);

  sprintf(buf, "%s/htmlize-blimps.pl %s | %s/htmlize-LAMA %s %s - -", 
          BIN_SUBDIR, LAMA_output, 
          BIN_SUBDIR, Qblock_file, database) ;

  if (!(fp = popen(buf, "r"))) printf("Error opening pipe\n") ;

  printf("<pre>\n");

  while (!feof(fp) &&
	 fgets(buf, LARGE_BUFF_LENGTH, fp) != NULL)
     printf(buf) ;

  printf("</pre>\n");

  pclose(fp);

} /* end of convert_LAMA_output */




void display_output()
{

  printf("<TITLE>LAMA Search Results</TITLE>\n");
  printf("<H1>LAMA Search Results (<A HREF=\"%s\">%s</A>)</H1>\n",
         LAMA_HELP_PAGE, LAMA_HELP_ICON);
    
}

/*====================================================================
   Every email request generates 3 entries in the bin/LAMA_queue file,
   which should be processed in order by the bin/run_queue script:
	1. execute LAMA, 2. mail the results, 3. remove the files
============================================================================*/
void queue_to_mail()
{
  char db[80];
  char datein[LARGE_BUFF_LENGTH];

  FILE *dfp;
  dfp = popen("date", "r");
  remove_trailing_whitespace(fgets(datein, LARGE_BUFF_LENGTH, dfp));
  pclose(dfp);

  /* put the commands into the queue */
  /*    run LAMA */
  sprintf(buf, "%s %s \"/usr/bin/nice -10 %s %s,%s %s 0 %d 0 %f > /dev/null 2>&1\"", add_queue_entry, LAMA_queue, LAMA, Qblock_file, database, LAMA_output, Debug_Level, Score_Cutoff_Level);
  system(buf);


  /*    mail the person the results */
  /*    NOTE: need to echo the header lines for the LAMA output :P */
  /*    NOTE: all this should need to do is to cat the ouput file. */
  sprintf(buf, "%s %s '(cat %s) | /usr/bin/mailx -s \"LAMA Results\" -r \"blocks\@fhcrc.org\" %s'", add_queue_entry, LAMA_queue, LAMA_output, Address_Ptr->val);
  system(buf);
  


  /*   clean up the files */
  sprintf(buf, "%s %s 'rm -f %s/%d.*'", add_queue_entry, LAMA_queue, 
	  TMP_SUBDIR, pid); 
  system(buf);


  /* enter the queued info into the LOG */
  /* OK, I know you can do this with system calls, but this was easier. */
  /* make the path if needed (and make sure of the protection) */
/*
  sprintf(buf, "mkdir -p %s", log_dir);
  system(buf);
  sprintf(buf, "chmod -f 775 %s", log_dir);
  system(buf);
*/

  /* enter the data into the log file (and make sure of the protection) */
/*
  if (Prints_Flag) {
    strcpy(db, "Prints");
  }
  if (Blplus_Flag) {
    strcpy(db, "Blplus");
  }
  else if (No_DB_Flag) {
    strcpy(db, "user_target");
  }
  else {
    strcpy(db, "Blocks");
  }
  sprintf(buf, 
	  "echo \"%s\tLAMA %s: `grep \'Probe Size\' %s`\t%s\t%s\" >> %s",
	  WWW_ADDRESS, db, LAMA_output, datein, "queued", log_file);

  system(buf);
  sprintf(buf, "chmod -f 664 %s", log_file);
  system(buf);
*/



  /* announce it is queued */
  printf("<TITLE>LAMA Search Submitted</TITLE>\n");
  printf("<H1>LAMA Search Submitted</H1>\n");
  printf("Your search has been submitted.\n");
  printf(
"You will receive the results via email to the address you gave (\"%s\").\n", 
         Address_Ptr->val);

  /* exit */
  exit(1);
}


void runCyrca ()
{
  int returnCode;
  char fname[20];

  sprintf(fname, "%dC.html", pid);
  sprintf (cyrcaOutputFile, "%s/%s", TMP_SUBDIR, fname);
  sprintf (buf, "%s %s %s %s",
	   CYRCA, LAMA_output, CYRCA_ZSCORE, cyrcaOutputFile); 

  returnCode = system(buf);

/*
printf("%s=%d\n", buf, returnCode);
*/

  /* examine the CYRCA output error code. */
  if (returnCode == 0)
    {
/*
      printf ("<A HREF=\"%s?%s\"><img src=\"%s\">CYRCA results</A>\n", SHOW_CYRCA_RESULTS, cyrcaOutputFile, CYRCA_LOGO); 
*/
      printf ("<A HREF=\"/blocks/tmp/%s\"><img src=\"%s\">CYRCA results</A>\n", 
    		fname, CYRCA_LOGO); 
    }

} /* end of run Cyrca */

void main(int argc, char *argv[]) {

  printf("Content-type: text/html\n\n\n");


  if(strcmp(getenv("REQUEST_METHOD"),"POST")) {
    printf("This script should be referenced with a METHOD of POST.\n");
    printf("If you don't understand this, see this ");
    printf("<A HREF=\"http://www.ncsa.uiuc.edu/SDG/Software/Mosaic/Docs/fill-out-forms/overview.html\">forms overview</A>.%c",10);

    printf("<P>What could have happened is that you just reloaded this page rather than redoing the search.\n");

    printf("<P>If you use NETSCAPE it has a tendency to try to reload a page when you go back to it.  This may be why you have this error.\n");

    exit(1);
  }

  if(strcmp(getenv("CONTENT_TYPE"),"application/x-www-form-urlencoded")) {
    printf("This script can only be used to decode form results. \n");
    exit(1);
  }

  /*    Set file permissions to "rw-rw----"     */
  system("umask 006");

  read_startup_info();

  parse();

  write_Qblock();

  if (Mail_Flag) {
    queue_to_mail();
  }
  
  run_search();

  display_output(); 

  /* Cyrca execution, added by Victor Kunin */
  if (!No_DB_Flag)
  {   runCyrca();   }
    
  convert_LAMA_output() ;

  /* End of modification*/

  clean_temp_files() ;

  exit(0) ;
}
