blc_command.cpp 10.3 KB
Newer Older
Arnaud Blanchard's avatar
Arnaud Blanchard committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
/* Basic Library for C/C++ (blclib)
 Copyright  ETIS — ENSEA, Université de Cergy-Pontoise, CNRS (2011 - 2014)
 
 Author:  Arnaud Blanchard
 
 This software is governed by the CeCILL v2.1 license under French law and abiding by the rules of distribution of free software.
 You can use, modify and/ or redistribute the software under the terms of the CeCILL v2.1 license as circulated by CEA, CNRS and INRIA at the following URL "http://www.cecill.info".
 As a counterpart to the access to the source code and  rights to copy, modify and redistribute granted by the license,
  users are provided only with a limited warranty and the software's author, the holder of the economic rights,  and the successive licensors have only limited liability.
  In this respect, the user's attention is drawn to the risks associated with loading, using, modifying and/or developing or reproducing the software by the user in light of its specific status of free software,
  that may mean  that it is complicated to manipulate, and that also therefore means that it is reserved for developers and experienced professionals having in-depth computer knowledge.
 Users are therefore encouraged to load and test the software's suitability as regards their requirements in conditions enabling the security of their systems and/or data to be ensured
  and, more generally, to use and operate it in the same conditions as regards security.
  The fact that you are presently reading this means that you have had knowledge of the CeCILL v2.1 license and that you accept its terms. */

#include "blc_command.h"
#include "blc_realtime.h" //us_

#include <unistd.h>
#include <fcntl.h>
#include <pthread.h>
#include <termios.h>
#include <getopt.h>
#include <signal.h>
#include <errno.h> //errno and EINT
#include <libgen.h> //basename
#include <unistd.h> //usleep
#include <sys/wait.h>
#include <sys/ioctl.h>
#include <sys/select.h>

#include "blc_text.h"
#include "blc_tools.h"
#include "blc_program.h"


//Identical to argparse in Python
#define POSITIONAL_ARGUMENTS_TITLE "\npositional arguments:\n"
#define OPTIONAL_ARGUMENTS_TITLE "\noptional arguments:\n"

typedef void*(*type_pthread_cb)(void*);

typedef struct
{
    const char *name;
    type_blc_command_cb callback;
    const char *prompt;
    const char *help;
    void *user_data;
}blc_command;

52

Arnaud Blanchard's avatar
Arnaud Blanchard committed
53
54
55
56
// Static means it is only existing in this file
static blc_command *blc_commands = NULL;
static int blc_commands_nb = 0;

57
58
59
static sem_t *sems_to_unlock=NULL;
static int sems_to_unlock_nb=0;

60
static int forward_blc_channel=0;
61
62

void blc_loop_sem_to_unlock_on_stop(void *sem){
63
    
64
65
66
    APPEND_ITEM(&sems_to_unlock, &sems_to_unlock_nb, &sem);
}

Arnaud Blanchard's avatar
Arnaud Blanchard committed
67
68
START_EXTERN_C

69
70
71
72
73

void blc_command_forward_blc_channels(){
    forward_blc_channel=1;
}

Arnaud Blanchard's avatar
Arnaud Blanchard committed
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
void blc_command_display_help()
{
    const blc_command *command;
    size_t command_length_max = 0;
    size_t command_length=0;
    
    FOR_EACH(command, blc_commands, blc_commands_nb)
    {
        command_length = strlen(command->name);
        if (command->prompt) command_length+=strlen(command->prompt)+2;
        command_length_max = MAX(command_length, command_length_max);
    }
    
    FOR_EACH(command, blc_commands, blc_commands_nb)
    {
        if (command->prompt) fprintf(stderr, "%s<%s>%*c:%s\n", command->name, command->prompt, (int)(command_length_max - strlen(command->name)-strlen(command->prompt)-1), ' ', command->help);
        else    fprintf(stderr, "%-*s :%s\n", (int)command_length_max, command->name, command->help);
    }
}

void blc_command_add(const char *command_name, type_blc_command_cb callback, const char *prompt, const char *help, void *user_data){
    blc_command tmp_command;
    int i;
    
    tmp_command.name = command_name;
    tmp_command.callback = callback;
    tmp_command.prompt = prompt;
    tmp_command.help = help;
    tmp_command.user_data = user_data;
    
    FOR_INV(i, blc_commands_nb)
105
    if (strcmp(blc_commands[i].name, command_name) == 0) EXIT_ON_ERROR("%s: command '%s' is already defined.", blc_program_id,  command_name);
Arnaud Blanchard's avatar
Arnaud Blanchard committed
106
107
108
    
    APPEND_ITEM(&blc_commands, &blc_commands_nb, &tmp_command);
}
109

