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* server = osrfStringArrayGetString( cmd_array, 1 );
707 const char* method = osrfStringArrayGetString( cmd_array, 2 );
711 growing_buffer* buffer = NULL;
713 int first = 1; // boolean
714 buffer = buffer_init( 128 );
715 buffer_add( buffer, "[" );
717 const char* word = osrfStringArrayGetString( cmd_array, i );
724 buffer_add( buffer, ", " );
726 buffer_add( buffer, word );
728 /* remove trailing semicolon if user accidentally entered it */
729 if( word[ strlen( word ) - 1 ] == ';' )
730 buffer_chomp( buffer );
732 buffer_add_char( buffer, ']' );
735 int rc = send_request( server, method, buffer, relay );
736 buffer_free( buffer );
743 int send_request( const char* server,
744 const char* method, growing_buffer* buffer, int relay ) {
745 if( server == NULL || method == NULL )
748 jsonObject* params = NULL;
750 if( buffer != NULL && buffer->n_used > 0 ) {
751 // Temporarily redirect parsing error messages to stderr
753 params = jsonParse( OSRF_BUFFER_C_STR( buffer ) );
754 osrfRestoreLogType();
757 if(!last_result || ! last_result->_result_content) {
758 printf("We're not going to call 'relay' with no result params\n");
762 params = jsonNewObject(NULL);
763 jsonObjectPush(params, last_result->_result_content );
767 if(buffer->n_used > 0 && params == NULL) {
768 fprintf(stderr, "JSON error detected, not executing\n");
769 jsonObjectFree(params);
773 int session_is_temporary; // boolean
774 osrfAppSession* session = osrfHashGet( server_hash, server );
776 session_is_temporary = 0; // use an existing session
778 session = osrfAppSessionClientInit(server); // open a session
779 session_is_temporary = 1; // just for this request
782 if(!osrfAppSessionConnect(session)) {
783 fprintf(stderr, "Unable to communicate with service %s\n", server);
784 osrfLogWarning( OSRF_LOG_MARK, "Unable to connect to remote service %s\n", server );
785 osrfAppSessionFree( session );
786 jsonObjectFree(params);
790 double start = get_timestamp_millis();
792 int req_id = osrfAppSessionSendRequest( session, params, method, 1 );
793 jsonObjectFree(params);
795 osrfMessage* omsg = osrfAppSessionRequestRecv( session, req_id, recv_timeout );
798 printf("\nReceived no data from server\n");
801 signal(SIGPIPE, SIG_IGN);
804 if(!is_from_script) less = popen( "less -EX", "w");
807 if( less == NULL ) { less = stdout; }
809 growing_buffer* resp_buffer = buffer_init(4096);
815 if(omsg->_result_content) {
817 osrfMessageFree(last_result);
823 char* j = jsonObjectToJSON(omsg->_result_content);
825 content = jsonFormatString(j);
828 content = strdup( "(null)" );
830 content = jsonObjectToJSON(omsg->_result_content);
832 content = strdup( "(null)" );
835 printf( "\nReceived Data: %s\n", content );
841 snprintf( code, sizeof(code), "%d", omsg->status_code );
842 buffer_add( resp_buffer, code );
844 printf( "\nReceived Exception:\nName: %s\nStatus: %s\nStatus: %s\n",
845 omsg->status_name, omsg->status_text, code );
848 osrfMessageFree(omsg);
853 if(omsg->_result_content) {
855 osrfMessageFree(last_result);
860 if( pretty_print && omsg->_result_content ) {
861 char* j = jsonObjectToJSON(omsg->_result_content);
863 content = jsonFormatString(j);
866 content = strdup( "(null)" );
868 content = jsonObjectToJSON(omsg->_result_content);
870 content = strdup( "(null)" );
873 buffer_add( resp_buffer, "\nReceived Data: " );
874 buffer_add( resp_buffer, content );
875 buffer_add_char( resp_buffer, '\n' );
880 buffer_add( resp_buffer, "\nReceived Exception:\nName: " );
881 buffer_add( resp_buffer, omsg->status_name );
882 buffer_add( resp_buffer, "\nStatus: " );
883 buffer_add( resp_buffer, omsg->status_text );
884 buffer_add( resp_buffer, "\nStatus: " );
886 snprintf( code, sizeof(code), "%d", omsg->status_code );
887 buffer_add( resp_buffer, code );
888 osrfMessageFree(omsg);
892 omsg = osrfAppSessionRequestRecv( session, req_id, recv_timeout );
896 double end = get_timestamp_millis();
898 fputs( resp_buffer->buf, less );
899 buffer_free( resp_buffer );
900 fputs("\n------------------------------------\n", less);
901 if( osrf_app_session_request_complete( session, req_id ))
902 fputs("Request Completed Successfully\n", less);
905 fprintf(less, "Request Time in seconds: %.6f\n", end - start );
906 fputs("------------------------------------\n", less);
910 osrf_app_session_request_finish( session, req_id );
912 if( session_is_temporary ) {
913 osrf_app_session_disconnect( session );
914 osrfAppSessionFree( session );
922 static int handle_time( const osrfStringArray* cmd_array ) {
924 const char* word_1 = osrfStringArrayGetString( cmd_array, 1 );
926 printf("%f\n", get_timestamp_millis());
928 time_t epoch = (time_t) atoi( word_1 );
929 printf("%s", ctime(&epoch));
935 static int router_query_servers( const char* router_server ) {
937 if( ! router_server || strlen(router_server) == 0 )
940 const static char router_text[] = "router@%s/router";
941 size_t len = sizeof( router_text ) + strlen( router_server ) + 1;
943 snprintf(rbuf, sizeof(rbuf), router_text, router_server );
945 transport_message* send =
946 message_init( "servers", NULL, NULL, rbuf, NULL );
947 message_set_router_info( send, NULL, NULL, NULL, "query", 0 );
949 client_send_message( client, send );
950 message_free( send );
952 transport_message* recv = client_recv( client, -1 );
954 fprintf(stderr, "NULL message received from router\n");
959 "---------------------------------------------------------------------------------\n"
960 "Received from 'server' query on %s\n"
961 "---------------------------------------------------------------------------------\n"
962 "original reg time | latest reg time | last used time | class | server\n"
963 "---------------------------------------------------------------------------------\n"
965 "---------------------------------------------------------------------------------\n"
966 , router_server, recv->body );
968 message_free( recv );
973 static int print_help( void ) {
976 "---------------------------------------------------------------------------------\n"
977 "General commands:\n"
978 "---------------------------------------------------------------------------------\n"
979 "help - Display this message\n"
980 "!<command> [args] - Forks and runs the given command in the shell\n"
982 "time - Prints the current time\n"
983 "time <timestamp> - Formats seconds since epoch into readable format\n"
985 "set <variable> <value> - Set a srfsh variable (e.g. set pretty_print true )\n"
986 "print <variable> - Displays the value of a srfsh variable\n"
988 "---------------------------------------------------------------------------------\n"
990 "---------------------------------------------------------------------------------\n"
991 "pretty_print - Display nicely formatted JSON results\n"
992 " - Accepted values: true, false\n"
993 " - Default value: true\n"
995 "raw_print - Pass JSON results through 'less' paging command\n"
996 " - Accepted values: true, false\n"
997 " - Default value: false\n"
999 "---------------------------------------------------------------------------------\n"
1000 "Commands for OpenSRF services and methods:\n"
1001 "---------------------------------------------------------------------------------\n"
1002 "introspect <service> [\"method-name\"]\n"
1003 " - Prints service API, limited to the methods that match the optional\n"
1004 " right-truncated method-name parameter\n"
1006 "request <service> <method> [ <JSON formatted string of params> ]\n"
1007 " - Anything passed in will be wrapped in a JSON array,\n"
1008 " so add commas if there is more than one param\n"
1010 "router query servers <server1 [, server2, ...]>\n"
1011 " - Returns stats on connected services\n"
1013 "relay <service> <method>\n"
1014 " - Performs the requested query using the last received result as the param\n"
1016 "math_bench <num_batches> [0|1|2]\n"
1017 " - 0 means don't reconnect, 1 means reconnect after each batch of 4, and\n"
1018 " 2 means reconnect after every request\n"
1020 "---------------------------------------------------------------------------------\n"
1021 " Commands for Evergreen\n"
1022 "---------------------------------------------------------------------------------\n"
1023 "login <username> <password> [type] [org_unit] [workstation]\n"
1024 " - Logs into the 'server' and displays the session id\n"
1025 " - To view the session id later, enter: print login\n"
1026 "---------------------------------------------------------------------------------\n"
1028 "Note: long output is piped through 'less' unless the 'raw_print' variable\n"
1029 "is true. To search in 'less', type: /<search>\n"
1030 "---------------------------------------------------------------------------------\n"
1039 static char* tabs(int count) {
1040 growing_buffer* buf = buffer_init(24);
1042 for(i=0;i!=count;i++)
1043 buffer_add(buf, " ");
1045 char* final = buffer_data( buf );
1052 static int handle_math( const osrfStringArray* cmd_array ) {
1053 const char* word = osrfStringArrayGetString( cmd_array, 1 );
1055 return do_math( atoi( word ), 0 );
1060 static int do_math( int count, int style ) {
1062 osrfAppSession* session = osrfAppSessionClientInit( "opensrf.math" );
1063 osrfAppSessionConnect(session);
1065 jsonObject* params = jsonNewObjectType( JSON_ARRAY );
1066 jsonObjectPush(params,jsonNewObject("1"));
1067 jsonObjectPush(params,jsonNewObject("2"));
1069 char* methods[] = { "add", "sub", "mult", "div" };
1070 char* answers[] = { "3", "-1", "2", "0.5" };
1072 float times[ count * 4 ];
1073 memset(times, 0, sizeof(times));
1076 for(k=0;k!=100;k++) {
1078 fprintf(stderr,"|");
1080 fprintf(stderr,".");
1083 fprintf(stderr,"\n\n");
1087 for(i=0; i!= count; i++) {
1090 for(j=0; j != 4; j++) {
1094 double start = get_timestamp_millis();
1095 int req_id = osrfAppSessionSendRequest( session, params, methods[j], 1 );
1096 osrfMessage* omsg = osrfAppSessionRequestRecv( session, req_id, 5 );
1097 double end = get_timestamp_millis();
1099 times[(4*i) + j] = end - start;
1103 if(omsg->_result_content) {
1104 char* jsn = jsonObjectToJSON(omsg->_result_content);
1105 if(!strcmp(jsn, answers[j]))
1106 fprintf(stderr, "+");
1108 fprintf(stderr, "\n![%s] - should be %s\n", jsn, answers[j] );
1113 osrfMessageFree(omsg);
1115 } else { fprintf( stderr, "\nempty message for tt: %d\n", req_id ); }
1117 osrf_app_session_request_finish( session, req_id );
1120 osrf_app_session_disconnect( session );
1123 fprintf(stderr,"\n");
1127 osrf_app_session_disconnect( session );
1130 osrfAppSessionFree( session );
1131 jsonObjectFree(params);
1135 for(c=0; c!= count*4; c++)
1138 float avg = total / (count*4);
1139 fprintf(stderr, "\n Average round trip time: %f\n", avg );
1145 @name Command line parser
1147 This group of functions parses the command line into a series of chunks, and stores
1148 the chunks in an osrfStringArray.
1150 A chunk may consist of a JSON string, complete with square brackets, curly braces, and
1151 embedded white space. It wouldn't work simply to break up the line into tokens
1152 separated by white space. Sometimes white space separates chunks, and sometimes it
1153 occurs within a chunk.
1155 When it sees a left square bracket or curly brace, the parser goes into JSON mode,
1156 collecting characters up to the corresponding right square bracket or curly brace.
1157 It also eliminates most kinds of unnecessary white space.
1159 The JSON parsing is rudimentary. It does not validate the syntax -- it merely looks
1160 for the end of the JSON string. Eventually the JSON string will be passed to a real
1161 JSON parser, which will detect and report syntax errors.
1163 When not in JSON mode, the parser collects tokens separated by white space. It also
1164 collects character strings in quotation marks, possibly including embedded white space.
1165 Within a quoted string, an embedded quotation mark does not terminate the string if it
1166 is escaped by a preceding backslash.
1170 @brief Collect a string literal enclosed by quotation marks.
1171 @param parser Pointer to an ArgParser
1173 A quotation mark serves as a terminator unless it is escaped by a preceding backslash.
1174 In the latter case, we collect both the backslash and the escaped quotation mark.
1176 static void get_string_literal( ArgParser* parser ) {
1177 // Collect character until the first unescaped quotation mark, or EOL
1179 OSRF_BUFFER_ADD_CHAR( parser->buf, *parser->itr );
1181 // Don't stop at a quotation mark if it's escaped
1182 if( '\\' == *parser->itr && '\"' == *( parser->itr + 1 ) ) {
1183 OSRF_BUFFER_ADD_CHAR( parser->buf, '\"' );
1188 } while( *parser->itr && *parser->itr != '\"' );
1190 OSRF_BUFFER_ADD_CHAR( parser->buf, '\"' );
1195 @brief Collect a JSON array (enclosed by square brackets).
1196 @param parser Pointer to an ArgParser.
1198 Collect characters until you find the closing square bracket. Collect any intervening
1199 JSON arrays, JSON objects, or string literals recursively.
1201 static void get_json_array( ArgParser* parser ) {
1203 OSRF_BUFFER_ADD_CHAR( parser->buf, '[' );
1206 // Collect characters through the closing square bracket
1207 while( *parser->itr != ']' ) {
1209 if( '\"' == *parser->itr ) {
1210 get_string_literal( parser );
1211 } else if( '[' == *parser->itr ) {
1212 get_json_array( parser );
1213 } else if( '{' == *parser->itr ) {
1214 get_json_object( parser );
1215 } else if( isspace( (unsigned char) *parser->itr ) ) {
1216 ++parser->itr; // Ignore white space
1217 } else if ( '\0' == *parser->itr ) {
1218 return; // Ignore failure to close the object
1221 // make sure that bare words don't run together
1222 OSRF_BUFFER_ADD_CHAR( parser->buf, ' ' );
1226 OSRF_BUFFER_ADD_CHAR( parser->buf, ']' );
1231 @brief Collect a JSON object (enclosed by curly braces).
1232 @param parser Pointer to an ArgParser.
1234 Collect characters until you find the closing curly brace. Collect any intervening
1235 JSON arrays, JSON objects, or string literals recursively.
1237 static void get_json_object( ArgParser* parser ) {
1239 OSRF_BUFFER_ADD_CHAR( parser->buf, '{' );
1242 // Collect characters through the closing curly brace
1243 while( *parser->itr != '}' ) {
1245 if( '\"' == *parser->itr ) {
1246 get_string_literal( parser );
1247 } else if( '[' == *parser->itr ) {
1248 get_json_array( parser );
1249 } else if( '{' == *parser->itr ) {
1250 get_json_object( parser );
1251 } else if( isspace( (unsigned char) *parser->itr ) ) {
1252 ++parser->itr; // Ignore white space
1253 } else if ( '\0' == *parser->itr ) {
1254 return; // Ignore failure to close the object
1257 // make sure that bare words don't run together
1258 OSRF_BUFFER_ADD_CHAR( parser->buf, ' ' );
1262 OSRF_BUFFER_ADD_CHAR( parser->buf, '}' );
1267 @brief Collect a token terminated by white space or a ']' or '}' character.
1268 @param parser Pointer to an ArgParser
1270 For valid JSON, the chunk collected here would be either a number or one of the
1271 JSON key words "null", "true", or "false". However at this stage we're not finicky.
1272 We just collect whatever we see until we find a terminator.
1274 static void get_misc( ArgParser* parser ) {
1275 // Collect characters until we see one that doesn't belong
1277 OSRF_BUFFER_ADD_CHAR( parser->buf, *parser->itr );
1279 char c = *parser->itr;
1280 if( '\0' == c || isspace( (unsigned char) c )
1281 || '{' == c || '}' == c || '[' == c || ']' == c || '\"' == c ) {
1288 @brief Parse the command line.
1289 @param request Pointer to the command line
1290 @param cmd_array Pointer to an osrfStringArray to hold the output of the parser.
1292 The parser operates by recursive descent. We build each chunk of command line in a
1293 growing_buffer belonging to an ArgParser, and then load the chunk into a slot in an
1294 osrfStringArray supplied by the calling code.
1296 static void parse_args( const char* request, osrfStringArray* cmd_array )
1300 // Initialize the ArgParser
1301 parser.itr = request;
1302 parser.buf = buffer_init( 128 );
1304 int done = 0; // boolean
1306 OSRF_BUFFER_RESET( parser.buf );
1308 // skip any white space or commas
1310 && ( isspace( (unsigned char) *parser.itr ) || ',' == *parser.itr ) )
1313 if( '\0' == *parser.itr )
1315 else if( '{' == *parser.itr ) {
1316 // Load a JSON object
1317 get_json_object( &parser );
1318 } else if( '[' == *parser.itr ) {
1319 // Load a JSON array
1320 get_json_array( &parser );
1321 } else if( '\"' == *parser.itr ) {
1322 // Load a string literal
1323 get_string_literal( &parser );
1325 // Anything else is delimited by white space
1327 OSRF_BUFFER_ADD_CHAR( parser.buf, *parser.itr );
1329 } while( *parser.itr && ! isspace( (unsigned char) *parser.itr ) );
1332 // Remove a trailing comma, if present
1333 char lastc = OSRF_BUFFER_C_STR( parser.buf )[
1334 strlen( OSRF_BUFFER_C_STR( parser.buf ) ) - 1 ];
1336 buffer_chomp( parser.buf );
1338 // Add the chunk to the osrfStringArray
1339 const char* s = OSRF_BUFFER_C_STR( parser.buf );
1341 osrfStringArrayAdd( cmd_array, s );
1345 buffer_free( parser.buf );