/* -------------------------------------------------------------------------- * Copyright 2003-2011 (inclusive) Nathan Angelacos * (nangel@users.sourceforge.net) * * This file is part of haserl. * * Haserl is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, * as published by the Free Software Foundation. * * Haserl is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with haserl. If not, see . * * ------------------------------------------------------------------------ */ #if HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include "common.h" #include "h_error.h" #include "h_script.h" #include "h_bash.h" #include "haserl.h" #ifdef BASHEXTENSIONS /* HTML, RUN, INCLUDE, EVAL, COMMENT, IF, ELIF, ELSE, ENDIF, CASE, WHEN, OTHERWISE, ENDCASE, WHILE, ENDWHILE, UNTIL, ENDUNTIL, FOR, ENDFOR, UNLESS, ELUN, UNELSE, ENDUNLESS, NOOP }; */ #else /* HTML, RUN, INCLUDE, EVAL, COMMENT, NOOP */ #endif const char *g_tag[] = { "", "", "in", "=", "#", #ifdef BASHEXTENSIONS "if", "elif", "else", "endif", "case", "when", "otherwise", "endcase", "while", "endwhile", "until", "enduntil", "for", "endfor", "unless", "elun", "unelse", "endunless", #endif "" }; /* the open and close tags */ char open_tag[3] = "<%"; char close_tag[3] = "%>"; /* Open a script and return a populated script_t structure */ script_t * load_script (char *filename, script_t * scriptlist) { script_t *scriptbuf; int scriptfp; struct stat filestat; scriptfp = open (filename, O_NONBLOCK + O_RDONLY); if (scriptfp == -1) { /* open failed */ die_with_message (NULL, NULL, g_err_msg[E_FILE_OPEN_FAIL], filename); } fstat (scriptfp, &filestat); scriptbuf = (script_t *) xmalloc (sizeof (script_t)); scriptbuf->name = (char *) xmalloc (strlen (filename) + 1); scriptbuf->buf = (char *) xmalloc (filestat.st_size + 1); memset (scriptbuf->name, 0, strlen (filename) + 1); memcpy (scriptbuf->name, filename, strlen (filename)); memset (scriptbuf->buf, 0, filestat.st_size + 1); read (scriptfp, scriptbuf->buf, filestat.st_size); scriptbuf->size = filestat.st_size; scriptbuf->uid = filestat.st_uid; scriptbuf->gid = filestat.st_gid; scriptbuf->curpos = 0; scriptbuf->next = NULL; /* if we already have scripts, add this one to the end */ if (scriptlist != NULL) { while (scriptlist->next) scriptlist = scriptlist->next; scriptlist->next = scriptbuf; } /* skip over the first line, if the file starts with a #! * This means that offset will start at the beginning of * the second line in most cases. */ if (memcmp (scriptbuf->buf, "#!", 2) == 0) { while ((scriptbuf->curpos < scriptbuf->size) && ((char) scriptbuf->buf[scriptbuf->curpos] != '\n')) { (scriptbuf->curpos)++; } (scriptbuf->curpos)++; } /* If this is the first script, switch to mode only * if <% is not used anywhere else in the script. Otherwise * don't change the tagging method */ if (scriptlist == NULL) { if (strstr (scriptbuf->buf + scriptbuf->curpos, open_tag) == NULL) { open_tag[1] = '?'; close_tag[0] = '?'; } } close (scriptfp); return (scriptbuf); } /* Free the script structures */ void free_script_list (script_t * script) { script_t *next; while (script) { next = script->next; if (script->name) free (script->name); if (script->buf) free (script->buf); free (script); script = next; } } /* Insert this token into the token chain. * If tokenlist is null, create a new one */ token_t * push_token_on_list (token_t * tokenlist, script_t * scriptbuf, char *start, size_t len) { token_t *me, *next; if (len == 0) return (tokenlist); me = (token_t *) xmalloc (sizeof (token_t)); if (tokenlist == NULL) { next = NULL; } else { next = tokenlist->next; tokenlist->next = me; } me->next = next; me->script = scriptbuf; me->buf = start; me->len = len; return (me); } /* Free a token chain */ void free_token_list (token_t * tokenlist) { token_t *next; while (tokenlist) { next = tokenlist->next; free (tokenlist); tokenlist = next; } } #ifndef JUST_LUACSHELL /* return the point in a script where the "comment" ends */ char * skip_comment (char *startbuf, char *endbuf) { unsigned int c_lev = 1; char *s_tag, *e_tag; startbuf += 2; while (startbuf < endbuf) { s_tag = strstr (startbuf, open_tag); e_tag = strstr (startbuf, close_tag); if (!e_tag) { break; } if ((s_tag) && (s_tag < e_tag)) { c_lev++; startbuf = s_tag + 2; continue; } /* otherwise, we have an end tag first */ c_lev--; startbuf = e_tag; if (c_lev == 0) { return (startbuf); } startbuf += 2; } return NULL; } /* build a tokenchain from a script. This step just * splits a script buf into parts that are separated * by <% %>. If the <% %> are out of order, then it * flags that, but doesn't try to do anything else. * * For nesting comments, this function IS aware of * the comment tag, and will try accomodate commented * tags. Commented script never enters the token list */ token_t * build_token_list (script_t * scriptbuf, token_t * tokenlist) { char *start, *end, *curpos, *endpos; token_t *curtoken, *firsttoken; curtoken = tokenlist; firsttoken = tokenlist; curpos = scriptbuf->buf + scriptbuf->curpos; endpos = scriptbuf->buf + scriptbuf->size; while (curpos < endpos) { start = strstr (curpos, open_tag); end = strstr (curpos, close_tag); /* if this is a comment tag, the end is at the end of the comment */ if ((start) && (memcmp (start + 2, g_tag[COMMENT], 1) == 0)) { /* save any bare html before the comment */ curtoken = push_token_on_list (curtoken, scriptbuf, curpos, start - curpos); if (firsttoken == NULL) firsttoken = curtoken; /* push start of token to end of token */ end = skip_comment (start, endpos); if (end) { curpos = end + 2; continue; } } if (start && !end) die_with_message (scriptbuf, start, g_err_msg[E_NO_END_MARKER], open_tag[1]); if ((start > end) || (!start && end)) die_with_message (scriptbuf, end, g_err_msg[E_END_BEFORE_BEGIN], open_tag[1], open_tag[1]); if (start && (strstr (start + 1, open_tag) && (strstr (start + 1, open_tag) < end))) die_with_message (scriptbuf, start, g_err_msg[E_NO_END_MARKER], open_tag[1]); if (end) { /* push curpos to the start of the token */ curtoken = push_token_on_list (curtoken, scriptbuf, curpos, start - curpos); if (firsttoken == NULL) firsttoken = curtoken; /* push start of token to end of token */ curtoken = push_token_on_list (curtoken, scriptbuf, start, end - start); if (firsttoken == NULL) firsttoken = curtoken; curpos = end + 2; } else { /* push curpos to end of script */ curtoken = push_token_on_list (curtoken, scriptbuf, curpos, endpos - curpos); if (firsttoken == NULL) firsttoken = curtoken; curpos = endpos; } } return (firsttoken); } /* syntax check a script */ void preprocess_token_list (token_t * tokenlist) { script_t *newscript; token_t *me; char *cp; me = tokenlist; /* walk the chain to fill in the tags */ while (me) { if (memcmp (me->buf, open_tag, 2)) { me->tag = HTML; } else { me->tag = NOOP; me->buf[me->len] = '\0'; cp = me->buf + 2; /* skip tag = INCLUDE; /* skip the first word - in, include, include-file, etc */ me->buf = find_whitespace (me->buf); me->buf = skip_whitespace (me->buf); cp = find_whitespace (me->buf); *cp = '\0'; me->len = strlen (me->buf) + 1; newscript = load_script (me->buf, me->script); build_token_list (newscript, me); } else if (memcmp (cp, g_tag[EVAL], 1) == 0) { me->tag = EVAL; me->buf = find_whitespace (me->buf); me->len = strlen (me->buf); } #ifdef BASHEXTENSIONS else if (memcmp (cp, g_tag[IF], 2) == 0) { me->tag = IF; me->buf = find_whitespace (me->buf); me->buf = skip_whitespace (me->buf); me->len = strlen (me->buf); } else if (memcmp (cp, g_tag[ELIF], 4) == 0) { me->tag = ELIF; me->buf = find_whitespace (me->buf); me->buf = skip_whitespace (me->buf); me->len = strlen (me->buf); } else if (memcmp (cp, g_tag[ELSE], 4) == 0) { me->tag = ELSE; me->buf = find_whitespace (me->buf); me->buf = skip_whitespace (me->buf); me->len = strlen (me->buf); } else if (memcmp (cp, g_tag[ENDIF], 5) == 0) { me->tag = ENDIF; me->buf = find_whitespace (me->buf); me->buf = skip_whitespace (me->buf); me->len = strlen (me->buf); } else if (memcmp (cp, g_tag[CASE], 4) == 0) { me->tag = CASE; me->buf = find_whitespace (me->buf); me->buf = skip_whitespace (me->buf); me->len = strlen (me->buf); } else if (memcmp (cp, g_tag[WHEN], 4) == 0) { me->tag = WHEN; me->buf = find_whitespace (me->buf); me->buf = skip_whitespace (me->buf); me->len = strlen (me->buf); } else if (memcmp (cp, g_tag[OTHERWISE], 9) == 0) { me->tag = OTHERWISE; me->buf = find_whitespace (me->buf); me->buf = skip_whitespace (me->buf); me->len = strlen (me->buf); } else if (memcmp (cp, g_tag[ENDCASE], 7) == 0) { me->tag = ENDCASE; me->buf = find_whitespace (me->buf); me->buf = skip_whitespace (me->buf); me->len = strlen (me->buf); } else if (memcmp (cp, g_tag[WHILE], 5) == 0) { me->tag = WHILE; me->buf = find_whitespace (me->buf); me->buf = skip_whitespace (me->buf); me->len = strlen (me->buf); } else if (memcmp (cp, g_tag[ENDWHILE], 8) == 0) { me->tag = ENDWHILE; me->buf = find_whitespace (me->buf); me->buf = skip_whitespace (me->buf); me->len = strlen (me->buf); } else if (memcmp (cp, g_tag[UNTIL], 5) == 0) { me->tag = UNTIL; me->buf = find_whitespace (me->buf); me->buf = skip_whitespace (me->buf); me->len = strlen (me->buf); } else if (memcmp (cp, g_tag[ENDUNTIL], 8) == 0) { me->tag = ENDUNTIL; me->buf = find_whitespace (me->buf); me->buf = skip_whitespace (me->buf); me->len = strlen (me->buf); } else if (memcmp (cp, g_tag[FOR], 3) == 0) { me->tag = FOR; me->buf = find_whitespace (me->buf); me->buf = skip_whitespace (me->buf); me->len = strlen (me->buf); } else if (memcmp (cp, g_tag[ENDFOR], 6) == 0) { me->tag = ENDFOR; me->buf = find_whitespace (me->buf); me->buf = skip_whitespace (me->buf); me->len = strlen (me->buf); } else if (memcmp (cp, g_tag[UNLESS], 6) == 0) { me->tag = UNLESS; me->buf = find_whitespace (me->buf); me->buf = skip_whitespace (me->buf); me->len = strlen (me->buf); } else if (memcmp (cp, g_tag[ELUN], 4) == 0) { me->tag = ELUN; me->buf = find_whitespace (me->buf); me->buf = skip_whitespace (me->buf); me->len = strlen (me->buf); } else if (memcmp (cp, g_tag[UNELSE], 6) == 0) { me->tag = UNELSE; me->buf = find_whitespace (me->buf); me->buf = skip_whitespace (me->buf); me->len = strlen (me->buf); } else if (memcmp (cp, g_tag[ENDUNLESS], 9) == 0) { me->tag = ENDUNLESS; me->buf = find_whitespace (me->buf); me->buf = skip_whitespace (me->buf); me->len = strlen (me->buf); } #endif /* BASHEXTENSIONS */ if (isspace (*cp)) { me->tag = RUN; me->buf = cp; } if (me->tag == NOOP) { die_with_message (me->script, cp, g_err_msg[E_NO_OP]); } me->len = strlen (me->buf); } me = me->next; } } token_t * process_token_list (buffer_t * buf, token_t * token) { char *c; haserl_buffer_init (buf); shell_exec (buf, "\n"); /* try to get the error reporting match the line number */ while (token) { switch (token->tag) { case HTML: /* Change from 0.8.0 - if the whole thing is just whitespace, don't print it */ c = token->buf; while ((c < (token->buf + token->len)) && (isspace (*c))) c++; if (c != token->buf + token->len) shell_echo (buf, token->buf, token->len); break; case RUN: shell_exec (buf, token->buf); shell_exec (buf, "\n"); break; case EVAL: shell_eval (buf, token->buf, token->len); break; #ifdef BASHEXTENSIONS case IF: shell_if (buf, token->buf, token->len); break; case ELIF: shell_elif (buf, token->buf, token->len); break; case ELSE: shell_else (buf, token->buf, token->len); break; case ENDIF: shell_endif (buf, token->buf, token->len); break; case CASE: shell_case (buf, token->buf, token->len); break; case WHEN: shell_when (buf, token->buf, token->len); break; case OTHERWISE: shell_otherwise (buf, token->buf, token->len); break; case ENDCASE: shell_endcase (buf, token->buf, token->len); break; case WHILE: shell_while (buf, token->buf, token->len); break; case ENDWHILE: shell_endwhile (buf, token->buf, token->len); break; case UNTIL: shell_until (buf, token->buf, token->len); break; case ENDUNTIL: shell_enduntil (buf, token->buf, token->len); break; case FOR: shell_for (buf, token->buf, token->len); break; case ENDFOR: shell_endfor (buf, token->buf, token->len); break; case UNLESS: shell_unless (buf, token->buf, token->len); break; case ELUN: shell_elun (buf, token->buf, token->len); break; case UNELSE: shell_unelse (buf, token->buf, token->len); break; case ENDUNLESS: shell_endunless (buf, token->buf, token->len); break; #endif default: break; } token = token->next; } return (token); } #endif