110
111
112
113
114
115
116
117
118
119
120
121

void blc_command_remove(const char *command_name){
    int i;
    
    FOR_INV(i, blc_commands_nb){
        if (strcmp(blc_commands[i].name, command_name) == 0) break;
    }
    if (i==-1) EXIT_ON_ERROR("You try to remove a command '%s' that does not exist", command_name);

    REMOVE_ITEM(&blc_commands, &blc_commands_nb, i);
}

122
void blc_command_interpret_string(char const *input_string, size_t size){
123
124
125
126
    char const*parameter;
    char *parameter2;
    
    size_t parameter_size, parameter2_size;
127
128
    const blc_command *command;
    size_t name_size, line_capability=0;
Arnaud Blanchard's avatar
Arnaud Blanchard committed
129
    
130
131
132
133
134
135
136
137
138
139
140
141
    
    if (forward_blc_channel) {
        if (strchr("./^:", input_string[0])) //it starts with ., ^ , / , :, it may be a blc_channel.
            if (memchr(&input_string[1], '/', size)==0) // There is no / after the first char it is a blc_channel. NOTE: It may be an error with /usr /bin the user has to use /usr/ /bin/ etc.
            {
                
                SYSTEM_ERROR_CHECK(puts(input_string), -1, NULL);
                SYSTEM_ERROR_CHECK(fflush(stdout), -1, NULL);
                return;
            }
    }
    
142
    FOR_EACH(command, blc_commands, blc_commands_nb){
Arnaud Blanchard's avatar
Arnaud Blanchard committed
143
        name_size = strlen(command->name);
144
        if (strncmp(command->name, input_string, name_size) == 0){
145
146
            parameter = input_string + name_size;
            parameter_size = size - name_size;
147
            if (parameter_size == 0){ //No text after the command
148
                parameter2 = NULL;
149
                if ((command->prompt != NULL) && (command->prompt[0]!='|')) {//A text was expected
Arnaud Blanchard's avatar
Arnaud Blanchard committed
150
                    fprintf(stderr, "%s:", command->prompt);
151
152
                    parameter2_size = getline(&parameter2, &line_capability, stdin)-1;
                    parameter2[parameter2_size]=0;
Arnaud Blanchard's avatar
Arnaud Blanchard committed
153
                }
154
155
                if (command->callback == NULL) EXIT_ON_ERROR("Command callback is NULL for command '%s'", command->name);
                command->callback(parameter2, command->user_data);
156
                FREE(parameter2);
Arnaud Blanchard's avatar
Arnaud Blanchard committed
157
                line_capability=0;
158
                break; //Command has been found
Arnaud Blanchard's avatar
Arnaud Blanchard committed
159
            }
160
            else{
161
162
163
164
165
166
167
168
169
                //A parameter was expected
                if (command->prompt)   {
                    command->callback(parameter, command->user_data);
                    break;
                }
                else continue; //Maybe it is a command with a longer name, we continue to check
            /*    if ((command->prompt != NULL) && (command->prompt[0]!='|')) continue; // If we do not wait for parameter, the command must be exact.
                else
                return;*/
Arnaud Blanchard's avatar
Arnaud Blanchard committed
170
171
172
            }
        }
    }
173
    if (command == blc_commands+blc_commands_nb) fprintf(stderr, "%s: unknown command in: '%s'\n", blc_program_id,  input_string);
174
175
}

176
void blc_command_interpret_block(){
177
178
179
180
    char *line=NULL;
    size_t line_capability=0;
    ssize_t line_size;
    
181
    //We do not consider error if it was an interruption. @TODO check wether the content of lin is correct.
182
183
184
185
186
187
    do{
        line_size = getline(&line, &line_capability, stdin);
    }while ((line_size==-1) && (errno==EINTR));
    
    if (line_size==-1)
    {
188
189
        if ((errno==0) || (errno==ETIMEDOUT) || (errno==EINVAL)) {
            if (line) FREE(line);
190
191
            blc_command_ask_quit(); //stdin closed
        }
192
        else EXIT_ON_SYSTEM_ERROR("getline(&line, &line_capability, stdin)");
193
194
195
196
197
198
    }
    else{
        line_size=line_size-1;
        line[line_size]=0; // Removing the last return char (\n)
        blc_command_interpret_string(line, line_size);
    }
199
    FREE(line);
Arnaud Blanchard's avatar
Arnaud Blanchard committed
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
}

