3 @brief Command-line tool for OSRF
6 #include <opensrf/transport_client.h>
7 #include <opensrf/osrf_message.h>
8 #include <opensrf/osrf_app_session.h>
11 #include <sys/timeb.h>
12 #include <sys/types.h>
15 #include <opensrf/utils.h>
16 #include <opensrf/log.h>
21 #include <readline/readline.h>
22 #include <readline/history.h>
25 @brief A struct of convenience for parsing a command line
28 const char* itr; /**< iterator for input buffer */
29 growing_buffer* buf; /**< output buffer */
32 static void get_string_literal( ArgParser* parser );
33 static void get_json_array( ArgParser* parser );
34 static void get_json_object( ArgParser* parser );
35 static void get_misc( ArgParser* parser );
37 #define SRFSH_PORT 5222
38 #define COMMAND_BUFSIZE 4096
42 static const char* prompt = "srfsh# ";
44 static char* history_file = NULL;
46 //static int child_dead = 0;
48 static char* login_session = NULL;
50 /* true if we're pretty printing json results */
51 static int pretty_print = 1;
52 /* true if we're bypassing 'less' */
53 static int raw_print = 0;
55 /* our jabber connection */
56 static transport_client* client = NULL;
58 /* the last result we received */
59 static osrfMessage* last_result = NULL;
62 static int process_request( const char* request );
63 static void parse_args( const char* request, osrfStringArray* cmd_array );
65 /* handles router requests */
66 static int handle_router( const osrfStringArray* cmd_array );
68 /* utility method for print time data */
69 static int handle_time( const osrfStringArray* cmd_array );
71 /* handles app level requests */
72 static int handle_request( const osrfStringArray* cmd_array, int relay );
73 static int handle_set( const osrfStringArray* cmd_array );
74 static int handle_print( const osrfStringArray* cmd_array );
75 static int send_request( const char* server,
76 const char* method, growing_buffer* buffer, int relay );
77 static int parse_error( const char* request );
78 static int router_query_servers( const char* server );
79 static int print_help( void );
81 //static int srfsh_client_connect();
82 //static char* tabs(int count);
83 //static void sig_child_handler( int s );
84 //static void sig_int_handler( int s );
86 static char* get_request( void );
87 static int load_history( void );
88 static int handle_math( const osrfStringArray* cmd_array );
89 static int do_math( int count, int style );
90 static int handle_introspect( const osrfStringArray* cmd_array );
91 static int handle_login( const osrfStringArray* cmd_array );
92 static int handle_open( const osrfStringArray* cmd_array );
93 static int handle_close( const osrfStringArray* cmd_array );
94 static void close_all_sessions( void );
96 static int recv_timeout = 120;
97 static int is_from_script = 0;
99 static osrfHash* server_hash = NULL;
101 int main( int argc, char* argv[] ) {
103 /* --------------------------------------------- */
104 /* see if they have a .srfsh.xml in their home directory */
105 char* home = getenv("HOME");
106 int l = strlen(home) + 36;
108 snprintf(fbuf, sizeof(fbuf), "%s/.srfsh.xml", home);
110 if(!access(fbuf, R_OK)) {
111 if( ! osrf_system_bootstrap_client(fbuf, "srfsh") ) {
112 fprintf(stderr,"Unable to bootstrap client for requests\n");
113 osrfLogError( OSRF_LOG_MARK, "Unable to bootstrap client for requests");
118 fprintf(stderr,"No Config file found at %s\n", fbuf );
123 /* for now.. the first arg is used as a script file for processing */
125 if( (f = open(argv[1], O_RDONLY)) == -1 ) {
126 osrfLogError( OSRF_LOG_MARK, "Unable to open file %s for reading, exiting...", argv[1]);
130 if(dup2(f, STDIN_FILENO) == -1) {
131 osrfLogError( OSRF_LOG_MARK, "Unable to duplicate STDIN, exiting...");
139 /* --------------------------------------------- */
142 client = osrfSystemGetTransportClient();
144 // Disable special treatment for tabs by readline
145 // (by default they invoke command completion, which
146 // is not useful for srfsh)
147 rl_bind_key( '\t', rl_insert );
149 /* main process loop */
150 int newline_needed = 1; /* used as boolean */
152 while( (request = get_request()) ) {
154 // Find first non-whitespace character
156 char * cmd = request;
157 while( isspace( (unsigned char) *cmd ) )
160 // Remove trailing whitespace. We know at this point that
161 // there is at least one non-whitespace character somewhere,
162 // or we would have already skipped this line. Hence we
163 // needn't check to make sure that we don't back up past
167 // The curly braces limit the scope of the end variable
169 char * end = cmd + strlen(cmd) - 1;
170 while( isspace( (unsigned char) *end ) )
175 if( !strcasecmp(cmd, "exit") || !strcasecmp(cmd, "quit"))
181 process_request( cmd );
182 if( request && *cmd ) {
183 add_history(request);
192 if( newline_needed ) {
194 // We left the readline loop after seeing an EOF, not after
195 // seeing "quit" or "exit". So we issue a newline in order
196 // to avoid leaving a dangling prompt.
201 if(history_file != NULL )
202 write_history(history_file);
208 if( osrfHashGetCount( server_hash ) > 0 )
209 close_all_sessions();
210 osrfHashFree( server_hash );
213 osrf_system_shutdown();
217 // Get a logical line from one or more calls to readline(),
218 // skipping blank lines and comments. Stitch continuation
219 // lines together as needed. Caller is responsible for
220 // freeing the string returned.
221 // If EOF appears before a logical line is completed,
223 static char* get_request( void ) {
227 // Get the first physical line of the logical line
229 line = readline( prompt );
231 return NULL; // end of file
233 // Skip leading white space
234 for( p = line; isspace( *p ); ++p )
237 if( '\\' == *p && '\0' == p[1] ) {
238 // Just a trailing backslash; skip to next line
241 } else if( '\0' == p[0] || '#' == *p ) {
243 continue; // blank line or comment; skip it
245 break; // Not blank, not comment; take it
248 char* end = line + strlen( line ) - 1;
250 return line; // No continuation line; we're done
252 // Remove the trailing backslash and collect
253 // the continuation line(s) into a growing_buffer
256 growing_buffer* logical_line = buffer_init( 256 );
257 buffer_add( logical_line, p );
260 // Append any continuation lines
261 int finished = 0; // boolean
263 line = readline( "> " );
266 // Check for another continuation
267 end = line + strlen( line ) - 1;
273 buffer_add( logical_line, line );
276 fprintf( stderr, "Expected continuation line; found end of file\n" );
277 buffer_free( logical_line );
282 return buffer_release( logical_line );
285 static int load_history( void ) {
287 char* home = getenv("HOME");
288 int l = strlen(home) + 24;
290 snprintf(fbuf, sizeof(fbuf), "%s/.srfsh_history", home);
291 history_file = strdup(fbuf);
293 if(!access(history_file, W_OK | R_OK )) {
294 history_length = 5000;
295 read_history(history_file);
301 static int parse_error( const char* request ) {
305 fprintf( stderr, "???: %s\n", request );
311 static int process_request( const char* request ) {
313 if( request == NULL )
317 osrfStringArray* cmd_array = osrfNewStringArray( 32 );
319 parse_args( request, cmd_array );
320 int wordcount = cmd_array->size;
321 if( 0 == wordcount ) {
322 printf( "No words found in command\n" );
323 osrfStringArrayFree( cmd_array );
327 /* pass off to the top level command */
328 const char* command = osrfStringArrayGetString( cmd_array, 0 );
329 if( !strcmp( command, "router" ) )
330 ret_val = handle_router( cmd_array );
332 else if( !strcmp( command, "time" ) )
333 ret_val = handle_time( cmd_array );
335 else if ( !strcmp( command, "request" ) )
336 ret_val = handle_request( cmd_array, 0 );
338 else if ( !strcmp( command, "relay" ) )
339 ret_val = handle_request( cmd_array, 1 );
341 else if ( !strcmp( command, "help" ) )
342 ret_val = print_help();
344 else if ( !strcmp( command, "set" ) )
345 ret_val = handle_set( cmd_array );
347 else if ( !strcmp( command, "print" ) )
348 ret_val = handle_print( cmd_array );
350 else if ( !strcmp( command, "math_bench" ) )
351 ret_val = handle_math( cmd_array );
353 else if ( !strcmp( command, "introspect" ) )
354 ret_val = handle_introspect( cmd_array );
356 else if ( !strcmp( command, "login" ) )
357 ret_val = handle_login( cmd_array );
359 else if ( !strcmp( command, "open" ) )
360 ret_val = handle_open( cmd_array );
362 else if ( !strcmp( command, "close" ) )
363 ret_val = handle_close( cmd_array );
365 else if ( request[0] == '!') {
366 system( request + 1 );
370 osrfStringArrayFree( cmd_array );
373 return parse_error( request );
379 static int handle_introspect( const osrfStringArray* cmd_array ) {
381 const char* service = osrfStringArrayGetString( cmd_array, 1 );
385 fprintf(stderr, "--> %s\n", service );
387 // Build a command in a suitably-sized
388 // buffer and then parse it
391 const char* method = osrfStringArrayGetString( cmd_array, 2 );
393 static const char text[] = "request %s opensrf.system.method %s";
394 len = sizeof( text ) + strlen( service ) + strlen( method );
396 snprintf( buf, sizeof(buf), text, service, method );
397 return process_request( buf );
400 static const char text[] = "request %s opensrf.system.method.all";
401 len = sizeof( text ) + strlen( service );
403 snprintf( buf, sizeof(buf), text, service );
404 return process_request( buf );
410 static int handle_login( const osrfStringArray* cmd_array ) {
412 const char* username = osrfStringArrayGetString( cmd_array, 1 );
413 const char* password = osrfStringArrayGetString( cmd_array, 2 );
415 if( username && password ) {
417 const char* type = osrfStringArrayGetString( cmd_array, 3 );
418 const char* orgloc = osrfStringArrayGetString( cmd_array, 4 );
419 const char* workstation = osrfStringArrayGetString( cmd_array, 5 );
420 int orgloci = (orgloc) ? atoi(orgloc) : 0;
421 if(!type) type = "opac";
423 char login_text[] = "request open-ils.auth open-ils.auth.authenticate.init \"%s\"";
424 size_t len = sizeof( login_text ) + strlen(username) + 1;
427 snprintf( buf, sizeof(buf), login_text, username );
428 process_request(buf);
431 if(last_result && last_result->_result_content) {
432 jsonObject* r = last_result->_result_content;
433 hash = jsonObjectGetString(r);
436 char* pass_buf = md5sum(password);
438 size_t both_len = strlen( hash ) + strlen( pass_buf ) + 1;
439 char both_buf[both_len];
440 snprintf(both_buf, sizeof(both_buf), "%s%s", hash, pass_buf);
442 char* mess_buf = md5sum(both_buf);
444 growing_buffer* argbuf = buffer_init(64);
446 "request open-ils.auth open-ils.auth.authenticate.complete "
447 "{ \"username\" : \"%s\", \"password\" : \"%s\"", username, mess_buf );
449 if(type) buffer_fadd( argbuf, ", \"type\" : \"%s\"", type );
450 if(orgloci) buffer_fadd( argbuf, ", \"org\" : %d", orgloci );
451 if(workstation) buffer_fadd( argbuf, ", \"workstation\" : \"%s\"", workstation);
452 buffer_add(argbuf, "}");
457 process_request( argbuf->buf );
460 if( login_session != NULL )
461 free( login_session );
463 const jsonObject* x = last_result->_result_content;
466 const char* authtoken = jsonObjectGetString(
467 jsonObjectGetKeyConst(jsonObjectGetKeyConst(x,"payload"), "authtoken"));
468 authtime = jsonObjectGetNumber(
469 jsonObjectGetKeyConst(jsonObjectGetKeyConst(x,"payload"), "authtime"));
472 login_session = strdup(authtoken);
474 login_session = NULL;
476 else login_session = NULL;
478 printf("Login Session: %s. Session timeout: %f\n",
479 (login_session ? login_session : "(none)"), authtime );
489 @brief Open connections to one or more specified services.
490 @param cmd_array Pointer to a list of command line chunks.
491 @return 1 in all cases.
493 The first chunk of the command line is the "open" command. Subsequent chunks, if any,
496 Try to open all specified servers. If no servers are specified, report what servers are
499 static int handle_open( const osrfStringArray* cmd_array ) {
500 if( NULL == osrfStringArrayGetString( cmd_array, 1 ) ) {
501 if( ! server_hash || osrfHashGetCount( server_hash ) == 0 ) {
502 printf( "No services are currently open\n" );
506 printf( "Service(s) currently open:\n" );
508 osrfHashIterator* itr = osrfNewHashIterator( server_hash );
509 while( osrfHashIteratorNext( itr ) ) {
510 printf( "\t%s\n", osrfHashIteratorKey( itr ) );
512 osrfHashIteratorFree( itr );
517 server_hash = osrfNewHash( 6 );
520 for( i = 1; ; ++i ) { // for each requested service
521 const char* server = osrfStringArrayGetString( cmd_array, i );
525 if( osrfHashGet( server_hash, server ) ) {
526 printf( "Service %s is already open\n", server );
530 // Try to open a session with the current specified server
531 osrfAppSession* session = osrfAppSessionClientInit(server);
533 if(!osrfAppSessionConnect(session)) {
534 fprintf(stderr, "Unable to open service %s\n", server);
535 osrfLogWarning( OSRF_LOG_MARK, "Unable to open remote service %s\n", server );
536 osrfAppSessionFree( session );
538 osrfHashSet( server_hash, session, server );
539 printf( "Service %s opened\n", server );
547 @brief Close connections to one or more specified services.
548 @param cmd_array Pointer to a list of command line chunks.
549 @return 1 if any services were closed, or 0 if there were none to close.
551 The first chunk of the command line is the "close" command. Subsequent chunks, if any,
554 static int handle_close( const osrfStringArray* cmd_array ) {
555 if( cmd_array->size < 2 ) {
556 fprintf( stderr, "No service specified for close\n" );
561 for( i = 1; ; ++i ) {
562 const char* server = osrfStringArrayGetString( cmd_array, i );
566 osrfAppSession* session = osrfHashRemove( server_hash, server );
568 printf( "Service \"%s\" is not open\n", server );
572 osrf_app_session_disconnect( session );
573 osrfAppSessionFree( session );
574 printf( "Service \"%s\" closed\n", server );
581 @brief Close all currently open connections to services.
583 static void close_all_sessions( void ) {
585 osrfAppSession* session;
586 osrfHashIterator* itr = osrfNewHashIterator( server_hash );
588 while(( session = osrfHashIteratorNext( itr ) )) {
589 osrf_app_session_disconnect( session );
590 osrfAppSessionFree( session );
593 osrfHashIteratorFree( itr );
596 static int handle_set( const osrfStringArray* cmd_array ) {
598 const char* variable = osrfStringArrayGetString( cmd_array, 1 );
601 const char* val = osrfStringArrayGetString( cmd_array, 2 );
604 if(!strcmp(variable,"pretty_print")) {
605 if(!strcmp(val,"true")) {
607 printf("pretty_print = true\n");
609 } else if(!strcmp(val,"false")) {
611 printf("pretty_print = false\n");
616 if(!strcmp(variable,"raw_print")) {
617 if(!strcmp(val,"true")) {
619 printf("raw_print = true\n");
621 } else if(!strcmp(val,"false")) {
623 printf("raw_print = false\n");
635 static int handle_print( const osrfStringArray* cmd_array ) {
637 const char* variable = osrfStringArrayGetString( cmd_array, 1 );
640 if(!strcmp(variable,"pretty_print")) {
642 printf("pretty_print = true\n");
645 printf("pretty_print = false\n");
650 if(!strcmp(variable,"raw_print")) {
652 printf("raw_print = true\n");
655 printf("raw_print = false\n");
660 if(!strcmp(variable,"login")) {
661 printf("login session = %s\n",
662 login_session ? login_session : "(none)" );
670 static int handle_router( const osrfStringArray* cmd_array ) {
677 const char* word_1 = osrfStringArrayGetString( cmd_array, 1 );
678 const char* word_2 = osrfStringArrayGetString( cmd_array, 2 );
680 if( !strcmp( word_1,"query") ) {
682 if( word_2 && !strcmp( word_2, "servers" ) ) {
683 for( i=3; i < COMMAND_BUFSIZE - 3; i++ ) {
684 const char* word = osrfStringArrayGetString( cmd_array, i );
686 router_query_servers( word );
701 static int handle_request( const osrfStringArray* cmd_array, int relay ) {
706 const char* word_1 = osrfStringArrayGetString( cmd_array, 1 );
707 const char* word_2 = osrfStringArrayGetString( cmd_array, 2 );
710 const char* server = word_1;
711 const char* method = word_2;
713 growing_buffer* buffer = NULL;
715 int first = 1; // boolean
716 buffer = buffer_init( 128 );
717 buffer_add( buffer, "[" );
719 const char* word = osrfStringArrayGetString( cmd_array, i );
726 buffer_add( buffer, ", " );
728 buffer_add( buffer, word );
730 /* remove trailing semicolon if user accidentally entered it */
731 if( word[ strlen( word ) - 1 ] == ';' )
732 buffer_chomp( buffer );
734 buffer_add_char( buffer, ']' );
737 int rc = send_request( server, method, buffer, relay );
738 buffer_free( buffer );
745 int send_request( const char* server,
746 const char* method, growing_buffer* buffer, int relay ) {
747 if( server == NULL || method == NULL )
750 jsonObject* params = NULL;
752 if( buffer != NULL && buffer->n_used > 0 ) {
753 // Temporarily redirect parsing error messages to stderr
755 params = jsonParse( OSRF_BUFFER_C_STR( buffer ) );
756 osrfRestoreLogType();
759 if(!last_result || ! last_result->_result_content) {
760 printf("We're not going to call 'relay' with no result params\n");
764 params = jsonNewObject(NULL);
765 jsonObjectPush(params, last_result->_result_content );
769 if(buffer->n_used > 0 && params == NULL) {
770 fprintf(stderr, "JSON error detected, not executing\n");
771 jsonObjectFree(params);
775 int session_is_temporary; // boolean
776 osrfAppSession* session = osrfHashGet( server_hash, server );
778 session_is_temporary = 0; // use an existing session
780 session = osrfAppSessionClientInit(server); // open a session
781 session_is_temporary = 1; // just for this request
784 if(!osrfAppSessionConnect(session)) {
785 fprintf(stderr, "Unable to communicate with service %s\n", server);
786 osrfLogWarning( OSRF_LOG_MARK, "Unable to connect to remote service %s\n", server );
787 osrfAppSessionFree( session );
788 jsonObjectFree(params);
792 double start = get_timestamp_millis();
794 int req_id = osrfAppSessionMakeRequest( session, params, method, 1, NULL );
795 jsonObjectFree(params);
797 osrfMessage* omsg = osrfAppSessionRequestRecv( session, req_id, recv_timeout );
800 printf("\nReceived no data from server\n");
803 signal(SIGPIPE, SIG_IGN);
806 if(!is_from_script) less = popen( "less -EX", "w");
809 if( less == NULL ) { less = stdout; }
811 growing_buffer* resp_buffer = buffer_init(4096);
817 if(omsg->_result_content) {
819 osrfMessageFree(last_result);
825 char* j = jsonObjectToJSON(omsg->_result_content);
826 //content = json_printer(j);
827 content = jsonFormatString(j);
830 const char * temp_content = jsonObjectGetString(omsg->_result_content);
832 temp_content = "[null]";
833 content = strdup( temp_content );
836 printf( "\nReceived Data: %s\n", content );
842 snprintf( code, sizeof(code), "%d", omsg->status_code );
843 buffer_add( resp_buffer, code );
845 printf( "\nReceived Exception:\nName: %s\nStatus: %s\nStatus: %s\n",
846 omsg->status_name, omsg->status_text, code );
849 osrfMessageFree(omsg);
854 if(omsg->_result_content) {
856 osrfMessageFree(last_result);
861 if( pretty_print && omsg->_result_content ) {
862 char* j = jsonObjectToJSON(omsg->_result_content);
863 //content = json_printer(j);
864 content = jsonFormatString(j);
867 const char * temp_content = jsonObjectGetString(omsg->_result_content);
869 content = strdup( temp_content );
874 buffer_add( resp_buffer, "\nReceived Data: " );
875 buffer_add( resp_buffer, content );
876 buffer_add( resp_buffer, "\n" );
881 buffer_add( resp_buffer, "\nReceived Exception:\nName: " );
882 buffer_add( resp_buffer, omsg->status_name );
883 buffer_add( resp_buffer, "\nStatus: " );
884 buffer_add( resp_buffer, omsg->status_text );
885 buffer_add( resp_buffer, "\nStatus: " );
887 snprintf( code, sizeof(code), "%d", omsg->status_code );
888 buffer_add( resp_buffer, code );
889 osrfMessageFree(omsg);
893 omsg = osrfAppSessionRequestRecv( session, req_id, recv_timeout );
897 double end = get_timestamp_millis();
899 fputs( resp_buffer->buf, less );
900 buffer_free( resp_buffer );
901 fputs("\n------------------------------------\n", less);
902 if( osrf_app_session_request_complete( session, req_id ))
903 fputs("Request Completed Successfully\n", less);
906 fprintf(less, "Request Time in seconds: %.6f\n", end - start );
907 fputs("------------------------------------\n", less);
911 osrf_app_session_request_finish( session, req_id );
913 if( session_is_temporary ) {
914 osrf_app_session_disconnect( session );
915 osrfAppSessionFree( session );
923 static int handle_time( const osrfStringArray* cmd_array ) {
925 const char* word_1 = osrfStringArrayGetString( cmd_array, 1 );
927 printf("%f\n", get_timestamp_millis());
929 time_t epoch = (time_t) atoi( word_1 );
930 printf("%s", ctime(&epoch));
936 static int router_query_servers( const char* router_server ) {
938 if( ! router_server || strlen(router_server) == 0 )
941 const static char router_text[] = "router@%s/router";
942 size_t len = sizeof( router_text ) + strlen( router_server ) + 1;
944 snprintf(rbuf, sizeof(rbuf), router_text, router_server );
946 transport_message* send =
947 message_init( "servers", NULL, NULL, rbuf, NULL );
948 message_set_router_info( send, NULL, NULL, NULL, "query", 0 );
950 client_send_message( client, send );
951 message_free( send );
953 transport_message* recv = client_recv( client, -1 );
955 fprintf(stderr, "NULL message received from router\n");
960 "---------------------------------------------------------------------------------\n"
961 "Received from 'server' query on %s\n"
962 "---------------------------------------------------------------------------------\n"
963 "original reg time | latest reg time | last used time | class | server\n"
964 "---------------------------------------------------------------------------------\n"
966 "---------------------------------------------------------------------------------\n"
967 , router_server, recv->body );
969 message_free( recv );
974 static int print_help( void ) {
977 "---------------------------------------------------------------------------------\n"
979 "---------------------------------------------------------------------------------\n"
980 "help - Display this message\n"
981 "!<command> [args] - Forks and runs the given command in the shell\n"
983 "time - Prints the current time\n"
984 "time <timestamp> - Formats seconds since epoch into readable format\n"
986 "set <variable> <value> - set a srfsh variable (e.g. set pretty_print true )\n"
987 "print <variable> - Displays the value of a srfsh variable\n"
988 "---------------------------------------------------------------------------------\n"
990 "router query servers <server1 [, server2, ...]>\n"
991 " - Returns stats on connected services\n"
994 "request <service> <method> [ <json formatted string of params> ]\n"
995 " - Anything passed in will be wrapped in a json array,\n"
996 " so add commas if there is more than one param\n"
999 "relay <service> <method>\n"
1000 " - Performs the requested query using the last received result as the param\n"
1003 "math_bench <num_batches> [0|1|2]\n"
1004 " - 0 means don't reconnect, 1 means reconnect after each batch of 4, and\n"
1005 " 2 means reconnect after every request\n"
1007 "introspect <service>\n"
1008 " - prints the API for the service\n"
1011 "---------------------------------------------------------------------------------\n"
1012 " Commands for Open-ILS\n"
1013 "---------------------------------------------------------------------------------\n"
1014 "login <username> <password> [type] [org_unit] [workstation]\n"
1015 " - Logs into the 'server' and displays the session id\n"
1016 " - To view the session id later, enter: print login\n"
1017 "---------------------------------------------------------------------------------\n"
1020 "Note: long output is piped through 'less'. To search in 'less', type: /<search>\n"
1021 "---------------------------------------------------------------------------------\n"
1030 static char* tabs(int count) {
1031 growing_buffer* buf = buffer_init(24);
1033 for(i=0;i!=count;i++)
1034 buffer_add(buf, " ");
1036 char* final = buffer_data( buf );
1043 static int handle_math( const osrfStringArray* cmd_array ) {
1044 const char* word = osrfStringArrayGetString( cmd_array, 1 );
1046 return do_math( atoi( word ), 0 );
1051 static int do_math( int count, int style ) {
1053 osrfAppSession* session = osrfAppSessionClientInit( "opensrf.math" );
1054 osrfAppSessionConnect(session);
1056 jsonObject* params = jsonNewObjectType( JSON_ARRAY );
1057 jsonObjectPush(params,jsonNewObject("1"));
1058 jsonObjectPush(params,jsonNewObject("2"));
1060 char* methods[] = { "add", "sub", "mult", "div" };
1061 char* answers[] = { "3", "-1", "2", "0.5" };
1063 float times[ count * 4 ];
1064 memset(times, 0, sizeof(times));
1067 for(k=0;k!=100;k++) {
1069 fprintf(stderr,"|");
1071 fprintf(stderr,".");
1074 fprintf(stderr,"\n\n");
1078 for(i=0; i!= count; i++) {
1081 for(j=0; j != 4; j++) {
1085 double start = get_timestamp_millis();
1086 int req_id = osrfAppSessionMakeRequest( session, params, methods[j], 1, NULL );
1087 osrfMessage* omsg = osrfAppSessionRequestRecv( session, req_id, 5 );
1088 double end = get_timestamp_millis();
1090 times[(4*i) + j] = end - start;
1094 if(omsg->_result_content) {
1095 char* jsn = jsonObjectToJSON(omsg->_result_content);
1096 if(!strcmp(jsn, answers[j]))
1097 fprintf(stderr, "+");
1099 fprintf(stderr, "\n![%s] - should be %s\n", jsn, answers[j] );
1104 osrfMessageFree(omsg);
1106 } else { fprintf( stderr, "\nempty message for tt: %d\n", req_id ); }
1108 osrf_app_session_request_finish( session, req_id );
1111 osrf_app_session_disconnect( session );
1114 fprintf(stderr,"\n");
1118 osrf_app_session_disconnect( session );
1121 osrfAppSessionFree( session );
1122 jsonObjectFree(params);
1126 for(c=0; c!= count*4; c++)
1129 float avg = total / (count*4);
1130 fprintf(stderr, "\n Average round trip time: %f\n", avg );
1136 @name Command line parser
1138 This group of functions parses the command line into a series of chunks, and stores
1139 the chunks in an osrfStringArray.
1141 A chunk may consist of a JSON string, complete with square brackets, curly braces, and
1142 embedded white space. It wouldn't work simply to break up the line into tokens
1143 separated by white space. Sometimes white space separates chunks, and sometimes it
1144 occurs within a chunk.
1146 When it sees a left square bracket or curly brace, the parser goes into JSON mode,
1147 collecting characters up to the corresponding right square bracket or curly brace.
1148 It also eliminates most kinds of unnecessary white space.
1150 The JSON parsing is rudimentary. It does not validate the syntax -- it merely looks
1151 for the end of the JSON string. Eventually the JSON string will be passed to a real
1152 JSON parser, which will detect and report syntax errors.
1154 When not in JSON mode, the parser collects tokens separated by white space. It also
1155 collects character strings in quotation marks, possibly including embedded white space.
1156 Within a quoted string, an embedded quotation mark does not terminate the string if it
1157 is escaped by a preceding backslash.
1161 @brief Collect a string literal enclosed by quotation marks.
1162 @param parser Pointer to an ArgParser
1164 A quotation mark serves as a terminator unless it is escaped by a preceding backslash.
1165 In the latter case, we collect both the backslash and the escaped quotation mark.
1167 static void get_string_literal( ArgParser* parser ) {
1168 // Collect character until the first unescaped quotation mark, or EOL
1170 OSRF_BUFFER_ADD_CHAR( parser->buf, *parser->itr );
1172 // Don't stop at a quotation mark if it's escaped
1173 if( '\\' == *parser->itr && '\"' == *( parser->itr + 1 ) ) {
1174 OSRF_BUFFER_ADD_CHAR( parser->buf, '\"' );
1179 } while( *parser->itr && *parser->itr != '\"' );
1181 OSRF_BUFFER_ADD_CHAR( parser->buf, '\"' );
1186 @brief Collect a JSON array (enclosed by square brackets).
1187 @param parser Pointer to an ArgParser.
1189 Collect characters until you find the closing square bracket. Collect any intervening
1190 JSON arrays, JSON objects, or string literals recursively.
1192 static void get_json_array( ArgParser* parser ) {
1194 OSRF_BUFFER_ADD_CHAR( parser->buf, '[' );
1197 // Collect characters through the closing square bracket
1198 while( *parser->itr != ']' ) {
1200 if( '\"' == *parser->itr ) {
1201 get_string_literal( parser );
1202 } else if( '[' == *parser->itr ) {
1203 get_json_array( parser );
1204 } else if( '{' == *parser->itr ) {
1205 get_json_object( parser );
1206 } else if( isspace( (unsigned char) *parser->itr ) ) {
1207 ++parser->itr; // Ignore white space
1208 } else if ( '\0' == *parser->itr ) {
1209 return; // Ignore failure to close the object
1212 // make sure that bare words don't run together
1213 OSRF_BUFFER_ADD_CHAR( parser->buf, ' ' );
1217 OSRF_BUFFER_ADD_CHAR( parser->buf, ']' );
1222 @brief Collect a JSON object (enclosed by curly braces).
1223 @param parser Pointer to an ArgParser.
1225 Collect characters until you find the closing curly brace. Collect any intervening
1226 JSON arrays, JSON objects, or string literals recursively.
1228 static void get_json_object( ArgParser* parser ) {
1230 OSRF_BUFFER_ADD_CHAR( parser->buf, '{' );
1233 // Collect characters through the closing curly brace
1234 while( *parser->itr != '}' ) {
1236 if( '\"' == *parser->itr ) {
1237 get_string_literal( parser );
1238 } else if( '[' == *parser->itr ) {
1239 get_json_array( parser );
1240 } else if( '{' == *parser->itr ) {
1241 get_json_object( parser );
1242 } else if( isspace( (unsigned char) *parser->itr ) ) {
1243 ++parser->itr; // Ignore white space
1244 } else if ( '\0' == *parser->itr ) {
1245 return; // Ignore failure to close the object
1248 // make sure that bare words don't run together
1249 OSRF_BUFFER_ADD_CHAR( parser->buf, ' ' );
1253 OSRF_BUFFER_ADD_CHAR( parser->buf, '}' );
1258 @brief Collect a token terminated by white space or a ']' or '}' character.
1259 @param parser Pointer to an ArgParser
1261 For valid JSON, the chunk collected here would be either a number or one of the
1262 JSON key words "null", "true", or "false". However at this stage we're not finicky.
1263 We just collect whatever we see until we find a terminator.
1265 static void get_misc( ArgParser* parser ) {
1266 // Collect characters until we see one that doesn't belong
1268 OSRF_BUFFER_ADD_CHAR( parser->buf, *parser->itr );
1270 char c = *parser->itr;
1271 if( '\0' == c || isspace( (unsigned char) c )
1272 || '{' == c || '}' == c || '[' == c || ']' == c || '\"' == c ) {
1279 @brief Parse the command line.
1280 @param request Pointer to the command line
1281 @param cmd_array Pointer to an osrfStringArray to hold the output of the parser.
1283 The parser operates by recursive descent. We build each chunk of command line in a
1284 growing_buffer belonging to an ArgParser, and then load the chunk into a slot in an
1285 osrfStringArray supplied by the calling code.
1287 static void parse_args( const char* request, osrfStringArray* cmd_array )
1291 // Initialize the ArgParser
1292 parser.itr = request;
1293 parser.buf = buffer_init( 128 );
1295 int done = 0; // boolean
1297 OSRF_BUFFER_RESET( parser.buf );
1299 // skip any white space
1300 while( *parser.itr && isspace( (unsigned char) *parser.itr ) )
1303 if( '\0' == *parser.itr )
1305 else if( '{' == *parser.itr ) {
1306 // Load a JSON object
1307 get_json_object( &parser );
1308 } else if( '[' == *parser.itr ) {
1309 // Load a JSON array
1310 get_json_array( &parser );
1311 } else if( '\"' == *parser.itr ) {
1312 // Load a string literal
1313 get_string_literal( &parser );
1315 // Anything else is delimited by white space
1317 OSRF_BUFFER_ADD_CHAR( parser.buf, *parser.itr );
1319 } while( *parser.itr && ! isspace( (unsigned char) *parser.itr ) );
1322 // Remove a trailing comma, if present
1323 char lastc = OSRF_BUFFER_C_STR( parser.buf )[
1324 strlen( OSRF_BUFFER_C_STR( parser.buf ) ) - 1 ];
1326 buffer_chomp( parser.buf );
1328 // Add the chunk to the osrfStringArray
1329 const char* s = OSRF_BUFFER_C_STR( parser.buf );
1331 osrfStringArrayAdd( cmd_array, s );
1335 buffer_free( parser.buf );