/* * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code 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 * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ #include #ifdef DEBUG_ARGFILE #ifndef NO_JNI #define NO_JNI #endif #define JLI_ReportMessage(p1, p2) printf((p1), (p2)) #else #include "java.h" #endif #include "jli_util.h" #include "emessages.h" static char* clone_substring(char *begin, size_t len) { char *rv; rv = (char *) JLI_MemAlloc(len + 1); memcpy(rv, begin, len); rv[len] = '\0'; return rv; } /* * get quoted token, *cptr points to the start of the token, the beginning * quote */ static char* dequote(char** cptr, const char * const eob) { char *nextc = *cptr; char quote_char = *nextc; char *anchor = nextc + 1; char *c; char d, c2, first; /* arbitarily pick 4, hopefully in most cases no more than 4 escaped chars */ JLI_List escaped = JLI_List_new(4); #ifdef DQ_READ #error "DQ_READ had been defined!" #endif #define DQ_READ(V) \ nextc++; \ if (nextc >= eob) { \ JLI_List_free(escaped); \ return NULL; \ } \ V = (*nextc) /* end DQ_READ */ DQ_READ(d); while (d != quote_char && d != '\n' && d != '\r') { if (d == '\\') { /* string before '\\' */ JLI_List_addSubstring(escaped, anchor, nextc - anchor); c = JLI_MemAlloc(2 * sizeof(char)); c[1] = '\0'; DQ_READ(c[0]); first = c[0]; /* To allow \377, but not \477 */ if (c[0] >= '0' && c[0] <= '7') { c[0] = c[0] - '0'; DQ_READ(c2); if ('0' <= c2 && c2 <= '7') { c[0] = (c[0] << 3) + (c2 - '0'); DQ_READ(c2); if ('0' <= c2 && c2 <= '7' && first <= '3') { c[0] = (c[0] << 3) + (c2 - '0'); DQ_READ(d); } else { d = c2; } } else { d = c2; } } else { switch (c[0]) { case 'a': c[0] = 0x7; break; case 'b': c[0] = '\b'; break; case 'f': c[0] = 0xC; break; case 'n': c[0] = '\n'; break; case 'r': c[0] = '\r'; break; case 't': c[0] = '\t'; break; case 'v': c[0] = 0xB; break; } DQ_READ(d); } JLI_List_add(escaped, c); anchor = nextc; } else { DQ_READ(d); } } #undef DQ_READ /* nextc points to end quote or eol */ // assert(nextc < eob); *cptr = nextc + 1; if (escaped->size == 0) { return clone_substring(anchor, nextc - anchor); } else { JLI_List_addSubstring(escaped, anchor, nextc - anchor); return JLI_List_combine(escaped); } } /* * cptr: the output parameter of a char* points at next read location * eob: the pointer points to end of buffer, one after the last character * return the pointer to start of next token, NULL if the buffer contains no token. * *cptr will point to next character after the token. * if returns NULL and cptr != eob indicated partial token remains in the buffer */ static char* nextToken(char** cptr, const char * const eob) { char *nextc = *cptr; char *anchor; size_t len; char *rv; /* whitespaceChars(0, ' ') */ while (nextc < eob && *nextc <= ' ') nextc++; if (nextc == eob) { *cptr = nextc; return NULL; } /* start with non-whitespace character */ anchor = nextc; switch (*nextc) { case '#': /* commentChar('#') */ /* ignore until eol */ for(nextc++; nextc < eob && *nextc != '\n'; nextc++); if (nextc < eob) { /* skip comment, continue read for next token */ *cptr = nextc++; return nextToken(cptr, eob); } break; case '"': /* quoteChar('"') */ case '\'': /* quoteChar('\'') */ *cptr = nextc; return dequote(cptr, eob); default: for (nextc++; nextc < eob && *nextc > ' '; nextc++) { /* comment or quote break a token just like whitespace */ if (*nextc == '#' || *nextc == '"' || *nextc == '\'') { break; } } if (nextc < eob) { len = nextc - anchor; rv = clone_substring(anchor, len); *cptr = nextc; return rv; } } /* nextc == eob, partial of next token * need to combine with next read */ *cptr = anchor; return NULL; } static JLI_List readArgFile(FILE *file) { char buf[4096]; JLI_List rv; size_t size = 0; size_t buf_size = sizeof(buf); char *nextc = buf; char *token = NULL; char *eob = buf; char *extra_buf = NULL; size_t extra_size = sizeof(buf); char *active_buf = buf; /* arbitrarily pick 8, seems to be a reasonable number of arguments */ rv = JLI_List_new(8); while (!feof(file)) { size = fread(eob, sizeof(char), buf_size - size, file); if (ferror(file)) { JLI_List_free(rv); if (extra_buf != NULL) { JLI_MemFree(extra_buf); } return NULL; } /* nextc is next character to read from the buffer * eob is the end of input * token is the copied token value, NULL if no a complete token */ nextc = active_buf; eob = eob + size; token = nextToken(&nextc, eob); while (token != NULL) { JLI_List_add(rv, token); token = nextToken(&nextc, eob); } /* remaining characters of a non-complete token, * need to combine with next read. * if size = 0, means the input is complete read and no token * available in this buffer */ size = eob - nextc; if (size == 0) { active_buf = buf; buf_size = sizeof(buf); } else { if (size < sizeof(buf)) { // use stack whenever possible active_buf = buf; buf_size = sizeof(buf); memmove(active_buf, nextc, size); } else { if (size >= extra_size) { // increase 4K extra_size += sizeof(buf); extra_buf = (char*) JLI_MemRealloc(extra_buf, extra_size); if (active_buf == buf) { memmove(extra_buf, nextc, size); } // else realloc takes care of copy } else { // size fit in extra but still to large for buf memmove(extra_buf, nextc, size); } active_buf = extra_buf; buf_size = extra_size; } } // assert(size < buf_size); eob = active_buf + size; } /* last token from remaining */ if (active_buf < eob) { if (*active_buf == '"' || *active_buf == '\'') { nextc = active_buf + 1; } else { nextc = active_buf; } if (*nextc != '#') { token = clone_substring(nextc, eob - nextc); JLI_List_add(rv, token); } } if (extra_buf != NULL) { JLI_MemFree(extra_buf); } return rv; } /* * if the arg represent a file, that is, prefix with a single '@', * return a list of arguments from the file. * otherwise, return NULL. */ JLI_List JLI_ExpandArgFile(const char *arg, jboolean not_java) { JLI_List rv; FILE *fptr; if (not_java) { if (JLI_StrNCmp("-J@", arg, 3) != 0) { return NULL; } else { arg += 3; } } else if (*arg != '@') { return NULL; } else { arg++; } if (*arg == '@' || *arg == '\0') { return NULL; } fptr = fopen(arg, "r"); /* arg file cannot be openned */ if (fptr == NULL) { JLI_ReportMessage(CFG_ERROR6, arg); exit(1); } rv = readArgFile(fptr); fclose(fptr); /* error occurred reading the file */ if (rv == NULL) { JLI_ReportMessage(DLL_ERROR4, arg); } return rv; } #ifdef DEBUG_ARGFILE void fail(char *expected, char *actual, size_t idx) { printf("FAILED: Token[%lu] expected to be <%s>, got <%s>\n", idx, expected, actual); exit(1); } void test_case(char *case_data, char **tokens, size_t cnt_tokens) { char *b, *eob; size_t actual_cnt; char *token; //char **tokens = case_data + 1; b = case_data; eob = b + strlen(b); actual_cnt = 0; printf("Test case: <%s>, expected %lu tokens.\n", b, cnt_tokens); for (token = nextToken(&b, eob); token != NULL; token = nextToken(&b, eob)) { // should not have more tokens than expected if (actual_cnt >= cnt_tokens) { printf("FAILED: Extra token detected: <%s>\n", token); exit(2); } if (JLI_StrCmp(token, tokens[actual_cnt]) != 0) { fail(tokens[actual_cnt], token, actual_cnt); } actual_cnt++; } if (actual_cnt >= cnt_tokens) { // same number of tokens, should have nothing left to parse if (b != eob) { // remainder could be comment if (*b != '#') { printf("FAILED: Leftover detected: <%s>\n", b); exit(2); } } } else { if (JLI_StrCmp(b, tokens[actual_cnt]) != 0) { fail(tokens[actual_cnt], b, actual_cnt); } actual_cnt++; } if (actual_cnt != cnt_tokens) { printf("FAILED: Number of tokens not match, expected %lu, got %lu\n", cnt_tokens, actual_cnt); exit(3); } printf("PASS\n"); } #define DO_CASE(name) \ test_case(name[0], name + 1, sizeof(name)/sizeof(char*) - 1) int main(int argc, char** argv) { int i, j; char* case1[] = { "-version -cp \"c:\\\\java libs\\\\one.jar\" \n", "-version", "-cp", "c:\\java libs\\one.jar" }; DO_CASE(case1); // note the open quote at the end char* case2[] = { "com.foo.Panda \"Furious 5\" 'Shi Fu' \"escape\tprison", "com.foo.Panda", "Furious 5", "Shi Fu", "\"escape\tprison"}; DO_CASE(case2); char* escaped_chars[] = { "escaped chars testing \"\\a\\b\\c\\f\\n\\r\\t\\v\\9\\6\\23\\82\\28\\377\\477\\278\\287\"", "escaped", "chars", "testing", "\a\bc\f\n\r\t\v9\006\02382\0028\377\0477\0278\00287"}; DO_CASE(escaped_chars); char* mixed_quote[] = { "\"mix 'single quote' in double\" 'mix \"double quote\" in single'", "mix 'single quote' in double", "mix \"double quote\" in single"}; DO_CASE(mixed_quote); char* comments[] = { "line one #comment\n'line #2' #rest are comment\r#comment on line 3\nline 4 #comment to eof", "line", "one", "line #2", "line", "4"}; DO_CASE(comments); if (argc > 1) { for (i = 0; i < argc; i++) { JLI_List tokens = JLI_ExpandArgFile(argv[i], JNI_FALSE); if (NULL != tokens) { for (j = 0; j < tokens->size; j++) { printf("Token[%d]: <%s>\n", j, tokens->elements[j]); } } } } } #endif // DEBUG_ARGFILE