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();
143 osrfAppSessionSetIngress("srfsh");
145 // Disable special treatment for tabs by readline
146 // (by default they invoke command completion, which
147 // is not useful for srfsh)
148 rl_bind_key( '\t', rl_insert );
150 /* main process loop */
151 int newline_needed = 1; /* used as boolean */
153 while( (request = get_request()) ) {
155 // Find first non-whitespace character
157 char * cmd = request;
158 while( isspace( (unsigned char) *cmd ) )
161 // Remove trailing whitespace. We know at this point that
162 // there is at least one non-whitespace character somewhere,
163 // or we would have already skipped this line. Hence we
164 // needn't check to make sure that we don't back up past
168 // The curly braces limit the scope of the end variable
170 char * end = cmd + strlen(cmd) - 1;
171 while( isspace( (unsigned char) *end ) )
176 if( !strcasecmp(cmd, "exit") || !strcasecmp(cmd, "quit"))
182 process_request( cmd );
183 if( request && *cmd ) {
184 add_history(request);
193 if( newline_needed ) {
195 // We left the readline loop after seeing an EOF, not after
196 // seeing "quit" or "exit". So we issue a newline in order
197 // to avoid leaving a dangling prompt.
202 if(history_file != NULL )
203 write_history(history_file);
209 if( osrfHashGetCount( server_hash ) > 0 )
210 close_all_sessions();
211 osrfHashFree( server_hash );
214 osrf_system_shutdown();
218 // Get a logical line from one or more calls to readline(),
219 // skipping blank lines and comments. Stitch continuation
220 // lines together as needed. Caller is responsible for
221 // freeing the string returned.
222 // If EOF appears before a logical line is completed,
224 static char* get_request( void ) {
228 // Get the first physical line of the logical line
230 line = readline( prompt );
232 return NULL; // end of file
234 // Skip leading white space
235 for( p = line; isspace( *p ); ++p )
238 if( '\\' == *p && '\0' == p[1] ) {
239 // Just a trailing backslash; skip to next line
242 } else if( '\0' == p[0] || '#' == *p ) {
244 continue; // blank line or comment; skip it
246 break; // Not blank, not comment; take it
249 char* end = line + strlen( line ) - 1;
251 return line; // No continuation line; we're done
253 // Remove the trailing backslash and collect
254 // the continuation line(s) into a growing_buffer
257 growing_buffer* logical_line = buffer_init( 256 );
258 buffer_add( logical_line, p );
261 // Append any continuation lines
262 int finished = 0; // boolean
264 line = readline( "> " );
267 // Check for another continuation
268 end = line + strlen( line ) - 1;
274 buffer_add( logical_line, line );
277 fprintf( stderr, "Expected continuation line; found end of file\n" );
278 buffer_free( logical_line );
283 return buffer_release( logical_line );
286 static int load_history( void ) {
288 char* home = getenv("HOME");
289 int l = strlen(home) + 24;
291 snprintf(fbuf, sizeof(fbuf), "%s/.srfsh_history", home);
292 history_file = strdup(fbuf);
294 if(!access(history_file, W_OK | R_OK )) {
295 history_length = 5000;
296 read_history(history_file);
302 static int parse_error( const char* request ) {
306 fprintf( stderr, "???: %s\n", request );
312 static int process_request( const char* request ) {
314 if( request == NULL )
318 osrfStringArray* cmd_array = osrfNewStringArray( 32 );
320 parse_args( request, cmd_array );
321 int wordcount = cmd_array->size;
322 if( 0 == wordcount ) {
323 printf( "No words found in command\n" );
324 osrfStringArrayFree( cmd_array );
328 /* pass off to the top level command */
329 const char* command = osrfStringArrayGetString( cmd_array, 0 );
330 if( !strcmp( command, "router" ) )
331 ret_val = handle_router( cmd_array );
333 else if( !strcmp( command, "time" ) )
334 ret_val = handle_time( cmd_array );
336 else if ( !strcmp( command, "request" ) )
337 ret_val = handle_request( cmd_array, 0 );
339 else if ( !strcmp( command, "relay" ) )
340 ret_val = handle_request( cmd_array, 1 );
342 else if ( !strcmp( command, "help" ) )
343 ret_val = print_help();
345 else if ( !strcmp( command, "set" ) )
346 ret_val = handle_set( cmd_array );
348 else if ( !strcmp( command, "print" ) )
349 ret_val = handle_print( cmd_array );
351 else if ( !strcmp( command, "math_bench" ) )
352 ret_val = handle_math( cmd_array );
354 else if ( !strcmp( command, "introspect" ) )
355 ret_val = handle_introspect( cmd_array );
357 else if ( !strcmp( command, "login" ) )
358 ret_val = handle_login( cmd_array );
360 else if ( !strcmp( command, "open" ) )
361 ret_val = handle_open( cmd_array );
363 else if ( !strcmp( command, "close" ) )
364 ret_val = handle_close( cmd_array );
366 else if ( request[0] == '!') {
367 system( request + 1 );
371 osrfStringArrayFree( cmd_array );
374 return parse_error( request );
380 static int handle_introspect( const osrfStringArray* cmd_array ) {
382 const char* service = osrfStringArrayGetString( cmd_array, 1 );
386 fprintf(stderr, "--> %s\n", service );
388 // Build a command in a suitably-sized
389 // buffer and then parse it
392 const char* method = osrfStringArrayGetString( cmd_array, 2 );
394 static const char text[] = "request %s opensrf.system.method %s";
395 len = sizeof( text ) + strlen( service ) + strlen( method );
397 snprintf( buf, sizeof(buf), text, service, method );
398 return process_request( buf );
401 static const char text[] = "request %s opensrf.system.method.all";
402 len = sizeof( text ) + strlen( service );
404 snprintf( buf, sizeof(buf), text, service );
405 return process_request( buf );
411 static int handle_login( const osrfStringArray* cmd_array ) {
413 const char* username = osrfStringArrayGetString( cmd_array, 1 );
414 const char* password = osrfStringArrayGetString( cmd_array, 2 );
416 if( username && password ) {
418 const char* type = osrfStringArrayGetString( cmd_array, 3 );
419 const char* orgloc = osrfStringArrayGetString( cmd_array, 4 );
420 const char* workstation = osrfStringArrayGetString( cmd_array, 5 );
421 int orgloci = (orgloc) ? atoi(orgloc) : 0;
422 if(!type) type = "opac";
424 char login_text[] = "request open-ils.auth open-ils.auth.authenticate.init \"%s\"";
425 size_t len = sizeof( login_text ) + strlen(username) + 1;
428 snprintf( buf, sizeof(buf), login_text, username );
429 process_request(buf);
432 if(last_result && last_result->_result_content) {
433 jsonObject* r = last_result->_result_content;
434 hash = jsonObjectGetString(r);
437 char* pass_buf = md5sum(password);
439 size_t both_len = strlen( hash ) + strlen( pass_buf ) + 1;
440 char both_buf[both_len];
441 snprintf(both_buf, sizeof(both_buf), "%s%s", hash, pass_buf);
443 char* mess_buf = md5sum(both_buf);
445 growing_buffer* argbuf = buffer_init(64);
447 "request open-ils.auth open-ils.auth.authenticate.complete "
448 "{ \"username\" : \"%s\", \"password\" : \"%s\"", username, mess_buf );
450 if(type) buffer_fadd( argbuf, ", \"type\" : \"%s\"", type );
451 if(orgloci) buffer_fadd( argbuf, ", \"org\" : %d", orgloci );
452 if(workstation) buffer_fadd( argbuf, ", \"workstation\" : \"%s\"", workstation);
453 buffer_add_char( argbuf, '}' );
458 process_request( argbuf->buf );
461 if( login_session != NULL )
462 free( login_session );
464 const jsonObject* x = last_result->_result_content;
467 const char* authtoken = jsonObjectGetString(
468 jsonObjectGetKeyConst(jsonObjectGetKeyConst(x,"payload"), "authtoken"));
469 authtime = jsonObjectGetNumber(
470 jsonObjectGetKeyConst(jsonObjectGetKeyConst(x,"payload"), "authtime"));
473 login_session = strdup(authtoken);
475 login_session = NULL;
477 else login_session = NULL;
479 printf("Login Session: %s. Session timeout: %f\n",
480 (login_session ? login_session : "(none)"), authtime );
490 @brief Open connections to one or more specified services.
491 @param cmd_array Pointer to a list of command line chunks.
492 @return 1 in all cases.
494 The first chunk of the command line is the "open" command. Subsequent chunks, if any,
497 Try to open all specified servers. If no servers are specified, report what servers are
500 static int handle_open( const osrfStringArray* cmd_array ) {
501 if( NULL == osrfStringArrayGetString( cmd_array, 1 ) ) {
502 if( ! server_hash || osrfHashGetCount( server_hash ) == 0 ) {
503 printf( "No services are currently open\n" );
507 printf( "Service(s) currently open:\n" );
509 osrfHashIterator* itr = osrfNewHashIterator( server_hash );
510 while( osrfHashIteratorNext( itr ) ) {
511 printf( "\t%s\n", osrfHashIteratorKey( itr ) );
513 osrfHashIteratorFree( itr );
518 server_hash = osrfNewHash( 6 );
521 for( i = 1; ; ++i ) { // for each requested service
522 const char* server = osrfStringArrayGetString( cmd_array, i );
526 if( osrfHashGet( server_hash, server ) ) {
527 printf( "Service %s is already open\n", server );
531 // Try to open a session with the current specified server
532 osrfAppSession* session = osrfAppSessionClientInit(server);
534 if(!osrfAppSessionConnect(session)) {
535 fprintf(stderr, "Unable to open service %s\n", server);
536 osrfLogWarning( OSRF_LOG_MARK, "Unable to open remote service %s\n", server );
537 osrfAppSessionFree( session );
539 osrfHashSet( server_hash, session, server );
540 printf( "Service %s opened\n", server );
548 @brief Close connections to one or more specified services.
549 @param cmd_array Pointer to a list of command line chunks.
550 @return 1 if any services were closed, or 0 if there were none to close.
552 The first chunk of the command line is the "close" command. Subsequent chunks, if any,
555 static int handle_close( const osrfStringArray* cmd_array ) {
556 if( cmd_array->size < 2 ) {
557 fprintf( stderr, "No service specified for close\n" );
562 for( i = 1; ; ++i ) {
563 const char* server = osrfStringArrayGetString( cmd_array, i );
567 osrfAppSession* session = osrfHashRemove( server_hash, server );
569 printf( "Service \"%s\" is not open\n", server );
573 osrf_app_session_disconnect( session );
574 osrfAppSessionFree( session );
575 printf( "Service \"%s\" closed\n", server );
582 @brief Close all currently open connections to services.
584 static void close_all_sessions( void ) {
586 osrfAppSession* session;
587 osrfHashIterator* itr = osrfNewHashIterator( server_hash );
589 while(( session = osrfHashIteratorNext( itr ) )) {
590 osrf_app_session_disconnect( session );
591 osrfAppSessionFree( session );
594 osrfHashIteratorFree( itr );
597 static int handle_set( const osrfStringArray* cmd_array ) {
599 const char* variable = osrfStringArrayGetString( cmd_array, 1 );
602 const char* val = osrfStringArrayGetString( cmd_array, 2 );
605 if(!strcmp(variable,"pretty_print")) {
606 if(!strcmp(val,"true")) {
608 printf("pretty_print = true\n");
610 } else if(!strcmp(val,"false")) {
612 printf("pretty_print = false\n");
617 if(!strcmp(variable,"raw_print")) {
618 if(!strcmp(val,"true")) {
620 printf("raw_print = true\n");
622 } else if(!strcmp(val,"false")) {
624 printf("raw_print = false\n");
636 static int handle_print( const osrfStringArray* cmd_array ) {
638 const char* variable = osrfStringArrayGetString( cmd_array, 1 );
641 if(!strcmp(variable,"pretty_print")) {
643 printf("pretty_print = true\n");
646 printf("pretty_print = false\n");
651 if(!strcmp(variable,"raw_print")) {
653 printf("raw_print = true\n");
656 printf("raw_print = false\n");
661 if(!strcmp(variable,"login")) {
662 printf("login session = %s\n",
663 login_session ? login_session : "(none)" );
671 static int handle_router( const osrfStringArray* cmd_array ) {
678 const char* word_1 = osrfStringArrayGetString( cmd_array, 1 );
679 const char* word_2 = osrfStringArrayGetString( cmd_array, 2 );
681 if( !strcmp( word_1,"query") ) {
683 if( word_2 && !strcmp( word_2, "servers" ) ) {
684 for( i=3; i < COMMAND_BUFSIZE - 3; i++ ) {
685 const char* word = osrfStringArrayGetString( cmd_array, i );
687 router_query_servers( word );
702 static int handle_request( const osrfStringArray* cmd_array, int relay ) {
707 const char* server = osrfStringArrayGetString( cmd_array, 1 );
708 const char* method = osrfStringArrayGetString( cmd_array, 2 );
712 growing_buffer* buffer = NULL;
714 int first = 1; // boolean
715 buffer = buffer_init( 128 );
716 buffer_add_char( buffer, '[' );
718 const char* word = osrfStringArrayGetString( cmd_array, i );
725 buffer_add( buffer, ", " );
727 buffer_add( buffer, word );
729 /* remove trailing semicolon if user accidentally entered it */
730 if( word[ strlen( word ) - 1 ] == ';' )
731 buffer_chomp( buffer );
733 buffer_add_char( buffer, ']' );
736 int rc = send_request( server, method, buffer, relay );
737 buffer_free( buffer );
744 int send_request( const char* server,
745 const char* method, growing_buffer* buffer, int relay ) {
746 if( server == NULL || method == NULL )
749 jsonObject* params = NULL;
751 if( buffer != NULL && buffer->n_used > 0 ) {
752 // Temporarily redirect parsing error messages to stderr
754 params = jsonParse( OSRF_BUFFER_C_STR( buffer ) );
755 osrfRestoreLogType();
758 if(!last_result || ! last_result->_result_content) {
759 printf("We're not going to call 'relay' with no result params\n");
763 params = jsonNewObject(NULL);
764 jsonObjectPush(params, last_result->_result_content );
768 if(buffer->n_used > 0 && params == NULL) {
769 fprintf(stderr, "JSON error detected, not executing\n");
770 jsonObjectFree(params);
774 int session_is_temporary; // boolean
775 osrfAppSession* session = osrfHashGet( server_hash, server );
777 session_is_temporary = 0; // use an existing session
779 session = osrfAppSessionClientInit(server); // open a session
780 session_is_temporary = 1; // just for this request
783 double start = get_timestamp_millis();
785 int req_id = osrfAppSessionSendRequest( session, params, method, 1 );
787 fprintf(stderr, "Unable to communicate with service %s\n", server);
788 osrfLogWarning( OSRF_LOG_MARK,
789 "Unable to communicate with remote service %s\n", server );
790 osrfAppSessionFree( session );
791 jsonObjectFree(params);
794 jsonObjectFree(params);
796 osrfMessage* omsg = osrfAppSessionRequestRecv( session, req_id, recv_timeout );
799 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);
904 fprintf(less, "Request Time in seconds: %.6f\n", end - start );
905 fputs("------------------------------------\n", less);
909 osrf_app_session_request_finish( session, req_id );
911 if( session_is_temporary )
912 osrfAppSessionFree( session );
919 static int handle_time( const osrfStringArray* cmd_array ) {
921 const char* word_1 = osrfStringArrayGetString( cmd_array, 1 );
923 printf("%f\n", get_timestamp_millis());
925 time_t epoch = (time_t) atoi( word_1 );
926 printf("%s", ctime(&epoch));
932 static int router_query_servers( const char* router_server ) {
934 if( ! router_server || strlen(router_server) == 0 )
937 const static char router_text[] = "router@%s/router";
938 size_t len = sizeof( router_text ) + strlen( router_server ) + 1;
940 snprintf(rbuf, sizeof(rbuf), router_text, router_server );
942 transport_message* send =
943 message_init( "servers", NULL, NULL, rbuf, NULL );
944 message_set_router_info( send, NULL, NULL, NULL, "query", 0 );
946 client_send_message( client, send );
947 message_free( send );
949 transport_message* recv = client_recv( client, -1 );
951 fprintf(stderr, "NULL message received from router\n");
956 "---------------------------------------------------------------------------------\n"
957 "Received from 'server' query on %s\n"
958 "---------------------------------------------------------------------------------\n"
959 "original reg time | latest reg time | last used time | class | server\n"
960 "---------------------------------------------------------------------------------\n"
962 "---------------------------------------------------------------------------------\n"
963 , router_server, recv->body );
965 message_free( recv );
970 static int print_help( void ) {
973 "---------------------------------------------------------------------------------\n"
974 "General commands:\n"
975 "---------------------------------------------------------------------------------\n"
976 "help - Display this message\n"
977 "!<command> [args] - Forks and runs the given command in the shell\n"
979 "time - Prints the current time\n"
980 "time <timestamp> - Formats seconds since epoch into readable format\n"
982 "set <variable> <value> - Set a srfsh variable (e.g. set pretty_print true )\n"
983 "print <variable> - Displays the value of a srfsh variable\n"
985 "---------------------------------------------------------------------------------\n"
987 "---------------------------------------------------------------------------------\n"
988 "pretty_print - Display nicely formatted JSON results\n"
989 " - Accepted values: true, false\n"
990 " - Default value: true\n"
992 "raw_print - Pass JSON results through 'less' paging command\n"
993 " - Accepted values: true, false\n"
994 " - Default value: false\n"
996 "---------------------------------------------------------------------------------\n"
997 "Commands for OpenSRF services and methods:\n"
998 "---------------------------------------------------------------------------------\n"
999 "introspect <service> [\"method-name\"]\n"
1000 " - Prints service API, limited to the methods that match the optional\n"
1001 " right-truncated method-name parameter\n"
1003 "request <service> <method> [ <JSON formatted string of params> ]\n"
1004 " - Anything passed in will be wrapped in a JSON array,\n"
1005 " so add commas if there is more than one param\n"
1007 "router query servers <server1 [, server2, ...]>\n"
1008 " - Returns stats on connected services\n"
1010 "relay <service> <method>\n"
1011 " - Performs the requested query using the last received result as the param\n"
1013 "math_bench <num_batches> [0|1|2]\n"
1014 " - 0 means don't reconnect, 1 means reconnect after each batch of 4, and\n"
1015 " 2 means reconnect after every request\n"
1017 "---------------------------------------------------------------------------------\n"
1018 " Commands for Evergreen\n"
1019 "---------------------------------------------------------------------------------\n"
1020 "login <username> <password> [type] [org_unit] [workstation]\n"
1021 " - Logs into the 'server' and displays the session id\n"
1022 " - To view the session id later, enter: print login\n"
1023 "---------------------------------------------------------------------------------\n"
1025 "Note: long output is piped through 'less' unless the 'raw_print' variable\n"
1026 "is true. To search in 'less', type: /<search>\n"
1027 "---------------------------------------------------------------------------------\n"
1036 static char* tabs(int count) {
1037 growing_buffer* buf = buffer_init(24);
1039 for(i=0;i!=count;i++)
1040 buffer_add(buf, " ");
1042 char* final = buffer_data( buf );
1049 @brief Execute the "math_bench" command.
1050 @param cmd_array A list of command arguments.
1051 @return 1 if successful, 0 if not.
1053 The first command argument is required. It is the number of iterations requested. If
1054 it is less than 1, it is coerced to 1.
1056 The second command argument is optional, with allowed values of 0 (the default), 1, or 2.
1057 It controls when and whether we call osrf_app_session_disconnect(). If this argument is
1058 out of range, it is coerced to a value of 0 or 2.
1060 static int handle_math( const osrfStringArray* cmd_array ) {
1061 const char* word = osrfStringArrayGetString( cmd_array, 1 );
1063 int count = atoi( word );
1068 const char* style_arg = osrfStringArrayGetString( cmd_array, 2 );
1070 style = atoi( style_arg );
1073 else if( style < 0 )
1077 return do_math( count, style );
1083 static int do_math( int count, int style ) {
1085 osrfAppSession* session = osrfAppSessionClientInit( "opensrf.math" );
1086 osrfAppSessionConnect(session);
1088 jsonObject* params = jsonNewObjectType( JSON_ARRAY );
1089 jsonObjectPush(params,jsonNewObject("1"));
1090 jsonObjectPush(params,jsonNewObject("2"));
1092 char* methods[] = { "add", "sub", "mult", "div" };
1093 char* answers[] = { "3", "-1", "2", "0.5" };
1095 // Initialize timings to zero. This shouldn't make a difference, because
1096 // we overwrite each timing anyway before reporting them.
1097 float times[ count * 4 ];
1099 for( fi = 0; fi < count; ++fi )
1103 for(k=0;k!=100;k++) {
1105 fprintf(stderr,"|");
1107 fprintf(stderr,".");
1110 fprintf(stderr,"\n\n");
1114 for(i=0; i!= count; i++) {
1117 for(j=0; j != 4; j++) {
1121 double start = get_timestamp_millis();
1122 int req_id = osrfAppSessionSendRequest( session, params, methods[j], 1 );
1123 osrfMessage* omsg = osrfAppSessionRequestRecv( session, req_id, 5 );
1124 double end = get_timestamp_millis();
1126 times[(4*i) + j] = end - start;
1130 if(omsg->_result_content) {
1131 char* jsn = jsonObjectToJSON(omsg->_result_content);
1132 if(!strcmp(jsn, answers[j]))
1133 fprintf(stderr, "+");
1135 fprintf(stderr, "\n![%s] - should be %s\n", jsn, answers[j] );
1140 osrfMessageFree(omsg);
1142 } else { fprintf( stderr, "\nempty message for tt: %d\n", req_id ); }
1144 osrf_app_session_request_finish( session, req_id );
1147 osrf_app_session_disconnect( session );
1150 fprintf(stderr,"\n");
1154 osrf_app_session_disconnect( session );
1157 osrfAppSessionFree( session );
1158 jsonObjectFree(params);
1162 for(c=0; c!= count*4; c++)
1165 float avg = total / (count*4);
1166 fprintf(stderr, "\n Average round trip time: %f\n", avg );
1172 @name Command line parser
1174 This group of functions parses the command line into a series of chunks, and stores
1175 the chunks in an osrfStringArray.
1177 A chunk may consist of a JSON string, complete with square brackets, curly braces, and
1178 embedded white space. It wouldn't work simply to break up the line into tokens
1179 separated by white space. Sometimes white space separates chunks, and sometimes it
1180 occurs within a chunk.
1182 When it sees a left square bracket or curly brace, the parser goes into JSON mode,
1183 collecting characters up to the corresponding right square bracket or curly brace.
1184 It also eliminates most kinds of unnecessary white space.
1186 The JSON parsing is rudimentary. It does not validate the syntax -- it merely looks
1187 for the end of the JSON string. Eventually the JSON string will be passed to a real
1188 JSON parser, which will detect and report syntax errors.
1190 When not in JSON mode, the parser collects tokens separated by white space. It also
1191 collects character strings in quotation marks, possibly including embedded white space.
1192 Within a quoted string, an embedded quotation mark does not terminate the string if it
1193 is escaped by a preceding backslash.
1197 @brief Collect a string literal enclosed by quotation marks.
1198 @param parser Pointer to an ArgParser
1200 A quotation mark serves as a terminator unless it is escaped by a preceding backslash.
1201 In the latter case, we collect both the backslash and the escaped quotation mark.
1203 static void get_string_literal( ArgParser* parser ) {
1204 // Collect character until the first unescaped quotation mark, or EOL
1206 OSRF_BUFFER_ADD_CHAR( parser->buf, *parser->itr );
1208 // Don't stop at a quotation mark if it's escaped
1209 if( '\\' == *parser->itr && '\"' == *( parser->itr + 1 ) ) {
1210 OSRF_BUFFER_ADD_CHAR( parser->buf, '\"' );
1215 } while( *parser->itr && *parser->itr != '\"' );
1217 OSRF_BUFFER_ADD_CHAR( parser->buf, '\"' );
1222 @brief Collect a JSON array (enclosed by square brackets).
1223 @param parser Pointer to an ArgParser.
1225 Collect characters until you find the closing square bracket. Collect any intervening
1226 JSON arrays, JSON objects, or string literals recursively.
1228 static void get_json_array( ArgParser* parser ) {
1230 OSRF_BUFFER_ADD_CHAR( parser->buf, '[' );
1233 // Collect characters through the closing square bracket
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 JSON object (enclosed by curly braces).
1259 @param parser Pointer to an ArgParser.
1261 Collect characters until you find the closing curly brace. Collect any intervening
1262 JSON arrays, JSON objects, or string literals recursively.
1264 static void get_json_object( ArgParser* parser ) {
1266 OSRF_BUFFER_ADD_CHAR( parser->buf, '{' );
1269 // Collect characters through the closing curly brace
1270 while( *parser->itr != '}' ) {
1272 if( '\"' == *parser->itr ) {
1273 get_string_literal( parser );
1274 } else if( '[' == *parser->itr ) {
1275 get_json_array( parser );
1276 } else if( '{' == *parser->itr ) {
1277 get_json_object( parser );
1278 } else if( isspace( (unsigned char) *parser->itr ) ) {
1279 ++parser->itr; // Ignore white space
1280 } else if ( '\0' == *parser->itr ) {
1281 return; // Ignore failure to close the object
1284 // make sure that bare words don't run together
1285 OSRF_BUFFER_ADD_CHAR( parser->buf, ' ' );
1289 OSRF_BUFFER_ADD_CHAR( parser->buf, '}' );
1294 @brief Collect a token terminated by white space or a ']' or '}' character.
1295 @param parser Pointer to an ArgParser
1297 For valid JSON, the chunk collected here would be either a number or one of the
1298 JSON key words "null", "true", or "false". However at this stage we're not finicky.
1299 We just collect whatever we see until we find a terminator.
1301 static void get_misc( ArgParser* parser ) {
1302 // Collect characters until we see one that doesn't belong
1304 OSRF_BUFFER_ADD_CHAR( parser->buf, *parser->itr );
1306 char c = *parser->itr;
1307 if( '\0' == c || isspace( (unsigned char) c )
1308 || '{' == c || '}' == c || '[' == c || ']' == c || '\"' == c ) {
1315 @brief Parse the command line.
1316 @param request Pointer to the command line
1317 @param cmd_array Pointer to an osrfStringArray to hold the output of the parser.
1319 The parser operates by recursive descent. We build each chunk of command line in a
1320 growing_buffer belonging to an ArgParser, and then load the chunk into a slot in an
1321 osrfStringArray supplied by the calling code.
1323 static void parse_args( const char* request, osrfStringArray* cmd_array )
1327 // Initialize the ArgParser
1328 parser.itr = request;
1329 parser.buf = buffer_init( 128 );
1331 int done = 0; // boolean
1333 OSRF_BUFFER_RESET( parser.buf );
1335 // skip any white space or commas
1337 && ( isspace( (unsigned char) *parser.itr ) || ',' == *parser.itr ) )
1340 if( '\0' == *parser.itr )
1342 else if( '{' == *parser.itr ) {
1343 // Load a JSON object
1344 get_json_object( &parser );
1345 } else if( '[' == *parser.itr ) {
1346 // Load a JSON array
1347 get_json_array( &parser );
1348 } else if( '\"' == *parser.itr ) {
1349 // Load a string literal
1350 get_string_literal( &parser );
1352 // Anything else is delimited by white space
1354 OSRF_BUFFER_ADD_CHAR( parser.buf, *parser.itr );
1356 } while( *parser.itr && ! isspace( (unsigned char) *parser.itr ) );
1359 // Remove a trailing comma, if present
1360 char lastc = OSRF_BUFFER_C_STR( parser.buf )[
1361 strlen( OSRF_BUFFER_C_STR( parser.buf ) ) - 1 ];
1363 buffer_chomp( parser.buf );
1365 // Add the chunk to the osrfStringArray
1366 const char* s = OSRF_BUFFER_C_STR( parser.buf );
1368 osrfStringArrayAdd( cmd_array, s );
1372 buffer_free( parser.buf );