command *p_st_empty(parser_info *pinfo)
{
    pinfo->request_for_lex = 1;
    pinfo->state = P_ST_START;
    pinfo->tmp_cmd.type = CMD_EMPTY;
    return new_cmd(pinfo);
}
command *p_st_expect_optional_str(parser_info *pinfo)
{
    command *cmd = NULL;

    switch (pinfo->cur_lex->type) {
    case LEX_WORD:
        pinfo->tmp_cmd.value = pinfo->cur_lex->value;
        pinfo->cur_lex_data_used = 1;
        pinfo->request_for_lex = 1;
        pinfo->state = P_ST_PROCESS_ARG2;
        break;
    case LEX_EOLN:
        pinfo->request_for_lex = 1;
        pinfo->state = P_ST_START;
        /* pinfo->tmp_cmd.type already defined. */
        pinfo->tmp_cmd.value.str = NULL;
        cmd = new_cmd(pinfo);
        break;
    case LEX_PROTOCOL_PARSE_ERROR:
        pinfo->state = P_ST_PROTOCOL_PARSE_ERROR;
        break;
    default:
        pinfo->state = P_ST_WRONG;
    }

    return cmd;
}
command *p_st_expect_eoln(parser_info *pinfo)
{
    command *cmd = NULL;

    switch (pinfo->cur_lex->type) {
    case LEX_EOLN:
        pinfo->state = P_ST_START;
        pinfo->request_for_lex = 1;
        /* pinfo->tmp_cmd already defined
         * (only type or both type and value). */
        cmd = new_cmd(pinfo);
        break;
    case LEX_PROTOCOL_PARSE_ERROR:
        pinfo->state = P_ST_PROTOCOL_PARSE_ERROR;
        break;
    default:
        pinfo->state = P_ST_WRONG;
    }

    return cmd;
}
command *p_st_protocol_parse_error(parser_info *pinfo)
{
    pinfo->tmp_cmd.type = CMD_PROTOCOL_PARSE_ERROR;
    return new_cmd(pinfo);
}
command *p_st_wrong(parser_info *pinfo)
{
    pinfo->state = P_ST_SKIP;
    pinfo->tmp_cmd.type = CMD_WRONG;
    return new_cmd(pinfo);
}
int
parse_action(int *argc_p, char ***argv_p, int tca_id, struct nlmsghdr *n)
{
	int argc = *argc_p;
	char **argv = *argv_p;
	struct rtattr *tail, *tail2;
	char k[16];
	int ok = 0;
	int eap = 0; 

	int ret = 0;
	int prio = 0;

	if (argc <= 0)
		return -1;

	tail = tail2 = NLMSG_TAIL(n);

	addattr_l(n, MAX_MSG, tca_id, NULL, 0);

	while (argc > 0) {

		memset(k, 0, sizeof (k));

		if (strcmp(*argv, "action") == 0 ) {
			argc--;
			argv++;
			eap = 1;
#ifdef CONFIG_GACT
			if (!gact_ld) {
				get_action_kind("gact");
			}
#endif
			continue;
		} else if (strcmp(*argv, "help") == 0) {
			return -1;
		} else if (new_cmd(argv)) {
			goto done0;
		} else {
			struct action_util *a = NULL;
			strncpy(k, *argv, sizeof (k) - 1);
			eap = 0;
			if (argc > 0 ) {
				a = get_action_kind(k);
			} else {
done0:
				if (ok)
					break;
				else
					goto done;
			}

			if (NULL == a) {
				goto bad_val;
			}

			tail = NLMSG_TAIL(n);
			addattr_l(n, MAX_MSG, ++prio, NULL, 0);
			addattr_l(n, MAX_MSG, TCA_ACT_KIND, k, strlen(k) + 1);

			ret = a->parse_aopt(a,&argc, &argv, TCA_ACT_OPTIONS, n);

			if (ret < 0) {
				fprintf(stderr,"bad action parsing\n");
				goto bad_val;
			}
			tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
			ok++;
		}

	}

	if (eap > 0) {
		fprintf(stderr,"bad action empty %d\n",eap);
		goto bad_val;
	}

	tail2->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail2;

done:
	*argc_p = argc;
	*argv_p = argv;
	return 0;
bad_val:
	fprintf(stderr, "parse_action: bad value (%d:%s)!\n",argc,*argv);
	return -1;
}