/* ==================================================================== * Copyright 1999 Web Juice, LLC. All rights reserved. * * parser.c * * The parsing bits of the template library. * * ==================================================================== */ #include #include #include #include #include /* ==================================================================== * NAME: parser * * DESCRIPTION: parses the given input using the current context, and * outputs to a new string pointed to by the output * parameter. * * RETURN VALUES: Returns -1 if there's a problem, and the length of the * output string if the parsing was successful. The real * output goes into the output string (which the caller is * responsible for freeing!) * * BUGS: Hopefully none. * ==================================================================== */ int parser(context_p ctx, int looping, token_group_p tokens, char **output) { context_p current = ctx; int output_size = 0; int output_length = 0; context_p rootctx = context_root(ctx); staglist_p simple_tags = NULL; tagplist_p tag_pairs = NULL; token_p token = NULL; unsigned int tokpos = 0; if (ctx == NULL) { template_errno = TMPL_ENULLARG; return -1; } if (tokens == NULL) { template_errno = TMPL_ENULLARG; return -1; } simple_tags = rootctx->simple_tags; tag_pairs = rootctx->tag_pairs; *output = NULL; do { /* let's avoid doing any work as long as we can */ if (! ctx_is_output(current)) { if (looping) { current = current->next_context; } continue; } /* rewind the token list */ token_rewind(tokens); /* while we have a token */ while ((token = token_next(current, tokens, &tokpos)) != NULL) { if (token->type == TOKEN_TYPE_TEXT) { append_output(output, token->t, token->length, &output_size, &output_length); continue; } if (token->type != TOKEN_TYPE_TAG_PARSED) { template_errno = TMPL_ESCREWY; return -1; } /* deal with the simple tag case */ if (staglist_exists(simple_tags, token->tag_argv[0])) { char *result; if ((staglist_exec(simple_tags, token->tag_argv[0], current, &result, token->tag_argc, token->tag_argv)) && (result != NULL)) { token_group_p subtokens = token_group_init(); char *parsed_result = NULL; int parsed_result_length = 0; if (tokenize(current, result, subtokens)) { parsed_result_length = parser(current, 0, subtokens, &parsed_result); } else { return -1; } token_group_destroy(subtokens); if (parsed_result_length < 0) { free(result); free(parsed_result); return -1; } append_output(output, parsed_result, parsed_result_length, &output_size, &output_length); free(result); free(parsed_result); } /* deal with the tag pair case */ } else if (tagplist_is_opentag(tag_pairs, token->tag_argv[0])) { int depth = 1; token_p subtok = NULL; unsigned int subtokpos = 0; while ((subtok = token_next(current, tokens, &subtokpos))!=NULL) { if (subtok->type != TOKEN_TYPE_TAG_PARSED) { continue; } /* if the close tag is the same as the open tag, we're nesting... */ if (strcmp(token->tag_argv[0], subtok->tag_argv[0]) == 0) { ++depth; /* if the close tag and open tag form a pair, we're un-nesting... */ } else if (tagplist_is_closetag(tag_pairs, token->tag_argv[0], subtok->tag_argv[0])) { --depth; } /* if depth is zero, this close tag is *the* close tag. */ if (depth == 0) { token_group_p newtokens; context_p newcontext; newcontext = tagplist_exec(tag_pairs,token->tag_argv[0], current, token->tag_argc, token->tag_argv); newtokens = token_subgroup_init(tokens, tokpos + 1, subtokpos - 1); if ((newcontext != NULL) && (newtokens != NULL)) { char *parsed_result = NULL; int parsed_result_length = 0; parsed_result_length = parser(newcontext, 1, newtokens, &parsed_result); token_subgroup_destroy(newtokens); if (parsed_result_length < 0) { free(parsed_result); if (ctx_is_anonymous(newcontext)) { context_destroy(newcontext); } return -1; } append_output(output, parsed_result, parsed_result_length, &output_size, &output_length); free(parsed_result); if (ctx_is_anonymous(newcontext)) { context_destroy(newcontext); } break; } } } if (depth != 0) { template_errno = TMPL_EPARSE; return -1; } } } /* done this iteration - move to the next */ if (looping) { current = current->next_context; } } while ((looping) && (current != NULL)); return output_length; }