/* $Id$ */ /* Copyright (c) 2011-2014 Pierre Pronchery */ /* This file is part of DeforaOS System libApp */ /* This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include "App.h" #ifndef PROGNAME # define PROGNAME "AppClient" #endif /* AppClient */ /* private */ /* types */ typedef enum _AppClientCallArgType { ACCAT_DOUBLE = 0, ACCAT_FLOAT, ACCAT_INTEGER, ACCAT_STRING } AppClientCallArgType; typedef struct _AppClientCallArg { AppClientCallArgType type; double _double; float _float; int integer; char const * string; } AppClientCallArg; typedef struct _AppClientCall { char const * name; AppClientCallArg * args; size_t args_cnt; } AppClientCall; /* prototypes */ static int _appclient(int verbose, char const * app, char const * name, AppClientCall calls[], size_t calls_cnt); static int _error(char const * message, int ret); /* functions */ static int _appclient_call(int verbose, AppClient * ac, AppClientCall * call); static int _appclient(int verbose, char const * app, char const * name, AppClientCall calls[], size_t calls_cnt) { int ret = 0; AppClient * ac; size_t i; #ifdef DEBUG fprintf(stderr, "DEBUG: %s(%d, %s, %s, %p, %zu)\n", __func__, verbose, hostname, service, (void *)calls, calls_cnt); #endif if((ac = appclient_new(NULL, app, name)) == NULL) return _error(PROGNAME, 1); if(verbose != 0) puts("Connected."); for(i = 0; i < calls_cnt; i++) if(_appclient_call(verbose, ac, &calls[i]) != 0) { ret |= _error(PROGNAME, 1); break; } if(verbose != 0) puts("Disconnecting"); appclient_delete(ac); return ret; } static int _appclient_call(int verbose, AppClient * ac, AppClientCall * call) { int ret = 0; Variable * v; VariableType type; int64_t res; double dres; String * sres; #ifdef DEBUG fprintf(stderr, "DEBUG: %s()\n", __func__); #endif if(verbose != 0) printf("Calling %s() with %lu arguments\n", call->name, (unsigned long)call->args_cnt); if((v = variable_new(VT_NULL, NULL)) == NULL) return -1; /* FIXME may segfault (check interface), use appclient_callv? */ switch(call->args_cnt) { case 0: #ifdef DEBUG fprintf(stderr, "DEBUG: %s() %s() res=%ld\n", __func__, call->name, res); #endif ret = appclient_call_variable(ac, v, call->name, NULL); break; case 1: #ifdef DEBUG fprintf(stderr, "DEBUG: %s() %s(%d)\n", __func__, call->name, call->args[0].integer); #endif if(call->args[0].type == ACCAT_DOUBLE) ret = appclient_call_variable(ac, v, call->name, call->args[0]._double); else if(call->args[0].type == ACCAT_FLOAT) ret = appclient_call_variable(ac, v, call->name, call->args[0]._float); else if(call->args[0].type == ACCAT_INTEGER) ret = appclient_call_variable(ac, v, call->name, call->args[0].integer); else if(call->args[0].type == ACCAT_STRING) ret = appclient_call_variable(ac, v, call->name, call->args[0].string); else ret = error_set_code(1, "%s", "Unsupported types"); break; case 2: /* FIXME arguments may be of different types */ #ifdef DEBUG fprintf(stderr, "DEBUG: %s() %s(%d, %d)\n", __func__, call->name, call->args[0].integer, call->args[1].integer); #endif ret = appclient_call_variable(ac, v, call->name, call->args[0].integer, call->args[1].integer); break; case 3: if(strcmp(call->name, "glTranslatef") == 0) { #ifdef DEBUG fprintf(stderr, "DEBUG: %s() %s(%.1f, %.1f," " %.1f)\n", __func__, call->name, call->args[0]._float, call->args[1]._float, call->args[2]._float); #endif ret = appclient_call_variable(ac, v, call->name, call->args[0]._float, call->args[1]._float, call->args[2]._float); break; } /* FIXME arguments may be of different types */ #ifdef DEBUG fprintf(stderr, "DEBUG: %s() %s(%d, %d, %d)\n", __func__, call->name, call->args[0].integer, call->args[1].integer, call->args[2].integer); #endif ret = appclient_call_variable(ac, v, call->name, call->args[0].integer, call->args[1].integer, call->args[2].integer); break; case 4: #ifdef DEBUG fprintf(stderr, "DEBUG: %s() %s(%.1f, %.1f, %.1f," " %.1f)\n", __func__, call->name, call->args[0]._float, call->args[1]._float, call->args[2]._float, call->args[3]._float); #endif ret = appclient_call_variable(ac, v, call->name, call->args[0]._float, call->args[1]._float, call->args[2]._float, call->args[3]._float); break; default: return error_set_code(1, "%s", "Unsupported number of arguments"); } if(ret == 0 && verbose) { type = (v != NULL) ? variable_get_type(v) : VT_NULL; switch(type) { case VT_BOOL: case VT_INT8: case VT_UINT8: case VT_INT16: case VT_UINT16: case VT_INT32: case VT_UINT32: case VT_INT64: case VT_UINT64: if(variable_get_as(v, VT_INT64, &res) == 0) printf("\"%s\"%s%ld\n", call->name, " returned ", res); else printf("\"%s\"%s\n", call->name, " returned"); break; case VT_FLOAT: case VT_DOUBLE: if(variable_get_as(v, VT_DOUBLE, &dres) == 0) printf("\"%s\"%s%f\n", call->name, " returned ", dres); else printf("\"%s\"%s\n", call->name, " returned"); break; case VT_STRING: sres = NULL; if(variable_get_as(v, VT_STRING, &sres) == 0) printf("\"%s\"%s\"%s\"\n", call->name, " returned ", sres); else printf("\"%s\"%s\n", call->name, " returned"); string_delete(sres); break; default: printf("\"%s\"%s\n", call->name, " returned"); break; } } if(v != NULL) variable_delete(v); return ret; } /* error */ static int _error(char const * message, int ret) { error_print(message); return ret; } /* usage */ static int _usage(void) { fputs("Usage: " PROGNAME " [-v][-H hostname] -S service" " [-C call [-d double|-f float|-i integer|-s string]...]...\n" " -v Be more verbose\n" " -H Hostname to connect to\n" " -S Service to connect to\n" " -C Enqueue a given call\n" " -d Add a double as an argument to the current call\n" " -f Add a float as an argument to the current call\n" " -i Add an integer as an argument to the current call\n" " -s Add a string as an argument to the current call\n", stderr); return 1; } /* main */ static int _main_call(AppClientCall ** calls, size_t * calls_cnt, char const * name); static int _main_call_arg(AppClientCall * calls, size_t calls_cnt, AppClientCallArgType type, char const * string); int main(int argc, char * argv[]) { int o; int res; int verbose = 0; char const * app = NULL; char const * name = NULL; AppClientCall * calls = NULL; size_t calls_cnt = 0; while((o = getopt(argc, argv, "vH:S:C:d:f:i:s:")) != -1) { res = 0; switch(o) { case 'v': verbose = 1; break; case 'H': name = optarg; break; case 'S': app = optarg; break; case 'C': res = _main_call(&calls, &calls_cnt, optarg); break; case 'd': res = _main_call_arg(calls, calls_cnt, ACCAT_DOUBLE, optarg); break; case 'f': res = _main_call_arg(calls, calls_cnt, ACCAT_FLOAT, optarg); break; case 'i': res = _main_call_arg(calls, calls_cnt, ACCAT_INTEGER, optarg); break; case 's': res = _main_call_arg(calls, calls_cnt, ACCAT_STRING, optarg); break; default: return _usage(); } if(res != 0) return _error(PROGNAME, 2); } if(app == NULL) return _usage(); return (_appclient(verbose, app, name, calls, calls_cnt) == 0) ? 0 : 2; } static int _main_call(AppClientCall ** calls, size_t * calls_cnt, char const * name) { AppClientCall * p; if((p = realloc(*calls, sizeof(*p) * ((*calls_cnt) + 1))) == NULL) return error_set_code(1, "%s", strerror(errno)); *calls = p; p = &(*calls)[(*calls_cnt)++]; memset(p, 0, sizeof(*p)); p->name = name; p->args = NULL; p->args_cnt = 0; return 0; } static int _main_call_arg(AppClientCall * calls, size_t calls_cnt, AppClientCallArgType type, char const * string) { AppClientCall * p; AppClientCallArg * q; if(calls_cnt == 0) return 1; /* FIXME report error */ p = &calls[calls_cnt - 1]; if((q = realloc(p->args, sizeof(*q) * (p->args_cnt + 1))) == NULL) return error_set_code(1, "%s", strerror(errno)); p->args = q; q = &q[p->args_cnt++]; memset(q, 0, sizeof(*q)); q->type = type; switch(type) { case ACCAT_DOUBLE: q->_double = strtod(string, NULL); /* XXX check */ break; case ACCAT_FLOAT: q->_float = strtof(string, NULL); /* XXX check */ break; case ACCAT_INTEGER: q->integer = strtol(string, NULL, 0); /* XXX check */ break; case ACCAT_STRING: q->string = string; break; } return 0; }