Merge branch 'develop' into MXS-329-develop-20151111

This commit is contained in:
Markus Makela
2015-11-13 07:44:23 +02:00
66 changed files with 4807 additions and 4056 deletions

View File

@ -580,6 +580,7 @@ char task_name[BLRM_TASK_NAME_LEN+1] = "";
inst->service->name)));
if (service->users) {
users_free(service->users);
service->users = NULL;
}
free(inst);
@ -658,6 +659,7 @@ char task_name[BLRM_TASK_NAME_LEN+1] = "";
if (service->users) {
users_free(service->users);
service->users = NULL;
}
if (service->dbref && service->dbref->server) {

View File

@ -86,8 +86,6 @@ return 1;
}
int main(int argc, char **argv) {
char** arg_vector;
int arg_count = 1;
ROUTER_INSTANCE *inst;
int fd;
int ret;
@ -126,21 +124,9 @@ int main(int argc, char **argv) {
num_args = optind;
arg_vector = malloc(sizeof(char*)*(arg_count + 1));
mxs_log_init(NULL, NULL, LOG_TARGET_DEFAULT);
if(arg_vector == NULL)
{
fprintf(stderr,"Error: Memory allocation failed for log manager arg_vector.\n");
return 1;
}
arg_vector[0] = "logmanager";
arg_vector[1] = NULL;
skygw_logmanager_init(NULL, arg_count, arg_vector);
skygw_log_set_augmentation(0);
free(arg_vector);
mxs_log_set_augmentation(0);
if (!debug_out)
skygw_log_disable(LOGFILE_DEBUG);
@ -151,8 +137,8 @@ int main(int argc, char **argv) {
LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR,
"Error: Memory allocation failed for ROUTER_INSTANCE")));
skygw_log_sync_all();
skygw_logmanager_done();
mxs_log_flush_sync();
mxs_log_finish();
return 1;
}
@ -175,8 +161,8 @@ int main(int argc, char **argv) {
"Failed to open binlog file %s: %s",
path, strerror(errno))));
skygw_log_sync_all();
skygw_logmanager_done();
mxs_log_flush_sync();
mxs_log_finish();
free(inst);
@ -208,13 +194,13 @@ int main(int argc, char **argv) {
close(inst->binlog_fd);
skygw_log_sync_all();
mxs_log_flush_sync();
LOGIF(LM, (skygw_log_write_flush(LOGFILE_MESSAGE,
"Check retcode: %i, Binlog Pos = %llu", ret, inst->binlog_position)));
skygw_log_sync_all();
skygw_logmanager_done();
mxs_log_flush_sync();
mxs_log_finish();
free(inst);

View File

@ -73,11 +73,9 @@ static struct option long_options[] = {
};
int main(int argc, char **argv) {
char** arg_vector;
ROUTER_INSTANCE *inst;
int ret;
int rc;
int arg_count = 1;
char error_string[BINLOG_ERROR_MSG_LEN + 1] = "";
CHANGE_MASTER_OPTIONS change_master;
char query[255+1]="";
@ -91,18 +89,7 @@ int main(int argc, char **argv) {
roptions = strdup("server-id=3,heartbeat=200,binlogdir=/not_exists/my_dir,transaction_safety=1,master_version=5.6.99-common,master_hostname=common_server,master_uuid=xxx-fff-cccc-fff,master-id=999");
arg_vector = malloc(sizeof(char*)*(arg_count + 1));
if(arg_vector == NULL)
{
fprintf(stderr,"Error: Memory allocation FAILED for log manager arg_vector.\n");
return 1;
}
arg_vector[0] = "logmanager";
arg_vector[1] = NULL;
skygw_logmanager_init(NULL, arg_count,arg_vector);
free(arg_vector);
mxs_log_init(NULL, NULL, LOG_TARGET_DEFAULT);
skygw_log_disable(LOGFILE_DEBUG);
skygw_log_disable(LOGFILE_TRACE);
@ -140,8 +127,8 @@ int main(int argc, char **argv) {
LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR,
"Error: Memory allocation FAILED for ROUTER_INSTANCE")));
skygw_log_sync_all();
skygw_logmanager_done();
mxs_log_flush_sync();
mxs_log_finish();
return 1;
}
@ -591,8 +578,8 @@ int main(int argc, char **argv) {
return 1;
}
skygw_log_sync_all();
skygw_logmanager_done();
mxs_log_flush_sync();
mxs_log_finish();
free(inst);

