Index: src/hg/qaPushQ/qaPushQ.c
===================================================================
RCS file: /projects/compbio/cvsroot/kent/src/hg/qaPushQ/qaPushQ.c,v
retrieving revision 1.118
retrieving revision 1.119
diff -b -B -U 1000000 -r1.118 -r1.119
--- src/hg/qaPushQ/qaPushQ.c	22 Jan 2010 09:09:23 -0000	1.118
+++ src/hg/qaPushQ/qaPushQ.c	27 Jan 2010 01:35:32 -0000	1.119
@@ -1,3908 +1,3935 @@
 
 /* qaPushQ - Push Queue cgi */
 
 /* we don't use the cart, we have our own table to store cart-like info */
 
 #include <time.h>
 #include <stdio.h>
 #include <crypt.h>
 #include <fcntl.h>
 #include <sys/utsname.h>
 #include <sys/stat.h>
 
 #include "common.h"
 #include "cheapcgi.h"
 #include "htmshell.h"
 #include "jksql.h"
 #include "hgConfig.h"
 #include "obscure.h"
 #include "portable.h"
 
 #include "pushQ.h"
 #include "formPushQ.h"
 
 #include "versionInfo.h"
 
 /* stuff to support outputting Release Log html */
 #include "web.h"
 #include "hui.h"
 #include "dbDb.h"
 #include "htmlPage.h"
 
 static char const rcsid[] = "$Id$";
 
 char msg[2048] = "";
 char ** saveEnv;
 
 #define BUFMAX 128000
 char html[BUFMAX];
 
 char *action = NULL;   /* have to put declarations first */
 boolean crossPost = FALSE;  /* are we doing cross-post from dev to beta (or vice versa)? */
 			    /* support showSizes across machines */
 
 char *database = NULL;
 char *host     = NULL;
 char *user     = NULL;
 char *password = NULL;
 
 struct sqlConnection *conn = NULL;
 struct sqlConnection *conn2 = NULL;
 
 char *qaUser = NULL;
 
 #define SSSZ 256  /* MySql String Size 255 + 1 */
 #define MAXBLOBSHOW 128
 
 
 #define TITLE "Push Queue v"CGI_VERSION
 
 time_t curtime;
 struct tm *loctime;
 
 struct utsname utsName;
 
 struct users myUser;
 
 char *showColumns = NULL;
 
 char *defaultColumns =
     "pqid,qid,priority,qadate,track,dbs,tbls,cgis,files,currLoc,makeDocYN,onlineHelp,ndxYN,stat,sponsor,reviewer,extSource,notes";
 
 char *newRandState = NULL;    
 char *oldRandState = NULL;    
     
 /*
 "qid,pqid,priority,rank,qadate,newYN,track,dbs,tbls,cgis,files,sizeMB,currLoc,"
 "makeDocYN,onlineHelp,ndxYN,joinerYN,stat,sponsor,reviewer,extSource,openIssues,notes,"
 pushState,initdate,bounces,lockUser,lockDateTime,releaseLog,featureBits,releaseLogUrl";
 */
 
 /* structural improvements suggested by MarkD:
 
 static struct {
 enum colEnum col;
 char *name;
 char *hdr;
 } colTbl [] = {
 {e_qid, "qid"},
 {0,      NULL},
 }
 
 ArraySize(colTbl)
 
 numWords = chopString(liststr, ",", NULL,  NULL);
 
 */
 
 
 static char const *colName[] = {
  "qid"       ,
  "pqid"      ,
  "priority"  ,
  "rank"      ,
  "qadate"    ,
  "newYN"     ,
  "track"     ,
  "dbs"       ,
  "tbls"      ,
  "cgis"      ,
  "files"     ,
  "sizeMB"    ,
  "currLoc"   ,
  "makeDocYN" ,
  "onlineHelp",
  "ndxYN"     ,
  "joinerYN"  ,
  "stat"      ,
  "featureBits",
  "sponsor"   ,
  "reviewer"  ,
  "extSource" ,
  "openIssues",
  "notes"     ,
  "pushState" ,
  "initdate"  ,  
  "lastdate"  ,  
  "bounces"   , 
  "lockUser"  , 
  "lockDateTime",
  "releaseLog", 
  "releaseLogUrl"
 };
 
 
 enum colEnum {
 e_qid       ,
 e_pqid      ,
 e_priority  ,
 e_rank      ,
 e_qadate    ,
 e_newYN     ,
 e_track     ,
 e_dbs       ,
 e_tbls      ,
 e_cgis      ,
 e_files     ,
 e_sizeMB    ,
 e_currLoc   ,
 e_makeDocYN ,
 e_onlineHelp,
 e_ndxYN     ,
 e_joinerYN  ,
 e_stat      ,
 e_featureBits,
 e_sponsor   ,
 e_reviewer  ,
 e_extSource ,
 e_openIssues,
 e_notes     ,
 e_pushState ,
 e_initdate  ,
 e_lastdate  ,
 e_bounces   ,
 e_lockUser  ,
 e_lockDateTime,
 e_releaseLog,
 e_releaseLogUrl,
 e_NUMCOLS
 };
 
 
 
 char *colHdr[] = {
 "Queue ID",
 "Parent Queue ID",
 "Priority",
 "Rank",
 "&nbsp;&nbsp;&nbsp;&nbsp;Date&nbsp;&nbsp;&nbsp;&nbsp;",
 "New?",
 "Track",
 "Databases",
 "Tables",
 "CGIs",
 "Files",
 "Size MB",
 "Current Location",
 "MakeDoc",
 "Online help",
 "Index",
 "All. Joiner",
 "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Status&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;",
 "Feature&nbsp;Bits",
 "Sponsor (local)",
 "Reviewer",
 "External Source or Collaborator",
 "Open Issues",
 "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Notes&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;",
 "PushState",
 "Initial &nbsp;&nbsp;Submission&nbsp;&nbsp; Date",
 "Last &nbsp;&nbsp;QA&nbsp;&nbsp; Date",
 "Bounce Count",
 "Lock User",
 "Lock&nbsp;Date&nbsp;Time",
 "Release&nbsp;Log",
 "Release&nbsp;Log&nbsp;Url"
 };
 
 char *numberToMonth[]={"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"};
 
 char pushQtbl[256] = "pushQ";   /* default */
 
 char month[256] = "";  
 
 enum colEnum colOrder[e_NUMCOLS];
 int numColumns = 0;
 
 int randInt(int N)
 /* generate random number from 0 to N-1 */
 {
 return (int) N * (rand() / (RAND_MAX + 1.0));
 }
 
 char *randDigits(int length)
 /* generate string of random digits 0-9. String will need to be free'd later. */
 {
 int i = 0;
 char *s = needMem(length+1);
 for(i=0;i<length;i++)
     {
     s[i]='0'+randInt(10);
     }
 s[length]=0;    
 return s;
 }
 
 
 bool isDateValid(char *s)
 /* check if date has valid format only YYYY-MM-DD tolerated. *
  * YYYY must be 1900-2100.  MM 01-12.  DD 01-31.
 */
 {
 int yyyy = 0, mm = 0, dd = 0;
 char *y = NULL, *m = NULL, *d = NULL;
 if (strlen(s)!=10) return FALSE;
 if (s[4]!='-') return FALSE; 
 if (s[7]!='-') return FALSE; 
 y = cloneStringZ(&s[0],4);
 m = cloneStringZ(&s[5],2);
 d = cloneStringZ(&s[8],2);
 if (sscanf(y,"%d",&yyyy) != 1) return FALSE;
 if (sscanf(m,"%d",&mm)   != 1) return FALSE;
 if (sscanf(d,"%d",&dd)   != 1) return FALSE;
 if (yyyy < 1900) return FALSE; 
 if (yyyy > 2100) return FALSE; 
 if (  mm > 12  ) return FALSE; 
 if (  mm <  1  ) return FALSE; 
 if (  dd > 31  ) return FALSE; 
 if (  dd <  1  ) return FALSE; 
 return TRUE;
 }
 
 
 void encryptPWD(char *password, char *salt, char *buf, int bufsize)
 /* encrypt a password */
 {
 /* encrypt user's password. */
 safef(buf,bufsize,crypt(password, salt));
 }
 
 
 void encryptNewPWD(char *password, char *buf, int bufsize)
 /* encrypt a new password */
 {
 unsigned long seed[2];
 char salt[] = "$1$........";
 const char *const seedchars = 
 "./0123456789ABCDEFGHIJKLMNOPQRST"
 "UVWXYZabcdefghijklmnopqrstuvwxyz";
 int i;
 /* Generate a (not very) random seed. */
 seed[0] = time(NULL);
 seed[1] = getpid() ^ (seed[0] >> 14 & 0x30000);
 /* Turn it into printable characters from `seedchars'. */
 for (i = 0; i < 8; i++)
     salt[3+i] = seedchars[(seed[i/5] >> (i%5)*6) & 0x3f];
 encryptPWD(password, salt, buf, bufsize);
 }
 
 bool checkPWD(char *password, char *encPassword)
 /* check an encrypted password */
 {
 char encPwd[35] = "";
 encryptPWD(password, encPassword, encPwd, sizeof(encPwd));
 if (sameString(encPassword,encPwd))
     {
     return TRUE;
     }
 else
     {
     return FALSE;
     }
 }
 
 
 
 void doMsg()
 /* callable from htmShell */
 {
 printf("%s",msg);
 }
 
 bool mySqlGetLock(char *name, int timeout)
 /* Tries to acquire (for 10 seconds) and set an advisory lock.
  *  note: mysql returns 1 if successful,
  *   0 if name already locked or NULL if error occurred 
  *   blocks another client from obtaining a lock with the same name
  *   lock is automatically released by mysql when connection is closed or detected broken
  *   may even detect program crash and release lock.
  */
 {
 char query[256];
 struct sqlResult *rs;
 char **row = NULL;
 bool result = FALSE;
 
 safef(query, sizeof(query), "select get_lock('%s', %d)", name, timeout);
 rs = sqlGetResult(conn, query);
 row=sqlNextRow(rs);
 if (row[0] == NULL)
     {
     safef(msg, sizeof(msg), "Attempt to GET_LOCK of %s caused an error\n",name);
     htmShell(TITLE, doMsg, NULL);
     exit(0);
     }
 if (sameWord(row[0], "1"))
     result = TRUE;
 else if (sameWord(row[0], "0"))
     result = FALSE;
 sqlFreeResult(&rs);
 return result;
 }
 
 void mySqlReleaseLock(char *name)
 /* Releases an advisory lock created by GET_LOCK in mySqlGetLock */
 {
 char query[256];
 safef(query, sizeof(query), "select release_lock('%s')", name);
 sqlUpdate(conn, query);
 }
                                                                                 
 
 
 void setLock()
 /* set a lock to reduce concurrency problems */
 {
 mySqlGetLock("qapushq",10);    /* just an advisory semaphore, really */
 }
 
 void releaseLock()
 /* release the advisory lock */
 {
 mySqlReleaseLock("qapushq");
 }
 
 
 enum colEnum mapFieldToEnum(char *f, bool must)
 {
 int i = 0;
 for(i=0;i<e_NUMCOLS;i++)
     {
     if (sameString(colName[i],f))
 	{
 	return (enum colEnum) i;
 	}
     }
 if (must)
     {
     errAbort("Field not found in mapFieldToEnum: %s",f);
     }
 return -1;
 }
 
 
 
 void replaceInStr(char *s, int ssize, char *t, char *r) 
 /* Strings: Replace an occurrence of t in s with r.
  * size is sizeof(s)
  * note: s must have room to hold any expansion, as it happens in-place in s */
 {
 char *temp = replaceChars(s,t,r);
 if (strlen(temp) >= ssize)
     {
     errAbort("buf size exceeded. strlen(temp)=%ld, size of buf= %d",(unsigned long)strlen(temp),ssize);
     }
 safef(s,ssize,"%s",temp);
 freez(&temp);
 }
 
 
 
 
 bool parseList(char *s, char delim, int num, char *buf, int bufsize)
 /* parse list to find nth element of delim-separated list in string
  * returns l if not found which points to the string end 0
  * otherwise returns offset in s where begins */
 {
 int n = 0;
 int i = 0;
 int l = strlen(s);
 int j = 0;
 char c;
 if (bufsize==0)
     {
     return FALSE;
     }
 while (TRUE) 
     {
     if (n==num)
 	{
 	while (j<bufsize)
 	    {
 	    c = s[i+j];
 	    if (c==delim)
 		{
 		c = 0;
 		}
 	    buf[j] = c;
 	    if (c == 0)
 		{
 		return TRUE;
 		}
 	    j++;
 	    }
 	buf[bufsize-1] = 0;  /* add term in case */
 	return TRUE;
 	}
     while (s[i] != delim)
 	{
 	if (i >= l)
 	    {
 	    buf[0] = 0;
 	    return FALSE;
 	    }
 	i++;
 	}
     i++;
     n++;
     }
 }
 
 
 void replaceSelectOptions(char *varname, char *values, char *value)
 /* Replace <!sel-name-val> tags in select option picklist.
  * This initializes the correct default selection.
  * Currently values is a list of single characters only, 
  * picklists with string-values not supported. */
 {
 int i = 0;
 char *p = NULL;
 char tempTag[256] = "";
 char tempVal[256];
 while(TRUE)
     { 
     parseList(values,',',i,tempVal,sizeof(tempVal));
     if (tempVal[0]==0) 
 	{
 	break;
 	}
     safef(tempTag,sizeof(tempTag),"<!sel-%s-%s>",varname,tempVal);
     if (sameString(value,tempVal))
 	{p = "selected";}
     else 
 	{p = "";}
     replaceInStr(html, sizeof(html), tempTag, p);
     i++;
     }
 }
 
 
 
 void initColsFromString()
 {
 int i = 0, e=0;
 char colName[256];
 char sep[2]="";
 struct dyString * s = NULL;
 
 s = newDyString(2048);  /* need room */
 numColumns=0;
 while(parseList(showColumns,',',i,colName,sizeof(colName)))
     {
     e = mapFieldToEnum(colName,FALSE);
     if (e >= 0) /* tolerate old nonexistent colnames in pseudocart more gracefully */ 
 	{
 	colOrder[i] = e;
 	dyStringPrintf(s, "%s%s", sep, colName);
 	safef(sep,sizeof(sep),",");
 	numColumns++;
 	}
     i++;
     }
 showColumns = cloneString(s->string);
 freeDyString(&s);
 
 }
 
 
 
 /* -------------------- Push Queue ----------------------- */
 
 void replacePushQFields(struct pushQ *ki, bool isNew)
 /* fill in dummy html tags with values from the sql pushQ record */
 {
 char tempLink[256];
 char tempSizeMB[256];
 char tempMsg[256];
 bool myLock = FALSE;
 
 if (sameString(ki->lockUser,qaUser))
     {
     myLock = TRUE;
     }
 
 safef(html,sizeof(html),"%s",formQ); 
 
 safef(tempSizeMB, sizeof(tempSizeMB), "%u", ki->sizeMB);
 if (ki->sizeMB == 0) 
     {
     safef(tempSizeMB,sizeof(tempSizeMB),"%s","");
     }
 
 
 replaceInStr(html, sizeof(html) , "<!qid>"         , ki->qid       ); 
 replaceSelectOptions("priority" , "A,B,C,D,L"      , ki->priority  );
 replaceInStr(html, sizeof(html) , "<!qadate>"      , ki->qadate    ); 
 replaceSelectOptions("newYN"    ,"Y,N,X"           , ki->newYN     );
 replaceInStr(html, sizeof(html) , "<!track>"       , ki->track     ); 
 replaceInStr(html, sizeof(html) , "<!dbs>"         , ki->dbs       ); 
 replaceInStr(html, sizeof(html) , "<!tbls>"        , ki->tbls      ); 
 replaceInStr(html, sizeof(html) , "<!cgis>"        , ki->cgis      ); 
 replaceInStr(html, sizeof(html) , "<!files>"       , ki->files     ); 
 replaceInStr(html, sizeof(html) , "<!sizeMB>"      , tempSizeMB    ); 
 replaceInStr(html, sizeof(html) , "<!currLoc>"     , ki->currLoc   ); 
 replaceSelectOptions("currLoc"  , "hgwdev,hgwbeta" , ki->currLoc   );
 replaceSelectOptions("makeDocYN", "Y,N,X"          , ki->makeDocYN );
 replaceInStr(html, sizeof(html) , "<!onlineHelp>"  , ki->onlineHelp); 
 replaceSelectOptions("ndxYN"    , "Y,N,X"          , ki->ndxYN     );
 replaceSelectOptions("joinerYN" , "Y,N,X"          , ki->joinerYN  );
 replaceInStr(html, sizeof(html) , "<!stat>"        , ki->stat      ); 
 replaceInStr(html, sizeof(html) , "<!featureBits>" , ki->featureBits); 
 replaceInStr(html, sizeof(html) , "<!sponsor>"     , ki->sponsor   ); 
 replaceInStr(html, sizeof(html) , "<!reviewer>"    , ki->reviewer  ); 
 replaceInStr(html, sizeof(html) , "<!extSource>"   , ki->extSource ); 
 replaceInStr(html, sizeof(html) , "<!openIssues>"  , ki->openIssues); 
 replaceInStr(html, sizeof(html) , "<!notes>"       , ki->notes     );
 replaceInStr(html, sizeof(html) , "<!initdate>"    , ki->initdate  ); 
 replaceInStr(html, sizeof(html) , "<!releaseLog>"  , ki->releaseLog); 
 replaceInStr(html, sizeof(html) , "<!releaseLogUrl>", ki->releaseLogUrl); 
 
 replaceInStr(html, sizeof(html) , "<!cb>"          , newRandState  ); 
 
 if (isNew)
     {
     replaceInStr(html, sizeof(html), "<!DISABLED>", ""); 
     replaceInStr(html, sizeof(html), "<!READONLY>", ""); 
     replaceInStr(html, sizeof(html), "<!submitbutton>", "<input TYPE=SUBMIT NAME=\"submit\" VALUE=\"Submit\" >&nbsp;&nbsp;"); 
     replaceInStr(html, sizeof(html), "<!delbutton>", ""); 
     replaceInStr(html, sizeof(html), "<!pushbutton>", ""); 
     replaceInStr(html, sizeof(html), "<!clonebutton>", ""); 
     replaceInStr(html, sizeof(html), "<!bouncebutton>", ""); 
     replaceInStr(html, sizeof(html), "<!lockbutton>", ""); 
     replaceInStr(html, sizeof(html), "<!sizesbutton>", "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<input TYPE=SUBMIT NAME=\"showSizes\" VALUE=\"Show Sizes\">"); 
     replaceInStr(html, sizeof(html), "<!refreshlink>", ""); 
     replaceInStr(html, sizeof(html), "<!transferbutton>", ""); 
     
     safef(tempLink, sizeof(tempLink), "<a href=\"/cgi-bin/qaPushQ?cb=%s\">CANCEL</a>&nbsp;&nbsp;",newRandState); 
     replaceInStr(html, sizeof(html), "<!cancellink>", tempLink ); 
 	
     }
 else
     {
     if (myLock) 
 	{
 	replaceInStr(html, sizeof(html), "<!DISABLED>", ""); 
     	replaceInStr(html, sizeof(html), "<!READONLY>", "");
 	
         replaceInStr(html, sizeof(html), "<!submitbutton>", "<input TYPE=SUBMIT NAME=\"submit\" VALUE=\"Submit\" >&nbsp;&nbsp;"); 
 	replaceInStr(html, sizeof(html), 
 	    "<!delbutton>" , 
 	    "<input TYPE=SUBMIT NAME=\"delbutton\"  VALUE=\"delete\">&nbsp;&nbsp;"
 	    );
 	    
 	if (ki->priority[0]!='L')
 	    {
 	    replaceInStr(html, sizeof(html), 
 		"<!pushbutton>", 
 		"<input TYPE=SUBMIT NAME=\"pushbutton\" VALUE=\"push requested\">&nbsp;&nbsp;"
 		); 
 	    }
 	    
 	replaceInStr(html, sizeof(html), 
 	    "<!clonebutton>", 
 	    "<input TYPE=SUBMIT NAME=\"clonebutton\" VALUE=\"clone\">&nbsp;&nbsp;"
 	    ); 
 	    
 	if (ki->priority[0]=='A')
 	    {
 	    replaceInStr(html, sizeof(html), 
 		"<!bouncebutton>", 
 		"<input TYPE=SUBMIT NAME=\"bouncebutton\" VALUE=\"bounce\">&nbsp;&nbsp;"
 		); 
 	    }
 	else
 	    {
 	    //replaceInStr(html, sizeof(html), "<!bouncebutton>", ""); 
 	    replaceInStr(html, sizeof(html), 
 		"<!bouncebutton>", 
 		"<input TYPE=SUBMIT NAME=\"bouncebutton\" VALUE=\"unbounce\">&nbsp;&nbsp;"
 		); 
 	    }
 	    
 	replaceInStr(html, sizeof(html), "<!lockbutton>", ""); 
     	
 	replaceInStr(html, sizeof(html), "<!cancellink>",  
 	    "<input TYPE=SUBMIT NAME=\"cancelbutton\" VALUE=\"Cancel\" >&nbsp;&nbsp;"); 
 	
 	replaceInStr(html, sizeof(html), "<!refreshlink>", ""); 
 	
 	replaceInStr(html, sizeof(html), 
 	    "<!transferbutton>", 
 	    "<input TYPE=SUBMIT NAME=\"transfer\" VALUE=\"Transfer\">&nbsp;&nbsp;"); 
 	
 	char sizesButton[1024];
 	safef(sizesButton, sizeof(sizesButton), 
 	    "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<input TYPE=SUBMIT NAME=\"showSizes\" VALUE=\"Show Sizes\""
 	    " ONCLICK=\"if (document.forms[0].currLoc.value!='%s') {"
 			" document.forms[0]._action.value='xpost';"
 			" document.forms[0].action='http://%s.cse.ucsc.edu/cgi-bin/qaPushQ';"
 			"};return true;\">"
 	    , utsName.nodename
 	    , sameString(utsName.nodename, "hgwdev") ? "hgwbeta" : "hgwdev" 
 	    ); 
 	replaceInStr(html, sizeof(html), "<!sizesbutton>", sizesButton);
 	}
     else 
 	{ /* we don't have a lock yet, disable and readonly */
 	replaceInStr(html, sizeof(html), "<!DISABLED>", "DISABLED"); 
     	replaceInStr(html, sizeof(html), "<!READONLY>", "READONLY");
 	replaceInStr(html, sizeof(html), "<!submitbutton>", ""); 
 	replaceInStr(html, sizeof(html), "<!delbutton>", ""); 
 	replaceInStr(html, sizeof(html), "<!pushbutton>", ""); 
 	replaceInStr(html, sizeof(html), "<!clonebutton>", ""); 
 	replaceInStr(html, sizeof(html), "<!bouncebutton>", ""); 
 	replaceInStr(html, sizeof(html), "<!lockbutton>", 
 	    "<input TYPE=SUBMIT NAME=\"lockbutton\" VALUE=\"Lock\" >&nbsp;&nbsp;"); 
 	
 	safef(tempLink, sizeof(tempLink), 
 	    "<a href=\"/cgi-bin/qaPushQ?cb=%s\">RETURN</a>&nbsp;&nbsp;",newRandState); 
 	replaceInStr(html, sizeof(html), "<!cancellink>", tempLink ); 
 	
 	safef(tempLink, sizeof(tempLink), 
 	    "<a href=\"/cgi-bin/qaPushQ?action=edit&qid=%s&cb=%s\">REFRESH</a>&nbsp;&nbsp;",
 	    ki->qid,newRandState); 
 	replaceInStr(html, sizeof(html), "<!refreshlink>", tempLink ); 
 	
 	replaceInStr(html, sizeof(html), "<!transferbutton>", "");
 	replaceInStr(html, sizeof(html), "<!sizesbutton>", "");
 	if (sameString(ki->lockUser,""))
 	    {
 	    safef(tempMsg,sizeof(tempMsg),"%s %s", msg, "READONLY view. Press Lock to edit. Click RETURN for the main queue.");
 	    }
 	else
 	    {
 	    safef(tempMsg,sizeof(tempMsg),"%s User %s currently has lock on Queue Id %s since %s.",
 		msg, ki->lockUser, ki->qid,ki->lockDateTime );
 	    }
 	safef(msg,sizeof(msg), "%s", tempMsg);
 	}   
     }   
     
 replaceInStr(html, sizeof(html) , "<!msg>"         , msg           );
 
 safef(msg,sizeof(msg),"%s","");
 
 printf("%s",html);
 
 }
 
 
 void doAdd()
 /* handle setup add form for new pushQ record */
 {
 
 struct pushQ q;
 ZeroVar(&q);
 
 q.next = NULL;
 safef(q.qid, sizeof(q.qid), "%s", "");
 safef(q.priority, sizeof(q.priority), "%s", "A");  /* default priority */
 strftime (q.qadate, sizeof(q.qadate), "%Y-%m-%d", loctime); /* default to today's date */
 safef(q.newYN, sizeof(q.newYN), "%s", "N");  /* default to not new track */
 q.track  = ""; 
 q.dbs    = ""; 
 q.tbls   = "";
 q.cgis   = "";
 q.files  = "";
 q.sizeMB = 0;
 safef(q.currLoc   , sizeof(q.currLoc)   , "%s", "hgwdev");  /* default loc */
 safef(q.makeDocYN , sizeof(q.makeDocYN) , "%s", "N");
 safef(q.onlineHelp, sizeof(q.onlineHelp), "%s", "" );
 safef(q.ndxYN     , sizeof(q.ndxYN)     , "%s", "N");  /* default to not checked yet */
 safef(q.joinerYN  , sizeof(q.joinerYN)  , "%s", "N");  /* default to all.joiner not checked yet */
 q.stat   = "";
 q.featureBits = "";
 
 safef(q.sponsor   , sizeof(q.sponsor)   , "%s", "" );
 safef(q.reviewer  , sizeof(q.reviewer)  , "%s", "" ); 
 if (sameString(myUser.role,"qa"))
     {
     safef(q.reviewer, sizeof(q.reviewer), "%s", qaUser);   /* if role is qa, default reviewer to this user */
     }
 if (sameString(myUser.role,"dev"))
     {
     safef(q.sponsor , sizeof(q.sponsor) , "%s", qaUser);   /* if role is dev, default sponsor to current user */
     }
 safef(q.extSource , sizeof(q.extSource) , "%s", "" );
 q.openIssues   = "";
 q.notes   = "";
 strftime (q.initdate, sizeof(q.initdate), "%Y-%m-%d", loctime); /* automatically use today date */
 safef(q.lastdate, sizeof(q.lastdate), "%s", "" );
 q.releaseLog = "";
 q.releaseLogUrl = "";
 
 if (sameString(myUser.role,"dev"))
     {
     safef(msg, sizeof(msg), "%s", 
 	"Developer: Please leave priority and date alone. "
 	"Do specify if the track is new. Enter the shortLabel for the track name. "
 	"Be sure to fill out the database, tables,  and external files, if any. "
 	"If relevant cgis have changed since last branch, please list them. "
 	"Enter collaborator if applicable. "
 	"Leave the other fields for QA. You may leave a note for QA staff in the notes field. "
 	"Thanks very much!"
 	);
     }
 
 replacePushQFields(&q, TRUE);  /* new rec = true */
 
 }
 
 void dotdotdot(char *s, int l)
 /* truncate a string for shorter display with ... at end */
 {
 if ((strlen(s)<l) || (strlen(s)<5))
     {
     return;
     }
 s[l]=0;
 s[l-1]='.';
 s[l-2]='.';
 s[l-3]='.';
 s[l-4]=' ';
 }
 
 char *fixLineBreaks(char *s)
 /* insert <br> before \n to have an effect inside html */
 {
 return replaceChars(s,"\n","<br>\n");
 }
 
 
 void drawDisplayLine(enum colEnum col, struct pushQ *ki)
 {
 
 switch(col)
     {
     case e_qid:
 	printf("<td><A href=\"/cgi-bin/qaPushQ?action=edit&qid=%s&cb=%s\">%s</A>%s",
 	    ki->qid, newRandState, ki->qid, sameString(ki->lockUser,"") ? "":"*" );
 	if (ki->pushState[0]=='Y')
 	    {
 	    printf("<BR><A href=\"/cgi-bin/qaPushQ?action=pushDone&qid=%s&cb=%s\">Done!</A>",
 		ki->qid, newRandState );
 	    }
 	printf("</td>\n");
 	break;
 
     case e_pqid:
 	printf("<td>%s</td>\n", ki->pqid   );
 	break;
 	
 	
     case e_priority:
 	if (ki->priority[0] == 'L')
 	    {
 	    printf("<td>%s</td>", ki->priority);
 	    }
 	else
 	    {
 	    printf(
 		"<td><table><tr><td>%s</td><td>"
 		"<A href=\"/cgi-bin/qaPushQ?action=promote&qid=%s&cb=%s\">^</A>&nbsp;&nbsp;"
 		"<A href=\"/cgi-bin/qaPushQ?action=top&qid=%s&cb=%s\">T</A>&nbsp;&nbsp;"
 		"</td></tr><tr><td>&nbsp</td><td>"
 		"<A href=\"/cgi-bin/qaPushQ?action=demote&qid=%s&cb=%s\">v</A>&nbsp;&nbsp;"
 		"<A href=\"/cgi-bin/qaPushQ?action=bottom&qid=%s&cb=%s\">B</A>"
 		"</td></tr></table></td>\n", 
 		ki->priority,
 		ki->qid, newRandState, 
 		ki->qid, newRandState, 
 		ki->qid, newRandState, 
 		ki->qid, newRandState
 		);
 	    }
 	break;
 	
     case e_rank:
 	printf("<td>%d</td>\n", ki->rank      );
 	break;
 	
     case e_qadate:
 	printf("<td>%s</td>\n", ki->qadate    );
 	break;
 	
     case e_newYN:
 	printf("<td>%s</td>\n", ki->newYN     );
 	break;
 	
     case e_track:
 	dotdotdot(ki->track,MAXBLOBSHOW);  /* chr(255) */
 	printf("<td>%s</td>\n", ki->track     );
 	break;
 	
     case e_dbs:
 	dotdotdot(ki->tbls ,MAXBLOBSHOW);  /* chr(255) */
 	printf("<td>%s</td>\n", ki->dbs       );
 	break;
 	
     case e_tbls:
 	dotdotdot(ki->tbls,MAXBLOBSHOW);  /* longblob */
 	printf("<td>%s</td>\n", ki->tbls      );
 	break;
 	
     case e_cgis:
 	dotdotdot(ki->cgis ,MAXBLOBSHOW);  /* chr(255) */
 	printf("<td>%s</td>\n", ki->cgis      );
 	break;
 	
     case e_files:
 	dotdotdot(ki->files,MAXBLOBSHOW);  /* chr(255) */
 	printf("<td>%s</td>\n", fixLineBreaks(ki->files)  );
 	break;
 	
     case e_sizeMB:
 	printf("<td>%u</td>\n", ki->sizeMB    );
 	break;
 	
     case e_currLoc:
 	printf("<td>%s</td>\n", ki->currLoc   );
 	break;
 	
     case e_makeDocYN:
 	printf("<td>%s</td>\n", ki->makeDocYN );
 	break;
 	
     case e_onlineHelp:
 	printf("<td>%s</td>\n", ki->onlineHelp);
 	break;
 	
     case e_ndxYN:
 	printf("<td>%s</td>\n", ki->ndxYN     );
 	break;
 	
     case e_joinerYN:
 	printf("<td>%s</td>\n", ki->joinerYN  );
 	break;
 	
     case e_stat:
 	dotdotdot(ki->stat ,MAXBLOBSHOW);  /* chr(255) */
 	printf("<td>%s</td>\n", ki->stat      );
 	break;
 	
     case e_featureBits:
 	dotdotdot(ki->featureBits,MAXBLOBSHOW);  /* longblob */
 	printf("<td>%s</td>\n", ki->featureBits);
 	break;
 	
     case e_sponsor:
 	printf("<td>%s</td>\n", ki->sponsor   );
 	break;
 	
     case e_reviewer:
 	printf("<td>%s</td>\n", ki->reviewer  );
 	break;
 	
     case e_extSource:
 	printf("<td>%s</td>\n", ki->extSource );
 	break;
 	
     case e_openIssues:
 	dotdotdot(ki->openIssues,MAXBLOBSHOW);  /* longblob */
 	printf("<td>%s</td>\n", ki->openIssues);
 	break;
 	
     case e_notes:
 	dotdotdot(ki->notes,MAXBLOBSHOW);       /* longblob */
 	printf("<td>%s</td>\n", ki->notes     );
 	break;
 	
     case e_pushState:
 	printf("<td>%s</td>\n", ki->pushState);
 	break;
 
     case e_initdate:
 	printf("<td>%s</td>\n", ki->initdate  );
 	break;
 	
     case e_lastdate:
 	printf("<td>%s</td>\n", ki->lastdate  );
 	break;
 	
     case e_bounces:
 	printf("<td>%u</td>\n", ki->bounces   );
 	break;
 	
     case e_lockUser:
 	printf("<td>%s</td>\n", ki->lockUser  );
 	break;
 	
     case e_lockDateTime:
 	printf("<td>%s</td>\n", ki->lockDateTime );
 	break;
 	
     case e_releaseLog:
 	dotdotdot(ki->releaseLog,MAXBLOBSHOW);  /* chr(255) */
 	printf("<td>%s</td>\n", ki->releaseLog   );
 	break;
 	
     case e_releaseLogUrl:
 	dotdotdot(ki->releaseLogUrl,MAXBLOBSHOW);  /* chr(255) */
 	printf("<td>%s</td>\n", ki->releaseLogUrl  );
 	break;
 
     default:
 	errAbort("drawDisplayLine: unexpected case enum %d.",col);
 	
 
     }
 
 }
 
 
 
 void doDisplay()
 /* handle display request, shows pushQ records, also this is the default action  */
 {
 struct pushQ *ki, *kiList = NULL;
 struct sqlResult *sr;
 char **row;
 char query[256];
 char lastP = ' ';
 int c = 0;
 char monthsql[256];
 char comment[256];
 
 /* initialize column display order */
 initColsFromString();
 
 safef(monthsql,sizeof(monthsql),"%s","");
 if (!sameString(month,""))
     {
     safef(monthsql,sizeof(monthsql)," where priority='L' and qadate like '%s%%' ",month);
     }
 
 /* Get a list of all (or in month). */
 safef(query, sizeof(query), "select * from %s%s%s", 
     pushQtbl,
     monthsql,
     " order by priority, rank, qadate desc, qid desc limit 200"
     );
 
 sr = sqlGetResult(conn, query);
 while ((row = sqlNextRow(sr)) != NULL)
     {
     ki = pushQLoad(row);
     slAddHead(&kiList, ki);
     }
 sqlFreeResult(&sr);
 slReverse(&kiList); 
 
 /* #rows returned
 slCount(kiList)
 */
 
 if (sameString(utsName.nodename,"hgwdev"))
     {
     printf("<p style=\"color:red\">Machine: %s THIS IS NOT THE REAL PUSHQ- GO TO <a href=http://hgwbeta.cse.ucsc.edu/cgi-bin/qaPushQ>HGWBETA</a> </p>\n",utsName.nodename);
     }
 
 if (!sameString(msg,""))
     {
     printf("<p style=\"color:red\">%s</p>\n",msg);
     }
 
 if (sameString(month,""))
     {
     printf("&nbsp;<A href=/cgi-bin/qaPushQ?action=add&cb=%s>ADD</A>\n",newRandState);
     }
 else
     {
     printf("&nbsp;<A href=/cgi-bin/qaPushQ?action=display&month=current&cb=%s>Current</A>\n",newRandState);
     }
 printf("&nbsp;<A href=/cgi-bin/qaPushQ?action=reset&cb=%s>Logout</A>\n",newRandState);
 printf("&nbsp;<A href=/cgi-bin/qaPushQ?action=showAllCol&cb=%s>All Columns</A>\n",newRandState);
 printf("&nbsp;<A href=/cgi-bin/qaPushQ?action=showDefaultCol&cb=%s>Default Columns</A>\n",newRandState);
 printf("&nbsp;<A href=/cgi-bin/qaPushQ?action=showMonths&cb=%s>Log by Month</A>\n",newRandState);
 printf("&nbsp;<A href=/cgi-bin/qaPushQ?action=showGateway&cb=%s>Gateway</A>\n",newRandState);
 printf("&nbsp;<A href=/cgi-bin/qaPushQ?action=showDisplayHelp target=\"_blank\">Help</A>\n");
 printf("&nbsp;<A href=/cgi-bin/qaPushQ?action=releaseLog target=\"_blank\">Release Log</A>\n");
 //printf("&nbsp;<A href=/cgi-bin/qaPushQ?action=releaseLogPush target=\"_blank\">Publish RL</A>\n");
 printf("&nbsp;<A href=/cgi-bin/qaPushQ?cb=%s#priorityA>A</A>\n",newRandState);
 printf("&nbsp;<A href=/cgi-bin/qaPushQ?cb=%s#priorityB>B</A>\n",newRandState);
 printf("&nbsp;<A href=/cgi-bin/qaPushQ?cb=%s#priorityC>C</A>\n",newRandState);
 //printf("&nbsp;<A href=/cgi-bin/qaPushQ?cb=%s#priorityD>D</A>\n",newRandState);
 printf("&nbsp;<A href=/cgi-bin/qaPushQ?cb=%s#priorityL>L</A>\n",newRandState);
 printf("&nbsp;<A href=/cgi-bin/qaPushQ?cb=%s>Refresh</A>\n",newRandState);
 //printf("&nbsp;newRandState=%s\n",newRandState);
 //printf("&nbsp;oldRandState=%s\n",oldRandState);
 
 /* draw table header */
 
 printf("<H2>Track Push Queue");
 if (!sameString(pushQtbl,"pushQ"))
     {
     printf(" for %s",pushQtbl);
     }
 if (!sameString(month,""))    
     {
     printf(" (%s)",month);
     }
 printf("</H2>\n");
     
 printf("<TABLE BORDER CELLSPACING=0 CELLPADDING=5>\n");
 printf("  <TR>\n");
 
 for (c=0; c<numColumns; c++)
     {
     printf("    <TH>"
 	"<a href=qaPushQ?action=promoteColumn&col=%s&cb=%s>&lt;</a>&nbsp;"
 	"<a href=qaPushQ?action=hideColumn&col=%s&cb=%s>!</a>&nbsp;"
 	"<a href=qaPushQ?action=demoteColumn&col=%s&cb=%s>&gt;</a>"
 	"<br></TH>\n",
 	colName[colOrder[c]], newRandState,
 	colName[colOrder[c]], newRandState,
 	colName[colOrder[c]], newRandState
 	);
     }
 printf("  <TR>\n");
 printf("  </TR>\n");
 
 for (c=0; c<numColumns; c++)
     {
     printf("    <TH>%s</TH>\n",colHdr[colOrder[c]]);
     }
 
 printf("  </TR>\n");
 
 
 /* Print some info for each match */
 for (ki = kiList; ki != NULL; ki = ki->next)
     {
 
     /* Major-priority section header */
     if (ki->priority[0] != lastP) 
 	{
 	lastP = ki->priority[0];
 	safef(comment,sizeof(comment),"%s","");
 	switch (ki->priority[0])
 	    {
 	    case 'A': 
 		safef(comment,sizeof(comment),"%s","active");
 		break;
 	    case 'B': 
 		safef(comment,sizeof(comment),"%s","short hold");
 		break;
 	    case 'C': 
 		safef(comment,sizeof(comment),"%s","long hold");
 		break;
 	    case 'L': 
 		safef(comment,sizeof(comment),"%s","log");
 		break;
 	    
 	    }
 	printf("<tr>");
 	printf("<td><h1><A name=\"priority%s\">%s</A></h1></td><td><b>%s</b></td>\n", ki->priority, ki->priority, comment);
 	printf("</tr>");
     
     }
 
     /* Regular row */
     printf("<tr>");
 
     for (c=0; c<numColumns; c++)
 	{
 	drawDisplayLine(colOrder[c],ki);
 	}
 
     printf("</tr>");
     }
 
 
 /* draw table footer */
 printf("</table>");
 
 pushQFreeList(&kiList);
 
 }
 
 
 struct pushQ *loadPushQ(char *qid)
 /* Return pushQ struct loading q with existing values.
  * Use pushQFree() when done.*/
 {
 char **row;
 struct sqlResult *sr;
 char query[256];
 struct pushQ *q = NULL;
 safef(query, sizeof(query), "select * from %s where qid = '%s'",pushQtbl,qid);
 sr = sqlGetResult(conn, query);
 row = sqlNextRow(sr);
 if (row)
     q = pushQLoad(row);
 sqlFreeResult(&sr);
 return q;
 }
 
 struct pushQ *mustLoadPushQ(char *qid)
 /* Load pushQ or die */
 {
 struct pushQ *q = loadPushQ(qid);
 if (!q)
     errAbort("loadPushQ: Queue Id %s not found.",qid);
 return q;    
 }
 
 
 
 void doPushDone()
 /* Mark record pushState=D, move priority to L for Log, and set rank=0  */
 {
 
 struct pushQ *q;
 char query[256];
 
 q=mustLoadPushQ(cgiString("qid"));
 if (sameString(q->lockUser,"") && sameString(q->pushState,"Y"))
     { /* not already locked and pushState=Y */
 
     safef(q->lastdate, sizeof(q->lastdate), q->qadate);
     strftime (q->qadate  , sizeof(q->qadate  ), "%Y-%m-%d", loctime); /* today's date */
 
     safef(query, sizeof(query), 
 	"update %s set rank = 0, priority ='L', pushState='D', qadate='%s', lastdate='%s' where qid = '%s' ", 
 	pushQtbl, q->qadate, q->lastdate, q->qid);
     sqlUpdate(conn, query);
 
 
     /* first close the hole where it was */
     safef(query, sizeof(query), 
 	"update %s set rank = rank - 1 where priority ='%s' and rank > %d ", 
 	pushQtbl, q->priority, q->rank);
     sqlUpdate(conn, query);
     }
 else
     {
     if (sameString(q->lockUser,""))
 	{
     	safef(msg, sizeof(msg), "Unable to mark record %s done-> Record is locked by %s->",q->qid,q->lockUser);
 	}
     else
 	{
     	safef(msg, sizeof(msg), "Invalid operation for qid %s, pushState is not Y, = %s->",q->qid,q->pushState);
 	}
     }
 
 pushQFree(&q);
 
 doDisplay();
 }
 
 
 void XdoPromote(int change)
 /* Promote the ranking of this Q item 
  * >0 means promote, <0 means demote */
 {
 
 struct pushQ *q;
 char query[256];
 char newQid[sizeof(q->qid)] = "";
 
 safef(newQid, sizeof(newQid), cgiString("qid"));
 
 q = mustLoadPushQ(newQid);
 
 if ((q->rank > 1) && (change>0))
     {
     /* swap places with rank-1 */
     safef(query, sizeof(query), 
     "update %s set rank = rank + 1 where priority ='%s' and rank = %d ", 
     pushQtbl, q->priority, q->rank-1);
     sqlUpdate(conn, query);
     q->rank--;
     safef(query, sizeof(query), 
     "update %s set rank = %d where qid ='%s'", 
     pushQtbl, q->rank, q->qid);
     sqlUpdate(conn, query);
     }
 
 if (change<0)
     {
     /* swap places with rank+1 */
     safef(query, sizeof(query), 
     "update %s set rank = rank - 1 where priority ='%s' and rank = %d ", 
     pushQtbl, q->priority, q->rank+1);
     if (sqlUpdateRows(conn, query, NULL)>0)
 	{
 	q->rank++;
 	safef(query, sizeof(query), 
 	    "update %s set rank = %d where qid ='%s'", 
 	    pushQtbl, q->rank, q->qid);
 	sqlUpdate(conn, query);
 	}
     }
 
 pushQFree(&q);
 
 doDisplay();
 }
 
 
 void doPromote()
 {
 XdoPromote(1);
 }
 
 void doDemote()
 {
 XdoPromote(-1);
 }
 
 
 
 
 int getNextAvailQid()
 /* adding new pushQ rec, get next available qid number */
 {
 struct pushQ q;
 int newqid = 0;
 char query[256];
 char *quickres = NULL;
 safef(query, sizeof(query), "select max(qid) from %s",pushQtbl);
 quickres = sqlQuickString(conn, query);
 if (quickres != NULL) 
     {
     safef(q.qid, sizeof(q.qid), quickres);
     sscanf(q.qid,"%d",&newqid);
     freez(&quickres);
     }
 newqid++;
 return newqid;
 }
 
 int getNextAvailRank(char *priority)
 /* get next available rank at end of priority section */
 {
 struct pushQ q;
 char query[256];
 char *quickres = NULL;
 safef(query, sizeof(query), 
     "select rank from %s where priority='%s' order by rank desc limit 1",
     pushQtbl, priority);
 quickres = sqlQuickString(conn, query);
 if (quickres == NULL) 
     {
     q.rank = 0;
     }
 else
     {
     sscanf(quickres,"%d",&q.rank);
     freez(&quickres);
     }
 q.rank++;
 return q.rank;
 }
 
 
 void doTop()
 {
 
 struct pushQ *q;
 char query[256];
 char newQid[sizeof(q->qid)] = "";
 
 safef(newQid, sizeof(newQid), cgiString("qid"));
 
 q = mustLoadPushQ(newQid);
 
 /* first close the hole where it was */
 safef(query, sizeof(query), 
 "update %s set rank = rank + 1 where priority ='%s' and rank < %d ", 
 pushQtbl, q->priority, q->rank);
 sqlUpdate(conn, query);
 
 q->rank = 1;
 safef(query, sizeof(query), 
 "update %s set rank = %d where qid = '%s' ", 
 pushQtbl, q->rank, q->qid);
 sqlUpdate(conn, query);
 
 pushQFree(&q);
 
 doDisplay();
 }
 
 void doBottom()
 {
 
 struct pushQ *q;
 char query[256];
 char newQid[sizeof(q->qid)] = "";
 
 safef(newQid, sizeof(newQid), cgiString("qid"));
 
 q = mustLoadPushQ(newQid);
 
 /* first close the hole where it was */
 safef(query, sizeof(query), 
 "update %s set rank = rank - 1 where priority ='%s' and rank > %d ", 
 pushQtbl, q->priority, q->rank);
 sqlUpdate(conn, query);
 
 q->rank = getNextAvailRank(q->priority);
 safef(query, sizeof(query), 
 "update %s set rank = %d where qid = '%s' ", 
 pushQtbl, q->rank, q->qid);
 sqlUpdate(conn, query);
 
 pushQFree(&q);
 
 doDisplay();
 }
 
 
 /* too bad this isn't part of autoSql's code generation */
 
 void pushQUpdateEscaped(struct sqlConnection *conn, struct pushQ *el, char *tableName, int updateSize)
 /* Update pushQ row to the table specified by tableName. 
  * As blob fields may be arbitrary size updateSize specifies the approx size.
  * of a string that would contain the entire query. Automatically 
  * escapes all simple strings (not arrays of string) but may be slower than pushQSaveToDb().
  * For example automatically copies and converts: 
  * "autosql's features include" --> "autosql\'s features include" 
  * before inserting into database. */ 
 {
 struct dyString *update = newDyString(updateSize);
 char  *qid, *pqid, *priority, *qadate, *newYN, *track, *dbs, *tbls, *cgis, *files, *currLoc, *makeDocYN, *onlineHelp, *ndxYN, *joinerYN, *stat, *featureBits, *sponsor, *reviewer, *extSource, *openIssues, *notes, *pushState, *initdate, *lastdate, *lockUser, *lockDateTime, *releaseLog, *releaseLogUrl;
 qid = sqlEscapeString(el->qid);
 pqid = sqlEscapeString(el->pqid);
 priority = sqlEscapeString(el->priority);
 qadate = sqlEscapeString(el->qadate);
 newYN = sqlEscapeString(el->newYN);
 track = sqlEscapeString(el->track);
 dbs = sqlEscapeString(el->dbs);
 tbls = sqlEscapeString(el->tbls);
 cgis = sqlEscapeString(el->cgis);
 files = sqlEscapeString(el->files);
 currLoc = sqlEscapeString(el->currLoc);
 makeDocYN = sqlEscapeString(el->makeDocYN);
 onlineHelp = sqlEscapeString(el->onlineHelp);
 ndxYN = sqlEscapeString(el->ndxYN);
 joinerYN = sqlEscapeString(el->joinerYN);
 stat = sqlEscapeString(el->stat);
 featureBits = sqlEscapeString(el->featureBits);
 sponsor = sqlEscapeString(el->sponsor);
 reviewer = sqlEscapeString(el->reviewer);
 extSource = sqlEscapeString(el->extSource);
 openIssues = sqlEscapeString(el->openIssues);
 notes = sqlEscapeString(el->notes);
 pushState = sqlEscapeString(el->pushState);
 initdate = sqlEscapeString(el->initdate);
 lastdate = sqlEscapeString(el->lastdate);
 lockUser = sqlEscapeString(el->lockUser);
 lockDateTime = sqlEscapeString(el->lockDateTime);
 releaseLog = sqlEscapeString(el->releaseLog);
 releaseLogUrl = sqlEscapeString(el->releaseLogUrl);
 
 /* had to split this up because dyStringPrintf only up to 4000 chars at one time */
 dyStringPrintf(update, 
     "update %s set "
     "pqid='%s',priority='%s',rank=%u,qadate='%s',newYN='%s',track='%s',",
     tableName,  pqid,  priority, el->rank,  qadate, newYN, track);
 dyStringPrintf(update, "dbs='%s',",dbs);
 dyStringPrintf(update, "tbls='%s',",tbls);
 dyStringPrintf(update, "cgis='%s',",cgis);
 dyStringPrintf(update, "files='%s',",files);
 dyStringPrintf(update, "sizeMB=%u,currLoc='%s',"
     "makeDocYN='%s',onlineHelp='%s',ndxYN='%s',joinerYN='%s',stat='%s',"
     "sponsor='%s',reviewer='%s',extSource='%s',",
     el->sizeMB ,  currLoc,  makeDocYN,  
     onlineHelp,  ndxYN,  joinerYN,  stat,  
     sponsor,  reviewer,  extSource);
 dyStringPrintf(update, "openIssues='%s',",openIssues);
 dyStringPrintf(update, "notes='%s',",notes);
 dyStringPrintf(update, "pushState='%s', initdate='%s', lastdate='%s', bounces='%u',lockUser='%s',lockDateTime='%s',releaseLog='%s',featureBits='%s',releaseLogUrl='%s' "
 	"where qid='%s'", 
 	pushState, initdate, lastdate, el->bounces, lockUser, lockDateTime, releaseLog, featureBits, releaseLogUrl,
 	qid
 	);
 
 sqlUpdate(conn, update->string);
 freeDyString(&update);
 freez(&qid);
 freez(&pqid);
 freez(&priority);
 freez(&qadate);
 freez(&newYN);
 freez(&track);
 freez(&dbs);
 freez(&tbls);
 freez(&cgis);
 freez(&files);
 freez(&currLoc);
 freez(&makeDocYN);
 freez(&onlineHelp);
 freez(&ndxYN);
 freez(&joinerYN);
 freez(&stat);
 freez(&sponsor);
 freez(&reviewer);
 freez(&extSource);
 freez(&openIssues);
 freez(&notes);
 freez(&pushState);
 freez(&initdate);
 freez(&lastdate);
 freez(&lockUser);
 freez(&lockDateTime);
 freez(&releaseLog);
 freez(&releaseLogUrl);
 }
 
 void getCgiData(bool *isOK, bool isPtr, void *ptr, int size, char *name)
 /* get data, truncate to fit in field to prevent safef buf overflows */
 {
 int l = 0;
 char **pfld = NULL;
 char *fld = NULL;
 char *cgi = NULL;
 cgi = cgiString(name);
 l = strlen(cgi);
 if (isPtr)
     {
     pfld = (char **) ptr;
     }
 else
     {
     fld = (char *) ptr;
     }
 if (size != -1)  /* -1 for blob, has no length */ 
     {
     if (l>(size-1))
 	{
 	*isOK = FALSE;
 	safef(msg,sizeof(msg),"%s: too large, max. %d chars.",name,size-1);
 	}
     }
 if (isPtr)
     {
     *pfld = cloneString(cgi);  /* set pointer to a copy of the whole thing */
     }
 else
     {
     safef(fld, size, cloneStringZ(cgi,size-1));  /* for non-ptr strings, copy into existing buffer */
     }
 }
 
 
 
 void doTransfer();    /* forward reference needed */
 
 void doShowSizes();  /* forward reference needed */
 
 void doEdit();       /* forward reference needed */
 
 void doPost()
 /* handle the  post (really just a get for now) from Add or Edit of a pushQ record */
 {
 
 
 int updateSize = 2456;
 int newqid = 0;
 
 char query[256];
 char *submitbutton = cgiUsualString("submit"   ,"");
 char *delbutton    = cgiUsualString("delbutton"   ,"");
 char *pushbutton   = cgiUsualString("pushbutton"  ,"");
 char *clonebutton  = cgiUsualString("clonebutton" ,"");
 char *bouncebutton = cgiUsualString("bouncebutton","");
 char *lockbutton   = cgiUsualString("lockbutton"  ,"");
 char *cancelbutton   = cgiUsualString("cancelbutton"  ,"");
 char *showSizes    = cgiUsualString("showSizes"   ,"");
 char *transfer     = cgiUsualString("transfer"   ,"");
 
 struct pushQ *q;
 
 bool isNew  = FALSE;   /* new rec */
 bool isRedo = FALSE;   /* need to return to edit form with error msg */
 bool isOK   = TRUE;    /* is data valid length (not too large) */
 bool lockOK = TRUE;    /* assume for now lock state OK */
 
 char newQid     [sizeof(q->qid)]      = "";
 char newPriority[sizeof(q->priority)] = "";
 
 safef(newQid, sizeof(newQid), cgiString("qid"));
 
 if (sameString(newQid,"")) 
     {
     isNew = TRUE;
     }
 else
     {
     isNew = FALSE;
     }
 
 if (!isNew) 
     {
     /* we need to preload q with existing values 
      * because some fields like rank are not carried in form 
     */	
     q = mustLoadPushQ(newQid); 
 	/* true means optional, it was asked if we could tolerate this, 
 	 *  e.g. delete, then hit back-button
 	* user is trying to use back button to recover deleted rec 
 	safef(newQid, sizeof(newQid), ""); 
 	isNew = TRUE;
 	*/	
 
     /* check lock status */
 
     if (sameString(cancelbutton,"Cancel"))  /* user cancelled */
 	{  /* unlock record */
 	safef(q->lockUser, sizeof(q->lockUser), "%s", "");
 	safef(q->lockDateTime, sizeof(q->lockDateTime), "%s", "");
 	pushQUpdateEscaped(conn, q, pushQtbl, updateSize);
 	lockOK = FALSE;
 	}
     else if (sameString(lockbutton,"Lock"))  /* try to lock the record for editing */
 	{
 	if (sameString(q->lockUser,""))  /* q->lockUser blank if nobody has lock */
 	    {
 	    safef(q->lockUser, sizeof(q->lockUser), qaUser);
 	    strftime(q->lockDateTime, sizeof(q->lockDateTime), "%Y-%m-%d %H:%M", loctime);
 	    pushQUpdateEscaped(conn, q, pushQtbl, updateSize);
 	    lockOK = FALSE;
 	    }
 	else
 	    { /* somebody else has lock-> */
 	    lockOK = FALSE;
 	    }
 	}
     else if (!sameString(q->lockUser,qaUser))  /* User supposed to already have lock, verify. */
 	{ /* if lock was lost, what do we do now? */
 	if (sameString(q->lockUser,""))
 	    {
 	    safef(msg,sizeof(msg),"Lost lock-> Must refresh data->");
 	    }
 	else
 	    {
 	    safef(msg,sizeof(msg),"Lost lock-> User %s currently has lock on Queue Id %s since %s->",
 		q->lockUser,q->qid,q->lockDateTime);
 	    }
 	lockOK = FALSE;
 	}
 	
     if (!lockOK)
 	{
 	doEdit();
 	pushQFree(&q);
 	return;
 	}
 
     }
 
 if (isNew) 
     {
     AllocVar(q);
     newqid = getNextAvailQid();
     safef(q->pqid, sizeof(q->pqid), "%s", "");
     safef(q->pushState,sizeof(q->pushState),"N");  /* default to: push not done yet */
     }
 
 
 safef(newPriority, sizeof(newPriority), cgiString("priority"));
 
     
 /* dates */
 getCgiData(&isOK, FALSE, q->qadate    , sizeof(q->qadate    ), "qadate"    );
 getCgiData(&isOK, FALSE, q->initdate  , sizeof(q->initdate  ), "initdate"  );
 
 /* YN select listboxes */
 getCgiData(&isOK, FALSE, q->newYN     , sizeof(q->newYN     ), "newYN"     );
 getCgiData(&isOK, FALSE, q->makeDocYN , sizeof(q->makeDocYN ), "makeDocYN" );
 getCgiData(&isOK, FALSE, q->ndxYN     , sizeof(q->ndxYN     ), "ndxYN"     );
 getCgiData(&isOK, FALSE, q->joinerYN  , sizeof(q->joinerYN  ), "joinerYN"  );
 
 /* chr(255) strings */
 getCgiData(&isOK, TRUE ,&q->track     , 256                 , "track"     );
 getCgiData(&isOK, TRUE ,&q->dbs       , 256                 , "dbs"       );
 getCgiData(&isOK, TRUE ,&q->cgis      , 256                 , "cgis"      );
 getCgiData(&isOK, TRUE ,&q->stat      , 256                 , "stat"      );
 
 /* integers */
 if (sscanf(cgiString("sizeMB"),"%u",&q->sizeMB) != 1) 
     {
     q->sizeMB = 0;
     }
 
 /* strings of various sizes */
 getCgiData(&isOK, FALSE, q->currLoc   , sizeof(q->currLoc   ), "currLoc"   );
 getCgiData(&isOK, FALSE, q->onlineHelp, sizeof(q->onlineHelp), "onlineHelp");
 getCgiData(&isOK, FALSE, q->sponsor   , sizeof(q->sponsor   ), "sponsor"   );
 getCgiData(&isOK, FALSE, q->reviewer  , sizeof(q->reviewer  ), "reviewer"  );
 getCgiData(&isOK, FALSE, q->extSource , sizeof(q->extSource ), "extSource" );
 
 /* blobs */
 getCgiData(&isOK, TRUE ,&q->tbls      , -1                  , "tbls"      );  
 getCgiData(&isOK, TRUE ,&q->files     , -1                  , "files"     );
 getCgiData(&isOK, TRUE ,&q->featureBits, -1                 , "featureBits");
 getCgiData(&isOK, TRUE ,&q->openIssues, -1                  , "openIssues");
 getCgiData(&isOK, TRUE ,&q->notes     , -1                  , "notes"     );
 getCgiData(&isOK, TRUE ,&q->releaseLog, -1                  , "releaseLog");
 getCgiData(&isOK, TRUE ,&q->releaseLogUrl, -1               , "releaseLogUrl");
 
 
 /* check for things too big  */
 if (!isOK)
     {
     isRedo = TRUE;
     }
 if ((q->sizeMB < 0) || (q->sizeMB > 1000000))
     {
     safef(msg,sizeof(msg),"Size(MB): invalid size. <br>\n");
     isRedo = TRUE;
     }
 if (!isDateValid(cgiString("qadate")))
     {
     safef(msg,sizeof(msg),"Date format invalid, should be YYYY-MM-DD. <br>\n");
     isRedo = TRUE;
     }
 if (strlen(cgiString("track"))>255)
     {
     safef(msg,sizeof(msg),"Track: too long for field, 255 char max. <br>\n");
     isRedo = TRUE;
     }
 if (strlen(cgiString("dbs"))>255)
     {
     safef(msg,sizeof(msg),"Database: too long for field, 255 char max. <br>\n");
     isRedo = TRUE;
     }
 if (strlen(cgiString("cgis"))>255)
     {
     safef(msg,sizeof(msg),"CGIs: too long for field, 255 char max. <br>\n");
     isRedo = TRUE;
     }
 if (strlen(cgiString("stat"))>255)
     {
     safef(msg,sizeof(msg),"Status: too long for field, 255 char max. <br>\n");
     isRedo = TRUE;
     }
 
 
 /* need to do this before delete or will lose the record */
 if ((sameString(pushbutton,"push requested"))&&(q->sizeMB==0))
     {
     safef(msg,sizeof(msg),"Size (MB) should not be zero. <br>\n");
     isRedo = TRUE;
     }
 
 if ((sameString(pushbutton,"push requested"))&&(!sameString(q->currLoc,"hgwbeta")))
     {
     safef(msg,sizeof(msg),"Current Location should be hgwbeta. <br>\n");
     isRedo = TRUE;
     }
 
 if ((sameString(pushbutton,"push requested"))&&(sameString(q->makeDocYN,"N")))
     {
     safef(msg,sizeof(msg),"MakeDoc not verified. <br>\n");
     isRedo = TRUE;
     }
 
 if ((sameString(pushbutton,"push requested"))&&(sameString(q->ndxYN,"N")))
     {
     safef(msg,sizeof(msg),"Index not verified. <br>\n");
     isRedo = TRUE;
     }
 
 if ((sameString(pushbutton,"push requested"))&&(sameString(q->joinerYN,"N")))
     {
     safef(msg,sizeof(msg),"All->Joiner not verified. <br>\n");
     isRedo = TRUE;
     }
 
 if ((sameString(bouncebutton,"bounce"))&&(!sameString(q->priority,"A")))
     {
     safef(msg,sizeof(msg),"Only priority A records should be bounced. <br>\n");
     isRedo = TRUE;
     }
 if ((sameString(bouncebutton,"unbounce"))&&(sameString(q->priority,"A")))
     {
     safef(msg,sizeof(msg),"Priority A records should not be unbounced. <br>\n");
     isRedo = TRUE;
     }
 
 
 if (isRedo)
     {
     replacePushQFields(q, isNew);  
     pushQFree(&q);
     return;
     }
 
 
 if (sameString(bouncebutton,"bounce")) 
     {
     safef(newPriority, sizeof(newPriority), "B");
     safef(q->lastdate, sizeof(q->lastdate), q->qadate);
     strftime (q->qadate, sizeof(q->qadate), "%Y-%m-%d", loctime); /* set to today's date */
     q->bounces++;
     }
 if (sameString(bouncebutton,"unbounce")) 
     {
     safef(newPriority, sizeof(newPriority), "A");
     safef(q->lastdate, sizeof(q->lastdate), q->qadate);
     strftime (q->qadate, sizeof(q->qadate), "%Y-%m-%d", loctime); /* set to today's date */
     }
 
 
 /* check if priority class has changed, or deleted, then close ranks */
 if ( (!sameString(newPriority,q->priority)) || (sameString(delbutton,"delete")) )
     {
     /* first close the hole where it was */
     safef(query, sizeof(query), 
     "update %s set rank = rank - 1 where priority ='%s' and rank > %d ", 
     pushQtbl, q->priority, q->rank);
     sqlUpdate(conn, query);
     }
 
 /* if not deleted, then if new or priority class change, then take last rank */
 if (!sameString(delbutton,"delete")) 
     {
     if ((!sameString(newPriority,q->priority)) || isNew)
 	{
 	q->rank = getNextAvailRank(newPriority);
 	safef(q->priority, sizeof(q->priority), newPriority);
 	}
     }
 
 if (q->priority[0]=='L') 
     {
     q->rank = 0;
     }
 
 
 if (sameString(pushbutton,"push requested")) 
     {
     /* reset pushState in case was prev-> a log already */
     safef(q->pushState,sizeof(q->pushState),"Y");
     }
 
 if (sameString(delbutton,"delete")) 
     {
     /* delete old record */
     safef(query, sizeof(query), "delete from %s where qid ='%s'", pushQtbl, q->qid);
     sqlUpdate(conn, query);
     }
 else
     {
     if (sameString(showSizes,"Show Sizes") || sameString(transfer,"Transfer")) 
 	{ /* mark record as locked */
 	safef(q->lockUser, sizeof(q->lockUser), qaUser);
 	strftime(q->lockDateTime, sizeof(q->lockDateTime), "%Y-%m-%d %H:%M", loctime);
 	}
     else
 	{ /* unlock record */
 	safef(q->lockUser, sizeof(q->lockUser), "%s", "");
 	safef(q->lockDateTime, sizeof(q->lockDateTime), "%s", "");
 	}
     if (isNew)
 	{
 	/* save new record */
 	safef(msg, sizeof(msg), "%%0%dd", (int)sizeof(q->qid)-1);
 	safef(newQid,sizeof(newQid),msg,newqid);
 	safef(q->qid, sizeof(q->qid), newQid);
     	safef(msg, sizeof(msg), "%s", "");
 	pushQSaveToDbEscaped(conn, q, pushQtbl, updateSize);
 	}
     else
 	{  
 	/* update existing record */
 	pushQUpdateEscaped(conn, q, pushQtbl, updateSize);
 	}
     }
 
 if (sameString(clonebutton,"clone")) 
     {  
     /* save new clone */
     safef(q->pqid,sizeof(q->pqid), q->qid);  /* daughter will point to parent */
     newqid = getNextAvailQid();
     safef(msg, sizeof(msg), "%%0%dd", (int)sizeof(q->qid)-1);
     safef(newQid,sizeof(newQid),msg,newqid);
     safef(q->qid, sizeof(q->qid), newQid);
     safef(msg, sizeof(msg), "%s", "");
     if (q->priority[0]=='L') 
 	{
 	q->rank = 0;
 	}
     else
 	{
 	q->rank = getNextAvailRank(q->priority);
 	}
     safef(q->pushState,sizeof(q->pushState),"N");  /* default to: push not done yet */
     pushQSaveToDbEscaped(conn, q, pushQtbl, updateSize);
     }
 
 
 if (sameString(showSizes,"Show Sizes")) 
     {
     cgiVarSet("qid", q->qid); /* for new rec */
     doShowSizes();
     }
 
 else if (sameString(transfer,"Transfer")) 
     {
     cgiVarSet("qid", q->qid); /* for new rec */
     doTransfer();
     }
 
 else if (sameString(submitbutton,"Submit")) 
     { /* if submit button, saved data, now return to readonly view->  */
     
     safef(msg, sizeof(msg), "Data saved->");
     
     cgiVarSet("qid", q->qid); /* for new rec */
     doEdit();
     }
 else
     {
     doDisplay();
     }
 
 pushQFree(&q);
 
 
 }
 
 
 
 void doEdit()
 /* Handle edit request for a pushQ entry */
 {
 int updateSize = 2456;
 struct pushQ *q;
 q = loadPushQ(cgiString("qid"));
 if (!q)
     {
     printf("Queue Id %s not found.", cgiString("qid"));
     return;
     }
 
 if ( sameString(qaUser,"kuhn") || 
      sameString(qaUser,"kuhn2") || 
      sameString(qaUser,"ann") 
      )  /* for users that want to automatically try to lock record immediately */
     {
     if (sameString(action,"edit") || sameString(action,"setSize")) 
 	{
 	if (sameString(q->lockUser,""))  /* q->lockUser blank if nobody has lock */
 	    {
 	    safef(q->lockUser, sizeof(q->lockUser), qaUser);
 	    strftime(q->lockDateTime, sizeof(q->lockDateTime), "%Y-%m-%d %H:%M", loctime);
 	    pushQUpdateEscaped(conn, q, pushQtbl, updateSize);
 	    }
 	}
     else /* we are coming back from a post? so return to display automatically */
 	{
 	doDisplay();
 	return;  /* this is needed? */
 	}
     }
     
 replacePushQFields(q, FALSE);  /* new rec = false */
 pushQFree(&q);
 }
 
 void doSetSize()
 /* save sizeMB */
 {
 struct pushQ *q;
 char tempSizeMB[10];
 int updateSize=2456;
 q = loadPushQ(cgiString("qid"));
 if (!q)
     {
     printf("Queue Id %s not found.", cgiString("qid"));
     return;
     }
 safef(tempSizeMB,sizeof(tempSizeMB), cgiUsualString("sizeMB",""));
 if (!sameString(tempSizeMB,""))
     {
     if (sscanf(tempSizeMB,"%u",&q->sizeMB) != 1)
 	{
 	q->sizeMB = 0;
 	}
     }
 pushQUpdateEscaped(conn, q, pushQtbl, updateSize);
 doEdit();
 pushQFree(&q);
 }
 
 
 void doLogin()
 /* make form for login */
 {
 printf("<h4>Login</h4>\n");
 
 printf("<FORM ACTION=\"/cgi-bin/qaPushQ\" NAME=\"loginForm\" METHOD=\"POST\">\n");
 
 printf("<input TYPE=\"hidden\" NAME=\"action\" VALUE=\"postLogin\"  >\n");
 
 printf("<TABLE cellpadding=6>\n");
 
 printf("<TR>\n");
 printf("<TD align=right>\n");
 printf("User:\n");
 printf("</TD>\n");
 printf("<TD>\n");
 printf("<INPUT TYPE=text NAME=user size = 8 value=\"\" >\n");
 printf("</TD>\n");
 printf("</TR>\n");
 
 printf("<TR>\n");
 printf("<TD align=right>\n");
 printf("Password:\n");
 printf("</TD>\n");
 printf("<TD>\n");
 printf("<INPUT TYPE=password NAME=password size=8 value=\"\" > <br>\n");
 printf("</TD>\n");
 printf("</TR>\n");
 
 printf("<TR>\n");
 printf("<TD align=right>\n");
 printf("&nbsp;\n");
 printf("</TD>\n");
 printf("<TD>\n");
 printf("<input TYPE=SUBMIT NAME=\"submit\" VALUE=\"Log In\">\n");
 printf("</TD>\n");
 printf("</TR>\n");
 
 printf("</TABLE>\n");
 
 if (sameString(utsName.nodename,"hgwdev"))
     {
     printf("<br><p style=\"color:red\">Machine: %s THIS IS NOT THE REAL PUSHQ- GO TO "
            "<a href=http://hgwbeta.cse.ucsc.edu/cgi-bin/qaPushQ>HGWBETA</a> </p>\n",utsName.nodename);
     }
 
 printf("</FORM>\n");
 
 printf("<br>\n");
 printf("%s<br>\n",msg);
 }
 
 void doLogoutMsg()
 /* let user know logged-out ok */
 {
 printf("<h4>Logged Out</h4>\n");
 printf("<FORM ACTION=\"/cgi-bin/qaPushQ\" NAME=\"logoutForm\" METHOD=\"POST\">\n");
 printf("<input TYPE=\"hidden\" NAME=\"action\" VALUE=\"login\"  >\n");
 printf("<TABLE cellpadding=6>\n");
 printf("<TR>\n");
 printf("<TD align=right>\n");
 printf("<input TYPE=SUBMIT NAME=\"submit\" VALUE=\"Re-Login\">\n");
 printf("</TD>\n");
 printf("</TR>\n");
 printf("</TABLE>\n");
 printf("</FORM>\n");
 }
 
 
 void doCookieReset()
 /* reset cookie, will cause new login next time */
 {
 htmlSetCookie("qapushq", "", NULL, NULL, ".cse.ucsc.edu", FALSE);
 htmShell(TITLE, doLogoutMsg, NULL);
 }
 
 
 
 
 bool readAUser(struct users *u, bool optional)
 /* read data for my user */
 {
 char query[256];
 char **row;
 struct sqlResult *sr;
 
 safef(query, sizeof(query), "select * from users where user = '%s'",u->user);
 sr = sqlGetResult(conn, query);
 row = sqlNextRow(sr);
 if (row == NULL)
     {
     if (optional)
 	{
 	sqlFreeResult(&sr);
 	return FALSE;
 	}
     else
 	{
 	errAbort("%s not found.",u->user);
 	}
     }
 else
     {
     usersStaticLoad(row,u);
     }
 sqlFreeResult(&sr);
 return TRUE;
 }
 
 
 void readMyUser()
 /* read data for my user */
 {
 int i = 1;  /* because it should start right off with ? */
 char tempVar[2048];
 char tempVarName[256];
 char tempVal[2048];
 
 if ((qaUser == NULL) || (sameString(qaUser,"")))
     {
     return;
     }
 safef(myUser.user,sizeof(myUser.user),qaUser);
 readAUser(&myUser, FALSE);
 
 while(parseList(myUser.contents,'?',i,tempVar,sizeof(tempVar)))
     {
 
     parseList(tempVar,'=',0,tempVarName,sizeof(tempVarName));
    
     parseList(tempVar,'=',1,tempVal,sizeof(tempVal));
    
     if (sameString(tempVarName,"showColumns"))
 	{
 	showColumns = cloneString(tempVal);
 	}
 
     if (sameString(tempVarName,"org"))
 	{
 	safef(pushQtbl,sizeof(pushQtbl),tempVal);
 	}
 
     if (sameString(tempVarName,"month"))
 	{
 	safef(month,sizeof(month),tempVal);
 	}
 
     if (sameString(tempVarName,"oldRandState"))
 	{
 	oldRandState = cloneString(tempVal);
 	}
 
     i++;
     }
 }
 
 void saveMyUser();  /* forward declaration */
 
 void doPostLogin()
 /* process Login post */
 {
 
 char *tbl = "users";
 char query[256];
 
 struct users u;
 
 char *userPassword = NULL;
 bool loginOK = FALSE;
 char *meta = ""
 "<head>"
 "<title>"
 "Meta Redirect Code"
 "</title>"
 "<meta http-equiv=\"refresh\" content=\"0;url=/cgi-bin/qaPushQ\">"
 "</head>";
 
 userPassword = cgiString("password");
 
 ZeroVar(&u);
 
 u.next = NULL;
 safef(u.user,  sizeof(u.user), cgiString("user"));
 
 conn = sqlConnectRemote(host, user, password, database);  /* do db conn here special for login */
 
 if (!readAUser(&u, TRUE))
     {
     /* unknown user not allowed */
     safef(msg,sizeof(msg),"Invalid user or password.");
     }
 else
     {
     if (strlen(u.password)==0) 
 	{ /* if pwd in db is blank, use this as their new password and encrypt it and save in db. */
 	if (strlen(userPassword) < 6)
 	    { /* bad pwd */
 	    safef(msg,sizeof(msg),"Invalid password. Password must be at least 6 characters long.");
 	    }
 	else
 	    {
 	    encryptNewPWD(userPassword, u.password, sizeof(u.password));  
 	    safef(query, sizeof(query), 
 		"update %s set password = '%s' where user = '%s' ", 
 		tbl, u.password, u.user);
 	    sqlUpdate(conn, query);
 	    loginOK = TRUE;
 	    }
 	}
     else
 	{ /* verify password matches db */
 	if (checkPWD(userPassword, u.password)) 
 	    { /* good pwd, save user in cookie */
 	    loginOK = TRUE;
 	    }
 	else
 	    { /* bad pwd */
 	    safef(msg,sizeof(msg),"Invalid user or password.");
 	    }
 	}
     }
     
 if (loginOK)
     {
     /* try to make same cookie work with both hgwdev and hgwbeta to obviate need for double-login */
     /* note: for permanent cookie, set NULL to expire in format "Wdy, DD-Mon-YYYY HH:MM:SS GMT" (must be GMT) */
     htmlSetCookie("qapushq", u.user, NULL, NULL, ".cse.ucsc.edu", FALSE);
    
     qaUser=u.user;
     oldRandState="";
     showColumns=cloneString(defaultColumns);
     readMyUser();
     oldRandState="";
     saveMyUser();
     safef(msg, sizeof(msg),"Login successful.");
     htmShellWithHead(TITLE, meta, doMsg ,NULL);  /* this is now the only redirect */
     }
 else
     {
     htmShell(TITLE, doLogin, NULL);
     }
 
 sqlDisconnect(&conn);
 }
 
 
 
 void saveMyUser()
 /* read data for my user */
 {
 char *tbl = "users";
 struct dyString * query = NULL;
 query = newDyString(2048);
 if ((qaUser == NULL) || (sameString(qaUser,"")))
     {
     return;
     }
 dyStringPrintf(query,  
     "update %s set contents = '?showColumns=%s?org=%s?month=%s?oldRandState=%s' where user = '%s'",
     tbl, showColumns, pushQtbl, month, oldRandState, myUser.user);
 sqlUpdate(conn, query->string);
 freeDyString(&query);
 }
 
 
 
 
 void XdoPromoteColumn(int change)
 /* Promote the column 
  * 1 = promote, 0 = hide, -1 = demote */
 {
 char target[256] = "";
 
 int i = 0;  
 char tempBefore[256] = "";
 char tempVal   [256] = "";
 char tempAfter [256] = "";
 char tempSwap  [256] = "";
 struct dyString * s = NULL;
 s = newDyString(2048);  /* need room */
 
 safef(target, sizeof(target), cgiString("col"));
 
 while(TRUE)
     { 
     parseList(showColumns,',',i,tempAfter,sizeof(tempAfter));
     if ((tempBefore[0]==0) && (tempVal[0]==0) && (tempAfter[0]==0))
 	{
 	break;
 	}
     if (sameString(target,tempVal))
 	{
 	
 	if (change==1)
 	    {
 	    /*  swap places with Before */
 	    safef(tempSwap  , sizeof(tempSwap  ), tempBefore);
 	    safef(tempBefore, sizeof(tempBefore), tempVal   );
 	    safef(tempVal   , sizeof(tempVal   ), tempSwap  );
 	    }
 	if (change==0)
 	    {
 	    /* remove */
 	    tempVal[0]=0;  /* set to empty string, output will be skipped */
 	    }
 	if (change==-1)
 	    {
 	    /* swap places with After */
 	    safef(tempSwap  , sizeof(tempSwap  ), tempAfter );
 	    safef(tempAfter , sizeof(tempAfter ), tempVal   );
 	    safef(tempVal   , sizeof(tempVal   ), tempSwap  );
 	    }
 	    
 	change = 99;  /* just suppress any more changes */
 	
 	}
     if (!sameString(tempBefore,""))
 	{
 	dyStringPrintf(s, "%s,", tempBefore);
 	}
     /* roll 'em! */
     safef(tempBefore, sizeof(tempBefore), tempVal  );
     safef(tempVal   , sizeof(tempVal)   , tempAfter);
     i++;
     }
 
 showColumns = cloneString(s->string);
 freeDyString(&s);
 
 showColumns[strlen(showColumns)-1]=0;  /* chop off trailing comma */
 
 doDisplay();
 
 }
 
 
 void doPromoteColumn()
 {
 XdoPromoteColumn(1);
 }
 
 void doHideColumn()
 {
 XdoPromoteColumn(0);
 }
 
 void doDemoteColumn()
 {
 XdoPromoteColumn(-1);
 }
 
 
 
 
 void doShowAllColumns()
 /* Display hidden columns available for resurrection */ 
 {
 int c = 0;
 char templist[512];
 char tempe[64];
 
 printf("<h4>Show Hidden Columns</h4>\n");
 printf("<br>\n");
 printf("<a href=\"/cgi-bin/qaPushQ?cb=%s\">RETURN</a><br>", newRandState); 
 printf("<br>\n");
 printf("Click on any column below to un-hide it.<br>\n");
 printf("<br>\n");
 
 safef(templist,sizeof(templist),",%s,",showColumns);  /* add sentinel comma values to the ends of the col list */
 
 for (c=0; c<e_NUMCOLS; c++)
     {
     safef(tempe,sizeof(tempe),",%s,",colName[c]);  /* add sentinel comma values to the ends of the col element */
     if (strstr(templist,tempe)==NULL)
 	{
 	printf("<a href=\"/cgi-bin/qaPushQ?action=showColumn&colName=%s&cb=%s\">%s</a><br><br>", 
 	    colName[c], newRandState, colName[c]);
 	}
     }
 
 printf("<br>\n");
 printf("<a href=\"/cgi-bin/qaPushQ?cb=%s\">RETURN</a><br>", newRandState); 
 }
 
 
 void doShowColumn()
 /* Make column visible again */
 {
 struct dyString * s = NULL;
 char *colName = NULL;
 char templist[512];
 char tempe[64];
 
 s = newDyString(2048);  /* need room */
 
 colName = cgiString("colName");
 
 mapFieldToEnum(colName,TRUE);  /* this will make sure it exists or errAbort */
 
 safef(templist,sizeof(templist),",%s,",showColumns);  /* add sentinel comma values to the ends of the col list */
 safef(tempe,sizeof(tempe),",%s,",colName);  /* add sentinel comma values to the ends of the col element */
 if (strstr(templist,tempe)==NULL)  /* make sure not already in list */
     {
     dyStringAppend(s, showColumns);
     dyStringPrintf(s, ",%s", colName);
     showColumns = cloneString(s->string);
     }
 
 freeDyString(&s);
 
 doDisplay();
 }
 
 
 void doShowDefaultColumns()
 /* Show Default Columns in default order */
 {
 showColumns = cloneString(defaultColumns);
 doDisplay();
 }
 
 
 
 void doShowMonths()
 /* This gives the user a choice of months to filter on */
 {
 
 struct sqlResult *sr;
 char **row;
 char query[256];
 
 printf("<h4>Logs for Month</h4>\n");
 printf("<br>\n");
 printf("<A href=qaPushQ?action=display&month=current&cb=%s>Current</A><br>\n", newRandState);
 printf("<br>\n");
 
 safef(query, sizeof(query), "select distinct substring(qadate,1,7) from %s where priority='L' order by qadate desc",pushQtbl);
 sr = sqlGetResult(conn, query);
 while ((row = sqlNextRow(sr)) != NULL)
     {
     printf("<A href=qaPushQ?action=display&month=%s&cb=%s>%s</A><br>\n",row[0],newRandState,row[0]);
     }
 sqlFreeResult(&sr);
 printf("<br>\n");
 printf("<a href=\"/cgi-bin/qaPushQ?cb=%s\">RETURN</a><br>", newRandState); 
 }
 
 
 
 void getIndexes(struct sqlConnection *conn, char *tbl, char *s, int ssize)
 /* Get indexes with show index on table command. Return -1 if err. Will match multiple if "%" used in tbl */ 
 {
 char query[256];
 char **row;
 struct sqlResult *sr;
 char *fld = NULL;
 int f = 0, i = 0, n = 0, c = 0;
 char lastKeyName[256]="";
 
 
 safef(query, sizeof(query), "show index from %s",tbl);
 sr = sqlGetResult(conn, query);
 f = 0;
 i = 0;
 n = 0;
 if (ssize > 0) s[0]=0;
 while ((fld = sqlFieldName(sr)) != NULL)
     {
     if (sameString(fld,"Key_name"))
 	{
 	n = f;
 	}
     if (sameString(fld,"Column_name"))
 	{
 	i = f;
 	}
     f++;
     }
 while ((row = sqlNextRow(sr)) != NULL)
     {
     c++;
     if (sameString(row[n],lastKeyName))
     	{
 	strcat(s,"+");
 	strcat(s,row[i]);
 	}
     else
 	{
 	if (c > 1) 
 	    {
 	    strcat(s,", ");
 	    }
 	strcat(s, row[i]);
 	safef(lastKeyName,sizeof(lastKeyName),row[n]);
 	}
     }
 sqlFreeResult(&sr);
 sqlDisconnect(&conn);
 }
 
 
 
 void mySprintWithCommas(char *s, int slength, long long size)
 /* the one in obscure.c was overflowing, so here's mine  */
 {
 char *temp=NULL;
 char sep[2]="";
 s[0]=0;
 temp = needMem(slength);
 while (size >= 1000)
     {
     safef(temp,slength,s);
     safef(s,slength,"%03d%s%s",(int)(size%1000),sep,temp);
     size/=1000;
     safef(sep,sizeof(sep),",");
     }
 if (size > 0)
     {
     safef(temp,slength,s);
     safef(s,slength,"%3d%s%s",(int)size,sep,temp);
     }
 else 
     {
     safef(s,slength,"0");  /* special case zero*/
     }
 freez(&temp);
 }
 
 
