/* ==================================================================== * Copyright 1999 J. David Lowe. All rights reserved. * * tokens.c * * Functions for manipulating a token list. * * ==================================================================== */ #include #include #include #include #include /* ==================================================================== * NAME: token_group_init * * DESCRIPTION: Initializes and returns a pointer to a new token_group * structure. * * RETURN VALUES: Returns NULL if the memory allocation fails; otherwise * returns a pointer to a token_group structure. * * BUGS: Hopefully none. * ==================================================================== */ token_group_p token_group_init(void) { token_group_p tgroup; tgroup = (token_group_p)malloc(sizeof(token_group)); if (tgroup == NULL) { template_errno = TMPL_EMALLOC; return NULL; } tgroup->tokens = NULL; tgroup->max_token = -1; tgroup->first = 0; tgroup->last = 0; tgroup->current = 0; return(tgroup); } /* ==================================================================== * NAME: token_subgroup_init * * DESCRIPTION: Initializes and returns a pointer to a new token_group * structure, "inherited" from an existing token_group. * * RETURN VALUES: Returns NULL if the memory allocation fails; otherwise * returns a pointer to a token_group structure. * * BUGS: Hopefully none. * ==================================================================== */ token_group_p token_subgroup_init(token_group_p tgroup, unsigned int first, unsigned int last) { token_group_p new; if (tgroup == NULL) { template_errno = TMPL_ENULLARG; return NULL; } new = token_group_init(); if (new == NULL) { return NULL; } new->tokens = tgroup->tokens; new->max_token = tgroup->max_token; new->first = first; new->last = last; new->current = 0; return(new); } /* ==================================================================== * NAME: token_group_destroy * * DESCRIPTION: Frees up all memory associated with a token_group. * * RETURN VALUES: None. * * BUGS: Because a free()d pointer still *looks* valid, it is * difficult to protect against the problems that arise * if the user calls this function too early. * ==================================================================== */ void token_group_destroy(token_group_p tgroup) { if (tgroup == NULL) { return; } if (tgroup->tokens != NULL) { int i; for (i = 0; i <= tgroup->max_token; i++) { if (tgroup->tokens[i].type == TOKEN_TYPE_TAG_PARSED) { int j; for (j = 0; j <= tgroup->tokens[i].tag_argc; j++) { free(tgroup->tokens[i].tag_argv[j]); } free(tgroup->tokens[i].tag_argv); tgroup->tokens[i].type = TOKEN_TYPE_TAG; } } free(tgroup->tokens); tgroup->tokens = NULL; } free(tgroup); } /* ==================================================================== * NAME: token_subgroup_destroy * * DESCRIPTION: Frees up all memory associated with a token_group * created by token_subgroup_init() * * RETURN VALUES: None. * * BUGS: Because a free()d pointer still *looks* valid, it is * difficult to protect against the problems that arise * if the user calls this function too early. * ==================================================================== */ void token_subgroup_destroy(token_group_p tgroup) { if (tgroup == NULL) { return; } tgroup->tokens = NULL; token_group_destroy(tgroup); } /* ==================================================================== * NAME: token_rewind * * DESCRIPTION: Reets the token group's "current" index. * * RETURN VALUES: None. * * BUGS: Hopefully none. * ==================================================================== */ void token_rewind(token_group_p tgroup) { if (tgroup != NULL) { tgroup->current = tgroup->first; } } /* ==================================================================== * NAME: token_next * * DESCRIPTION: Returns the next token in a token group, and increments * the token group's "current" index. * * RETURN VALUES: Returns NULL if there are no more tokens to return; * otherwise returns a pointer to a token structure. * * BUGS: Hopefully none. * ==================================================================== */ token_p token_next(context_p ctx, token_group_p tgroup, unsigned int *position) { token_p rtok; if (tgroup == NULL) { template_errno = TMPL_ENULLARG; return NULL; } if ((tgroup->max_token < 0) || (tgroup->tokens == NULL)) { template_errno = TMPL_ENOTOKEN; return NULL; } /* If current > last, we're done, so return NULL, and wrap */ if (tgroup->current > tgroup->last) { tgroup->current = tgroup->first; template_errno = TMPL_ENOTOKEN; return NULL; } /* Otherwise, increment and return the next token */ *position = tgroup->current; ++(tgroup->current); rtok = &(tgroup->tokens[*position]); /* Side effect: parse tag if not already done */ if (rtok->type == TOKEN_TYPE_TAG) { token_parsetag(ctx, rtok); } else if (rtok->type == TOKEN_TYPE_TAG_PARSED) { int j; for (j = 1; j <= rtok->tag_argc; j++) { free(rtok->tag_argv[j]); rtok->tag_argv[j] = NULL; } token_parsetag(ctx, rtok); } return(rtok); } /* ==================================================================== * NAME: token_parsetag * * DESCRIPTION: Parses a tag's argument list in the current context. * * RETURN VALUES: None. * * BUGS: Hopefully none. * ==================================================================== */ void token_parsetag(context_p ctx, token_p token) { int length = 0; int point = 0; int total = token->length; int argbegin; char *tag, *p, last, instring; int current_argc; tag = token->t; for (; (point < total) && isspace(tag[point]); point++); for (p = tag + point; (point + length < total) && (!isspace(*p)); p++, length++) ; current_argc = 0; if (token->tag_argc < current_argc) { token->tag_argv = (char **)malloc((current_argc + 1) * sizeof(char **)); token->tag_argc = current_argc; /* Copy the tag name into argv[0] only if it's not already done */ token->tag_argv[0] = (char *)malloc(length + 1); strncpy(token->tag_argv[0], tag + point, length); (token->tag_argv[0])[length] = '\0'; } last = '\0'; instring = 0; argbegin = 0; for (point += length + 1; point < total; point++) { last = *p; p = tag + point; if ((! isspace((int)*p)) && (current_argc == 0)) { argbegin = point; ++current_argc; if (token->tag_argc < current_argc) { token->tag_argv = (char **)realloc(token->tag_argv, (current_argc + 1) * (sizeof(char *))); token->tag_argc = current_argc; } } if (*p == '"') { if ((instring) && (last != '\\')) { instring = 0; } else if (! instring) { instring = 1; } } else if (*p == ',') { if (! instring) { /* parse the current argument string into tag_argv */ token_parsearg(ctx, tag + argbegin, point - argbegin, &(token->tag_argv[current_argc])); /* point to the next argument string */ argbegin = point + 1; ++current_argc; if (token->tag_argc < current_argc) { token->tag_argv = (char **)realloc(token->tag_argv, (current_argc + 1) * (sizeof(char *))); token->tag_argc = current_argc; } } } } if (current_argc > 0) { token_parsearg(ctx, tag + argbegin, total - argbegin, &(token->tag_argv[current_argc])); } token->type = TOKEN_TYPE_TAG_PARSED; return; } /* ==================================================================== * NAME: token_push * * DESCRIPTION: Adds a new token to a token group, extending the group's * token list if necessary. * * RETURN VALUES: Returns 0 on failure, 1 on success. * * BUGS: Hopefully none. * ==================================================================== */ int token_push(token_group_p tgroup, char *t, unsigned long length, unsigned char type) { if (tgroup == NULL) { template_errno = TMPL_ENULLARG; return 0; } if (tgroup->max_token < 0) { --(tgroup->last); } if ((tgroup->max_token < 0) || (tgroup->last >= tgroup->max_token)) { /* We have to allocate some new token space */ unsigned int i; tgroup->max_token += TOKEN_GROWFACTOR; tgroup->tokens = (token_p)realloc((void *)tgroup->tokens, sizeof(token) * (tgroup->max_token + 1)); for (i = tgroup->last + 1; i <= tgroup->max_token; i++) { (tgroup->tokens[i]).type = TOKEN_TYPE_NONE; } } ++(tgroup->last); tgroup->tokens[tgroup->last].t = t; tgroup->tokens[tgroup->last].tag_argc = -1; tgroup->tokens[tgroup->last].tag_argv = NULL; tgroup->tokens[tgroup->last].length = length; tgroup->tokens[tgroup->last].type = type; return(1); } /* ==================================================================== * NAME: tokenize * * DESCRIPTION: Breaks a string into a token group using the rules in * the current context. * * RETURN VALUES: Returns 0 if the input string contains unrecoverable * syntax errors, 1 otherwise. * * BUGS: Hopefully none. * ==================================================================== */ int tokenize(context_p ctx, char *input, token_group_p tokens) { char strip = ctx_is_strip(ctx); char *otag = context_get_value(ctx, TMPL_VARNAME_OTAG); int slo = strlen(otag); char *ctag = context_get_value(ctx, TMPL_VARNAME_CTAG); int slc = strlen(ctag); char *position = input; char *tagstart, *tagend; while ((tagstart = strstr(position, otag)) != NULL) { /* everything before the open tag is one token */ token_push(tokens, position, tagstart - position, TOKEN_TYPE_TEXT); /* find the end of the tag */ tagend = strstr(tagstart, ctag); if (tagend == NULL) { /* tokens_destroy */ template_errno = TMPL_EPARSE; return 0; } /* the tag is one token */ token_push(tokens, tagstart + slo, tagend - tagstart - slo, TOKEN_TYPE_TAG); /* move past the end of the tag */ position = tagend + slc; if ((strip) && (*position == '\n')) position++; } /* everything after the last tag is one token */ token_push(tokens, position, strlen(position), TOKEN_TYPE_TEXT); return 1; } /* ==================================================================== * NAME: token_parsearg * * DESCRIPTION: Parses a string (inarg) as a single argument. Does * variable substitution and string concatentation, and * outputs the result into outarg. * * RETURN VALUES: None - output is placed into outarg. * * BUGS: Character by character parsing may be avoidable - not * sure. * ==================================================================== */ void token_parsearg(context_p ctx, char *inarg, int size, char **outarg) { char *begin, *p, *varvalue, *b; char instring, last; int index, cursize, i, length; context_p rootctx = NULL; i = 0; index = 0; cursize = 0; *outarg = NULL; /* move past leading whitespace */ for (begin = inarg; isspace((int)*begin); ++begin, ++i) ; instring = 0; last = '\0'; for (p = begin; i < size; last = *p, p++, i++) { if (*p == '"') { if (instring) { if (last == '\\') { --index; append_output(outarg, "\"", 1, &cursize, &index); } else { instring = 0; } } else if (! instring) { instring = 1; } } else if (*p == '$') { if (instring) { append_output(outarg, p, 1, &cursize, &index); } else { b = ++p; for (++i; ((i <= size) && (isalnum((int)*p) || (*p == '_') || (*p == '.'))); p++, i++) ; length = p - b; if (rootctx == NULL) { rootctx = context_root(ctx); } if (rootctx->bufsize < (length + 1)) { if (rootctx->buffer != NULL) { free(rootctx->buffer); } rootctx->buffer = (char *)malloc(length + 1); rootctx->bufsize = length + 1; } strncpy(rootctx->buffer, b, length); (rootctx->buffer)[length] = '\0'; varvalue = context_get_value(ctx, rootctx->buffer); if (varvalue != NULL) { append_output(outarg, varvalue, strlen(varvalue), &cursize, &index); } --p; --i; } } else { if (instring) { append_output(outarg, p, 1, &cursize, &index); } } } /* ensure null termination even if append_output was never called */ if (*outarg != NULL) { (*outarg)[index] = '\0'; } } /* ==================================================================== * NAME: append_output * * DESCRIPTION: Function used by parser to dynamically expand a string * as needed. This is really a glorified strncat which * grows the destination string as needed. * * RETURN VALUES: None, but *output is modified. * * BUGS: Hopefully none. * ==================================================================== */ void append_output(char **output, char *append, int append_size, int *current_size, int *current_length) { if (((*current_length) + append_size + 1) > *current_size) { char *temp; if (((*current_length) + append_size + 1) > ((*current_size) * 2)) { *current_size = ((*current_length) + append_size + 1) * 2; } else { *current_size = (*current_size) * 2; } temp = (char *)malloc(*current_size); if (*output != NULL) { strncpy(temp, *output, *current_length); temp[*current_length] = '\0'; free(*output); } *output = temp; } strncpy((*output) + (*current_length), append, append_size); (*output)[(*current_length) + append_size] = '\0'; (*current_length) += append_size; }