/* * Copyright (C) 2009 Sourcefire, Inc. * * Authors: aCaB <acab@clamav.net> * * This program 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. * * This program 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 this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301, USA. */ #if HAVE_CONFIG_H #include "clamav-config.h" #endif #include "dirent.h" #include "libgen.h" #include <fcntl.h> /* THIS IS A HACK ! */ /* _setargv is the designed way to customize command line parsing which we use here for globbing reasons (incidentally the globbing in setargv.obj is badly broken) The crt first calls OUR _setargv from pre_c_init but later, from within pre_cpp_init, it also calls ITS OWN BUILTIN NATIVE CRAP, which re-parses the command line and eventually overrides our override. So, we additionally replace the command line global pointer _acmdln with a crafted set of arguments in order to fool buggy CRT's. */ #define _MY_CRT_INSISTS_ON_PARSING_THE_COMMAND_LINE_TWICE_FOR_NO_REASONS_ #ifdef _MY_CRT_INSISTS_ON_PARSING_THE_COMMAND_LINE_TWICE_FOR_NO_REASONS_ extern char ** __p__acmdln(void); #endif int glob_add(const char *path, int *argc, char ***argv); int _setargv() { char *cur = GetCommandLineA(), *begparm = NULL, *endparm = NULL; char **argv = NULL, c; int argc = 0, in_sq = 0, in_dq = 0, need_glob = 0, allarglen = 0; int *g_argc = __p___argc(); char ***g_argv = __p___argv(); _setmode(_fileno(stdin), _O_BINARY); do { c = *cur; switch(c) { case '\0': endparm = cur; break; case ' ': if(begparm && !(in_sq | in_dq)) endparm = cur; break; case '\'': if(!in_dq) { in_sq = !in_sq; if(!in_sq) endparm = cur; } break; case '"': if(!in_sq) { in_dq = !in_dq; if(!in_dq) endparm = cur; } break; case '*': case '?': if(!in_sq) need_glob = 1; default: if(!begparm) { begparm = cur; endparm = NULL; } } if (begparm && endparm) { if(begparm < endparm) { char *path = malloc(endparm - begparm + 1), *quotes; int arglen = 0; memcpy(path, begparm, endparm - begparm); path[endparm - begparm] = '\0'; quotes = path; while((quotes = strchr(quotes, '"'))) memmove(quotes, quotes + 1, (endparm - begparm) - (quotes - path)); if(argc && need_glob) { arglen = glob_add(path, &argc, &argv); if(!arglen) { path = malloc(endparm - begparm + 1); memcpy(path, begparm, endparm - begparm); path[endparm - begparm] = '\0'; } } if(!arglen) { argv = realloc(argv, sizeof(*argv) * (argc + 1)); argv[argc] = path; argc++; arglen = endparm - begparm; } allarglen += arglen; } need_glob = 0; in_sq = 0; in_dq = 0; begparm = NULL; endparm = NULL; } cur++; } while (c); if(argc) { int i, argvlen = sizeof(*argv) * (argc + 1), argclen = 0; argv = realloc(argv, argvlen + allarglen + argc); argv[argc] = NULL; for(i=0; i<argc; i++) { int curlen = strlen(argv[i]) + 1; char *curarg = (char *)argv + argvlen + argclen; memcpy(curarg, argv[i], curlen); argclen += curlen; free(argv[i]); argv[i] = curarg; } #ifdef _MY_CRT_INSISTS_ON_PARSING_THE_COMMAND_LINE_TWICE_FOR_NO_REASONS_ { char *fake_cmdl = malloc(argclen + 1 + 2*argc); char *curarg = fake_cmdl; char **g_cmdl = __p__acmdln(); for(i=0; i<argc; i++) curarg += sprintf(curarg, "\"%s\" ", argv[i]); curarg--; *curarg = '\0'; *g_cmdl = fake_cmdl; } #endif *g_argc = argc; *g_argv = argv; } return 0; } int glob_add(const char *path, int *argc, char ***argv) { char *tail = strchr(path, '*'), *tailqmark; char *dup1, *dup2, *dir, *base, *taildirsep, *tailwldsep; struct dirent *de; int baselen, taillen, dirlen, mergedir = 0, outlen = 0; int qmarklen = 0; DIR *d; if(strlen(path) > 4 && !memcmp(path, "\\\\?\\", 4)) tailqmark = strchr(&path[4], '?'); else tailqmark = strchr(path, '?'); if(tailqmark && (!tail || tailqmark < tail)) tail = tailqmark; if(!tail) { *argv = realloc(*argv, sizeof(**argv) * (*argc + 1)); (*argv)[*argc] = path; (*argc)++; return strlen(path); } if(tail!=path && tail[-1] == '\\') { tail[-1] = '\0'; mergedir = 1; } while(*tail) { if(*tail == '?') { if(tail == tailqmark || qmarklen) qmarklen++; *tail = 0; } else if(*tail == '*') { *tail = '\0'; qmarklen = 0; } else break; tail++; } taillen = strlen(tail); taildirsep = strchr(tail, '\\'); if(taildirsep && taildirsep - tail == taillen - 1) { *taildirsep = '\0'; taildirsep = NULL; taillen--; } if(!taildirsep) taildirsep = tail + taillen; tailwldsep = strchr(tail, '*'); tailqmark = strchr(tail, '?'); if(tailqmark && (!tailwldsep || tailqmark < tailwldsep)) tailwldsep = tailqmark; if(!tailwldsep) tailwldsep = tail + taillen; dup1 = strdup(path); dup2 = strdup(path); if(!mergedir) { dir = dirname(dup1); base = basename(dup2); } else { dir = dup1; base = dup2; *dup2 = '\0'; } dirlen = strlen(dir); baselen = strlen(base); d = opendir(dir); while(d && (de = readdir(d))) { int namelen = strlen(de->d_name); char *newpath; if(!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) continue; if(namelen < baselen) continue; if(strncasecmp(base, de->d_name, baselen)) continue; if(de->d_type == DT_DIR && taildirsep < tailwldsep) { int d_taillen = taildirsep - tail; if(namelen < baselen + d_taillen) continue; if(strncasecmp(tail, &de->d_name[namelen - d_taillen], d_taillen)) continue; newpath = malloc(dirlen + namelen + taillen - d_taillen + 3); sprintf(newpath, "%s\\%s\\%s", dir, de->d_name, &tail[d_taillen+1]); outlen += glob_add(newpath, argc, argv); } else { int d_taillen = tailwldsep - tail; char *start; if(namelen < baselen + d_taillen) continue; if(qmarklen && baselen + qmarklen + d_taillen != namelen) continue; if(d_taillen == taillen) { start = &de->d_name[namelen - d_taillen]; namelen = d_taillen; } else { start = &de->d_name[baselen]; namelen -= baselen; } for(; namelen >= d_taillen; start++, namelen--) { if(strncasecmp(start, tail, d_taillen)) continue; newpath = malloc(dirlen + (start - de->d_name) + taillen + 2); sprintf(newpath, "%s\\", dir); memcpy(&newpath[dirlen + 1], de->d_name, start - de->d_name); strcpy(&newpath[dirlen + 1 + start - de->d_name], tail); outlen += glob_add(newpath, argc, argv); } } } if(d) closedir(d); free(dup1); free(dup2); free(path); return outlen; }