View File

@ -621,29 +621,48 @@ struct subcommand removeoptions[] = {
static void
flushlog(DCB *pdcb, char *logname)
{
if (logname == NULL)
bool unrecognized = false;
bool deprecated = false;
if (!strcasecmp(logname, "error"))
{
}
else if (!strcasecmp(logname, "error"))
{
skygw_log_rotate(LOGFILE_ERROR);
deprecated = true;
}
else if (!strcasecmp(logname, "message"))
{
skygw_log_rotate(LOGFILE_MESSAGE);
deprecated = true;
}
else if (!strcasecmp(logname, "trace"))
{
skygw_log_rotate(LOGFILE_TRACE);
deprecated = true;
}
else if (!strcasecmp(logname, "debug"))
{
skygw_log_rotate(LOGFILE_DEBUG);
deprecated = true;
}
else if (!strcasecmp(logname, "maxscale"))
{
; // nop
}
else
{
dcb_printf(pdcb, "Unexpected logfile name, expected "
"error, message, trace or debug.\n");
unrecognized = true;
}
if (unrecognized)
{
dcb_printf(pdcb, "Unexpected logfile name '%s', expected: 'maxscale'.\n", logname);
}
else
{
mxs_log_rotate();
if (deprecated)
{
dcb_printf(pdcb,
"'%s' is deprecated, currently there is only one log 'maxscale', "
"which was rotated.\n", logname);
}
}
}
@ -655,10 +674,7 @@ flushlog(DCB *pdcb, char *logname)
static void
flushlogs(DCB *pdcb)
{
skygw_log_rotate(LOGFILE_ERROR);
skygw_log_rotate(LOGFILE_MESSAGE);
skygw_log_rotate(LOGFILE_TRACE);
skygw_log_rotate(LOGFILE_DEBUG);
mxs_log_rotate();
}
@ -678,8 +694,8 @@ struct subcommand flushoptions[] = {
"logs",
0,
flushlogs,
"Flush the content of all log files, close that logs, rename them and open a new log files",
"Flush the content of all log files, close that logs, rename them and open a new log files",
"Flush the content of all log files, close those logs, rename them and open a new log files",
"Flush the content of all log files, close those logs, rename them and open a new log files",
{0, 0, 0}
},
{
@ -1100,40 +1116,6 @@ restart_service(DCB *dcb, SERVICE *service)
serviceRestart(service);
}
static struct {
char *str;
unsigned int bit;
} ServerBits[] = {
{ "running", SERVER_RUNNING },
{ "master", SERVER_MASTER },
{ "slave", SERVER_SLAVE },
{ "synced", SERVER_JOINED },
{ "ndb", SERVER_NDB },
{ "maintenance", SERVER_MAINT },
{ "maint", SERVER_MAINT },
{ NULL, 0 }
};
/**
* Map the server status bit
*
* @param str String representation
* @return bit value or 0 on error
*/
static unsigned int
server_map_status(char *str)
{
int i;
for (i = 0; ServerBits[i].str; i++)
{
if (!strcasecmp(str, ServerBits[i].str))
{
return ServerBits[i].bit;
}
}
return 0;
}
/**
* Set the status bit of a server
*
@ -1603,7 +1585,7 @@ static void enable_log_priority(DCB *dcb, char *arg1)
if (priority != -1)
{
mxs_log_enable_priority(priority);
mxs_log_set_priority_enabled(priority, true);
}
else
{
@ -1621,7 +1603,7 @@ static void disable_log_priority(DCB *dcb, char *arg1)
if (priority != -1)
{
mxs_log_enable_priority(priority);
mxs_log_set_priority_enabled(priority, false);
}
else
{

View File

@ -780,7 +780,7 @@ maxinfo_add_mysql_user(SERVICE *service) {
"maxinfo: create hex_sha1_sha1_password failed for service user %s",
service_user)));
users_free(service->users);
service->users = NULL;
return 1;
}

View File

@ -54,7 +54,12 @@ static void exec_select(DCB *dcb, MAXINFO_TREE *tree);
static void exec_show_variables(DCB *dcb, MAXINFO_TREE *filter);
static void exec_show_status(DCB *dcb, MAXINFO_TREE *filter);
static int maxinfo_pattern_match(char *pattern, char *str);
static void exec_flush(DCB *dcb, MAXINFO_TREE *tree);
static void exec_set(DCB *dcb, MAXINFO_TREE *tree);
static void exec_clear(DCB *dcb, MAXINFO_TREE *tree);
static void exec_shutdown(DCB *dcb, MAXINFO_TREE *tree);
static void exec_restart(DCB *dcb, MAXINFO_TREE *tree);
void maxinfo_send_ok(DCB *dcb);
/**
* Execute a parse tree and return the result set or runtime error
*
@ -72,6 +77,23 @@ maxinfo_execute(DCB *dcb, MAXINFO_TREE *tree)
case MAXOP_SELECT:
exec_select(dcb, tree);
break;
case MAXOP_FLUSH:
exec_flush(dcb, tree);
break;
case MAXOP_SET:
exec_set(dcb, tree);
break;
case MAXOP_CLEAR:
exec_clear(dcb, tree);
break;
case MAXOP_SHUTDOWN:
exec_shutdown(dcb, tree);
break;
case MAXOP_RESTART:
exec_restart(dcb, tree);
break;
case MAXOP_TABLE:
case MAXOP_COLUMNS:
case MAXOP_LITERAL:
@ -274,6 +296,448 @@ char errmsg[120];
LOGIF(LM, (skygw_log_write(LOGFILE_MESSAGE, errmsg)));
}
/**
* Flush all logs to disk and rotate them.
* @param dcb The DCB that connects to the client
* @param tree The parse tree for the query
*/
void exec_flush_logs(DCB *dcb, MAXINFO_TREE *tree)
{
mxs_log_rotate();
maxinfo_send_ok(dcb);
}
/**
* The table of flush commands that are supported
*/
static struct
{
char *name;
void (*func)(DCB *, MAXINFO_TREE *);
} flush_commands[] = {
{ "logs", exec_flush_logs},
{ NULL, NULL}
};
/**
* Execute a flush command parse tree and return the result set or runtime error
*
* @param dcb The DCB that connects to the client
* @param tree The parse tree for the query
*/
static void
exec_flush(DCB *dcb, MAXINFO_TREE *tree)
{
int i;
char errmsg[120];
for (i = 0; flush_commands[i].name; i++)
{
if (strcasecmp(flush_commands[i].name, tree->value) == 0)
{
(*flush_commands[i].func)(dcb, tree->right);
return;
}
}
if (strlen(tree->value) > 80) // Prevent buffer overrun
{
tree->value[80] = 0;
}
sprintf(errmsg, "Unsupported flush command '%s'", tree->value);
maxinfo_send_error(dcb, 0, errmsg);
skygw_log_write(LE, errmsg);
}
/**
* Set the server status.
* @param dcb Client DCB
* @param tree Parse tree
*/
void exec_set_server(DCB *dcb, MAXINFO_TREE *tree)
{
SERVER* server = server_find_by_unique_name(tree->value);
char errmsg[120];
if (server)
{
int status = server_map_status(tree->right->value);
if (status != 0)
{
server_set_status(server, status);
maxinfo_send_ok(dcb);
}
else
{
if (strlen(tree->right->value) > 80) // Prevent buffer overrun
{
tree->right->value[80] = 0;
}
sprintf(errmsg, "Invalid argument '%s'", tree->right->value);
maxinfo_send_error(dcb, 0, errmsg);
}
}
else
{
if (strlen(tree->value) > 80) // Prevent buffer overrun
{
tree->value[80] = 0;
}
sprintf(errmsg, "Invalid argument '%s'", tree->value);
maxinfo_send_error(dcb, 0, errmsg);
}
}
/**
* The table of set commands that are supported
*/
static struct
{
char *name;
void (*func)(DCB *, MAXINFO_TREE *);
} set_commands[] = {
{ "server", exec_set_server},
{ NULL, NULL}
};
/**
* Execute a set command parse tree and return the result set or runtime error
*
* @param dcb The DCB that connects to the client
* @param tree The parse tree for the query
*/
static void
exec_set(DCB *dcb, MAXINFO_TREE *tree)
{
int i;
char errmsg[120];
for (i = 0; set_commands[i].name; i++)
{
if (strcasecmp(set_commands[i].name, tree->value) == 0)
{
(*set_commands[i].func)(dcb, tree->right);
return;
}
}
if (strlen(tree->value) > 80) // Prevent buffer overrun
{
tree->value[80] = 0;
}
sprintf(errmsg, "Unsupported set command '%s'", tree->value);
maxinfo_send_error(dcb, 0, errmsg);
skygw_log_write(LE, errmsg);
}
/**
* Clear the server status.
* @param dcb Client DCB
* @param tree Parse tree
*/
void exec_clear_server(DCB *dcb, MAXINFO_TREE *tree)
{
SERVER* server = server_find_by_unique_name(tree->value);
char errmsg[120];
if (server)
{
int status = server_map_status(tree->right->value);
if (status != 0)
{
server_clear_status(server, status);
maxinfo_send_ok(dcb);
}
else
{
if (strlen(tree->right->value) > 80) // Prevent buffer overrun
{
tree->right->value[80] = 0;
}
sprintf(errmsg, "Invalid argument '%s'", tree->right->value);
maxinfo_send_error(dcb, 0, errmsg);
}
}
else
{
if (strlen(tree->value) > 80) // Prevent buffer overrun
{
tree->value[80] = 0;
}
sprintf(errmsg, "Invalid argument '%s'", tree->value);
maxinfo_send_error(dcb, 0, errmsg);
}
}
/**
* The table of clear commands that are supported
*/
static struct
{
char *name;
void (*func)(DCB *, MAXINFO_TREE *);
} clear_commands[] = {
{ "server", exec_clear_server},
{ NULL, NULL}
};
/**
* Execute a clear command parse tree and return the result set or runtime error
*
* @param dcb The DCB that connects to the client
* @param tree The parse tree for the query
*/
static void
exec_clear(DCB *dcb, MAXINFO_TREE *tree)
{
int i;
char errmsg[120];
for (i = 0; clear_commands[i].name; i++)
{
if (strcasecmp(clear_commands[i].name, tree->value) == 0)
{
(*clear_commands[i].func)(dcb, tree->right);
return;
}
}
if (strlen(tree->value) > 80) // Prevent buffer overrun
{
tree->value[80] = 0;
}
sprintf(errmsg, "Unsupported clear command '%s'", tree->value);
maxinfo_send_error(dcb, 0, errmsg);
skygw_log_write(LE, errmsg);
}
extern void shutdown_server();
/**
* MaxScale shutdown
* @param dcb Client DCB
* @param tree Parse tree
*/
void exec_shutdown_maxscale(DCB *dcb, MAXINFO_TREE *tree)
{
shutdown_server();
maxinfo_send_ok(dcb);
}
/**
* Stop a monitor
* @param dcb Client DCB
* @param tree Parse tree
*/
void exec_shutdown_monitor(DCB *dcb, MAXINFO_TREE *tree)
{
char errmsg[120];
if (tree && tree->value)
{
MONITOR* monitor = monitor_find(tree->value);
if (monitor)
{
monitorStop(monitor);
maxinfo_send_ok(dcb);
}
else
{
if (strlen(tree->value) > 80) // Prevent buffer overrun
{
tree->value[80] = 0;
}
sprintf(errmsg, "Invalid argument '%s'", tree->value);
maxinfo_send_error(dcb, 0, errmsg);
}
}
else
{
sprintf(errmsg, "Missing argument for 'SHUTDOWN MONITOR'");
maxinfo_send_error(dcb, 0, errmsg);
}
}
/**
* Stop a service
* @param dcb Client DCB
* @param tree Parse tree
*/
void exec_shutdown_service(DCB *dcb, MAXINFO_TREE *tree)
{
char errmsg[120];
if (tree && tree->value)
{
SERVICE* service = service_find(tree->value);
if (service)
{
serviceStop(service);
maxinfo_send_ok(dcb);
}
else
{
if (strlen(tree->value) > 80) // Prevent buffer overrun
{
tree->value[80] = 0;
}
sprintf(errmsg, "Invalid argument '%s'", tree->value);
maxinfo_send_error(dcb, 0, errmsg);
}
}
else
{
sprintf(errmsg, "Missing argument for 'SHUTDOWN SERVICE'");
maxinfo_send_error(dcb, 0, errmsg);
}
}
/**
* The table of shutdown commands that are supported
*/
static struct
{
char *name;
void (*func)(DCB *, MAXINFO_TREE *);
} shutdown_commands[] = {
{ "maxscale", exec_shutdown_maxscale},
{ "monitor", exec_shutdown_monitor},
{ "service", exec_shutdown_service},
{ NULL, NULL}
};
/**
* Execute a shutdown command parse tree and return OK or runtime error
*
* @param dcb The DCB that connects to the client
* @param tree The parse tree for the query
*/
static void
exec_shutdown(DCB *dcb, MAXINFO_TREE *tree)
{
int i;
char errmsg[120];
for (i = 0; shutdown_commands[i].name; i++)
{
if (strcasecmp(shutdown_commands[i].name, tree->value) == 0)
{
(*shutdown_commands[i].func)(dcb, tree->right);
return;
}
}
if (strlen(tree->value) > 80) // Prevent buffer overrun
{
tree->value[80] = 0;
}
sprintf(errmsg, "Unsupported shutdown command '%s'", tree->value);
maxinfo_send_error(dcb, 0, errmsg);
skygw_log_write(LE, errmsg);
}
/**
* Restart a monitor
* @param dcb Client DCB
* @param tree Parse tree
*/
void exec_restart_monitor(DCB *dcb, MAXINFO_TREE *tree)
{
char errmsg[120];
if (tree && tree->value)
{
MONITOR* monitor = monitor_find(tree->value);
if (monitor)
{
monitorStart(monitor, NULL);
maxinfo_send_ok(dcb);
}
else
{
if (strlen(tree->value) > 80) // Prevent buffer overrun
{
tree->value[80] = 0;
}
sprintf(errmsg, "Invalid argument '%s'", tree->value);
maxinfo_send_error(dcb, 0, errmsg);
}
}
else
{
sprintf(errmsg, "Missing argument for 'RESTART MONITOR'");
maxinfo_send_error(dcb, 0, errmsg);
}
}
/**
* Restart a service
* @param dcb Client DCB
* @param tree Parse tree
*/
void exec_restart_service(DCB *dcb, MAXINFO_TREE *tree)
{
char errmsg[120];
if (tree && tree->value)
{
SERVICE* service = service_find(tree->value);
if (service)
{
serviceRestart(service);
maxinfo_send_ok(dcb);
}
else
{
if (strlen(tree->value) > 80) // Prevent buffer overrun
{
tree->value[80] = 0;
}
sprintf(errmsg, "Invalid argument '%s'", tree->value);
maxinfo_send_error(dcb, 0, errmsg);
}
}
else
{
sprintf(errmsg, "Missing argument for 'RESTART SERVICE'");
maxinfo_send_error(dcb, 0, errmsg);
}
}
/**
* The table of restart commands that are supported
*/
static struct
{
char *name;
void (*func)(DCB *, MAXINFO_TREE *);
} restart_commands[] = {
{ "monitor", exec_restart_monitor},
{ "service", exec_restart_service},
{ NULL, NULL}
};
/**
* Execute a restart command parse tree and return OK or runtime error
*
* @param dcb The DCB that connects to the client
* @param tree The parse tree for the query
*/
static void
exec_restart(DCB *dcb, MAXINFO_TREE *tree)
{
int i;
char errmsg[120];
for (i = 0; restart_commands[i].name; i++)
{
if (strcasecmp(restart_commands[i].name, tree->value) == 0)
{
(*restart_commands[i].func)(dcb, tree->right);
return;
}
}
if (strlen(tree->value) > 80) // Prevent buffer overrun
{
tree->value[80] = 0;
}
sprintf(errmsg, "Unsupported restart command '%s'", tree->value);
maxinfo_send_error(dcb, 0, errmsg);
skygw_log_write(LE, errmsg);
}
/**
* Return the current MaxScale version
*
@ -764,3 +1228,25 @@ extern char *strcasestr();
return rval;
}
}
/**
* Send an OK packet to the client.
* @param dcb The DCB that connects to the client
*/
void maxinfo_send_ok(DCB *dcb)
{
static const char ok_packet[] ={
0x07, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00,
0x00, 0x00,
0x00, 0x00
};
GWBUF* buffer = gwbuf_alloc(sizeof(ok_packet));
if (buffer)
{
memcpy(buffer->start, ok_packet, sizeof(ok_packet));
dcb->func.write(dcb, buffer);
}
}

View File

@ -50,7 +50,8 @@ static void free_tree(MAXINFO_TREE *);
static char *fetch_token(char *, int *, char **);
static MAXINFO_TREE *parse_column_list(char **sql);
static MAXINFO_TREE *parse_table_name(char **sql);
MAXINFO_TREE* maxinfo_parse_literals(MAXINFO_TREE *tree, int min_args, char *ptr,
PARSE_ERROR *parse_error);
/**
* Parse a SQL subset for the maxinfo plugin and return a parse tree
@ -111,6 +112,67 @@ MAXINFO_TREE *col, *table;
table = parse_table_name(&ptr);
return make_tree_node(MAXOP_SELECT, NULL, col, table);
#endif
case LT_FLUSH:
free(text); // not needed
ptr = fetch_token(ptr, &token, &text);
return make_tree_node(MAXOP_FLUSH, text, NULL, NULL);
case LT_SHUTDOWN:
free(text);
ptr = fetch_token(ptr, &token, &text);
tree = make_tree_node(MAXOP_SHUTDOWN, text, NULL, NULL);
if ((ptr = fetch_token(ptr, &token, &text)) == NULL)
{
/** Possibly SHUTDOWN MAXSCALE */
return tree;
}
tree->right = make_tree_node(MAXOP_LITERAL, text, NULL, NULL);
if ((ptr = fetch_token(ptr, &token, &text)) != NULL)
{
/** Unknown token after SHUTDOWN MONITOR|SERVICE */
*parse_error = PARSE_SYNTAX_ERROR;
free_tree(tree);
return NULL;
}
return tree;
case LT_RESTART:
free(text);
ptr = fetch_token(ptr, &token, &text);
tree = make_tree_node(MAXOP_RESTART, text, NULL, NULL);
if ((ptr = fetch_token(ptr, &token, &text)) == NULL)
{
/** Missing token for RESTART MONITOR|SERVICE */
*parse_error = PARSE_SYNTAX_ERROR;
free_tree(tree);
return NULL;
}
tree->right = make_tree_node(MAXOP_LITERAL, text, NULL, NULL);
if ((ptr = fetch_token(ptr, &token, &text)) != NULL)
{
/** Unknown token after RESTART MONITOR|SERVICE */
*parse_error = PARSE_SYNTAX_ERROR;
free_tree(tree);
return NULL;
}
return tree;
case LT_SET:
free(text); // not needed
ptr = fetch_token(ptr, &token, &text);
tree = make_tree_node(MAXOP_SET, text, NULL, NULL);
return maxinfo_parse_literals(tree, 2, ptr, parse_error);
case LT_CLEAR:
free(text); // not needed
ptr = fetch_token(ptr, &token, &text);
tree = make_tree_node(MAXOP_CLEAR, text, NULL, NULL);
return maxinfo_parse_literals(tree, 2, ptr, parse_error);
break;
default:
*parse_error = PARSE_SYNTAX_ERROR;
return NULL;
@ -231,18 +293,24 @@ free_tree(MAXINFO_TREE *tree)
/**
* The set of keywords known to the tokeniser
*/
static struct {
char *text;
int token;
static struct
{
char *text;
int token;
} keywords[] = {
{ "show", LT_SHOW },
{ "select", LT_SELECT },
{ "from", LT_FROM },
{ "like", LT_LIKE },
{ "=", LT_EQUAL },
{ ",", LT_COMMA },
{ "*", LT_STAR },
{ NULL, 0 }
{ "show", LT_SHOW},
{ "select", LT_SELECT},
{ "from", LT_FROM},
{ "like", LT_LIKE},
{ "=", LT_EQUAL},
{ ",", LT_COMMA},
{ "*", LT_STAR},
{ "flush", LT_FLUSH},
{ "set", LT_SET},
{ "clear", LT_CLEAR},
{ "shutdown", LT_SHUTDOWN},
{ "restart", LT_RESTART},
{ NULL, 0}
};
/**
@ -322,3 +390,36 @@ int i;
*token = LT_STRING;
return s2;
}
/**
* Parse the remaining arguments as literals.
* @param tree Previous head of the parse tree
* @param min_args Minimum required number of arguments
* @param ptr Pointer to client command
* @param parse_error Pointer to parsing error to fill
* @return Parsed tree or NULL if parsing failed
*/
MAXINFO_TREE* maxinfo_parse_literals(MAXINFO_TREE *tree, int min_args, char *ptr,
PARSE_ERROR *parse_error)
{
int token;
MAXINFO_TREE* node = tree;
char *text;
for(int i = 0; i < min_args; i++)
{
if((ptr = fetch_token(ptr, &token, &text)) == NULL ||
(node->right = make_tree_node(MAXOP_LITERAL, text, NULL, NULL)) == NULL)
{
*parse_error = PARSE_SYNTAX_ERROR;
free_tree(tree);
if(ptr)
{
free(text);
}
return NULL;
}
node = node->right;
}
return tree;
}