+void mySprintWithGreekByte(char *s, int slength, long long size)
+/* Numbers formatted with PB, TB, GB, MB, KB, B */
+{
+char *greek[] = {"B", "KB", "MB", "GB", "TB", "PB"};
+int i = 0;
+long long d = 1;
+while ((size/d) >= 1024)
+    {
+    ++i;
+    d *= 1024;
+    }
+double result = ((double)size)/d;
+if (result < 10)
+    safef(s,slength,"%3.1f %s",((double)size)/d, greek[i]);
+else
+    safef(s,slength,"%3.0f %s",((double)size)/d, greek[i]);
+}
+
+
 
 long long pq_getTableSize(char *rhost, char *db, char *tbl, int *errCount)  /* added extension pq_ to supress name conflict in hdb.c */
 /* Get table size via show table status command. Return -1 if err. Will match multiple if "%" used in tbl */ 
 {
 char query[256];
 char **row;
 struct sqlResult *sr;
 char *fld = NULL;
 int f = 0, d = 0, i = 0, n = 0, c = 0;
 unsigned long size = 0;
 long long totalsize = 0;
 char nicenumber[256]="";
 char  indexlist[256]="";
 
 char *host     = NULL;
 char *user     = NULL;
 char *password = NULL;
 
 struct sqlConnection *conn = NULL;
 
 host     = cfgOption("db.host"    );
 user     = cfgOption("db.user"    );
 password = cfgOption("db.password");
 
 if ((sameString(utsName.nodename,"hgwbeta")) && (sameString(rhost,"hgwdev")))
     {
     host     = "hgwdev";
     user     = cfgOption("db.user"    );
     password = cfgOption("db.password");
     }
 	
 if ((sameString(utsName.nodename,"hgwdev")) && (sameString(rhost,"hgwbeta")))
     {  // inaccurate but doesn't matter since we only use it to test qaPushQ cgi on dev.
     host     = cfgOption("central.host"    );
     user     = cfgOption("central.user"    );
     password = cfgOption("central.password");
     }
 
 conn = sqlConnectRemote(host, user, password, db);
 
 safef(query, sizeof(query), "show table status like '%s'",tbl);
 sr = sqlGetResult(conn, query);
 f = 0;
 d = 0;
 i = 0;
 n = 0;
 while ((fld = sqlFieldName(sr)) != NULL)
     {
     if (sameString(fld,"Name"))
 	{
 	n = f;
 	}
     if (sameString(fld,"Data_length"))
 	{
 	d = f;
 	}
     if (sameString(fld,"Index_length"))
 	{
 	i = f;
 	}
     f++;
     }
 while ((row = sqlNextRow(sr)) != NULL)
     {
     c++;
     printf("<tr>");
     printf("<td>%s</td>",row[n]);
     
     sscanf(row[d],"%lu",&size);
     totalsize+=size;
     mySprintWithCommas(nicenumber, sizeof(nicenumber), size);
     printf("<td align=right>%s</td>",nicenumber);
 	
     sscanf(row[i],"%lu",&size);
     totalsize+=size;
     mySprintWithCommas(nicenumber, sizeof(nicenumber), size);
     printf("<td align=right>%s</td>",nicenumber);
 	
     getIndexes( sqlConnectRemote(host, user, password, db), row[n], indexlist, sizeof(indexlist));
     printf("<td>%s</td>",indexlist);
 	
     printf("</tr>\n");
     }
 sqlFreeResult(&sr);
 
 if (c == 0)
     {
     printf("<tr><td style=\"color:red\">%s</td><td style=\"color:red\">%s</td></tr>\n",tbl,"error fetching");
     ++(*errCount);
     }
 
 sqlDisconnect(&conn);
 return totalsize;
 }
 
 void cutParens(char *s)
 /* internally modify string to remove parens and anything they contain.  nested ok, but must be balanced. */
 {
 int i=0,j=0,l=0,c=0;
 l=strlen(s);
 for(i=0;i<=l;i++)
     {
     switch (s[i])
 	{
 	case '(': c++; break;
 	case ')': c--; break;
 	case 0: c = 0;
 	default:
 	    if (c==0)
 		{
 		s[j++]=s[i];
 		}
 	}
     }
 
 }
 
 void whiteSpace(char *s)
 /* internally modify string to convert whitespace to space chars. */
 {
 char *ss = NULL;
 int i=0,ii=0,l=0;
 char c=' ';
 l=strlen(s);
 ss =needMem(l+1);
 for(i=0;i<=l;i++)
     {
     c = s[i];
     switch (c)
 	{
 	case '\t' : 
 	case '\n' :
 	case '\r' :
 	case '\f' :
 	  c = ' ';
 	default:
 	    ss[ii++]=c;
 	}
     }
 safef(s, l+1, "%s", ss);
 }
 
 #define BLSIZE 100000  /* size of strings for processing big lists of tables and files */
 
 void doShowSizes()
 /* show the sizes of all the track tables, cgis, and general files in separate window target= _blank  */
 {
 char tbl[BLSIZE] = "";
 char  db[1024] = "";
 off_t size = 0;
 off_t totalsize = 0;
 unsigned long sizeMB = 0;
 int errCount = 0;
 off_t totalTable = 0;
 off_t totalGbdb = 0;
 off_t totalGoldenPath = 0;
 int i = 0, ii = 0, iii = 0;
 int j = 0, jj = 0, jjj = 0;
 int g = 0, gg = 0, ggg = 0;
 char nicenumber[1024]="";
 struct pushQ *q;
 char newQid[sizeof(q->qid)] = "";
 
 char dbsComma[1024];
 char dbsSpace[1024];
 char dbsVal[1024];
 char d;
 
 char tempComma[BLSIZE];
 char tempSpace[BLSIZE];
 char tempVal[BLSIZE];
 char c;
 
 char gComma[BLSIZE];
 char gSpace[BLSIZE];
 char gVal[BLSIZE];
 char gc;
 char cgiPath[1024];
 char pathName[1024];
 char filePath[1024];
 char fileName[1024];
 char *found=NULL;
 struct fileInfo *fi = NULL;
 char *crossUrl = "";
 
 safef(newQid, sizeof(newQid), cgiString("qid"));
 
 printf("<H2>Show File Sizes </H2>\n");
 
 q = mustLoadPushQ(newQid); 
 
 if (crossPost)  // support showSizes across machines
     {
     crossUrl = sameString(utsName.nodename, "hgwdev") ? "http://hgwbeta.cse.ucsc.edu" : "http://hgwdev.cse.ucsc.edu";
     }
 
 printf("<a href=\"%s/cgi-bin/qaPushQ?action=showSizesHelp&qid=%s&cb=%s\" target=\"_blank\">HELP</a> \n",
     crossUrl,q->qid,newRandState);
 printf("<a href=\"%s/cgi-bin/qaPushQ?action=edit&qid=%s&cb=%s\">RETURN</a> \n",crossUrl,newQid,newRandState);
 printf(" <br>\n");
 printf("Location: %s <br>\n",q->currLoc);
 printf("Database: %s <br>\n",q->dbs    );
-printf("  Tables: %s <br>\n",q->tbls   );
-printf("    CGIs: %s <br>\n",q->cgis   );
-printf("   Files: %s <br>\n",q->files  );
-printf(" <br>\n");
+/* deemed too verbose: */
+//printf("  Tables: %s <br>\n",q->tbls   );
+//printf("    CGIs: %s <br>\n",q->cgis   );
+//printf("   Files: %s <br>\n",q->files  );
 
 cutParens(q->dbs);
 cutParens(q->tbls);
 cutParens(q->cgis);
 cutParens(q->files);
 
 whiteSpace(q->dbs);
 whiteSpace(q->tbls);
 whiteSpace(q->cgis);
 whiteSpace(q->files);
 
 q->tbls = replaceChars(q->tbls,"chrN_","chr*_");
 q->tbls = replaceChars(q->tbls,"\\","\\\\");
 q->tbls = replaceChars(q->tbls,"%","\\%");
 q->tbls = replaceChars(q->tbls,"_","\\_");
 
 for(j=0;parseList(q->dbs, ',' ,j,dbsComma,sizeof(dbsComma));j++)
     {
     if (dbsComma[0]==0) 
 	{
 	continue;
 	}
     for(jj=0;parseList(dbsComma, ' ' ,jj,dbsSpace,sizeof(dbsSpace));jj++)
 	{
 	if (dbsSpace[0]==0) 
 	    {
 	    continue;
 	    }
 	dbsVal[0]=0;
 	for (jjj=0;jjj<=strlen(dbsSpace);jjj++)
 	    {
 	    d = dbsSpace[jjj];
 	    if (
 		((d>='A')&&(d<='Z'))
 	     || ((d>='a')&&(d<='z'))
 	     || ((d>='0')&&(d<='9'))
 	     )
 		{
 		dbsVal[jjj]=d;
 		}
 	    else
 		{
 		dbsVal[jjj]=0;
 		break;
 		}
 	    }
 	if (dbsVal[0]!=0) 
 	    {
 	    safef(db,sizeof(db),"%s",dbsVal);
 
 	    printf(" <br>\n");
 	    printf("<h4>%s on %s:</h4>\n",db,q->currLoc);
 	    printf("<table cellpadding=5 >");
 	    printf("<th>Table</th>"
 	           "<th>data size</th>"
 	           "<th>index size</th>"
 	           "<th>index keys</th>"
 	    );
 
 	    /* we parsed the db multiples, now parse the tbl mutiples  */
 	    for(i=0;parseList(q->tbls, ',' ,i,tempComma,sizeof(tempComma));i++)
 		{
 		if (tempComma[0]==0) 
 		    {
 		    continue;
 		    }
 		for(ii=0;parseList(tempComma, ' ' ,ii,tempSpace,sizeof(tempSpace));ii++)
 		    {
 		    if (tempSpace[0]==0) 
 			{
 			continue;
 			}
 		    tempVal[0]=0;
 		    for (iii=0;iii<=strlen(tempSpace);iii++)
 			{
 			c = tempSpace[iii];
 			if (c=='*') c = '%';
 			if (
 			    ((c>='A')&&(c<='Z'))
 			 || ((c>='a')&&(c<='z'))
 			 || ((c>='0')&&(c<='9'))
 			 || (c=='_')
 			 || (c=='%')
 			 || (c=='\\')
 			)
 			    {
 			    tempVal[iii]=c;
 			    }
 			else
 			    {
 			    tempVal[iii]=0;
 			    break;
 			    }
 			}
 		    if (tempVal[0]!=0) 
 			{
 			safef(tbl,sizeof(tbl),"%s",tempVal);
 			long long tableSize = pq_getTableSize(q->currLoc,db,tbl,&errCount);
 			totalsize += tableSize;
 			totalTable += tableSize;
 			}
 		    }
 		}
 
 	    printf("</table>");
 	    }   
 	 }
      }
 
 
 if (!sameString(q->cgis,""))
     {
     printf(" <br>\n");
     printf("<h4>CGIs on %s:</h4>\n",utsName.nodename);
     printf("<table cellpadding=5 >");
     printf("<th>cgi</th><th># bytes</th>");
     for(g=0;parseList(q->cgis, ',' ,g,gComma,sizeof(gComma));g++)
 	{
 	if (gComma[0]==0) 
 	    {
 	    continue;
 	    }
 	for(gg=0;parseList(gComma, ' ' ,gg,gSpace,sizeof(gSpace));gg++)
 	    {
 	    if (gSpace[0]==0) 
 		{
 		continue;
 		}
 	    gVal[0]=0;
 	    for (ggg=0;ggg<=strlen(gSpace);ggg++)
 		{
 		gc = gSpace[ggg];
 		if (
 		    ((gc>='A')&&(gc<='Z'))
 		 || ((gc>='a')&&(gc<='z'))
 		 || ((gc>='0')&&(gc<='9'))
 		 || (gc=='.')
 		 )
 		    {
 		    gVal[ggg]=gc;
 		    }
 		else
 		    {
 		    gVal[ggg]=0;
 		    break;
 		    }
 		}
 	    if (gVal[0]!=0) 
 		{
 		safef(cgiPath,sizeof(cgiPath),"%s%s","./",gVal);
 		size=fileSize(cgiPath);
 		if (size == -1)
 		    {
 		    printf("<tr><td style=\"color:red\">%s<td/><td style=\"color:red\">not found</td></tr>\n",gVal);
 		    ++errCount;
 		    }
 		else
 		    {
 		    totalsize+=size;
 		    sprintLongWithCommas(nicenumber, size);
 		    printf("<tr><td>%s<td/><td>%s</td></tr>\n",gVal,nicenumber);
 		    }
 		}   
 	     }
 	 }
     printf("</table>");
     }
 
 if (!sameString(q->files,""))
     {
     printf(" <br>\n");
     printf("<h4>Files on %s:</h4>\n",utsName.nodename);
     printf("<table cellpadding=5 >");
     printf("<th>file</th><th># bytes</th>");
     for(g=0;parseList(q->files, ',' ,g,gComma,sizeof(gComma));g++)
 	{
 	if (gComma[0]==0) 
 	    {
 	    continue;
 	    }
 	for(gg=0;parseList(gComma, ' ' ,gg,gSpace,sizeof(gSpace));gg++)
 	    {
 	    if (gSpace[0]==0) 
 		{
 		continue;
 		}
 	    gVal[0]=0;
 	    for (ggg=0;ggg<=strlen(gSpace);ggg++)
 		{
 		gc = gSpace[ggg];
 		if (
 		    ((gc>='A')&&(gc<='Z'))
 		 || ((gc>='a')&&(gc<='z'))
 		 || ((gc>='0')&&(gc<='9'))
 		 || (gc=='.')
 		 || (gc=='/')
 		 || (gc=='-')
 		 || (gc=='_')
 		 || (gc=='*')
 		 )
 		    {
 		    gVal[ggg]=gc;
 		    }
 		else
 		    {
 		    gVal[ggg]=0;
 		    break;
 		    }
 		}
 	
 	    if (gVal[0]!=0) 
 		{
 
 		if (strrchr(gVal, '*') == NULL)
 		    { /* no wildcards in filename, do it the normal way */
 		    safef(pathName,sizeof(pathName),"%s",gVal);
 		    size=fileSize(pathName);
 		    if (size == -1)
 			{
 			printf("<tr><td style=\"color:red\">%s<td/><td style=\"color:red\">not found</td></tr>\n",gVal);
 			++errCount;
 			}
 		    else
 			{
 			totalsize+=size;
 			sprintLongWithCommas(nicenumber, size);
 			printf("<tr><td>%s<td/><td>%s</td></tr>\n",gVal,nicenumber);
 			if (startsWith("/gbdb/", pathName))
 			    totalGbdb+=size;
 			if (stringIn("/goldenPath/", pathName))
     			    totalGoldenPath+=size;
 			}
 		    }
 		else
 		    { /* wildcards found in name, use listDirX */
 		    printf("<tr><td>expansion for %s<td/></tr>\n",gVal);
 		    found = strrchr(gVal, '/');
 		    if (found == NULL) 
 			{
 			filePath[0]=0;
 			safef(fileName,sizeof(fileName),"%s",gVal);
 			}
 		    else
 			{
 			*found = 0;
 			safef(filePath,sizeof(filePath),"%s",gVal);
 			found++;
 			safef(fileName,sizeof(fileName),"%s",found);
 			}
 		    for (fi = listDirXExt(filePath,fileName,FALSE,TRUE);fi!=NULL;fi=fi->next)
 			{
 			if (fi->statErrno > 0)  // usually due to bad symlink
 			    {
 			    char *errStr = htmlEncode(strerror(fi->statErrno));
     			    printf("<tr><td style=\"color:red\">%s<td/>"
 				"<td style=\"color:red\">stat() failed: %s</td></tr>\n"
 				, fi->name 
 				, errStr);
 			    freeMem(errStr);
 			    ++errCount;
 			    }
 			else if (fi->isDir)
 			    {
 			    printf("<tr><td style=\"color:red\">error: %s is a directory<td/></tr>\n",fi->name);
 			    ++errCount;
 			    }
 			else
 			    {
     			    totalsize+=fi->size;
 			    if (startsWith("/gbdb/", filePath))
 				totalGbdb+=fi->size;
     			    if (stringIn("/goldenPath/", filePath))
 				totalGoldenPath+=fi->size;
     			    sprintLongWithCommas(nicenumber, fi->size);
     			    printf("<tr><td>%s<td/><td>%s</td></tr>\n",fi->name,nicenumber);
 			    }
 			}
 		    printf("<tr><td>&nbsp;<td/></tr>\n"); /* spacer */
 		    }
 		    
 		}   
 	     }
 	 }
     printf("</table>");
     }
 
 if (totalTable > 0)
     {
     printf(" <br>\n");
     mySprintWithCommas(nicenumber, sizeof(nicenumber), totalTable);
-    printf(" Total size of tables: %s <br>\n",nicenumber);
+    printf(" Total size of tables: %s ",nicenumber);
+    mySprintWithGreekByte(nicenumber, sizeof(nicenumber), totalTable);
+    printf("&nbsp; ( %s ) <br>\n",nicenumber);
     }
 
 if (totalGbdb > 0)
     {
     printf(" <br>\n");
     mySprintWithCommas(nicenumber, sizeof(nicenumber), totalGbdb);
-    printf(" Total size of /gbdb/ files: %s <br>\n",nicenumber);
+    printf(" Total size of /gbdb/ files: %s ",nicenumber);
+    mySprintWithGreekByte(nicenumber, sizeof(nicenumber), totalGbdb);
+    printf("&nbsp; ( %s ) <br>\n",nicenumber);
     }
 
 if (totalGoldenPath > 0)
     {
     printf(" <br>\n");
     mySprintWithCommas(nicenumber, sizeof(nicenumber), totalGoldenPath);
-    printf(" Total size of .../goldenPath/ files: %s <br>\n",nicenumber);
+    printf(" Total size of .../goldenPath/ files: %s ",nicenumber);
+    mySprintWithGreekByte(nicenumber, sizeof(nicenumber), totalGoldenPath);
+    printf("&nbsp; ( %s ) <br>\n",nicenumber);
     }
 
 printf(" <br>\n");
 mySprintWithCommas(nicenumber, sizeof(nicenumber), totalsize);
-printf(" Total size of all: %s <br>\n",nicenumber);
+printf(" Total size of all: %s ",nicenumber);
+mySprintWithGreekByte(nicenumber, sizeof(nicenumber), totalsize);
+printf("&nbsp; ( %s ) <br>\n",nicenumber);
 
 
 printf(" <br>\n");
 sizeMB = (((totalsize * 1.0) / (1024 * 1024)) + 0.5);
 if ((sizeMB == 0) && (totalsize > 0))
     {
     sizeMB = 1;
     }
 sprintLongWithCommas(nicenumber, sizeMB );
 printf("<p style=\"color:red\">Total: %s MB",nicenumber);
 if (errCount)
     {
     printf("&nbsp;&nbsp; Errors: %d",errCount);
     }
 printf("</p>\n");
 
 
 printf(" <br>\n");
 printf("<a href=\"%s/cgi-bin/qaPushQ?action=setSize&qid=%s&sizeMB=%lu&cb=%s\">"
        "Set Size as %s MB</a> <br>\n",crossUrl,newQid,sizeMB,newRandState,nicenumber);
 printf(" <br>\n");
 printf("<a href=\"%s/cgi-bin/qaPushQ?action=edit&qid=%s&cb=%s\">RETURN</a> <br>\n",crossUrl,newQid,newRandState);
 
 pushQFree(&q);
 
 }
 
 
 void listQueues(char *action, boolean isTransfer);  /* forward decl */
 
 void doTransfer()
 /* Present choice of queues to which the selected and locked record may be transferred */
 {
 struct pushQ *q;
 char newQid[sizeof(q->qid)] = "";
 char tempUrl[256];
 
 ZeroVar(&q);
 
 safef(newQid, sizeof(newQid), cgiString("qid"));
 
 printf("<H2>Transfer Queue Entry %s:%s to Another Queue </H2>\n", pushQtbl, newQid);
 
 q=mustLoadPushQ(newQid); 
 
 printf("<a href=\"/cgi-bin/qaPushQ?action=edit&qid=%s&cb=%s\">RETURN</a> \n",newQid,newRandState);
 printf("<br>\n");
 printf("<br>\n");
 
 safef(tempUrl, sizeof(tempUrl), "action=transferTo&qid=%s&toOrg", newQid);
 listQueues(tempUrl, TRUE);
 
 printf("<a href=\"/cgi-bin/qaPushQ?action=edit&qid=%s&cb=%s\">RETURN</a> <br>\n",newQid,newRandState);
 pushQFree(&q);
 }
 
 
 void doTransferTo()
 /* Execute transfer of qid from current pushQtbl to named target Q table */
 {
 struct pushQ *q;
 int updateSize = 2456; /* almost anything works here */
 char *toOrg = cgiString("toOrg");  /* required cgi var */
 char *origQid = NULL;
 int newqid = 0;
 char newQid     [sizeof(q->qid)]      = "";
 char *savePushQtbl = cloneString(pushQtbl);
 char query[256];
 
 origQid = cloneString(cgiString("qid"));  /* required cgi var */
 
 /* get the data from the record */
 q = mustLoadPushQ(origQid);
 
 /* first close the hole where it was */
 safef(query, sizeof(query),
 "update %s set rank = rank - 1 where priority ='%s' and rank > %d ",
 pushQtbl, q->priority, q->rank);
 sqlUpdate(conn, query);
 
 /* delete old record */
 safef(query, sizeof(query), "delete from %s where qid ='%s'", pushQtbl, origQid);
 sqlUpdate(conn, query);
 
 /* temporarily set pushQtbl to target */
 safef(pushQtbl, sizeof(pushQtbl), "%s",toOrg);
 
 /* unlock record - don't want it to keep the lock status after xfer */
 safef(q->lockUser, sizeof(q->lockUser), "%s","");
 safef(q->lockDateTime, sizeof(q->lockDateTime), "%s","");
 
 /* get the maxQid from the target Q tbl */
 newqid = getNextAvailQid();
 safef(msg, sizeof(msg), "%%0%dd", (int)sizeof(q->qid)-1);
 safef(newQid,sizeof(newQid),msg,newqid);
 safef(q->qid, sizeof(q->qid), newQid);
 safef(msg, sizeof(msg), "%s", "");
 
 /* get the maxrank from the target Q tbl for given priority */
 if (q->priority[0]=='L')
     {
     q->rank = 0;
     }
 else
     {
     q->rank = getNextAvailRank(q->priority);
     }
 
 /* clear parent link since can not be maintained */
 safef(q->pqid, sizeof(q->pqid), "%s", "");
 
 /* save record into new target Q */
 pushQSaveToDbEscaped(conn, q, pushQtbl, updateSize);
 
 /* restore pushQtbl */
 safef(pushQtbl, sizeof(pushQtbl), "%s", savePushQtbl);
 
 /* set msg to display the new Qid to the user, visible on return */
 safef(msg,sizeof(msg),"Transferred %s Qid %s to %s Qid %s. <br>\n", pushQtbl, origQid, toOrg, q->qid);
 
 doDisplay();
 
 freez(&origQid);
 freez(&savePushQtbl);
 pushQFree(&q);
 }
 
 
 
 
 void doShowDisplayHelp()
 /* show the sizes of all the track tables, cgis, and general files in separate window target= _blank  */
 {
 printf("<h4>Display Help</h4>\n");
 printf("<br>\n");
 printf("NOTE: DO NOT USE BACK-BUTTON OR REFRESH ON YOUR BROWSER. <br>\n");
 printf("<br>\n");
 printf(" &nbsp;&nbsp;&nbsp;   NAVIGATION BUTTONS AND LINKS ARE PROVIDED. <br>\n");
 printf("<br>\n");
 printf("ADD - add a new Push Queue record.<br>\n");
 printf("Logout - clears your cookie and logs out.<br>\n");
 printf("All Columns - allows you to bring back hidden columns.<br>\n");
 printf("Default Columns - reset your column preferences to default columns and order.<br>\n");
 printf("Log by Month - view old log records by month<br>\n");
 printf("Gateway - click to select alternate push queues, if any, e.g. new track/org<br>\n");
 printf("HELP - click to see this help.<br>\n");
 printf("Refresh - click to see if others have made changes.<br>\n");
 printf("<br>\n");
 printf("! - click to hide a column you do not wish to see.<br>\n");
 printf("< - click to move the column to the left.<br>\n");
 printf("> - click to move the column to the right.<br>\n");
 printf("<br>\n");
 printf("^ - click to raise the priority of the record higher within the priority-class.<br>\n");
 printf("v - click to lower the priority.<br>\n");
 printf("T - click to raise to top priority.<br>\n");
 printf("B - click to lower to bottom priority.<br>\n");
 printf("<br>\n");
 printf("Queue Id - click to edit or see the details page for the record.<br>\n");
 printf("<br>\n");
 printf("<a href=\"javascript:window.close();\" >CLOSE</a> <br>\n");
 }
 
 
 void doShowEditHelp()
 /* show the sizes of all the track tables, cgis, and general files in separate window target= _blank  */
 {
 struct pushQ q;
 ZeroVar(&q);
 safef(q.qid,sizeof(q.qid),cgiString("qid"));
 printf("<h4>Details/Edit Help</h4>\n");
 printf("<br>\n");
 printf("CANCEL - click to return to main display without saving changes.<br>\n");
 printf("HELP - click to see this help.<br>\n");
 printf("<br>\n");
 printf("Initial submission - displays date automatically generated when push queue record is created.<br>\n");
 printf("Date Opened - date QA (re)opened. (YYYY-MM-DD) Defaults originally to current date to save typing.<br>\n");
 printf("New track? - choose Y if this is a new track (i.e. has never before appeared on beta).<br>\n");
 printf("Track - enter the track name as it will appear in the genome browser (use the shortLabel).<br>\n");
 printf("Release Log- enter the short Label (usually) followed by notes in parentheses if any. This appears in the release log unless empty.<br>\n");
 printf("Databases - enter db name. May be comma-separated list if more than one organism, etc.<br>\n");
 printf("Tables - enter as comma-separated list all tables that apply. They must exist in the database specified. Wildcard * supported. (Put comments in parentheses).<br>\n");
 printf("CGIs - enter names of any new cgis that are applicable. Must be found on hgwbeta.<br>\n");
 printf("Files - enter pathnames of any additional files if needed.<br>\n");
 printf("Size(MB) - enter the size of the total push in megabytes (MB).<br>\n");
 printf("Show Sizes button - click to see a complete list of sizes of all tables and cgis.  Tables are relative to Current Location specified.<br>\n");
 printf("Current Location - chooose the current location of the files.  Should default to hgwdev at start, after sudo mypush to hgwbeta, change this to hgwbeta.<br>\n");
 printf("Makedoc verified? - choose Y if you have verified the MakeAssembly.doc in kent/src/hg/makeDb.<br>\n");
 printf("Online help - enter status of online help. Verify <a href=http://hgwbeta.cse.ucsc.edu/goldenPath/help/hgTracksHelp.html#IndivTracks>hgTracksHelp</a><br>\n");
 printf("Index verified? - choose Y if the index has been verified. Use the ShowSizes button for a quick view.<br>\n");
 printf("All.joiner verified? - choose Y if the all.joiner in /hg/makeDb/schema has been verified.<br>\n");
 printf("Status - enter current status (255 char max). Put long notes in Open Issues or Notes.<br>\n");
 printf("Sponsor - usually the developer.<br>\n");
 printf("Reviewer - usually the QA person handling the push queue for the track.<br>\n");
 printf("External Source or Collaborator - external contact outside our staff that may be involved.<br>\n");
 printf("Open Issues - Record any remaining open issues that are not completely resolved (no size limit here).<br>\n");
 printf("Notes - Any other notes you would like to make (no size limit here).<br>\n");
 printf("<br>\n");
 printf("Submit button - save changes and return to main display.<br>\n");
 printf("delete button - delete this push queue record and return to main display.<br>\n");
 printf("push requested button - press only if you are QA staff and about to submit the push-request. It will try to verify that required entries are present.<br>\n");
 printf("clone button - press if you wish to split the original push queue record into multiple parts. Saves typing, used rarely.<br>\n");
 printf("bounce button - press to bounce from priority A, the QA queue, to B, the developer queue if it needs developer attention.<br>\n");
 printf("transfer button - press to transfer the pushQ entry to another queue.<br>\n");
 printf("lock - press lock to lock the record and edit it.  When in edit mode, make your changes and submit.  Do not leave the record locked.<br>\n");
 printf("<br>\n");
 printf("<a href=\"javascript:window.close();\">CLOSE</a> <br>\n");
 }
 
 
 void doShowSizesHelp()
 /* show the sizes of all the track tables, cgis, and general files in separate window target= _blank  */
 {
 struct pushQ q;
 ZeroVar(&q);
 safef(q.qid,sizeof(q.qid),cgiString("qid"));
 printf("<h4>Show File Sizes Help</h4>\n");
 printf("<br>\n");
 printf("Tables: Shows sizes of database data and indexes.<br>\n");
 printf("Expands wildcard * in table names list. <br>\n");
 printf("Shows total index size, and the key expression of each index.<br>\n");
 printf("Location of tables is relative to the Current Location setting in the record.<br>\n");
 printf("<br>\n");
 printf("CGIs: shows files specified. Currently limited to checking localhost (hgwbeta in this case).<br>\n");
 printf("<br>\n");
 printf("Total size of all:  total size of all files found in bytes.<br>\n");
 printf("Total: size in megabytes(MB) which is what should be entered into the size(MB) field of the push queue record.<br>\n");
 printf("<br>\n");
 printf("RETURN - click to return to the details/edit page.<br>\n");
 printf("Set Size As - click to set size to that found, and return to the details/edit page. Saves typing. Be sure to press submit to save changes.<br>\n");
 printf("<br>\n");
 printf("<br>\n");
 printf("<a href=\"javascript:window.close();\">CLOSE</a> <br>\n");
 }
 
 void checkConn2()
 /* get 2nd conn, if not already done */
 {
 if (!conn2)
     conn2 = sqlConnectRemote(host, user, password, database);
 }
 
 boolean verifyTableIsQueue(char *table)
 /* Return TRUE if table is a push Q */
 {
 boolean result = TRUE;
 char query[256];
 char *field = NULL;
 safef(query, sizeof(query), "desc %s",table);
 checkConn2();
 field = sqlQuickString(conn2, query);
 result = sameString(field,"qid"); 
 freez(&field);
 return result;
 }
 
 void listQueues(char *action, boolean isTransfer)
 /* list the available queues other than self */
 {
 struct sqlResult *sr;
 char **row;
 char query[256];
 char *monthChange = isTransfer ? "" : "&month=current";
 if (!(isTransfer && sameString(pushQtbl,"pushQ")))
     {
     char *extra = (sameString(pushQtbl,"pushQ") ? " (you are here)" : "");
     printf("<A href=qaPushQ?%s=%s%s&cb=%s>Main Push Queue</A>%s<br>\n",action,"pushQ",monthChange,newRandState,extra);
     printf("<br>\n");
     }
 safef(query, sizeof(query), "show tables");
 sr = sqlGetResult(conn, query);
 while ((row = sqlNextRow(sr)) != NULL)
     {
     if (!(isTransfer && sameString(row[0],pushQtbl)) &&
 	!sameString(row[0],"pushQ") && 
 	!sameString(row[0],"users") && 
 	!sameString(row[0],"gbjunk"))
 	{
 	if (verifyTableIsQueue(row[0]))
 	    {
 	    char *displayQ = row[0];
 	    if (sameString(displayQ,"pushQ"))
 		displayQ = "Main Push Queue";
 	    char *extra = (sameString(row[0],pushQtbl) ? " (you are here)" : "");
     	    printf("<A href=qaPushQ?%s=%s%s&cb=%s>%s</A>%s<br>\n",action,row[0],monthChange,newRandState,displayQ,extra);
 	    }
 	}
     }
 sqlFreeResult(&sr);
 printf("<br>\n");
 }
 
 void doShowGateway()
 /* This gives the user a choice of queues to use */
 {
 
 printf("<h2>Gateway - Choose Queue</h2>\n");
 printf("<br>\n");
 
 listQueues("org", FALSE);
 
 printf("<a href=\"/cgi-bin/qaPushQ?cb=%s\">RETURN</a> <br>\n",newRandState);
 }
 
 void doUnlock()
 /* currently a backdoor for logged-in users to unlock a record */
 {
 struct pushQ *q;
 int updateSize = 2456; /* almost anything works here */
 
 q = mustLoadPushQ(cgiString("qid")); /* required cgi var */
 
 /* unlock record */
 safef(q->lockUser, sizeof(q->lockUser), "%s","");
 safef(q->lockDateTime, sizeof(q->lockDateTime), "%s","");
 
 /* update existing record */
 pushQUpdateEscaped(conn, q, pushQtbl, updateSize);
 
 pushQFree(&q);
 
 doDisplay();
 
 }
 
 
 /* ======================================================== */
 		
 
 void doDrawReleaseLog(boolean isEncode)
 /* Test - draw the release log using log data in pushQ  */
 {
 
 char *centraldb  = NULL;
 char *chost       = NULL;
 char *cuser       = NULL;
 char *cpassword   = NULL;
 
 struct sqlConnection *betaconn = NULL;
 
 struct dbDb *ki=NULL, *kiList=NULL, *dbDbTemp=NULL;
 struct sqlResult *sr;
 char **row;
 char query[256];
 char tempName[256];
 char now[256];
 
 int m=0,d=0;
 int topCount=0;
 
 char *encodeClause = "";
 
 if (isEncode)
     encodeClause = " and releaseLog like '%ENCODE%'";
 
 ZeroVar(&dbDbTemp);
 
 
 chost     = cfgOption("rrcentral.host"    );
 cuser     = cfgOption("rrcentral.user"    );
 cpassword = cfgOption("rrcentral.password");
 centraldb = cfgOption("rrcentral.db");
 
 webStart(NULL, NULL, "Track and Table Releases");
 
 
 
 // only allowed one connection at a time?
 sqlDisconnect(&conn);
 
 betaconn = sqlConnectRemote(chost, cuser, cpassword, centraldb);
     
 
 printf(" This page contains track and table release information for the following genome assemblies:<br>\n");
 
 safef(query,sizeof(query),
     "select * from dbDb "
     "where active=1 "
     "order by orderKey, name desc");
 sr = sqlGetResult(betaconn, query);
 while ((row = sqlNextRow(sr)) != NULL)
     {
     ki = dbDbLoad(row);
     slAddHead(&kiList, ki);
     }
 sqlFreeResult(&sr);
 
 slReverse(&kiList);
 sqlDisconnect(&betaconn);
 
 // are we really only allowed one remoteconn at a time?
 conn = sqlConnectRemote(host, user, password, database);
 
 /* filter the db list to make sure we actually have data */
 struct dbDb *newList=NULL, *kiNext;
 for (ki = kiList; ki != NULL; ki = kiNext)
     {
     kiNext = ki->next;
     safef(query,sizeof(query),
 	"select count(*) from pushQ "
 	"where priority='L' and releaseLog != '' and dbs like '%%%s%%' %s"
 	"order by qadate desc, qid desc",
 	ki->name,
 	encodeClause
 	);
     if (sqlQuickNum(conn, query) > 0)
 	{
     	slAddHead(&newList, ki);
 	}
     }
 slReverse(&newList);
 kiList = newList;
 
 /* 10 Latest Changes */
 printf("<ul>\n");
 printf("<li><a CLASS=\"toc\" HREF=\"#recent\" ><b>10 Latest Changes (all assemblies)</b></a></li>");
 
 /* regular log index #links */
 for (ki = kiList; ki != NULL; ki = ki->next)
     {
     safef(tempName,sizeof(tempName),ki->organism);
     if (!sameString(ki->organism, ki->genome))
 	{
 	safef(tempName,sizeof(tempName),"<em>%s</em>",ki->genome);
 	}
     printf("<li><a CLASS=\"toc\" HREF=\"#%s\">%s %s (%s)</a></li>\n",
 	ki->name,tempName,ki->description,ki->name);
     }
 
 printf("</ul>\n");
 printf("<p>\n");
 printf(" For more information about the tracks and tables listed on this page, refer to the "
 "<a href=/goldenPath/help/hgTracksHelp.html#IndivTracks>User's Guide</a>.<p>\n");
 
 strftime (now, sizeof(now), "%02d %b %Y", loctime); /* default to today's date */
 printf("<em>Last updated %s. <a HREF=\"/contacts.html\">Inquiries and feedback welcome</a>.</em>\n",now);
 /* 10 LATEST CHANGES */
 webNewSection("<A NAME=recent></A> 10 Latest Changes (all assemblies)");
 
 printf("<TABLE BORDER=1 BORDERCOLOR=\"#aaaaaa\" CELLPADDING=4 WIDTH=\"100%%\">\n"
     "<TR>\n"
     "<TD nowrap><FONT color=\"#006666\"><B>Track/Table Name</B></FONT></TD>\n"
     "<TD nowrap><FONT color=\"#006666\"><B>Assembly</B></FONT></TD>\n"
     "<TD nowrap><FONT color=\"#006666\"><B>Release Date</B></FONT></TD>\n"
     "</TR>\n"
     );
 safef(query,sizeof(query),
     "select releaseLog, dbs, qadate, releaseLogUrl from pushQ "
     "where priority='L' and releaseLog != '' and dbs != '' %s"
     "order by qadate desc, qid desc ", encodeClause 
     );
 sr = sqlGetResult(conn, query);
 while ((row = sqlNextRow(sr)) != NULL)
     {
     sscanf(cloneStringZ(&row[2][5],2),"%d",&m);
     sscanf(cloneStringZ(&row[2][8],2),"%d",&d);
 	{  /* parse dblist and make sure it's kosher and active=1 good */
 	char* dbs = cloneString(row[1]);
 	char dbsComma[1024];
 	char dbsSpace[1024];
 	struct dyString* dbList = newDyString(1024);
 	int j = 0, jj = 0;
 	char* sep = "";
 	boolean found = FALSE;
 	cutParens(dbs);
 	whiteSpace(dbs);
 	for(j=0;parseList(dbs, ',' ,j,dbsComma,sizeof(dbsComma));j++)
 	    {
 	    if (dbsComma[0]==0)
 		{ continue; }
 	    for(jj=0;parseList(dbsComma, ' ' ,jj,dbsSpace,sizeof(dbsSpace));jj++)
 		{
 		if (dbsSpace[0]==0)
 		    { continue; }
 		for (ki = kiList; ki != NULL; ki = ki->next)
 		    {
 		    if (sameWord(ki->name,dbsSpace))
 			{
 			found = TRUE;
 			dyStringPrintf(dbList,"%s%s",sep,dbsSpace);
 			sep=",";
 			}
 		    }
 		}
 	    }
 
 	if (found)
 	    {
 	    topCount++;
 	    printf("<TR valign=top><TD align=left>\n");
 	    if (sameOk(row[3], ""))
 		{
     		printf("%s", row[0]);
 		}
 	    else
 		{
     		printf("<a href=\"%s\" target=_blank>%s</a>", row[3], row[0]);
 		}
 	    printf("</td>\n");
 	    printf("<td>%s</td>\n"
 		"<td>%02d %s %s</td>\n"
 		"</tr>\n",
 		row[1], d, numberToMonth[m-1], cloneStringZ(row[2],4) );
 	    }
 
 	freez(&dbs);
 	freeDyString(&dbList);
 
 	if (topCount>=10)
 	    {break;}
 
 	}
 	
     }
 sqlFreeResult(&sr);
 printf("</table>\n");
 
 /* REGULAR LOG */
 for (ki = kiList; ki != NULL; ki = ki->next)
     {
     safef(tempName,sizeof(tempName),ki->organism);
     if (!sameString(ki->organism, ki->genome))
 	{
 	safef(tempName,sizeof(tempName),"<em>%s</em>",ki->genome);
 	}
     
     webNewSection("<A NAME=%s></A>%s %s (%s, %s)", 
 	ki->name, tempName, ki->description, ki->name, ki->sourceName);
     printf("<TABLE BORDER=1 BORDERCOLOR=\"#aaaaaa\" CELLPADDING=4 WIDTH=\"100%%\">\n"
 	"<TR><TD nowrap><FONT color=\"#006666\"><B>Track/Table Name</B></FONT></TD>\n"
 	"    <TD nowrap><FONT color=\"#006666\"><B>Release Date</B></FONT>\n"
 	"</TD></TR>\n"
 	);
     
     safef(query,sizeof(query),
 	"select releaseLog, qadate, releaseLogUrl from pushQ "
 	"where priority='L' and releaseLog != '' and dbs like '%%%s%%' %s"
 	"order by qadate desc, qid desc",
 	ki->name,
 	encodeClause
 	);
 	
     //printf("query=%s\n",query);
     sr = sqlGetResult(conn, query);
     while ((row = sqlNextRow(sr)) != NULL)
 	{
 	sscanf(cloneStringZ(&row[1][5],2),"%d",&m);
 	sscanf(cloneStringZ(&row[1][8],2),"%d",&d);
 	printf("<TR valign=top><TD align=left>\n");
 	if (sameOk(row[2], ""))
 	    {
 	    printf("%s", row[0]);
 	    }
 	else
 	    {
 	    printf("<a href=\"%s\" target=_blank>%s</a>", row[2], row[0]);
 	    }
 	printf("</td>\n");
 	printf(
 	    "<td>%02d %s %s</td>\n"
 	    "</tr>\n",
 	    d, numberToMonth[m-1], cloneStringZ(row[1],4) );
 	}
     sqlFreeResult(&sr);
     printf("</table>\n");
 
     }
 
 dbDbFreeList(&kiList);
 
 webEnd();
 }
 
 void doReleaseLogPush()
 /* fetch and write releaseLog and display cut-and-pastable push-request */
 {
 //char *rlPath = "/goldenPath/releaseLogNew.html";
 //char *apache = "/usr/local/apache/htdocs";
 char *rlPath = "/trash/releaseLogNew.html";
 char *apache = "/usr/local/apache";
 char url[256] = "";
 struct htmlPage *page = NULL;
 char filePath[256] = "";
 FILE *f=NULL;
 
 safef(url, sizeof(url), "http://%s/cgi-bin/qaPushQ?action=releaseLog",utsName.nodename);
 page = htmlPageGet(url);
 if (page->status->status == 200)
     {
     safef(filePath,sizeof(filePath),"%s%s",apache,rlPath);
     f=mustOpen(filePath, "w");
     mustWrite(f, page->htmlText, strlen(page->htmlText));
     carefulClose(&f);
     printf("<br>\n");
     printf("Updated release log html %s<br>\n",rlPath);
     printf("<br>\n");
     printf("push-request:<br>\n");
     printf("<br>\n");
     printf("Please push from beta to RR,MGC: <br>\n");
     printf("&nbsp;&nbsp;&nbsp;%s<br>\n",filePath);
     printf("<br>\n");
     printf("Thanks!<br>\n");
     printf("<br>\n");
     printf("<br>\n");
     printf("See <a href=%s>Release Log</a><br>\n",rlPath);
     }
 else
     {
     printf("Error reading %s: %d<br>\n",rlPath,page->status->status);
     }
 printf("<br>\n");
 printf("<a href=\"javascript:window.close();\">CLOSE</a> <br>\n");
 
 htmlPageFree(&page);
 
 }
 
 
 /* ======================================================== */
 
 /* ------------------------------------------------------- */
 
 
 void doMiddle()
 /* dispatch events */
 {
 
 char *org = NULL;      /* changes pushQtbl */
 char *newmonth = NULL; /* changes month */
 char *temp = NULL;
 char *reqRandState = NULL;
 
 /* debug *
 safef(msg,sizeof(msg),"db='%s', host='%s', user='%s', password='%s' <br>\n",database,host,user,password);
 htmShell("Push Queue debug", doMsg, NULL);
 exit(0);
 */
 
 if (crossPost)  // support showSizes across machines
     {
     //host = sameString(utsName.nodename, "hgwdev") ? "hgwbeta.cse.ucsc.edu" : "hgwdev.cse.ucsc.edu";
     host = cfgOption("pq.crossHost");
     }
 
 newRandState = randDigits(20);
 
 conn = sqlConnectRemote(host, user, password, database);
 
 setLock();
 
 /* default columns */
 showColumns = cloneString(defaultColumns);
     
 readMyUser();
 
 org = cgiUsualString("org","");  /* get org, defaults to display of main push queue */
 if (!sameString(org,""))
     {
     safef(pushQtbl,sizeof(pushQtbl),org);
     }
 if (!sqlTableExists(conn, pushQtbl))  /* if pushQtbl no longer exists, switch to main "pushQ" and set action to "display" */
     {
     safef(pushQtbl,sizeof(pushQtbl),"pushQ");
     action=cloneString("display");    /* do not need to free action because it just points to a cgi-var hash element */
     }
 
 newmonth = cgiUsualString("month","");  /* get month, if =current then resets to normal */
 if (!sameString(newmonth,""))
     {
     if (sameString(newmonth,"current"))
 	{
 	safef(month, sizeof(month), "%s", "");
 	}
     else
 	{
 	temp = needMem(strlen(newmonth)+1+3);
 	safef(temp, strlen(newmonth)+1+3, "%s-01",newmonth);
 	if (isDateValid(temp))
 	    {
 	    safef(month,sizeof(month),newmonth);
 	    }
 	}
     }
 
 
 
 if (sameString(action,"unlock"))
     {
     /* user probably didnt type in the right cachebuster cb= parm, we will supply it. */
     cgiVarSet("cb",oldRandState);
     }
 
 reqRandState = cgiUsualString("cb","");  /* get cb (cache-buster), ignores request, defaults to main display page */
 if (!sameString(reqRandState,oldRandState))
     {
     printf("req != old. \n\n req=%s,  old=%s \n\n",reqRandState,oldRandState);
     action = cloneString("display");
     }
 
 
 
 /* ---- Push Queue  ----- */
 
 if (sameString(action,"display")) 
     {
     doDisplay();
     }
 
 else if (sameString(action,"add")) 
     {
     doAdd();
     }
 
 else if (sameString(action,"edit")) 
     {
     doEdit();
     }
 
 else if (sameString(action,"post")) 
     {
     doPost();
     }
 
 else if (sameString(action,"promote")) 
     {
     doPromote();
     }
 
 else if (sameString(action,"demote")) 
     {
     doDemote();
     }
 
 else if (sameString(action,"top")) 
     {
     doTop();
     }
 
 else if (sameString(action,"bottom")) 
     {
     doBottom();
     }
 
 else if (sameString(action,"pushDone")) 
     {
     doPushDone();
     }
     
 else if (sameString(action,"demoteColumn" )) 
     {
     doDemoteColumn();
     }
 
 else if (sameString(action,"hideColumn"   )) 
     {
     doHideColumn();
     }
 
 else if (sameString(action,"promoteColumn")) 
     {
     doPromoteColumn();
     }
 
 else if (sameString(action,"showAllCol" )) 
     {
     doShowAllColumns(); 
     }
 
 else if (sameString(action,"showColumn"   )) 
     {
     doShowColumn(); 
     }
 
 else if (sameString(action,"showDefaultCol" )) 
     {
     doShowDefaultColumns(); 
     }
 
 else if (sameString(action,"showMonths" )) 
     {
     doShowMonths();
     }
 
 else if (sameString(action,"showSizes" )) 
     {
     doShowSizes();
     }
 
 else if (sameString(action,"transfer" )) 
     {
     doTransfer();
     }
 
 else if (sameString(action,"transferTo" )) 
     {
     doTransferTo();
     }
 
 else if (sameString(action,"showGateway" )) 
     {
     doShowGateway();
     }
 
 else if (sameString(action,"unlock" )) 
     {
     doUnlock();
     }
 
 else if (sameString(action,"setSize" )) 
     {
     doSetSize();
     }
 
 else
     {
     safef(msg,sizeof(msg),"action='%s' is invalid! <br>\n",action);
     doMsg();
     }
 
 oldRandState=newRandState;
 
 saveMyUser();
 releaseLock();
 sqlDisconnect(&conn);
 sqlDisconnect(&conn2);
 
 }
 
 
 int main(int argc, char *argv[], char *env[])
 /* Process command line if any. */
 {
 
 assert(e_NUMCOLS == ArraySize(colName));
 assert(e_NUMCOLS == ArraySize(colHdr));
 
 if (!cgiIsOnWeb())
     {
     warn("This is a CGI script - attempting to fake environment from command line");
     cgiSpoof(&argc, argv);
     }
 
 saveEnv = env;
 
 curtime = time (NULL);           /* Get the current time. */
 loctime = localtime (&curtime);  /* Convert it to local time representation. */
 
 srand( (unsigned)time( NULL ) );  /* Set seed (initial seed) off clock (not the best quality random input ;) */
 
 uname(&utsName);
 
 ZeroVar(&myUser);
 
 database = cfgOption("pq.db"      );
 host     = cfgOption("pq.host"    );
 user     = cfgOption("pq.user"    );
 password = cfgOption("pq.password");
 
 /* workaround for name-collision on form.action now as form._action on form */
 action = cgiUsualString("_action","display");  /* get action, defaults to display of push queue */
 action = cgiUsualString("action",action);  
 
 if (sameString(action,"xpost"))
     {
     crossPost = TRUE;  /* are we doing cross-post from dev to beta (or vice versa)? */
     action = "post";
     }
 
 /* initCgiInput() is not exported in cheapcgi.h, but it should get called by cgiUsualString
 So it will find all input regardless of Get/Put/Post/etc and make available as cgivars */
 
 
 if (sameString(action,"releaseLog"))
     {
     doDrawReleaseLog(FALSE);
     return 0;
     }
 if (sameString(action,"encodeReleaseLog"))
     {
     doDrawReleaseLog(TRUE);
     return 0;
     }
 if (sameString(action,"releaseLogPush"))
     {
     htmShell(TITLE, doReleaseLogPush, NULL);
     return 0;
     }
 
 
 
 qaUser = findCookieData("qapushq");  /* will also cause internal structures to load cookie data */
 if ((qaUser == NULL) || (sameString(qaUser,"")))
     {
     if (!sameString(action,"postLogin"))
     	action = cloneString("login");
     }
 
 if (sameString(action,"login"))
     {
     htmShell(TITLE, doLogin, NULL);
     }
 
 else if (sameString(action,"postLogin")) 
     {
     doPostLogin();        /* cant start htmShell until cookie is set */
     }
 else if (sameString(action,"reset")) 
     {
     doCookieReset();      /* cant start htmShell until cookie is re-set */
     }
 
 /* The help screens open in a separate window and don't hurt anything. Only displays text. Ignore cb. */
 else if (sameString(action,"showDisplayHelp" )) 
     {
     htmShell(TITLE, doShowDisplayHelp , NULL);
     }
 else if (sameString(action,"showEditHelp"    )) 
     {
     htmShell(TITLE, doShowEditHelp    , NULL);
     }
 else if (sameString(action,"showSizesHelp"   )) 
     {
     htmShell(TITLE, doShowSizesHelp   , NULL);
     }
 
 else
     {
     htmShell(TITLE, doMiddle, NULL);
     }
 
 return 0;
 
 }
 