void blc_command_interpret(){
    fd_set rfds;
    int retval=-1;
    
    /* Surveiller stdin (fd 0) en attente d'entrées */
    FD_ZERO(&rfds);
    FD_SET(0, &rfds);
    
    //Block until there is data or there is an interuption (Ctrl+C, Terminate, ..)  which is not the case with getline.
    while (retval==-1){
        retval = select(1, &rfds, NULL, NULL, NULL);
        if (retval!=-1) blc_command_interpret_block();
        else if (errno!=EINTR) EXIT_ON_SYSTEM_ERROR("Waiting to interpret a command");
    }
}

int blc_command_try_to_interpret(){
    fd_set rfds;
    struct timeval time_val={0,0};
    int retval;
    
    /* Surveiller stdin (fd 0) en attente d'entrées */
    FD_ZERO(&rfds);
    FD_SET(STDIN_FILENO, &rfds);
    
    SYSTEM_ERROR_CHECK(retval = select(STDIN_FILENO+1, &rfds, NULL, NULL, &time_val), -1, NULL);
    if (retval) blc_command_interpret_block();
    else return 0;
    
    return 1;
}

234
/*
Arnaud Blanchard's avatar
Arnaud Blanchard committed
235
236
void blc_command_interpret_thread(char const *option, void (*ask_quit_funcion)()){
    pthread_t thread;
237
    
Arnaud Blanchard's avatar
Arnaud Blanchard committed
238
239
    if (ask_quit_funcion==NULL) ask_quit_funcion=blc_command_ask_quit;
    blc_command_loop_period=-2;
240
241
    if (strchr(option, 'h')) blc_command_add("h",  (type_blc_command_cb)blc_command_display_help, NULL, "display this help", NULL);
    if (strchr(option, 'q')) blc_command_add("q",  (type_blc_command_cb)ask_quit_funcion, NULL, "quit the application", NULL);
Arnaud Blanchard's avatar
Arnaud Blanchard committed
242
    SYSTEM_ERROR_CHECK(pthread_create(&thread, NULL, command_thread_interpret_loop, NULL), -1, NULL);
243
}*/
Arnaud Blanchard's avatar
Arnaud Blanchard committed
244
245
246
247
248
249
250

void blc_command_update_int_cb(char const *argument, void *int_pt){
    char *endptr;
    int value;
    
    if (argument==NULL || argument[0]==0) fprintf(stderr, "%d\n", *(int*)int_pt);
    else{
251
252
253
        value=strtod(argument, &endptr);
        if (endptr!=argument+strlen(argument)) fprintf(stderr, "Error reading '%s' as an int. The value is still:%d\n", argument,  *(int*)int_pt);
        else *(int*)int_pt=value;
Arnaud Blanchard's avatar
Arnaud Blanchard committed
254
255
256
257
258
259
260
261
262
    }
}

void blc_command_update_float_cb(char const *argument, void *float_pt){
    char *endptr;
    float value;
    
    if (argument==NULL || argument[0]==0) fprintf(stderr, "%f\n", *(float*)float_pt);
    else{
263
264
265
        value=strtof(argument, &endptr);
        if (endptr!=argument+strlen(argument)) fprintf(stderr, "Error reading '%s' as a float. The value is still:%f\n", argument,  *(float*)float_pt);
        else *(float*)float_pt=value;
Arnaud Blanchard's avatar
Arnaud Blanchard committed
266
267
268
269
270
271
272
273
274
    }
}

void blc_command_update_double_cb(char const *argument, void *double_pt){
    char *endptr;
    double value;
    
    if (argument==NULL || argument[0]==0) fprintf(stderr, "%lf\n", *(double*)double_pt);
    else{
275
276
277
        value=strtod(argument, &endptr);
        if (endptr!=argument+strlen(argument)) fprintf(stderr, "Error reading '%s' as a double. The value is still:%lf\n", argument,  *(double*)double_pt);
        else *(double*)double_pt=value;
Arnaud Blanchard's avatar
Arnaud Blanchard committed
278
279
280
281
282
    }
}


END_EXTERN_C