/* File: jade2ace.c * Author: Lincoln Stein (Whitehead) and Jean Thierry-Mieg * Copyright (C) L Stein and J Thierry-Mieg, 1996 * ------------------------------------------------------------------- * Acedb is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * or see the on-line version at http://www.gnu.org/copyleft/gpl.txt * ------------------------------------------------------------------- * This file is part of the ACEDB genome database package, written by * Richard Durbin (MRC LMB, UK) rd@mrc-lmb.cam.ac.uk, and * Jean Thierry-Mieg (CRBM du CNRS, France) mieg@kaa.cnrs-mop.fr * * Description: * Modified from aceclient to handle communication * with a java client * * Exported functions: * HISTORY: * Last edited: Apr 3 16:17 2000 (edgrif) * * Dec 2 16:33 1998 (edgrif): Corrected decl. of main, added code to * record build time of this module. * * Oct 16 16:20 1998 (fw): reverted strerror back to messSysErrorText() * because it resolves system differences * at that level (SunOS has no strerror) * * Jul 7 10:26 1998 (edgrif): Replace references to sys_errlist with * strerror function. * Created: Wed Nov 25 20:02:45 1992 (mieg) *------------------------------------------------------------------- */ /* $Id: jade2ace.c,v 1.9 2000/04/03 15:42:43 edgrif Exp $ */ #if !defined(WIN32) #include #endif #include #include #include #include #include "regular.h" #include "array.h" #include "aceclient.h" #include "version.h" #define OK 200 #define GOODBYE 201 #define DEBUG 301 #define COMMENT 302 #define REDIRECT 303 #define PARAMERROR 401 #define SYNTAXERROR 402 #define UNIMPLEMENTED 403 #define TIMEOUTERROR 501 #define COMMERROR 502 #define MEMERROR 503 #define FILEERROR 504 #define FIND "find" #define SHOW "show" #define QUERY "query" #define UNDO "undo" #define FOLLOW "follow" #define TABLE "table" #define NTABLE "ntable" #define AQL "AQL" #define MODEL "model" #define GIFACE "giface" #define IS "is" #define LIST "list" #define QUIT "quit" #define HELP "help" #define COMMENTSTRING "//" #define ERRORSTRING "//!! " #define KEYSETSTRING "KeySet" #define EOT "." /* I promise never to make my commands larger than this! */ #define COMMAND_MAX 128 /* I promise that Ace class names can't get larger than this! */ #define MAX_CLASSNAME 512 /* I promise never to send a message back to the user larger than this! */ #define MAX_MESSAGE 512 #ifndef FILENAME_MAX #define FILENAME_MAX 125 #endif static int chunkSize = 10; /* Opaque database handle JC do not need to know what it is */ static void *handle; void tStatus (void) { return ; } void writeStatus (int status,char* message) { fprintf(stdout,"%d %s\r\n",status,message); fflush(stdout); } void writeln(char* line) { fprintf(stdout,"%s\r\n",line); fflush(stdout); } void die (int status, char* message) { writeStatus(status,message); exit(-1); } void reallocate (void** buffer, int old_size, int new_size) { void* newbuffer; int bytes_to_copy; bytes_to_copy = (old_size < new_size) ? old_size : new_size; newbuffer = malloc(new_size); if (newbuffer == NULL) die(MEMERROR,"ran out of memory allocating input buffer"); if (*buffer != NULL) { memcpy(newbuffer,*buffer,bytes_to_copy); free(*buffer); } *buffer = newbuffer; } char* readln() { static char* linebuffer = NULL; static int linebufsize = 512; int length; char* result; int oldsize = 0; int offset = 0; if (linebuffer == NULL) { reallocate((void**)&linebuffer,oldsize,linebufsize); } while (1) { result = fgets(linebuffer+offset,linebufsize-offset,stdin); if (result == NULL) return NULL; length = strlen(linebuffer); /* empty string is OK */ if (length == 0) return linebuffer; /* huge buffers present a security risk */ if (length > (1<<20)) { free (linebuffer) ; return NULL ; } /* if the last character is a \n, then we got the end of the line so we're done */ if (linebuffer[length-1] == '\n') { if ( (length >= 2) && (linebuffer[length-2] == '\r') ) linebuffer[length-2] = '\0'; else linebuffer[length-1] = '\0'; return linebuffer; } /* otherwise we got a truncated, and we need to make the buffer bigger and try again */ oldsize = linebufsize; linebufsize *= 2; reallocate((void**)&linebuffer,oldsize,linebufsize); offset = length; } } char* readCommand(char** extraData) { static char command[COMMAND_MAX]; /* command can't be longer than 512 characters */ char *line; char nextchar; int i=0,j=0; if ( (line = readln()) == NULL) return NULL; /* Skip over whitespace and other non-alphanumeric chars at the beginning of the line. */ while ((i < COMMAND_MAX-1) && line[i] && !isalnum((int)line[i])) i++; while (i < COMMAND_MAX-1 && line[i] && !isspace((int)(nextchar = line[i]))) { command[j++] = nextchar; i++; } command[j]='\0'; *extraData = isspace((int)line[i]) ? line + i + 1 : line + i; return command; } BOOL streq (char* s1, char* s2) { int length; length = strlen(s2); if (strncasecmp(s1,s2,length) == 0) return TRUE; return FALSE; } /*************************************************************************/ /* Find the "%d Active Objects" line and return the count */ /*************************************************************************/ int countActive (char* text) { int objectCount; while (text) { if (sscanf(text,"// %d Active Objects",&objectCount)) return objectCount; /* if we didn't find it, skip to the next line and try again */ text = strchr(text,'\n'); if (!text) break; if (!*(++text)) break; } /* if we get here, we never found the string. Return -1 */ return -1; } /*************************************************************************/ /* replace newlines with CRLF pairs. Strip // comments entirely. */ /*************************************************************************/ void stripAndPrint (char* text,int length, BOOL stripBlanks,BOOL stripLastBlank, int encore) { char* end_of_line; char* current = text; if (encore && *text) /* mieg: because writeln adds an extra \n */ { end_of_line = text + strlen(text) - 1 ; if (*end_of_line == '\n') *end_of_line = 0 ; } if (current[0] == '\n') current++; while (current < text + length) { if ((strncmp(current,COMMENTSTRING, 2) == 0) || (strncmp(current,KEYSETSTRING, 6) == 0) ) { if ((end_of_line = strchr(current,'\n')) == NULL) return; current = end_of_line + 1; continue; } end_of_line = strchr(current,'\n'); /* sorry, this is a little weird because the number of lines that come back from the ace server is not entirely predictable */ if (end_of_line != NULL) { *end_of_line = '\0'; if (!stripBlanks || strlen(current) > 0) writeln(current); current = end_of_line + 1; } else { if ((!stripLastBlank && !stripBlanks) || (strlen(current) > 0) ) writeln(current); return; } } } /*************************************************************************/ /* All-purpose query handler. If show is TRUE, then */ /*************************************************************************/ void doQuery(char* query,BOOL show) { char message[MAX_MESSAGE]; char subclass[MAX_CLASSNAME],superclass[MAX_CLASSNAME]; unsigned char* answer; int retval, length, active = 0, encore = 0; BOOL encoring = FALSE; do { retval = askServerBinary(handle,query,&answer,&length,&encore,chunkSize); if (retval > 0) { sprintf(message,"ACE: error code %d",retval); writeStatus(TIMEOUTERROR,message); if (answer != NULL) free(answer); break; } if (show && !encoring) { /* Some error detection and reporting. This has to be much expanded.*/ *message = '\0'; /* paranoia */ if (2 == sscanf((char*)answer,"// %s is a sub class of %[a-zA-Z_0-9]",subclass,superclass)) { sprintf(message,"%s is the superclass for %s",superclass,subclass); writeStatus(REDIRECT,message); free(answer); return; } else if (1 == sscanf((char*)answer,"// Sorry, %[a-z A-Z_0-9:]",message)) { writeStatus(SYNTAXERROR,message); free(answer); return; } else if (!strncmp((char*)answer,"// This table has a problem",22)) { writeStatus(SYNTAXERROR,"table syntax error"); free(answer); return; } writeStatus(OK,"Results follow, terminated by \".\""); } encoring = encore; if (show) stripAndPrint((char*)answer,length,FALSE,FALSE, encore); else if ((active = countActive((char*)answer)) >= 0) { sprintf(message,"%d objects",active); writeStatus(OK,message); encoring = FALSE; } free(answer); if (!encore) break; else query = "encore"; } while (encoring); if (show) writeln(EOT); } /*************************************************************************/ /* Handling of externally-defined tables: 1. opens up a temporary file using tempnam() function 2. writes the user's table into that file 3. asks the server to process the file 4. prints the result 5. unlinks the table */ #define TMP "/tmp" void doTable() { char* tempName; char message[FILENAME_MAX + COMMAND_MAX + 1]; char* line; FILE* temp; if (!(tempName = tempnam("/var/tmp", ".acenetcl"))) { sprintf(message,"Error getting temp file name: %s", messSysErrorText()) ; writeStatus(FILEERROR,message); return; } unlink(tempName); /* just to be safe */ temp = fopen(tempName,"w"); if (temp == NULL) { sprintf(message,"Error creating temp table file \"%s\": %s", tempName, messSysErrorText()) ; writeStatus(FILEERROR,message); return; } /* Here is where we accept lines from the remote and write them into the temp file. This is very, very hacky */ writeStatus(OK,"Enter table data. End with \".\" on a line by itself."); while ((line = readln()) && (line[0] != '.') ) { fprintf(temp,"%s\n",line); } fclose(temp); /* it's possible that the input suddenly died. In this case we just clean up and get out of here */ if (line != NULL) { /* Perform a "table" style query using the current file for input This is the obviously gross part. */ sprintf(message,"table -j %s",tempName); doQuery(message,TRUE); } unlink(tempName); free((void*) tempName); } /*************************************************************************/ void printHelp () { char* helpText[] = { "- Commands:", "- FIND SHOW FOLLOW TABLE NTABLE MODEL GIF", "- LIST IS QUERY AQL UNDO QUIT HELP", "- You need to know the Ace query language to use this", "- service. Sorry.", "End of HELP info", NULL }; char** m = helpText; while (*m != NULL) writeStatus(COMMENT,*m++); } /*************************************************************************/ void unimplemented (char* command) { char message[MAX_MESSAGE]; sprintf(message,"%s is currently unimplemented",command); writeStatus(UNIMPLEMENTED,message); } /*************************************************************************/ BOOL processCommand(char* command,char* data) { /* the following stuff is a slightly awkward dynamic string copy. It works and is not particularly inefficient since the commands are really quite small.*/ static char* message = NULL; static int currentsize = 0; BOOL show = FALSE; BOOL pCheck = TRUE; int sizeneeded = strlen(data) + COMMAND_MAX + 1; if ((message == NULL) || (currentsize < sizeneeded)) { reallocate((void**)&message,currentsize,sizeneeded); currentsize = sizeneeded; } *message = '\0'; if (streq(command,QUIT)) return TRUE; if (streq(command,HELP)) printHelp(); else if (streq(command,FIND)) strcat(message,"find "); else if (streq(command,FOLLOW)) strcat(message,"follow "); else if (streq(command,IS)) strcat(message,"is "); else if (streq(command,QUERY)) strcat(message,"query "); else if (streq(command,UNDO)) { strcat(message,"undo "); pCheck = FALSE; } else if (streq(command,SHOW)) { strcat(message,"show -j "); pCheck = FALSE; show = TRUE; } else if (streq(command,NTABLE)) { strcat(message,"table -j -n "); show = TRUE; } else if (streq(command,AQL)) { strcat(message,"aql -j "); show = TRUE; } else if (streq(command,MODEL)) { strcat(message,"model "); show = TRUE; } else if (streq(command,GIFACE)) { strcat(message,"gif "); show = TRUE; } else if (streq(command,LIST)) { strcat(message,"list -j "); pCheck = FALSE; show = TRUE; } else if (streq(command,TABLE)) doTable(); else if (!command[0]) /* do nothing for empty commands */ ; else writeStatus(SYNTAXERROR,"unknown command"); if (*message != '\0') { if (pCheck && !*data) { writeStatus(PARAMERROR,"missing parameter(s)"); return FALSE; } strcat(message,data); doQuery(message,show); } return FALSE; } /* Defines a routine to return the compile date of this file. */ UT_MAKE_GETCOMPILEDATEROUTINE() /*************************************************************************/ int main(int argc, char *argv[]) { char *host = "localhost"; unsigned long port = DEFAULT_PORT; int timeOut = 300; BOOL isQuit = FALSE; char *command,*extraData ; /* Read command line parameters */ while (argc > 1) { argv++; argc--; if ( (argc > 1) && !strcmp("-host",*argv) ) { argv++; argc--; host = *argv; } else if ( (argc > 1) && !strcmp("-port",*argv) ) { argv++; argc--; port = atoi(*argv); } else if ( (argc > 1) && !strcmp("-time_out",*argv) ) { argv++; argc--; timeOut = atoi(*argv); } else { fprintf(stderr,"Usage: netclient [-host host] [-port port_num] [-time_out nn_in_seconds]\n"); return (EXIT_FAILURE); } } if ((handle = openServer(host, port, timeOut)) == NULL) { writeStatus(COMMERROR,"cannot establish connection"); return (EXIT_FAILURE); } writeStatus(OK,"Ace server ready and waiting"); while (!isQuit && (command = readCommand(&extraData)) ) isQuit = processCommand(command,extraData); closeServer(handle); writeStatus(GOODBYE,"A bientot"); return(EXIT_SUCCESS); } /*************************************************************************/ /*************************************************************************/