#define WIN32_LEAN_AND_MEAN
#include "wcmd.h"
#include <shellapi.h>
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(cmd);
extern BOOL echo_mode;
struct env_stack *pushd_directories;
const WCHAR inbuilt[][10] = {
L"CALL",
L"CD",
L"CHDIR",
L"CLS",
L"COPY",
L"",
L"DATE",
L"DEL",
L"DIR",
L"ECHO",
L"ERASE",
L"FOR",
L"GOTO",
L"HELP",
L"IF",
L"LABEL",
L"MD",
L"MKDIR",
L"MOVE",
L"PATH",
L"PAUSE",
L"PROMPT",
L"REM",
L"REN",
L"RENAME",
L"RD",
L"RMDIR",
L"SET",
L"SHIFT",
L"START",
L"TIME",
L"TITLE",
L"TYPE",
L"VERIFY",
L"VER",
L"VOL",
L"ENDLOCAL",
L"SETLOCAL",
L"PUSHD",
L"POPD",
L"ASSOC",
L"COLOR",
L"FTYPE",
L"MORE",
L"CHOICE",
L"MKLINK",
L"",
L"EXIT"
};
static const WCHAR externals[][10] = {
L"ATTRIB",
L"XCOPY"
};
static HINSTANCE hinst;
static struct env_stack *saved_environment;
static BOOL verify_mode = FALSE;
#define OP_POSITIVE 'P'
#define OP_NEGATIVE 'N'
#define OP_ASSSIGNMUL 'a'
#define OP_ASSSIGNDIV 'b'
#define OP_ASSSIGNMOD 'c'
#define OP_ASSSIGNADD 'd'
#define OP_ASSSIGNSUB 'e'
#define OP_ASSSIGNAND 'f'
#define OP_ASSSIGNNOT 'g'
#define OP_ASSSIGNOR 'h'
#define OP_ASSSIGNSHL 'i'
#define OP_ASSSIGNSHR 'j'
typedef struct _OPSTACK
{
int precedence;
WCHAR op;
struct _OPSTACK *next;
} OPSTACK;
typedef struct _VARSTACK
{
BOOL isnum;
WCHAR *variable;
int value;
struct _VARSTACK *next;
} VARSTACK;
static struct
{
WCHAR op;
WCHAR calculatedop;
} calcassignments[] =
{
{'*', OP_ASSSIGNMUL},
{'/', OP_ASSSIGNDIV},
{'%', OP_ASSSIGNMOD},
{'+', OP_ASSSIGNADD},
{'-', OP_ASSSIGNSUB},
{'&', OP_ASSSIGNAND},
{'^', OP_ASSSIGNNOT},
{'|', OP_ASSSIGNOR},
{'<', OP_ASSSIGNSHL},
{'>', OP_ASSSIGNSHR},
{' ',' '}
};
DIRECTORY_STACK *WCMD_dir_stack_create(const WCHAR *dir, const WCHAR *file)
{
DIRECTORY_STACK *new = xalloc(sizeof(DIRECTORY_STACK));
new->next = NULL;
new->fileName = NULL;
if (!dir && !file)
{
DWORD sz = GetCurrentDirectoryW(0, NULL);
new->dirName = xalloc(sz * sizeof(WCHAR));
GetCurrentDirectoryW(sz, new->dirName);
}
else if (!file)
new->dirName = xstrdupW(dir);
else
{
new->dirName = xalloc((wcslen(dir) + 1 + wcslen(file) + 1) * sizeof(WCHAR));
wcscpy(new->dirName, dir);
wcscat(new->dirName, L"\\");
wcscat(new->dirName, file);
}
return new;
}
DIRECTORY_STACK *WCMD_dir_stack_free(DIRECTORY_STACK *dir)
{
DIRECTORY_STACK *next;
if (!dir) return NULL;
next = dir->next;
free(dir->dirName);
free(dir);
return next;
}
static BOOL WCMD_ask_confirm (const WCHAR *message, BOOL showSureText,
BOOL *optionAll) {
UINT msgid;
WCHAR confirm[MAXSTRING];
WCHAR options[MAXSTRING];
WCHAR Ybuffer[MAXSTRING];
WCHAR Nbuffer[MAXSTRING];
WCHAR Abuffer[MAXSTRING];
WCHAR answer[MAX_PATH] = {'\0'};
DWORD count = 0;
if (showSureText)
LoadStringW(hinst, WCMD_CONFIRM, confirm, ARRAY_SIZE(confirm));
msgid = optionAll ? WCMD_YESNOALL : WCMD_YESNO;
LoadStringW(hinst, msgid, options, ARRAY_SIZE(options));
LoadStringW(hinst, WCMD_YES, Ybuffer, ARRAY_SIZE(Ybuffer));
LoadStringW(hinst, WCMD_NO, Nbuffer, ARRAY_SIZE(Nbuffer));
LoadStringW(hinst, WCMD_ALL, Abuffer, ARRAY_SIZE(Abuffer));
if (optionAll)
*optionAll = FALSE;
while (1)
{
WCMD_output_asis (message);
if (showSureText)
WCMD_output_asis (confirm);
WCMD_output_asis (options);
if (!WCMD_ReadFile(GetStdHandle(STD_INPUT_HANDLE), answer, ARRAY_SIZE(answer), &count) || !count)
return FALSE;
answer[0] = towupper(answer[0]);
if (answer[0] == Ybuffer[0])
return TRUE;
if (answer[0] == Nbuffer[0])
return FALSE;
if (optionAll && answer[0] == Abuffer[0])
{
*optionAll = TRUE;
return TRUE;
}
}
}
RETURN_CODE WCMD_clear_screen(void)
{
CONSOLE_SCREEN_BUFFER_INFO consoleInfo;
HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
if (*quals)
return errorlevel = ERROR_INVALID_FUNCTION;
if (GetConsoleScreenBufferInfo(hStdOut, &consoleInfo))
{
COORD topLeft;
DWORD screenSize, written;
screenSize = consoleInfo.dwSize.X * (consoleInfo.dwSize.Y + 1);
topLeft.X = 0;
topLeft.Y = 0;
FillConsoleOutputCharacterW(hStdOut, ' ', screenSize, topLeft, &written);
FillConsoleOutputAttribute(hStdOut, consoleInfo.wAttributes, screenSize, topLeft, &written);
SetConsoleCursorPosition(hStdOut, topLeft);
}
return NO_ERROR;
}
RETURN_CODE WCMD_choice(WCHAR *args)
{
RETURN_CODE return_code = NO_ERROR;
WCHAR answer[16];
WCHAR buffer[16];
WCHAR *ptr = NULL;
WCHAR *opt_c = NULL;
WCHAR *opt_m = NULL;
WCHAR opt_default = 0;
DWORD opt_timeout = -1;
WCHAR *end;
DWORD oldmode;
BOOL have_console;
BOOL opt_n = FALSE;
BOOL opt_cs = FALSE;
int argno;
for (argno = 0; ; argno++)
{
WCHAR *arg = WCMD_parameter(args, argno, NULL, FALSE, FALSE);
if (!*arg) break;
if (!wcsicmp(arg, L"/N")) opt_n = TRUE;
else if (!wcsicmp(arg, L"/CS")) opt_cs = TRUE;
else if (arg[0] == L'/' && wcschr(L"CDTM", towupper(arg[1])))
{
WCHAR opt = towupper(arg[1]);
if (arg[2] == L'\0')
{
arg = WCMD_parameter(args, ++argno, NULL, FALSE, FALSE);
if (!*arg)
{
return_code = ERROR_INVALID_FUNCTION;
break;
}
}
else if (arg[2] == L':')
arg += 3;
else
{
return_code = ERROR_INVALID_FUNCTION;
break;
}
switch (opt)
{
case L'C':
opt_c = wcsdup(arg);
break;
case L'M':
opt_m = wcsdup(arg);
break;
case L'D':
opt_default = *arg;
break;
case L'T':
opt_timeout = wcstol(arg, &end, 10);
if (end == arg || (*end && !iswspace(*end)))
opt_timeout = 10000;
break;
}
}
else
return_code = ERROR_INVALID_FUNCTION;
}
if (!opt_c)
{
LoadStringW(hinst, WCMD_YES, buffer, ARRAY_SIZE(buffer));
LoadStringW(hinst, WCMD_NO, buffer + 1, ARRAY_SIZE(buffer) - 1);
opt_c = buffer;
buffer[2] = L'\0';
}
if (!opt_cs) wcsupr(opt_c);
if (!wcschr(opt_c, opt_cs ? opt_default : towupper(opt_default)))
return_code = ERROR_INVALID_FUNCTION;
for (ptr = opt_c; *ptr; ptr++)
if (wcschr(ptr + 1, opt_cs ? *ptr : towupper(*ptr)))
return_code = ERROR_INVALID_FUNCTION;
TRACE("CHOICE message(%s) choices(%s) timeout(%ld) default(%c)\n",
debugstr_w(opt_m), debugstr_w(opt_c), opt_timeout, opt_default ? opt_default : '?');
if (return_code != NO_ERROR ||
(opt_timeout == -1) != (opt_default == L'\0') ||
(opt_timeout != -1 && opt_timeout > 9999))
{
WCMD_output_stderr(WCMD_LoadMessage(WCMD_ARGERR));
errorlevel = 255;
if (opt_c != buffer) free(opt_c);
free(opt_m);
return ERROR_INVALID_FUNCTION;
}
have_console = GetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), &oldmode);
if (have_console)
SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), 0);
if (opt_m)
WCMD_output_asis(opt_m);
if (!opt_n)
{
if (opt_m) WCMD_output_asis(L" ");
WCMD_output_asis(L"[");
answer[1] = L'\0';
for (ptr = opt_c; *ptr; ptr++)
{
if (ptr != opt_c)
WCMD_output_asis(L",");
answer[0] = *ptr;
WCMD_output_asis(answer);
}
WCMD_output_asis(L"]?");
}
while (return_code == NO_ERROR)
{
if (opt_timeout == 0)
answer[0] = opt_default;
else
{
LARGE_INTEGER li, zeroli = {0};
OVERLAPPED overlapped = {0};
DWORD count;
char choice;
overlapped.hEvent = CreateEventW(NULL, TRUE, FALSE, NULL);
if (SetFilePointerEx(GetStdHandle(STD_INPUT_HANDLE), zeroli, &li, FILE_CURRENT))
{
overlapped.Offset = li.LowPart;
overlapped.OffsetHigh = li.HighPart;
}
if (ReadFile(GetStdHandle(STD_INPUT_HANDLE), &choice, 1, NULL, &overlapped))
{
switch (WaitForSingleObject(overlapped.hEvent, opt_timeout == -1 ? INFINITE : opt_timeout * 1000))
{
case WAIT_OBJECT_0:
answer[0] = choice;
break;
case WAIT_TIMEOUT:
answer[0] = opt_default;
break;
default:
return_code = ERROR_INVALID_FUNCTION;
}
}
else if (ReadFile(GetStdHandle(STD_INPUT_HANDLE), &choice, 1, &count, NULL))
{
if (count == 0)
{
if (opt_timeout != -1)
answer[0] = opt_default;
else
return_code = ERROR_INVALID_FUNCTION;
}
else
answer[0] = choice;
}
else
return_code = ERROR_INVALID_FUNCTION;
CloseHandle(overlapped.hEvent);
}
if (return_code != NO_ERROR)
{
errorlevel = 255;
break;
}
if (!opt_cs)
answer[0] = towupper(answer[0]);
answer[1] = L'\0';
ptr = wcschr(opt_c, answer[0]);
if (ptr)
{
WCMD_output_asis(answer);
WCMD_output_asis(L"\r\n");
return_code = errorlevel = (ptr - opt_c) + 1;
TRACE("answer: %d\n", return_code);
}
else
{
TRACE("key not allowed: %s\n", wine_dbgstr_w(answer));
WCMD_output_asis(L"\a");
}
}
if (have_console)
SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), oldmode);
if (opt_c != buffer) free(opt_c);
free(opt_m);
return return_code;
}
static BOOL WCMD_AppendEOF(WCHAR *filename)
{
HANDLE h;
DWORD bytes_written;
char eof = '\x1a';
WINE_TRACE("Appending EOF to %s\n", wine_dbgstr_w(filename));
h = CreateFileW(filename, GENERIC_WRITE, 0, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (h == INVALID_HANDLE_VALUE) {
WINE_ERR("Failed to open %s (%ld)\n", wine_dbgstr_w(filename), GetLastError());
return FALSE;
} else {
SetFilePointer (h, 0, NULL, FILE_END);
if (!WriteFile(h, &eof, 1, &bytes_written, NULL)) {
WINE_ERR("Failed to append EOF to %s (%ld)\n", wine_dbgstr_w(filename), GetLastError());
CloseHandle(h);
return FALSE;
}
CloseHandle(h);
}
return TRUE;
}
static BOOL WCMD_IsSameFile(const WCHAR *name1, const WCHAR *name2)
{
BOOL ret = FALSE;
HANDLE file1 = INVALID_HANDLE_VALUE, file2 = INVALID_HANDLE_VALUE;
BY_HANDLE_FILE_INFORMATION info1, info2;
file1 = CreateFileW(name1, 0, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0);
if (file1 == INVALID_HANDLE_VALUE || !GetFileInformationByHandle(file1, &info1))
goto end;
file2 = CreateFileW(name2, 0, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0);
if (file2 == INVALID_HANDLE_VALUE || !GetFileInformationByHandle(file2, &info2))
goto end;
ret = info1.dwVolumeSerialNumber == info2.dwVolumeSerialNumber
&& info1.nFileIndexHigh == info2.nFileIndexHigh
&& info1.nFileIndexLow == info2.nFileIndexLow;
end:
if (file1 != INVALID_HANDLE_VALUE)
CloseHandle(file1);
if (file2 != INVALID_HANDLE_VALUE)
CloseHandle(file2);
return ret;
}
static BOOL WCMD_ManualCopy(WCHAR *srcname, WCHAR *dstname, BOOL ascii, BOOL append)
{
HANDLE in,out;
BOOL ok;
DWORD bytesread, byteswritten;
WINE_TRACE("Manual Copying %s to %s (ascii: %u) (append: %u)\n",
wine_dbgstr_w(srcname), wine_dbgstr_w(dstname), ascii, append);
in = CreateFileW(srcname, GENERIC_READ, 0, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (in == INVALID_HANDLE_VALUE) {
WINE_ERR("Failed to open %s (%ld)\n", wine_dbgstr_w(srcname), GetLastError());
return FALSE;
}
out = CreateFileW(dstname, GENERIC_WRITE, 0, NULL,
append?OPEN_EXISTING:CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (out == INVALID_HANDLE_VALUE) {
WINE_ERR("Failed to open %s (%ld)\n", wine_dbgstr_w(dstname), GetLastError());
CloseHandle(in);
return FALSE;
}
if (append) {
SetFilePointer(out, 0, NULL, FILE_END);
}
do
{
char buffer[MAXSTRING];
ok = ReadFile(in, buffer, MAXSTRING, &bytesread, NULL);
if (ok) {
if (ascii) {
char *ptr = (char *)memchr((void *)buffer, '\x1a', bytesread);
if (ptr) bytesread = (ptr - buffer);
}
if (bytesread) {
ok = WriteFile(out, buffer, bytesread, &byteswritten, NULL);
if (!ok || byteswritten != bytesread) {
WINE_ERR("Unexpected failure writing to %s, rc=%ld\n",
wine_dbgstr_w(dstname), GetLastError());
}
}
} else {
WINE_ERR("Unexpected failure reading from %s, rc=%ld\n",
wine_dbgstr_w(srcname), GetLastError());
}
} while (ok && bytesread > 0);
CloseHandle(out);
CloseHandle(in);
return ok;
}
RETURN_CODE WCMD_copy(WCHAR * args)
{
BOOL opt_d, opt_v, opt_n, opt_z, opt_y, opt_noty;
WCHAR *thisparam;
int argno = 0;
WCHAR *rawarg;
WIN32_FIND_DATAW fd;
HANDLE hff = INVALID_HANDLE_VALUE;
int binarymode = -1;
BOOL concatnextfilename = FALSE;
BOOL anyconcats = FALSE;
BOOL appendfirstsource = FALSE;
BOOL writtenoneconcat = FALSE;
BOOL prompt;
WCHAR destname[MAX_PATH];
BOOL destisdirectory = FALSE;
BOOL status;
WCHAR copycmd[4];
DWORD len;
BOOL dstisdevice = FALSE;
unsigned numcopied = 0;
typedef struct _COPY_FILES
{
struct _COPY_FILES *next;
BOOL concatenate;
WCHAR *name;
int binarycopy;
} COPY_FILES;
COPY_FILES *sourcelist = NULL;
COPY_FILES *lastcopyentry = NULL;
COPY_FILES *destination = NULL;
COPY_FILES *thiscopy = NULL;
COPY_FILES *prevcopy = NULL;
RETURN_CODE return_code;
return_code = NO_ERROR;
if (param1[0] == 0x00) {
WCMD_output_stderr (WCMD_LoadMessage(WCMD_NOARG));
return errorlevel = ERROR_INVALID_FUNCTION;
}
opt_d = opt_v = opt_n = opt_z = opt_y = opt_noty = FALSE;
thisparam = WCMD_parameter(args, argno++, &rawarg, TRUE, FALSE);
while (*(thisparam)) {
WCHAR *pos1, *pos2;
BOOL inquotes;
WINE_TRACE("Working on parameter '%s'\n", wine_dbgstr_w(thisparam));
if (*thisparam == '/') {
while (*thisparam == '/') {
thisparam++;
if (towupper(*thisparam) == 'D') {
opt_d = TRUE;
if (opt_d) WINE_FIXME("copy /D support not implemented yet\n");
} else if (towupper(*thisparam) == 'Y') {
opt_y = TRUE;
} else if (towupper(*thisparam) == '-' && towupper(*(thisparam+1)) == 'Y') {
opt_noty = TRUE;
} else if (towupper(*thisparam) == 'V') {
opt_v = TRUE;
if (opt_v) WINE_FIXME("copy /V support not implemented yet\n");
} else if (towupper(*thisparam) == 'N') {
opt_n = TRUE;
if (opt_n) WINE_FIXME("copy /N support not implemented yet\n");
} else if (towupper(*thisparam) == 'Z') {
opt_z = TRUE;
if (opt_z) WINE_FIXME("copy /Z support not implemented yet\n");
} else if (towupper(*thisparam) == 'A') {
if (binarymode != 0) {
binarymode = 0;
WINE_TRACE("Subsequent files will be handled as ASCII\n");
if (destination != NULL) {
WINE_TRACE("file %s will be written as ASCII\n", wine_dbgstr_w(destination->name));
destination->binarycopy = binarymode;
} else if (lastcopyentry != NULL) {
WINE_TRACE("file %s will be read as ASCII\n", wine_dbgstr_w(lastcopyentry->name));
lastcopyentry->binarycopy = binarymode;
}
}
} else if (towupper(*thisparam) == 'B') {
if (binarymode != 1) {
binarymode = 1;
WINE_TRACE("Subsequent files will be handled as binary\n");
if (destination != NULL) {
WINE_TRACE("file %s will be written as binary\n", wine_dbgstr_w(destination->name));
destination->binarycopy = binarymode;
} else if (lastcopyentry != NULL) {
WINE_TRACE("file %s will be read as binary\n", wine_dbgstr_w(lastcopyentry->name));
lastcopyentry->binarycopy = binarymode;
}
}
} else {
WINE_FIXME("Unexpected copy switch %s\n", wine_dbgstr_w(thisparam));
}
thisparam++;
}
thisparam = WCMD_parameter(args, argno++, &rawarg, TRUE, FALSE);
continue;
}
if (*thisparam=='+') {
if (lastcopyentry == NULL) {
WCMD_output_stderr(WCMD_LoadMessage(WCMD_SYNTAXERR));
return_code = ERROR_INVALID_FUNCTION;
goto exitreturn;
} else {
concatnextfilename = TRUE;
anyconcats = TRUE;
}
thisparam++;
if (*thisparam == 0x00)
thisparam = WCMD_parameter(args, argno++, &rawarg, TRUE, FALSE);
continue;
}
thiscopy = xalloc(sizeof(COPY_FILES));
WINE_TRACE("Not a switch, but probably a filename/list %s\n", wine_dbgstr_w(thisparam));
thiscopy->concatenate = concatnextfilename;
thiscopy->binarycopy = binarymode;
thiscopy->next = NULL;
len = lstrlenW(thisparam) + (sizeof(WCHAR) * 5);
thiscopy->name = xalloc(len * sizeof(WCHAR));
memset(thiscopy->name, 0x00, len);
pos1 = thisparam;
pos2 = thiscopy->name;
inquotes = FALSE;
while (*pos1 && (inquotes || (*pos1 != '+' && *pos1 != '/'))) {
if (*pos1 == '"') {
inquotes = !inquotes;
pos1++;
} else *pos2++ = *pos1++;
}
*pos2 = 0;
WINE_TRACE("Calculated file name %s\n", wine_dbgstr_w(thiscopy->name));
if (sourcelist == NULL) {
WINE_TRACE("Adding as first source part\n");
sourcelist = thiscopy;
lastcopyentry = thiscopy;
} else if (concatnextfilename) {
WINE_TRACE("Adding to source file list to be concatenated\n");
lastcopyentry->next = thiscopy;
lastcopyentry = thiscopy;
} else if (destination == NULL) {
destination = thiscopy;
} else {
WCMD_output_stderr(WCMD_LoadMessage(WCMD_SYNTAXERR));
return_code = ERROR_INVALID_FUNCTION;
goto exitreturn;
}
concatnextfilename = FALSE;
if (*pos1 == '/' || *pos1 == '+') {
thisparam = pos1;
continue;
} else {
thisparam = WCMD_parameter(args, argno++, &rawarg, TRUE, FALSE);
}
}
if (!sourcelist) {
WCMD_output_stderr(WCMD_LoadMessage(WCMD_SYNTAXERR));
return_code = ERROR_INVALID_FUNCTION;
goto exitreturn;
}
if (opt_noty) prompt = TRUE;
else if (opt_y) prompt = FALSE;
else {
prompt = !context;
len = GetEnvironmentVariableW(L"COPYCMD", copycmd, ARRAY_SIZE(copycmd));
if (len && len < ARRAY_SIZE(copycmd)) {
if (!lstrcmpiW(copycmd, L"/Y"))
prompt = FALSE;
else if (!lstrcmpiW(copycmd, L"/-Y"))
prompt = TRUE;
}
}
if (destination == NULL) {
WINE_TRACE("No destination supplied, so need to calculate it\n");
lstrcpyW(destname, L".");
lstrcatW(destname, L"\\");
destination = xalloc(sizeof(COPY_FILES));
if (destination == NULL) goto exitreturn;
destination->concatenate = FALSE;
destination->binarycopy = binarymode;
destination->next = NULL;
destination->name = NULL;
destisdirectory = TRUE;
} else {
WCHAR *filenamepart;
DWORD attributes;
WINE_TRACE("Destination supplied, processing to see if file or directory\n");
if (!WCMD_get_fullpath(destination->name, ARRAY_SIZE(destname), destname, &filenamepart))
return errorlevel = ERROR_INVALID_FUNCTION;
WINE_TRACE("Full dest name is '%s'\n", wine_dbgstr_w(destname));
attributes = GetFileAttributesW(destname);
if (ends_with_backslash( destname ) ||
((attributes != INVALID_FILE_ATTRIBUTES) &&
(attributes & FILE_ATTRIBUTE_DIRECTORY))) {
destisdirectory = TRUE;
if (!ends_with_backslash(destname)) lstrcatW(destname, L"\\");
WINE_TRACE("Directory, so full name is now '%s'\n", wine_dbgstr_w(destname));
}
}
if (anyconcats) {
if (destisdirectory) appendfirstsource = TRUE;
if (destination->binarycopy == -1) destination->binarycopy = 0;
} else if (!destisdirectory) {
if (wcspbrk(sourcelist->name, L"*?") != NULL) {
anyconcats = TRUE;
if (destination->binarycopy == -1) {
destination->binarycopy = 0;
}
} else {
if (destination->binarycopy == -1) {
destination->binarycopy = 1;
}
}
}
free(destination->name);
destination->name = xstrdupW(destname);
WINE_TRACE("Resolved destination is '%s' (calc later %d)\n",
wine_dbgstr_w(destname), appendfirstsource);
if (wcsncmp(destination->name, L"\\\\.\\", lstrlenW(L"\\\\.\\")) == 0) {
WINE_TRACE("Destination is a device\n");
dstisdevice = TRUE;
}
thiscopy = sourcelist;
prevcopy = NULL;
return_code = NO_ERROR;
while (thiscopy != NULL) {
WCHAR srcpath[MAX_PATH];
const WCHAR *srcname;
WCHAR *filenamepart;
DWORD attributes;
BOOL srcisdevice = FALSE;
BOOL havewildcards = FALSE;
BOOL displaynames = anyconcats;
if (thiscopy->binarycopy == -1) thiscopy->binarycopy = !anyconcats;
if (!WCMD_get_fullpath(thiscopy->name, ARRAY_SIZE(srcpath), srcpath, &filenamepart))
return errorlevel = ERROR_INVALID_FUNCTION;
WINE_TRACE("Full src name is '%s'\n", wine_dbgstr_w(srcpath));
havewildcards = wcspbrk(srcpath, L"*?") ? TRUE : FALSE;
if (!displaynames) {
displaynames = havewildcards;
}
attributes = GetFileAttributesW(srcpath);
if (ends_with_backslash( srcpath )) {
lstrcatW(thiscopy->name, L"*");
displaynames = TRUE;
if (!WCMD_get_fullpath(thiscopy->name, ARRAY_SIZE(srcpath), srcpath, &filenamepart))
return errorlevel = ERROR_INVALID_FUNCTION;
WINE_TRACE("Directory, so full name is now '%s'\n", wine_dbgstr_w(srcpath));
} else if (!havewildcards &&
(attributes != INVALID_FILE_ATTRIBUTES) &&
(attributes & FILE_ATTRIBUTE_DIRECTORY)) {
lstrcatW(thiscopy->name, L"\\*");
displaynames = TRUE;
if (!WCMD_get_fullpath(thiscopy->name, ARRAY_SIZE(srcpath), srcpath, &filenamepart))
return errorlevel = ERROR_INVALID_FUNCTION;
WINE_TRACE("Directory, so full name is now '%s'\n", wine_dbgstr_w(srcpath));
}
WINE_TRACE("Copy source (calculated): path: '%s' (Concats: %d)\n",
wine_dbgstr_w(srcpath), anyconcats);
if (wcsncmp(srcpath, L"\\\\.\\", lstrlenW(L"\\\\.\\")) == 0) {
WINE_TRACE("Source is a device\n");
srcisdevice = TRUE;
srcname = &srcpath[4];
if (!wcsnicmp(srcname, L"CON", 3)) {
thiscopy->binarycopy = FALSE;
}
} else {
WINE_TRACE("Searching for: '%s'\n", wine_dbgstr_w(srcpath));
hff = FindFirstFileW(srcpath, &fd);
if (hff != INVALID_HANDLE_VALUE) {
srcname = fd.cFileName;
}
}
if (srcisdevice || hff != INVALID_HANDLE_VALUE) {
do {
WCHAR outname[MAX_PATH];
BOOL overwrite;
BOOL appendtofirstfile = FALSE;
if (!srcisdevice && fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
WINE_TRACE("Skipping directories\n");
} else {
lstrcpyW(outname, destination->name);
if (destisdirectory || appendfirstsource) lstrcatW(outname, srcname);
if (!srcisdevice) lstrcpyW(filenamepart, srcname);
overwrite = !prompt;
if (dstisdevice || (anyconcats && writtenoneconcat)) {
overwrite = TRUE;
}
WINE_TRACE("Copying from : '%s'\n", wine_dbgstr_w(srcpath));
WINE_TRACE("Copying to : '%s'\n", wine_dbgstr_w(outname));
WINE_TRACE("Flags: srcbinary(%d), dstbinary(%d), over(%d), prompt(%d)\n",
thiscopy->binarycopy, destination->binarycopy, overwrite, prompt);
if (!writtenoneconcat) {
appendtofirstfile = anyconcats && WCMD_IsSameFile(srcpath, outname);
}
if (appendtofirstfile) {
overwrite = TRUE;
} else if (!overwrite) {
DWORD attributes = GetFileAttributesW(outname);
if (attributes != INVALID_FILE_ATTRIBUTES) {
WCHAR* question;
question = WCMD_format_string(WCMD_LoadMessage(WCMD_OVERWRITE), outname);
overwrite = WCMD_ask_confirm(question, FALSE, NULL);
LocalFree(question);
}
else overwrite = TRUE;
}
if (appendfirstsource && overwrite) {
free(destination->name);
destination->name = xstrdupW(outname);
WINE_TRACE("Final resolved destination name : '%s'\n", wine_dbgstr_w(outname));
appendfirstsource = FALSE;
destisdirectory = FALSE;
}
if (overwrite) {
if (displaynames) {
WCMD_output_asis(srcpath);
WCMD_output_asis(L"\r\n");
}
if (anyconcats && WCMD_IsSameFile(srcpath, outname)) {
HANDLE file = CreateFileW(srcpath, GENERIC_WRITE, FILE_SHARE_WRITE, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (file != INVALID_HANDLE_VALUE)
{
FILETIME file_time;
SYSTEMTIME system_time;
GetSystemTime(&system_time);
SystemTimeToFileTime(&system_time, &file_time);
status = SetFileTime(file, NULL, NULL, &file_time);
CloseHandle(file);
}
else status = FALSE;
} else if (anyconcats && writtenoneconcat) {
status = WCMD_ManualCopy(srcpath, outname, !thiscopy->binarycopy, TRUE);
} else if (!thiscopy->binarycopy || srcisdevice) {
status = WCMD_ManualCopy(srcpath, outname, !thiscopy->binarycopy, FALSE);
} else {
status = CopyFileW(srcpath, outname, FALSE);
}
if (!status) {
WCMD_print_error ();
return_code = ERROR_INVALID_FUNCTION;
} else {
WINE_TRACE("Copied successfully\n");
if (anyconcats) {
writtenoneconcat = TRUE;
numcopied = 1;
} else {
numcopied++;
}
if (!destination->binarycopy && !anyconcats && !thiscopy->binarycopy) {
if (!WCMD_AppendEOF(outname)) {
WCMD_print_error ();
return_code = ERROR_INVALID_FUNCTION;
}
}
}
} else if (prompt)
return_code = ERROR_INVALID_FUNCTION;
}
} while (!srcisdevice && FindNextFileW(hff, &fd) != 0);
if (!srcisdevice) FindClose (hff);
} else {
if (!anyconcats || !writtenoneconcat) {
WCMD_print_error ();
return_code = ERROR_INVALID_FUNCTION;
}
}
thiscopy = thiscopy -> next;
}
if (!return_code && !destination->binarycopy && anyconcats && writtenoneconcat) {
if (!WCMD_AppendEOF(destination->name)) {
WCMD_print_error ();
return_code = ERROR_INVALID_FUNCTION;
}
}
if (numcopied) {
WCMD_output(WCMD_LoadMessage(WCMD_NUMCOPIED), numcopied);
}
exitreturn:
thiscopy = sourcelist;
while (thiscopy != NULL) {
prevcopy = thiscopy;
thiscopy = thiscopy -> next;
free(prevcopy->name);
free(prevcopy);
}
if (destination) {
free(destination->name);
free(destination);
}
return errorlevel = return_code;
}
static BOOL create_full_path(WCHAR* path)
{
WCHAR *p, *start;
start = path;
if (path[1] == ':')
start = path+2;
for (p = path + lstrlenW(path) - 1; p != start && *p == '\\'; p--)
*p = 0;
p = start;
for (;;) {
DWORD rv;
while (*p == '\\') p++;
while (*p && *p != '\\') p++;
if (!*p) {
return CreateDirectoryW(path, NULL);
}
*p = 0;
rv = CreateDirectoryW(path, NULL);
*p = '\\';
if (!rv && GetLastError() != ERROR_ALREADY_EXISTS)
return FALSE;
}
return FALSE;
}
RETURN_CODE WCMD_create_dir(WCHAR *args)
{
int argno = 0;
WCHAR *argN = args;
RETURN_CODE return_code;
if (param1[0] == L'\0')
{
WCMD_output_stderr(WCMD_LoadMessage(WCMD_NOARG));
return errorlevel = ERROR_INVALID_FUNCTION;
}
return_code = NO_ERROR;
for (;;)
{
WCHAR *thisArg = WCMD_parameter(args, argno++, &argN, FALSE, FALSE);
if (!argN) break;
if (!create_full_path(thisArg))
{
WCMD_print_error();
return_code = ERROR_INVALID_FUNCTION;
}
}
return errorlevel = return_code;
}
static void WCMD_delete_parse_attributes(DWORD *wantSet, DWORD *wantClear) {
WCHAR *p;
*wantSet=0;
*wantClear=0;
for (p=wcsstr(quals, L"/A"); p != NULL; p=wcsstr(p, L"/A")) {
p += 2;
if (*p == ':') p++;
for (; *p != 0 && *p != '/'; p++) {
BOOL negate = FALSE;
DWORD mask = 0;
if (*p == '-') {
negate=TRUE;
p++;
}
switch (*p) {
case 'R': mask = FILE_ATTRIBUTE_READONLY; break;
case 'H': mask = FILE_ATTRIBUTE_HIDDEN; break;
case 'S': mask = FILE_ATTRIBUTE_SYSTEM; break;
case 'A': mask = FILE_ATTRIBUTE_ARCHIVE; break;
default:
WCMD_output_stderr(WCMD_LoadMessage(WCMD_SYNTAXERR));
}
if (negate)
*wantClear |= mask;
else
*wantSet |= mask;
}
}
}
static BOOL WCMD_delete_confirm_wildcard(const WCHAR *filename, BOOL *pPrompted) {
if ((wcsstr(quals, L"/Q") == NULL) && (wcsstr(quals, L"/P") == NULL)) {
WCHAR drive[10];
WCHAR dir[MAX_PATH];
WCHAR fname[MAX_PATH];
WCHAR ext[MAX_PATH];
WCHAR fpath[MAX_PATH];
if (!WCMD_get_fullpath(filename, ARRAY_SIZE(fpath), fpath, NULL)) return FALSE;
_wsplitpath(fpath, drive, dir, fname, ext);
if ((lstrcmpW(fname, L"*") == 0) && (*ext == 0x00 || (lstrcmpW(ext, L".*") == 0))) {
WCHAR question[MAXSTRING];
*pPrompted = TRUE;
wsprintfW(question, L"%s ", fpath);
return WCMD_ask_confirm(question, TRUE, NULL);
}
}
return TRUE;
}
static BOOL WCMD_delete_one (const WCHAR *thisArg) {
DWORD wanted_attrs;
DWORD unwanted_attrs;
BOOL found = FALSE;
WCHAR argCopy[MAX_PATH];
WIN32_FIND_DATAW fd;
HANDLE hff;
WCHAR fpath[MAX_PATH];
WCHAR *p;
BOOL handleParm = TRUE;
WCMD_delete_parse_attributes(&wanted_attrs, &unwanted_attrs);
lstrcpyW(argCopy, thisArg);
WINE_TRACE("del: Processing arg %s (quals:%s)\n",
wine_dbgstr_w(argCopy), wine_dbgstr_w(quals));
if (!WCMD_delete_confirm_wildcard(argCopy, &found)) {
return FALSE;
}
hff = FindFirstFileW(argCopy, &fd);
if (hff == INVALID_HANDLE_VALUE) {
handleParm = FALSE;
found = wcschr(argCopy,'*') != NULL || wcschr(argCopy,'?') != NULL;
} else {
found = TRUE;
}
if (handleParm
&& (wcschr(argCopy,'*') == NULL)
&& (wcschr(argCopy,'?') == NULL)
&& (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
{
WCHAR modifiedParm[MAX_PATH];
lstrcpyW(modifiedParm, argCopy);
lstrcatW(modifiedParm, L"\\*");
FindClose(hff);
found = TRUE;
WCMD_delete_one(modifiedParm);
} else if (handleParm) {
lstrcpyW (fpath, argCopy);
do {
p = wcsrchr (fpath, '\\');
if (p != NULL) {
*++p = '\0';
lstrcatW (fpath, fd.cFileName);
}
else lstrcpyW (fpath, fd.cFileName);
if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
BOOL ok;
ok = ((fd.dwFileAttributes & wanted_attrs) == wanted_attrs)
&& ((fd.dwFileAttributes & unwanted_attrs) == 0);
if (ok && wcsstr(quals, L"/P") != NULL) {
WCHAR* question;
question = WCMD_format_string(WCMD_LoadMessage(WCMD_DELPROMPT), fpath);
ok = WCMD_ask_confirm(question, FALSE, NULL);
LocalFree(question);
}
if (ok) {
if (fd.dwFileAttributes & FILE_ATTRIBUTE_READONLY &&
((wanted_attrs & FILE_ATTRIBUTE_READONLY) ||
wcsstr(quals, L"/F") != NULL)) {
SetFileAttributesW(fpath, fd.dwFileAttributes & ~FILE_ATTRIBUTE_READONLY);
}
if (!DeleteFileW(fpath)) WCMD_print_error ();
}
}
} while (FindNextFileW(hff, &fd) != 0);
FindClose (hff);
}
if (wcsstr(quals, L"/S") != NULL) {
WCHAR thisDir[MAX_PATH];
int cPos;
WCHAR drive[10];
WCHAR dir[MAX_PATH];
WCHAR fname[MAX_PATH];
WCHAR ext[MAX_PATH];
if (!WCMD_get_fullpath(argCopy, ARRAY_SIZE(thisDir), thisDir, NULL)) return FALSE;
_wsplitpath(thisDir, drive, dir, fname, ext);
lstrcpyW(thisDir, drive);
lstrcatW(thisDir, dir);
cPos = lstrlenW(thisDir);
WINE_TRACE("Searching recursively in '%s'\n", wine_dbgstr_w(thisDir));
thisDir[cPos] = '*';
thisDir[cPos+1] = 0x00;
hff = FindFirstFileW(thisDir, &fd);
thisDir[cPos] = 0x00;
if (hff != INVALID_HANDLE_VALUE) {
DIRECTORY_STACK *allDirs = NULL;
DIRECTORY_STACK *lastEntry = NULL;
do {
if ((fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
(lstrcmpW(fd.cFileName, L"..") != 0) && (lstrcmpW(fd.cFileName, L".") != 0)) {
DIRECTORY_STACK *nextDir;
WCHAR subParm[MAX_PATH];
if (wcslen(thisDir) + wcslen(fd.cFileName) + 1 + wcslen(fname) + wcslen(ext) >= MAX_PATH)
{
WINE_TRACE("Skipping path too long %s%s\\%s%s\n",
debugstr_w(thisDir), debugstr_w(fd.cFileName),
debugstr_w(fname), debugstr_w(ext));
continue;
}
lstrcpyW (subParm, thisDir);
lstrcatW (subParm, fd.cFileName);
lstrcatW (subParm, L"\\");
lstrcatW (subParm, fname);
lstrcatW (subParm, ext);
WINE_TRACE("Recursive, Adding to search list '%s'\n", wine_dbgstr_w(subParm));
nextDir = WCMD_dir_stack_create(subParm, NULL);
if (allDirs == NULL) allDirs = nextDir;
if (lastEntry != NULL) lastEntry->next = nextDir;
lastEntry = nextDir;
}
} while (FindNextFileW(hff, &fd) != 0);
FindClose (hff);
while (allDirs != NULL) {
found |= WCMD_delete_one (allDirs->dirName);
allDirs = WCMD_dir_stack_free(allDirs);
}
}
}
return found;
}
RETURN_CODE WCMD_delete(WCHAR *args)
{
int argno;
WCHAR *argN;
BOOL argsProcessed = FALSE;
errorlevel = NO_ERROR;
for (argno = 0; ; argno++)
{
WCHAR *thisArg;
argN = NULL;
thisArg = WCMD_parameter(args, argno, &argN, FALSE, FALSE);
if (!argN)
break;
if (argN[0] == '/')
continue;
argsProcessed = TRUE;
if (!WCMD_delete_one(thisArg))
{
errorlevel = ERROR_INVALID_FUNCTION;
}
}
if (!argsProcessed)
{
WCMD_output_stderr(WCMD_LoadMessage(WCMD_NOARG));
errorlevel = ERROR_INVALID_FUNCTION;
}
return errorlevel;
}
RETURN_CODE WCMD_echo(const WCHAR *args)
{
const WCHAR *toskip = L".:;/(";
const WCHAR *skipped = NULL;
WCHAR *trimmed;
if (iswspace(args[0]) || (args[0] && (skipped = wcschr(toskip, args[0])))) args++;
trimmed = WCMD_skip_leading_spaces((WCHAR *)args);
if (CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE | SORT_STRINGSORT, trimmed, 2, L"ON", 2) == CSTR_EQUAL &&
*WCMD_skip_leading_spaces(trimmed + 2) == L'\0')
echo_mode = TRUE;
else if (CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE | SORT_STRINGSORT, trimmed, 3, L"OFF", 3) == CSTR_EQUAL &&
*WCMD_skip_leading_spaces(trimmed + 3) == L'\0')
echo_mode = FALSE;
else if (!trimmed[0] && !skipped)
WCMD_output(WCMD_LoadMessage(WCMD_ECHOPROMPT), echo_mode ? L"ON" : L"OFF");
else
{
WCMD_output_asis(args);
WCMD_output_asis(L"\r\n");
}
return NO_ERROR;
}
void WCMD_add_dirstowalk(DIRECTORY_STACK *dirsToWalk)
{
DIRECTORY_STACK *remainingDirs = dirsToWalk;
WCHAR fullitem[MAX_PATH];
WIN32_FIND_DATAW fd;
HANDLE hff;
lstrcpyW(fullitem, dirsToWalk->dirName);
lstrcatW(fullitem, L"\\*");
if ((hff = FindFirstFileW(fullitem, &fd)) == INVALID_HANDLE_VALUE) return;
do
{
TRACE("Looking for subdirectories\n");
if ((fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
(lstrcmpW(fd.cFileName, L"..") != 0) && (lstrcmpW(fd.cFileName, L".") != 0))
{
DIRECTORY_STACK *toWalk;
if (wcslen(dirsToWalk->dirName) + 1 + wcslen(fd.cFileName) >= MAX_PATH)
{
TRACE("Skipping too long path %s\\%s\n",
debugstr_w(dirsToWalk->dirName), debugstr_w(fd.cFileName));
continue;
}
toWalk = WCMD_dir_stack_create(dirsToWalk->dirName, fd.cFileName);
TRACE("(%p->%p)\n", remainingDirs, remainingDirs->next);
toWalk->next = remainingDirs->next;
remainingDirs->next = toWalk;
remainingDirs = toWalk;
TRACE("Added to stack %s (%p->%p)\n", wine_dbgstr_w(toWalk->dirName),
toWalk, toWalk->next);
}
} while (FindNextFileW(hff, &fd) != 0);
TRACE("Finished adding all subdirectories\n");
FindClose(hff);
}
static int find_in_array(const WCHAR array[][10], size_t sz, const WCHAR *what)
{
int i;
for (i = 0; i < sz; i++)
if (CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE | SORT_STRINGSORT,
what, -1, array[i], -1) == CSTR_EQUAL)
return i;
return -1;
}
RETURN_CODE WCMD_give_help(WCHAR *args)
{
WCHAR *help_on = WCMD_parameter(args, 0, NULL, FALSE, FALSE);
if (!*help_on)
WCMD_output_asis(WCMD_LoadMessage(WCMD_ALLHELP));
else
{
int i;
if ((i = find_in_array(inbuilt, ARRAY_SIZE(inbuilt), help_on)) >= 0)
WCMD_output_asis(WCMD_LoadMessage(i));
else if ((i = find_in_array(externals, ARRAY_SIZE(externals), help_on)) >= 0)
{
WCHAR cmd[128];
lstrcpyW(cmd, help_on);
lstrcatW(cmd, L" /?");
WCMD_run_builtin_command(WCMD_HELP, cmd);
}
else
{
WCMD_output(WCMD_LoadMessage(WCMD_NOCMDHELP), help_on);
return errorlevel = NO_ERROR;
}
}
return errorlevel = ERROR_INVALID_FUNCTION;
}
RETURN_CODE WCMD_goto(void)
{
if (context != NULL)
{
WCHAR *paramStart = param1;
HANDLE h;
BOOL ret;
if (!param1[0])
{
WCMD_output_stderr(WCMD_LoadMessage(WCMD_NOARG));
return ERROR_INVALID_FUNCTION;
}
if (!context->batch_file) return ERROR_INVALID_FUNCTION;
if (lstrcmpiW(L":eof", param1) == 0)
{
context->file_position.QuadPart = WCMD_FILE_POSITION_EOF;
return RETURN_CODE_GOTO;
}
h = CreateFileW(context->batch_file->path_name, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (h == INVALID_HANDLE_VALUE)
{
SetLastError(ERROR_FILE_NOT_FOUND);
WCMD_print_error();
return ERROR_INVALID_FUNCTION;
}
if (*paramStart == ':') paramStart++;
WCMD_set_label_end(paramStart);
TRACE("goto label: '%s'\n", wine_dbgstr_w(paramStart));
ret = WCMD_find_label(h, paramStart, &context->file_position);
CloseHandle(h);
if (ret) return RETURN_CODE_GOTO;
WCMD_output_stderr(WCMD_LoadMessage(WCMD_NOTARGET));
context->file_position.QuadPart = WCMD_FILE_POSITION_EOF;
}
return ERROR_INVALID_FUNCTION;
}
RETURN_CODE WCMD_pushd(const WCHAR *args)
{
struct env_stack *curdir;
WCHAR *thisdir;
RETURN_CODE return_code;
if (!*args)
return errorlevel = NO_ERROR;
if (wcschr(args, '/') != NULL) {
SetLastError(ERROR_INVALID_PARAMETER);
WCMD_print_error();
return errorlevel = ERROR_INVALID_FUNCTION;
}
curdir = xalloc(sizeof(struct env_stack));
thisdir = xalloc(1024 * sizeof(WCHAR));
lstrcpyW(quals, L"/D");
GetCurrentDirectoryW (1024, thisdir);
return_code = WCMD_setshow_default(args);
if (return_code != NO_ERROR)
{
free(curdir);
free(thisdir);
return errorlevel = ERROR_INVALID_FUNCTION;
} else {
curdir -> next = pushd_directories;
curdir -> strings = thisdir;
if (pushd_directories == NULL) {
curdir -> u.stackdepth = 1;
} else {
curdir -> u.stackdepth = pushd_directories -> u.stackdepth + 1;
}
pushd_directories = curdir;
}
return errorlevel = return_code;
}
RETURN_CODE WCMD_popd(void)
{
struct env_stack *temp = pushd_directories;
if (!pushd_directories)
return ERROR_INVALID_FUNCTION;
pushd_directories = temp->next;
SetCurrentDirectoryW(temp->strings);
free(temp->strings);
free(temp);
return NO_ERROR;
}
RETURN_CODE WCMD_move(void)
{
WIN32_FIND_DATAW fd;
HANDLE hff;
WCHAR input[MAX_PATH];
WCHAR output[MAX_PATH];
WCHAR drive[10];
WCHAR dir[MAX_PATH];
WCHAR fname[MAX_PATH];
WCHAR ext[MAX_PATH];
if (param1[0] == 0x00) {
WCMD_output_stderr(WCMD_LoadMessage(WCMD_NOARG));
return errorlevel = ERROR_INVALID_FUNCTION;
}
if (param2[0] == 0x00) {
lstrcpyW(param2, L".");
}
if (!WCMD_get_fullpath(param1, ARRAY_SIZE(input), input, NULL) ||
!WCMD_get_fullpath(param2, ARRAY_SIZE(output), output, NULL))
return errorlevel = ERROR_INVALID_FUNCTION;
WINE_TRACE("Move from '%s'('%s') to '%s'\n", wine_dbgstr_w(input),
wine_dbgstr_w(param1), wine_dbgstr_w(output));
_wsplitpath(input, drive, dir, fname, ext);
hff = FindFirstFileW(input, &fd);
if (hff == INVALID_HANDLE_VALUE)
return errorlevel = ERROR_INVALID_FUNCTION;
errorlevel = NO_ERROR;
do {
WCHAR dest[MAX_PATH];
WCHAR src[MAX_PATH];
DWORD attribs;
BOOL ok = TRUE;
DWORD flags = 0;
WINE_TRACE("Processing file '%s'\n", wine_dbgstr_w(fd.cFileName));
lstrcpyW(src, drive);
lstrcatW(src, dir);
attribs = GetFileAttributesW(output);
if (attribs != INVALID_FILE_ATTRIBUTES &&
(attribs & FILE_ATTRIBUTE_DIRECTORY)) {
lstrcpyW(dest, output);
lstrcatW(dest, L"\\");
lstrcatW(dest, fd.cFileName);
} else {
lstrcpyW(dest, output);
}
lstrcatW(src, fd.cFileName);
WINE_TRACE("Source '%s'\n", wine_dbgstr_w(src));
WINE_TRACE("Dest '%s'\n", wine_dbgstr_w(dest));
if (GetFileAttributesW(dest) != INVALID_FILE_ATTRIBUTES) {
BOOL force = FALSE;
WCHAR copycmd[MAXSTRING];
DWORD len;
if (wcsstr(quals, L"/-Y"))
force = FALSE;
else if (wcsstr(quals, L"/Y"))
force = TRUE;
else {
force = !!context;
len = GetEnvironmentVariableW(L"COPYCMD", copycmd, ARRAY_SIZE(copycmd));
if (len && len < ARRAY_SIZE(copycmd)) {
if (!lstrcmpiW(copycmd, L"/Y"))
force = TRUE;
else if (!lstrcmpiW(copycmd, L"/-Y"))
force = FALSE;
}
}
if (!force) {
WCHAR* question;
question = WCMD_format_string(WCMD_LoadMessage(WCMD_OVERWRITE), dest);
ok = WCMD_ask_confirm(question, FALSE, NULL);
LocalFree(question);
}
if (ok)
flags |= MOVEFILE_REPLACE_EXISTING;
}
if (!ok || !MoveFileExW(src, dest, flags))
{
if (!ok) WCMD_print_error();
errorlevel = ERROR_INVALID_FUNCTION;
}
} while (FindNextFileW(hff, &fd) != 0);
FindClose(hff);
return errorlevel;
}
RETURN_CODE WCMD_pause(void)
{
RETURN_CODE return_code = NO_ERROR;
WCMD_output_asis(anykey);
return_code = WCMD_wait_for_input(GetStdHandle(STD_INPUT_HANDLE));
WCMD_output_asis(L"\r\n");
return return_code;
}
RETURN_CODE WCMD_remove_dir(WCHAR *args)
{
int argno = 0;
int argsProcessed = 0;
WCHAR *argN = args;
while (argN) {
WCHAR *thisArg = WCMD_parameter (args, argno++, &argN, FALSE, FALSE);
if (argN && argN[0] != '/') {
WINE_TRACE("rd: Processing arg %s (quals:%s)\n", wine_dbgstr_w(thisArg),
wine_dbgstr_w(quals));
argsProcessed++;
if (wcsstr(quals, L"/S") == NULL) {
if (!RemoveDirectoryW(thisArg))
{
RETURN_CODE return_code = GetLastError();
WCMD_print_error();
return return_code;
}
} else {
SHFILEOPSTRUCTW lpDir;
if (wcsstr(quals, L"/Q") == NULL) {
BOOL ok;
WCHAR question[MAXSTRING];
wsprintfW(question, L"%s ", thisArg);
ok = WCMD_ask_confirm(question, TRUE, NULL);
if (!ok) return ERROR_INVALID_FUNCTION;
}
lpDir.hwnd = NULL;
lpDir.pTo = NULL;
lpDir.pFrom = thisArg;
lpDir.fFlags = FOF_SILENT | FOF_NOCONFIRMATION | FOF_NOERRORUI;
lpDir.wFunc = FO_DELETE;
thisArg[lstrlenW(thisArg) + 1] = 0x00;
if (SHFileOperationW(&lpDir)) WCMD_print_error ();
}
}
}
if (argsProcessed == 0) {
WCMD_output_stderr(WCMD_LoadMessage(WCMD_NOARG));
return ERROR_INVALID_FUNCTION;
}
return NO_ERROR;
}
RETURN_CODE WCMD_rename(void)
{
HANDLE hff;
WIN32_FIND_DATAW fd;
WCHAR input[MAX_PATH];
WCHAR *dotDst = NULL;
WCHAR drive[10];
WCHAR dir[MAX_PATH];
WCHAR fname[MAX_PATH];
WCHAR ext[MAX_PATH];
errorlevel = NO_ERROR;
if (param1[0] == 0x00 || param2[0] == 0x00) {
WCMD_output_stderr(WCMD_LoadMessage(WCMD_NOARG));
return errorlevel = ERROR_INVALID_FUNCTION;
}
if ((wcschr(param2,':') != NULL) || (wcschr(param2,'\\') != NULL)) {
SetLastError(ERROR_INVALID_PARAMETER);
WCMD_print_error();
return errorlevel = ERROR_INVALID_FUNCTION;
}
if (!WCMD_get_fullpath(param1, ARRAY_SIZE(input), input, NULL)) return errorlevel = ERROR_INVALID_FUNCTION;
WINE_TRACE("Rename from '%s'('%s') to '%s'\n", wine_dbgstr_w(input),
wine_dbgstr_w(param1), wine_dbgstr_w(param2));
dotDst = wcschr(param2, '.');
_wsplitpath(input, drive, dir, fname, ext);
hff = FindFirstFileW(input, &fd);
if (hff == INVALID_HANDLE_VALUE)
return errorlevel = ERROR_INVALID_FUNCTION;
errorlevel = NO_ERROR;
do {
WCHAR dest[MAX_PATH];
WCHAR src[MAX_PATH];
WCHAR *dotSrc = NULL;
int dirLen;
WINE_TRACE("Processing file '%s'\n", wine_dbgstr_w(fd.cFileName));
dotSrc = wcschr(fd.cFileName, '.');
lstrcpyW(src, drive);
lstrcatW(src, dir);
lstrcpyW(dest, src);
dirLen = lstrlenW(src);
lstrcatW(src, fd.cFileName);
if (param2[0] == '*') {
lstrcatW(dest, fd.cFileName);
if (dotSrc) dest[dirLen + (dotSrc - fd.cFileName)] = 0x00;
} else {
lstrcatW(dest, param2);
if (dotDst) dest[dirLen + (dotDst - param2)] = 0x00;
}
if (dotDst && (*(dotDst+1)=='*')) {
if (dotSrc) lstrcatW(dest, dotSrc);
} else if (dotDst) {
lstrcatW(dest, dotDst);
}
WINE_TRACE("Source '%s'\n", wine_dbgstr_w(src));
WINE_TRACE("Dest '%s'\n", wine_dbgstr_w(dest));
if (!MoveFileW(src, dest)) {
WCMD_print_error ();
errorlevel = ERROR_INVALID_FUNCTION;
}
} while (FindNextFileW(hff, &fd) != 0);
FindClose(hff);
return errorlevel;
}
static WCHAR *WCMD_dupenv( const WCHAR *env )
{
WCHAR *env_copy;
int len;
if( !env )
return NULL;
len = 0;
while ( env[len] )
len += lstrlenW(&env[len]) + 1;
len++;
env_copy = xalloc(len * sizeof (WCHAR));
memcpy(env_copy, env, len*sizeof (WCHAR));
return env_copy;
}
RETURN_CODE WCMD_setlocal(WCHAR *args)
{
WCHAR *env;
struct env_stack *env_copy;
WCHAR cwd[MAX_PATH];
BOOL newdelay;
int argno = 0;
WCHAR *argN = args;
if (!WCMD_is_in_context(NULL))
return NO_ERROR;
newdelay = delayedsubst;
while (argN)
{
WCHAR *thisArg = WCMD_parameter (args, argno++, &argN, FALSE, FALSE);
if (!thisArg || !*thisArg) break;
if (!wcsicmp(thisArg, L"ENABLEDELAYEDEXPANSION"))
newdelay = TRUE;
else if (!wcsicmp(thisArg, L"DISABLEDELAYEDEXPANSION"))
newdelay = FALSE;
else if (!wcsicmp(thisArg, L"ENABLEEXTENSIONS") || !wcsicmp(thisArg, L"DISABLEEXTENSIONS"))
{}
else
return errorlevel = ERROR_INVALID_FUNCTION;
TRACE("Setting delayed expansion to %d\n", newdelay);
}
env_copy = xalloc( sizeof(struct env_stack));
env = GetEnvironmentStringsW ();
env_copy->strings = WCMD_dupenv (env);
if (env_copy->strings)
{
env_copy->context = context;
env_copy->next = saved_environment;
env_copy->delayedsubst = delayedsubst;
delayedsubst = newdelay;
saved_environment = env_copy;
GetCurrentDirectoryW(MAX_PATH, cwd);
env_copy->u.cwd = cwd[0];
}
else
free(env_copy);
FreeEnvironmentStringsW (env);
return errorlevel = NO_ERROR;
}
RETURN_CODE WCMD_endlocal(void)
{
WCHAR *env, *old, *p;
struct env_stack *temp;
int len, n;
if (!WCMD_is_in_context(NULL)) return NO_ERROR;
if (!saved_environment || saved_environment->context != context)
return ERROR_INVALID_FUNCTION;
temp = saved_environment;
saved_environment = temp->next;
env = GetEnvironmentStringsW ();
old = WCMD_dupenv (env);
len = 0;
while (old[len]) {
n = lstrlenW(&old[len]) + 1;
p = wcschr(&old[len] + 1, '=');
if (p)
{
*p++ = 0;
SetEnvironmentVariableW (&old[len], NULL);
}
len += n;
}
free(old);
FreeEnvironmentStringsW (env);
env = temp->strings;
len = 0;
delayedsubst = temp->delayedsubst;
WINE_TRACE("Delayed expansion now %d\n", delayedsubst);
while (env[len]) {
n = lstrlenW(&env[len]) + 1;
p = wcschr(&env[len] + 1, '=');
if (p)
{
*p++ = 0;
SetEnvironmentVariableW (&env[len], p);
}
len += n;
}
if (IsCharAlphaW(temp->u.cwd)) {
WCHAR envvar[4];
WCHAR cwd[MAX_PATH];
wsprintfW(envvar, L"=%c:", temp->u.cwd);
if (GetEnvironmentVariableW(envvar, cwd, MAX_PATH)) {
WINE_TRACE("Resetting cwd to %s\n", wine_dbgstr_w(cwd));
SetCurrentDirectoryW(cwd);
}
}
free(env);
free(temp);
return NO_ERROR;
}
RETURN_CODE WCMD_setshow_default(const WCHAR *args)
{
RETURN_CODE return_code;
BOOL status;
WCHAR string[1024];
WCHAR cwd[1024];
WCHAR *pos;
WIN32_FIND_DATAW fd;
HANDLE hff;
WINE_TRACE("Request change to directory '%s'\n", wine_dbgstr_w(args));
if (lstrlenW(args) >= 2 &&
CompareStringW(LOCALE_USER_DEFAULT,
NORM_IGNORECASE | SORT_STRINGSORT,
args, 2, L"/D", -1) == CSTR_EQUAL) {
args += 2;
while (*args && (*args==' ' || *args=='\t'))
args++;
}
GetCurrentDirectoryW(ARRAY_SIZE(cwd), cwd);
return_code = NO_ERROR;
if (!*args) {
lstrcatW(cwd, L"\r\n");
WCMD_output_asis (cwd);
}
else {
pos = string;
while (*args) {
if (*args != '"') *pos++ = *args;
args++;
}
while (pos > string && (*(pos-1) == ' ' || *(pos-1) == '\t'))
pos--;
*pos = 0x00;
WINE_TRACE("Looking for directory '%s'\n", wine_dbgstr_w(string));
hff = FindFirstFileW(string, &fd);
if (hff != INVALID_HANDLE_VALUE) {
do {
if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
WCHAR fpath[MAX_PATH];
WCHAR drive[10];
WCHAR dir[MAX_PATH];
WCHAR fname[MAX_PATH];
WCHAR ext[MAX_PATH];
if (!WCMD_get_fullpath(string, ARRAY_SIZE(fpath), fpath, NULL))
return errorlevel = ERROR_INVALID_FUNCTION;
_wsplitpath(fpath, drive, dir, fname, ext);
wsprintfW(string, L"%s%s%s", drive, dir, fd.cFileName);
break;
}
} while (FindNextFileW(hff, &fd) != 0);
FindClose(hff);
}
WINE_TRACE("Really changing to directory '%s'\n", wine_dbgstr_w(string));
status = SetCurrentDirectoryW(string);
if (!status) {
WCMD_print_error ();
return_code = ERROR_INVALID_FUNCTION;
} else {
GetCurrentDirectoryW(ARRAY_SIZE(string), string);
if ((wcsstr(quals, L"/D") == NULL) &&
(param1[1] == ':') && (towupper(param1[0]) != towupper(cwd[0]))) {
SetCurrentDirectoryW(cwd);
}
}
if ((string[1] == ':') && IsCharAlphaW(string[0])) {
WCHAR env[4];
lstrcpyW(env, L"=");
memcpy(env+1, string, 2 * sizeof(WCHAR));
env[3] = 0x00;
WINE_TRACE("Setting '%s' to '%s'\n", wine_dbgstr_w(env), wine_dbgstr_w(string));
SetEnvironmentVariableW(env, string);
}
}
return errorlevel = return_code;
}
RETURN_CODE WCMD_setshow_date(void)
{
RETURN_CODE return_code = NO_ERROR;
WCHAR curdate[64], buffer[64];
DWORD count;
if (!*param1) {
if (GetDateFormatW(LOCALE_USER_DEFAULT, 0, NULL, NULL, curdate, ARRAY_SIZE(curdate))) {
WCMD_output (WCMD_LoadMessage(WCMD_CURRENTDATE), curdate);
if (wcsstr(quals, L"/T") == NULL) {
WCMD_output (WCMD_LoadMessage(WCMD_NEWDATE));
if (WCMD_ReadFile(GetStdHandle(STD_INPUT_HANDLE), buffer, ARRAY_SIZE(buffer), &count) &&
count > 2) {
WCMD_output_stderr (WCMD_LoadMessage(WCMD_NYI));
}
}
}
else WCMD_print_error ();
}
else {
return_code = ERROR_INVALID_FUNCTION;
WCMD_output_stderr (WCMD_LoadMessage(WCMD_NYI));
}
return errorlevel = return_code;
}
static int __cdecl WCMD_compare( const void *a, const void *b )
{
int r;
const WCHAR * const *str_a = a, * const *str_b = b;
r = CompareStringW( LOCALE_USER_DEFAULT, NORM_IGNORECASE | SORT_STRINGSORT,
*str_a, wcscspn(*str_a, L"="), *str_b, wcscspn(*str_b, L"=") );
if( r == CSTR_LESS_THAN ) return -1;
if( r == CSTR_GREATER_THAN ) return 1;
return 0;
}
static int WCMD_setshow_sortenv(const WCHAR *s, const WCHAR *stub)
{
UINT count=0, len=0, i, displayedcount=0, stublen=0;
const WCHAR **str;
if (stub) stublen = lstrlenW(stub);
while ( s[len] ) {
len += lstrlenW(&s[len]) + 1;
count++;
}
str = xalloc(count * sizeof (WCHAR*) );
str[0] = s;
for( i=1; i<count; i++ )
str[i] = str[i-1] + lstrlenW(str[i-1]) + 1;
qsort( str, count, sizeof (WCHAR*), WCMD_compare );
for( i=0; i<count; i++ ) {
if (!stub || CompareStringW(LOCALE_USER_DEFAULT,
NORM_IGNORECASE | SORT_STRINGSORT,
str[i], stublen, stub, -1) == CSTR_EQUAL) {
if (str[i][0] != '=') {
WCMD_output_asis(str[i]);
WCMD_output_asis(L"\r\n");
displayedcount++;
}
}
}
free( str );
return displayedcount;
}
static int WCMD_getprecedence(const WCHAR in)
{
switch (in) {
case '!':
case '~':
case OP_POSITIVE:
case OP_NEGATIVE:
return 8;
case '*':
case '/':
case '%':
return 7;
case '+':
case '-':
return 6;
case '<':
case '>':
return 5;
case '&':
return 4;
case '^':
return 3;
case '|':
return 2;
case '=':
case OP_ASSSIGNMUL:
case OP_ASSSIGNDIV:
case OP_ASSSIGNMOD:
case OP_ASSSIGNADD:
case OP_ASSSIGNSUB:
case OP_ASSSIGNAND:
case OP_ASSSIGNNOT:
case OP_ASSSIGNOR:
case OP_ASSSIGNSHL:
case OP_ASSSIGNSHR:
return 1;
default:
return 0;
}
}
static void WCMD_pushnumber(WCHAR *var, int num, VARSTACK **varstack) {
VARSTACK *thisstack = xalloc(sizeof(VARSTACK));
thisstack->isnum = (var == NULL);
if (var) {
thisstack->variable = var;
WINE_TRACE("Pushed variable %s\n", wine_dbgstr_w(var));
} else {
thisstack->value = num;
WINE_TRACE("Pushed number %d\n", num);
}
thisstack->next = *varstack;
*varstack = thisstack;
}
static int WCMD_peeknumber(VARSTACK **varstack) {
int result = 0;
VARSTACK *thisvar;
if (varstack) {
thisvar = *varstack;
if (!thisvar->isnum) {
WCHAR tmpstr[MAXSTRING];
if (GetEnvironmentVariableW(thisvar->variable, tmpstr, MAXSTRING)) {
result = wcstol(tmpstr,NULL,0);
}
WINE_TRACE("Envvar %s converted to %d\n", wine_dbgstr_w(thisvar->variable), result);
} else {
result = thisvar->value;
}
}
WINE_TRACE("Peeked number %d\n", result);
return result;
}
static int WCMD_popnumber(VARSTACK **varstack) {
int result = 0;
VARSTACK *thisvar;
if (varstack) {
thisvar = *varstack;
result = WCMD_peeknumber(varstack);
if (!thisvar->isnum) free(thisvar->variable);
*varstack = thisvar->next;
free(thisvar);
}
WINE_TRACE("Popped number %d\n", result);
return result;
}
static void WCMD_pushoperator(WCHAR op, int precedence, OPSTACK **opstack) {
OPSTACK *thisstack = xalloc(sizeof(OPSTACK));
thisstack->precedence = precedence;
thisstack->op = op;
thisstack->next = *opstack;
WINE_TRACE("Pushed operator %c\n", op);
*opstack = thisstack;
}
static WCHAR WCMD_popoperator(OPSTACK **opstack) {
WCHAR result = 0;
OPSTACK *thisop;
if (opstack) {
thisop = *opstack;
result = thisop->op;
*opstack = thisop->next;
free(thisop);
}
WINE_TRACE("Popped operator %c\n", result);
return result;
}
static int WCMD_reduce(OPSTACK **opstack, VARSTACK **varstack) {
WCHAR thisop;
int var1,var2;
int rc = 0;
if (!*opstack || !*varstack) {
WINE_TRACE("No operators for the reduce\n");
return WCMD_NOOPERATOR;
}
thisop = WCMD_popoperator(opstack);
WINE_TRACE("Reducing the stacks - processing operator %c\n", thisop);
var1 = WCMD_popnumber(varstack);
switch (thisop) {
case '!': WCMD_pushnumber(NULL, !var1, varstack);
break;
case '~': WCMD_pushnumber(NULL, ~var1, varstack);
break;
case OP_POSITIVE: WCMD_pushnumber(NULL, var1, varstack);
break;
case OP_NEGATIVE: WCMD_pushnumber(NULL, -var1, varstack);
break;
}
if (!*varstack) {
WINE_TRACE("No operands left for the reduce?\n");
return WCMD_NOOPERAND;
}
switch (thisop) {
case '!':
case '~':
case OP_POSITIVE:
case OP_NEGATIVE:
break;
case '*': var2 = WCMD_popnumber(varstack);
WCMD_pushnumber(NULL, var2*var1, varstack);
break;
case '/': var2 = WCMD_popnumber(varstack);
if (var1 == 0) return WCMD_DIVIDEBYZERO;
WCMD_pushnumber(NULL, var2/var1, varstack);
break;
case '+': var2 = WCMD_popnumber(varstack);
WCMD_pushnumber(NULL, var2+var1, varstack);
break;
case '-': var2 = WCMD_popnumber(varstack);
WCMD_pushnumber(NULL, var2-var1, varstack);
break;
case '&': var2 = WCMD_popnumber(varstack);
WCMD_pushnumber(NULL, var2&var1, varstack);
break;
case '%': var2 = WCMD_popnumber(varstack);
if (var1 == 0) return WCMD_DIVIDEBYZERO;
WCMD_pushnumber(NULL, var2%var1, varstack);
break;
case '^': var2 = WCMD_popnumber(varstack);
WCMD_pushnumber(NULL, var2^var1, varstack);
break;
case '<': var2 = WCMD_popnumber(varstack);
if (var1 < 0 || var1 >= (8 * sizeof(INT))) {
WCMD_pushnumber(NULL, 0, varstack);
} else {
WCMD_pushnumber(NULL, var2<<var1, varstack);
}
break;
case '>': var2 = WCMD_popnumber(varstack);
WCMD_pushnumber(NULL, var2>>var1, varstack);
break;
case '|': var2 = WCMD_popnumber(varstack);
WCMD_pushnumber(NULL, var2|var1, varstack);
break;
case OP_ASSSIGNMUL:
case OP_ASSSIGNDIV:
case OP_ASSSIGNMOD:
case OP_ASSSIGNADD:
case OP_ASSSIGNSUB:
case OP_ASSSIGNAND:
case OP_ASSSIGNNOT:
case OP_ASSSIGNOR:
case OP_ASSSIGNSHL:
case OP_ASSSIGNSHR:
{
int i = 0;
if (!(*varstack) || (*varstack)->isnum) {
return WCMD_NOOPERAND;
}
var2 = WCMD_peeknumber(varstack);
WCMD_pushnumber(NULL, var2, varstack);
WCMD_pushnumber(NULL, var1, varstack);
while (calcassignments[i].op != ' ' &&
calcassignments[i].calculatedop != thisop) {
i++;
}
if (calcassignments[i].calculatedop == ' ') {
WINE_ERR("Unexpected operator %c\n", thisop);
return WCMD_NOOPERATOR;
}
WCMD_pushoperator('=', WCMD_getprecedence('='), opstack);
WCMD_pushoperator(calcassignments[i].op,
WCMD_getprecedence(calcassignments[i].op), opstack);
break;
}
case '=':
{
WCHAR result[MAXSTRING];
swprintf(result, ARRAY_SIZE(result), L"%d", var1);
WINE_TRACE("Assigning %s a value %s\n", wine_dbgstr_w((*varstack)->variable),
wine_dbgstr_w(result));
SetEnvironmentVariableW((*varstack)->variable, result);
var2 = WCMD_popnumber(varstack);
WCMD_pushnumber(NULL, var1, varstack);
break;
}
default: WINE_ERR("Unrecognized operator %c\n", thisop);
}
return rc;
}
static int WCMD_handleExpression(WCHAR **expr, int *ret, int depth)
{
static const WCHAR mathDelims[] = L" \t()!~-*/%+<>&^|=,";
int rc = 0;
WCHAR *pos;
BOOL lastwasnumber = FALSE;
OPSTACK *opstackhead = NULL;
VARSTACK *varstackhead = NULL;
WCHAR foundhalf = 0;
WINE_TRACE("Handling expression '%s'\n", wine_dbgstr_w(*expr));
pos = *expr;
while (pos && *pos) {
BOOL treatasnumber;
while (*pos && (*pos==' ' || *pos=='\t')) pos++;
if (!*pos) goto exprreturn;
if (wcschr(mathDelims, *pos) == NULL) {
WCHAR *parmstart, *parm, *dupparm;
WCHAR *nextpos;
if (lastwasnumber || foundhalf) {
rc = WCMD_NOOPERATOR;
goto exprerrorreturn;
}
lastwasnumber = TRUE;
if (iswdigit(*pos)) {
int num = wcstoul(pos, &nextpos, 0);
WCMD_pushnumber(NULL, num, &varstackhead);
pos = nextpos;
if (*nextpos && (wcschr(mathDelims, *nextpos) == NULL)) {
rc = WCMD_BADHEXOCT;
goto exprerrorreturn;
}
} else {
parm = WCMD_parameter_with_delims(pos, 0, &parmstart, FALSE, FALSE, mathDelims);
dupparm = xstrdupW(parm);
WCMD_pushnumber(dupparm, 0, &varstackhead);
pos = parmstart + lstrlenW(dupparm);
}
continue;
}
if (foundhalf && (*pos != foundhalf)) {
rc = WCMD_NOOPERATOR;
goto exprerrorreturn;
}
treatasnumber = FALSE;
switch (*pos) {
case '>':
case '<': if (!foundhalf) {
foundhalf = *pos;
pos++;
break;
}
foundhalf = 0;
case '=': if (*pos=='=') {
if (!lastwasnumber && opstackhead) {
int i = 0;
while (calcassignments[i].op != ' ' && calcassignments[i].op != opstackhead->op) {
i++;
}
if (calcassignments[i].op == ' ') {
rc = WCMD_NOOPERAND;
goto exprerrorreturn;
} else {
*pos = calcassignments[i].calculatedop;
WCMD_popoperator(&opstackhead);
}
}
}
case '+': if (!lastwasnumber && *pos=='+') *pos = OP_POSITIVE;
case '-': if (!lastwasnumber && *pos=='-') *pos = OP_NEGATIVE;
case '!':
case '~':
case '/':
case '%':
case '&':
case '^':
case '*':
case '|':
{
int precedence = WCMD_getprecedence(*pos);
WINE_TRACE("Found operator %c precedence %d (head is %d)\n", *pos,
precedence, !opstackhead?-1:opstackhead->precedence);
while (!rc && opstackhead &&
((opstackhead->precedence > precedence) ||
((opstackhead->precedence == precedence) &&
(precedence != 1) && (precedence != 8)))) {
rc = WCMD_reduce(&opstackhead, &varstackhead);
}
if (rc) goto exprerrorreturn;
WCMD_pushoperator(*pos, precedence, &opstackhead);
pos++;
break;
}
case ',':
{
int prevresult = -1;
WINE_TRACE("Found expression delimiter - reducing existing stacks\n");
while (!rc && opstackhead) {
rc = WCMD_reduce(&opstackhead, &varstackhead);
}
if (rc) goto exprerrorreturn;
if (!varstackhead || varstackhead->next) {
rc = WCMD_NOOPERATOR;
goto exprerrorreturn;
}
prevresult = WCMD_popnumber(&varstackhead);
WINE_TRACE("Expression resolved to %d\n", prevresult);
free(varstackhead);
varstackhead = NULL;
pos++;
break;
}
case '(' : {
int exprresult = 0;
pos++;
rc = WCMD_handleExpression(&pos, &exprresult, depth+1);
if (rc) goto exprerrorreturn;
WCMD_pushnumber(NULL, exprresult, &varstackhead);
break;
}
case ')' : {
pos++;
treatasnumber = TRUE;
if (depth == 0) {
rc = WCMD_BADPAREN;
goto exprerrorreturn;
}
goto exprreturn;
}
default:
WINE_ERR("Unrecognized operator %c\n", *pos);
pos++;
}
lastwasnumber = treatasnumber;
}
exprreturn:
*expr = pos;
while (!rc && opstackhead) {
rc = WCMD_reduce(&opstackhead, &varstackhead);
}
if (rc) goto exprerrorreturn;
if (!varstackhead || varstackhead->next) {
rc = WCMD_NOOPERATOR;
goto exprerrorreturn;
}
*ret = WCMD_popnumber(&varstackhead);
exprerrorreturn:
while (opstackhead) WCMD_popoperator(&opstackhead);
while (varstackhead) WCMD_popnumber(&varstackhead);
WINE_TRACE("Returning result %d, rc %d\n", *ret, rc);
return rc;
}
RETURN_CODE WCMD_setshow_env(WCHAR *s)
{
RETURN_CODE return_code = NO_ERROR;
WCHAR *p;
BOOL status;
WCHAR string[MAXSTRING];
if (!*s) {
WCHAR *env = GetEnvironmentStringsW();
WCMD_setshow_sortenv( env, NULL );
FreeEnvironmentStringsW(env);
}
else if (CompareStringW(LOCALE_USER_DEFAULT,
NORM_IGNORECASE | SORT_STRINGSORT,
s, 2, L"/P", -1) == CSTR_EQUAL) {
DWORD count;
s += 2;
while (*s && (*s==' ' || *s=='\t')) s++;
if (*s=='\"') {
WCHAR *lastquote;
lastquote = WCMD_strip_quotes(s);
if (lastquote) *lastquote = 0x00;
WINE_TRACE("set: Stripped command line '%s'\n", wine_dbgstr_w(s));
}
if (!(*s) || ((p = wcschr(s, '=')) == NULL )) {
WCMD_output_stderr(WCMD_LoadMessage(WCMD_NOARG));
return_code = ERROR_INVALID_FUNCTION;
}
else
{
*p++ = '\0';
if (*p) {
if (*p == L'"') {
WCHAR* last = wcsrchr(p+1, L'"');
p++;
if (last) *last = L'\0';
}
WCMD_output_asis(p);
}
if (WCMD_ReadFile(GetStdHandle(STD_INPUT_HANDLE), string, ARRAY_SIZE(string), &count) && count > 1) {
string[count-1] = '\0';
if (string[count-2] == '\r') string[count-2] = '\0';
TRACE("set /p: Setting var '%s' to '%s'\n", wine_dbgstr_w(s),
wine_dbgstr_w(string));
if (*string) SetEnvironmentVariableW(s, string);
}
}
} else if (CompareStringW(LOCALE_USER_DEFAULT,
NORM_IGNORECASE | SORT_STRINGSORT,
s, 2, L"/A", -1) == CSTR_EQUAL) {
int result = 0;
int rc = 0;
WCHAR *thisexpr;
WCHAR *src,*dst;
thisexpr = xalloc((wcslen(s + 2) + 1) * sizeof(WCHAR));
src = s+2;
dst = thisexpr;
while (*src) {
if (*src != '"') *dst++ = *src;
src++;
}
*dst = 0;
src = thisexpr;
rc = WCMD_handleExpression(&src, &result, 0);
free(thisexpr);
if (rc > 0) {
WCMD_output_stderr(WCMD_LoadMessage(rc));
return_code = ERROR_INVALID_FUNCTION;
}
else if (!WCMD_is_in_context(NULL)) {
swprintf(string, ARRAY_SIZE(string), L"%d", result);
WCMD_output_asis(string);
}
} else {
DWORD gle;
if (*s=='\"') {
WCHAR *lastquote;
lastquote = WCMD_strip_quotes(s);
if (lastquote) *lastquote = 0x00;
WINE_TRACE("set: Stripped command line '%s'\n", wine_dbgstr_w(s));
}
p = wcschr (s, '=');
if (p == NULL) {
WCHAR *env = GetEnvironmentStringsW();
if (WCMD_setshow_sortenv( env, s ) == 0) {
WCMD_output_stderr(WCMD_LoadMessage(WCMD_MISSINGENV), s);
return_code = ERROR_INVALID_FUNCTION;
}
FreeEnvironmentStringsW(env);
}
else
{
*p++ = '\0';
if (!*p) p = NULL;
TRACE("set: Setting var '%s' to '%s'\n", wine_dbgstr_w(s),
wine_dbgstr_w(p));
status = SetEnvironmentVariableW(s, p);
gle = GetLastError();
if ((!status) & (gle == ERROR_ENVVAR_NOT_FOUND)) {
return_code = ERROR_INVALID_FUNCTION;
} else if (!status) WCMD_print_error();
}
}
return WCMD_is_in_context(L".bat") && return_code == NO_ERROR ?
return_code : (errorlevel = return_code);
}
RETURN_CODE WCMD_setshow_path(const WCHAR *args)
{
WCHAR string[1024];
if (!*param1 && !*param2) {
if (!GetEnvironmentVariableW(L"PATH", string, ARRAY_SIZE(string)))
wcscpy(string, L"(null)");
WCMD_output_asis(L"PATH=");
WCMD_output_asis(string);
WCMD_output_asis(L"\r\n");
}
else {
if (*args == '=') args++;
if (args[0] == L';' && *WCMD_skip_leading_spaces((WCHAR *)(args + 1)) == L'\0') args = NULL;
if (!SetEnvironmentVariableW(L"PATH", args))
{
WCMD_print_error();
return errorlevel = ERROR_INVALID_FUNCTION;
}
}
return WCMD_is_in_context(L".bat") ? NO_ERROR : (errorlevel = NO_ERROR);
}
RETURN_CODE WCMD_setshow_prompt(void)
{
WCHAR *s;
if (!*param1) {
SetEnvironmentVariableW(L"PROMPT", NULL);
}
else {
s = param1;
while ((*s == '=') || (*s == ' ') || (*s == '\t')) s++;
if (!*s) {
SetEnvironmentVariableW(L"PROMPT", NULL);
}
else SetEnvironmentVariableW(L"PROMPT", s);
}
return WCMD_is_in_context(L".bat") ? NO_ERROR : (errorlevel = NO_ERROR);
}
RETURN_CODE WCMD_setshow_time(void)
{
RETURN_CODE return_code = NO_ERROR;
WCHAR curtime[64], buffer[64];
DWORD count;
SYSTEMTIME st;
if (!*param1) {
GetLocalTime(&st);
if (GetTimeFormatW(LOCALE_USER_DEFAULT, 0, &st, NULL, curtime, ARRAY_SIZE(curtime))) {
WCMD_output (WCMD_LoadMessage(WCMD_CURRENTTIME), curtime);
if (wcsstr(quals, L"/T") == NULL) {
WCMD_output (WCMD_LoadMessage(WCMD_NEWTIME));
if (WCMD_ReadFile(GetStdHandle(STD_INPUT_HANDLE), buffer, ARRAY_SIZE(buffer), &count) &&
count > 2) {
WCMD_output_stderr (WCMD_LoadMessage(WCMD_NYI));
}
}
}
else WCMD_print_error ();
}
else {
return_code = ERROR_INVALID_FUNCTION;
WCMD_output_stderr (WCMD_LoadMessage(WCMD_NYI));
}
return errorlevel = return_code;
}
RETURN_CODE WCMD_shift(const WCHAR *args)
{
int start;
if (context != NULL) {
WCHAR *pos = wcschr(args, '/');
int i;
if (pos == NULL) {
start = 0;
} else if (*(pos+1)>='0' && *(pos+1)<='8') {
start = (*(pos+1) - '0');
} else {
SetLastError(ERROR_INVALID_PARAMETER);
WCMD_print_error();
return errorlevel = ERROR_INVALID_FUNCTION;
}
WINE_TRACE("Shifting variables, starting at %d\n", start);
for (i=start;i<=8;i++) {
context -> shift_count[i] = context -> shift_count[i+1] + 1;
}
context -> shift_count[9] = context -> shift_count[9] + 1;
}
return NO_ERROR;
}
RETURN_CODE WCMD_start(WCHAR *args)
{
RETURN_CODE return_code = NO_ERROR;
int argno;
int have_title;
WCHAR file[MAX_PATH];
WCHAR *cmdline, *cmdline_params;
STARTUPINFOW st;
PROCESS_INFORMATION pi;
GetSystemDirectoryW( file, MAX_PATH );
lstrcatW(file, L"\\start.exe");
cmdline = xalloc( (wcslen(file) + wcslen(args) + 8) * sizeof(WCHAR) );
lstrcpyW( cmdline, file );
lstrcatW(cmdline, L" ");
cmdline_params = cmdline + lstrlenW(cmdline);
have_title = FALSE;
for (argno=0; ; argno++) {
WCHAR *thisArg, *argN;
argN = NULL;
thisArg = WCMD_parameter_with_delims(args, argno, &argN, FALSE, FALSE, L" \t/");
if (!argN)
break;
if (argN[0] == '"') {
TRACE("detected console title: %s\n", wine_dbgstr_w(thisArg));
have_title = TRUE;
memcpy(cmdline_params, args, sizeof(WCHAR) * (argN - args));
cmdline_params[argN - args] = '\0';
lstrcatW(cmdline_params, L"\"\\\"");
lstrcatW(cmdline_params, thisArg);
lstrcatW(cmdline_params, L"\\\"\"");
thisArg = WCMD_parameter_with_delims(args, argno, &argN, TRUE, FALSE, L" \t/");
lstrcatW(cmdline_params, argN + lstrlenW(thisArg));
break;
}
else if (argN != args && argN[-1] == '/') {
continue;
} else
break;
}
if (!have_title) {
lstrcatW( cmdline, args );
}
memset( &st, 0, sizeof(STARTUPINFOW) );
st.cb = sizeof(STARTUPINFOW);
if (CreateProcessW( file, cmdline, NULL, NULL, TRUE, 0, NULL, NULL, &st, &pi ))
{
DWORD exit_code;
WaitForSingleObject( pi.hProcess, INFINITE );
GetExitCodeProcess( pi.hProcess, &exit_code );
errorlevel = (exit_code == STILL_ACTIVE) ? NO_ERROR : exit_code;
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
else
{
SetLastError(ERROR_FILE_NOT_FOUND);
WCMD_print_error ();
return_code = errorlevel = ERROR_INVALID_FUNCTION;
}
free(cmdline);
return return_code;
}
RETURN_CODE WCMD_title(const WCHAR *args)
{
SetConsoleTitleW(args);
return NO_ERROR;
}
RETURN_CODE WCMD_type(WCHAR *args)
{
RETURN_CODE return_code;
int argno = 0;
WCHAR *argN = args;
BOOL writeHeaders = FALSE;
if (param1[0] == 0x00) {
WCMD_output_stderr(WCMD_LoadMessage(WCMD_NOARG));
return errorlevel = ERROR_INVALID_FUNCTION;
}
if (param2[0] != 0x00) writeHeaders = TRUE;
return_code = NO_ERROR;
while (argN) {
WCHAR *thisArg = WCMD_parameter (args, argno++, &argN, FALSE, FALSE);
HANDLE h;
WCHAR buffer[512];
DWORD count;
if (!argN) break;
WINE_TRACE("type: Processing arg '%s'\n", wine_dbgstr_w(thisArg));
h = CreateFileW(thisArg, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, NULL);
if (h == INVALID_HANDLE_VALUE) {
WCMD_print_error ();
WCMD_output_stderr(WCMD_LoadMessage(WCMD_READFAIL), thisArg);
return errorlevel = ERROR_INVALID_FUNCTION;
} else {
if (writeHeaders) {
WCMD_output_stderr(L"\n%1\n\n\n", thisArg);
}
while (WCMD_ReadFile(h, buffer, ARRAY_SIZE(buffer) - 1, &count)) {
if (count == 0) break;
buffer[count] = 0;
WCMD_output_asis (buffer);
}
CloseHandle (h);
}
}
return errorlevel = return_code;
}
RETURN_CODE WCMD_more(WCHAR *args)
{
int argno = 0;
WCHAR *argN = args;
WCHAR moreStr[100];
WCHAR moreStrPage[100];
WCHAR buffer[512];
DWORD count;
RETURN_CODE return_code = NO_ERROR;
lstrcpyW(moreStr, L"-- ");
LoadStringW(hinst, WCMD_MORESTR, &moreStr[3], ARRAY_SIZE(moreStr)-3);
if (param1[0] == 0x00) {
HANDLE hstdin = GetStdHandle(STD_INPUT_HANDLE);
HANDLE hConIn = CreateFileW(L"CONIN$", GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ, NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, 0);
WINE_TRACE("No parms - working probably in pipe mode\n");
SetStdHandle(STD_INPUT_HANDLE, hConIn);
wsprintfW(moreStrPage, L"%s --\n", moreStr);
WCMD_enter_paged_mode(moreStrPage);
while (WCMD_ReadFile(hstdin, buffer, ARRAY_SIZE(buffer)-1, &count)) {
if (count == 0) break;
buffer[count] = 0;
WCMD_output_asis (buffer);
}
WCMD_leave_paged_mode();
SetStdHandle(STD_INPUT_HANDLE, hstdin);
CloseHandle(hConIn);
WCMD_output_asis (L"\r\n");
} else {
BOOL needsPause = FALSE;
WINE_TRACE("Parms supplied - working through each file\n");
WCMD_enter_paged_mode(moreStrPage);
while (argN) {
WCHAR *thisArg = WCMD_parameter (args, argno++, &argN, FALSE, FALSE);
HANDLE h;
if (!argN) break;
if (needsPause) {
wsprintfW(moreStrPage, L"%s (%2.2d%%) --\n", moreStr, 100);
WCMD_leave_paged_mode();
WCMD_output_asis(moreStrPage);
WCMD_ReadFile(GetStdHandle(STD_INPUT_HANDLE), buffer, ARRAY_SIZE(buffer), &count);
WCMD_enter_paged_mode(moreStrPage);
}
WINE_TRACE("more: Processing arg '%s'\n", wine_dbgstr_w(thisArg));
h = CreateFileW(thisArg, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, NULL);
if (h == INVALID_HANDLE_VALUE) {
WCMD_print_error ();
WCMD_output_stderr(WCMD_LoadMessage(WCMD_READFAIL), thisArg);
} else {
ULONG64 curPos = 0;
ULONG64 fileLen = 0;
WIN32_FILE_ATTRIBUTE_DATA fileInfo;
GetFileAttributesExW(thisArg, GetFileExInfoStandard, (void*)&fileInfo);
fileLen = (((ULONG64)fileInfo.nFileSizeHigh) << 32) + fileInfo.nFileSizeLow;
needsPause = TRUE;
while (WCMD_ReadFile(h, buffer, ARRAY_SIZE(buffer)-1, &count)) {
if (count == 0) break;
buffer[count] = 0;
curPos += count;
wsprintfW(moreStrPage, L"%s (%2.2d%%) --\n", moreStr, (int) min(99, (curPos * 100)/fileLen));
WCMD_output_asis (buffer);
}
CloseHandle (h);
}
}
WCMD_leave_paged_mode();
}
return errorlevel = return_code;
}
RETURN_CODE WCMD_verify(void)
{
RETURN_CODE return_code = NO_ERROR;
if (!param1[0])
WCMD_output(WCMD_LoadMessage(WCMD_VERIFYPROMPT), verify_mode ? L"ON" : L"OFF");
else if (lstrcmpiW(param1, L"ON") == 0)
verify_mode = TRUE;
else if (lstrcmpiW(param1, L"OFF") == 0)
verify_mode = FALSE;
else
{
WCMD_output_stderr(WCMD_LoadMessage(WCMD_VERIFYERR));
return_code = ERROR_INVALID_FUNCTION;
}
return errorlevel = return_code;
}
RETURN_CODE WCMD_version(void)
{
RETURN_CODE return_code;
WCMD_output_asis(L"\r\n");
if (*quals)
{
WCMD_output_stderr(WCMD_LoadMessage(WCMD_SYNTAXERR));
return_code = ERROR_INVALID_FUNCTION;
}
else
{
WCMD_output_asis(version_string);
return_code = NO_ERROR;
}
return errorlevel = return_code;
}
BOOL WCMD_print_volume_information(const WCHAR *path)
{
WCHAR label[MAX_PATH];
DWORD serial;
if (!GetVolumeInformationW(path, label, ARRAY_SIZE(label), &serial, NULL, NULL, NULL, 0))
return FALSE;
if (label[0])
WCMD_output(WCMD_LoadMessage(WCMD_VOLUMELABEL), path[0], label);
else
WCMD_output(WCMD_LoadMessage(WCMD_VOLUMENOLABEL), path[0]);
WCMD_output(WCMD_LoadMessage(WCMD_VOLUMESERIALNO), HIWORD(serial), LOWORD(serial));
return TRUE;
}
RETURN_CODE WCMD_label(void)
{
DWORD count;
WCHAR string[MAX_PATH], curdir[MAX_PATH];
if (*quals)
return errorlevel = ERROR_INVALID_FUNCTION;
if (!*param1) {
if (!GetCurrentDirectoryW(ARRAY_SIZE(curdir), curdir)) {
WCMD_print_error();
return errorlevel = ERROR_INVALID_FUNCTION;
}
}
else if (param1[1] == ':' && !param1[2]) {
curdir[0] = param1[0];
curdir[1] = param1[1];
} else {
WCMD_output_stderr(WCMD_LoadMessage(WCMD_SYNTAXERR));
return errorlevel = ERROR_INVALID_FUNCTION;
}
curdir[2] = L'\\';
curdir[3] = L'\0';
if (!WCMD_print_volume_information(curdir)) {
WCMD_print_error();
return errorlevel = ERROR_INVALID_FUNCTION;
}
if (WCMD_ReadFile(GetStdHandle(STD_INPUT_HANDLE), string, ARRAY_SIZE(string), &count) &&
count > 1) {
string[count-1] = '\0';
if (string[count-2] == '\r') string[count-2] = '\0';
}
else return errorlevel = ERROR_INVALID_FUNCTION;
if (*param1) {
if (!SetVolumeLabelW(curdir, string))
{
errorlevel = GetLastError();
WCMD_print_error();
return errorlevel;
}
}
return errorlevel = NO_ERROR;
}
RETURN_CODE WCMD_volume(void)
{
WCHAR curdir[MAX_PATH];
RETURN_CODE return_code = NO_ERROR;
if (*quals)
return errorlevel = ERROR_INVALID_FUNCTION;
if (!*param1)
{
if (!GetCurrentDirectoryW(ARRAY_SIZE(curdir), curdir))
return errorlevel = ERROR_INVALID_FUNCTION;
}
else if (param1[1] == L':' && !param1[2])
{
memcpy(curdir, param1, 2 * sizeof(WCHAR));
}
else
return errorlevel = ERROR_INVALID_FUNCTION;
curdir[2] = L'\\';
curdir[3] = L'\0';
if (!WCMD_print_volume_information(curdir))
{
return_code = GetLastError();
WCMD_print_error();
}
return errorlevel = return_code;
}
RETURN_CODE WCMD_exit(void)
{
int rc = wcstol(param1, NULL, 10);
if (context && lstrcmpiW(quals, L"/B") == 0)
{
errorlevel = rc;
context->file_position.QuadPart = WCMD_FILE_POSITION_EOF;
return RETURN_CODE_ABORTED;
}
ExitProcess(rc);
}
RETURN_CODE WCMD_assoc(const WCHAR *args, BOOL assoc)
{
RETURN_CODE return_code;
HKEY key;
DWORD accessOptions = KEY_READ;
WCHAR *newValue;
LONG rc = ERROR_SUCCESS;
WCHAR keyValue[MAXSTRING];
DWORD valueLen;
HKEY readKey;
return_code = NO_ERROR;
newValue = wcschr(args, '=');
if (newValue) accessOptions |= KEY_WRITE;
if (RegOpenKeyExW(HKEY_CLASSES_ROOT, L"", 0, accessOptions, &key) != ERROR_SUCCESS) {
WINE_FIXME("Unexpected failure opening HKCR key: %ld\n", GetLastError());
return ERROR_INVALID_FUNCTION;
}
if (*args == 0x00) {
int index = 0;
while (rc != ERROR_NO_MORE_ITEMS) {
WCHAR keyName[MAXSTRING];
DWORD nameLen;
nameLen = MAXSTRING;
rc = RegEnumKeyExW(key, index++, keyName, &nameLen, NULL, NULL, NULL, NULL);
if (rc == ERROR_SUCCESS) {
if ((keyName[0] == '.' && assoc) ||
(!(keyName[0] == '.') && (!assoc)))
{
WCHAR subkey[MAXSTRING];
lstrcpyW(subkey, keyName);
if (!assoc) lstrcatW(subkey, L"\\Shell\\Open\\Command");
if (RegOpenKeyExW(key, subkey, 0, accessOptions, &readKey) == ERROR_SUCCESS) {
valueLen = sizeof(keyValue);
rc = RegQueryValueExW(readKey, NULL, NULL, NULL, (LPBYTE)keyValue, &valueLen);
WCMD_output_asis(keyName);
WCMD_output_asis(L"=");
if (rc == ERROR_SUCCESS) {
WCMD_output_asis(keyValue);
}
WCMD_output_asis(L"\r\n");
RegCloseKey(readKey);
}
}
}
}
} else {
if (newValue == NULL) {
WCHAR *space;
WCHAR subkey[MAXSTRING];
lstrcpyW(keyValue, args);
space = wcschr(keyValue, ' ');
if (space) *space=0x00;
lstrcpyW(subkey, keyValue);
if (!assoc) lstrcatW(subkey, L"\\Shell\\Open\\Command");
valueLen = sizeof(keyValue);
if (RegOpenKeyExW(key, subkey, 0, accessOptions, &readKey) == ERROR_SUCCESS &&
RegQueryValueExW(readKey, NULL, NULL, NULL, (LPBYTE)keyValue, &valueLen) == ERROR_SUCCESS) {
WCMD_output_asis(args);
WCMD_output_asis(L"=");
WCMD_output_asis(keyValue);
WCMD_output_asis(L"\r\n");
RegCloseKey(readKey);
return_code = NO_ERROR;
} else {
WCHAR msgbuffer[MAXSTRING];
if (assoc) {
LoadStringW(hinst, WCMD_NOASSOC, msgbuffer, ARRAY_SIZE(msgbuffer));
} else {
LoadStringW(hinst, WCMD_NOFTYPE, msgbuffer, ARRAY_SIZE(msgbuffer));
}
WCMD_output_stderr(msgbuffer, keyValue);
return_code = assoc ? ERROR_INVALID_FUNCTION : ERROR_FILE_NOT_FOUND;
}
} else {
WCHAR subkey[MAXSTRING];
*newValue = 0x00;
newValue++;
lstrcpyW(subkey, args);
if (!assoc) lstrcatW(subkey, L"\\Shell\\Open\\Command");
if (*newValue == 0x00) {
if (assoc)
rc = RegDeleteKeyW(key, args);
else {
rc = RegCreateKeyExW(key, subkey, 0, NULL, REG_OPTION_NON_VOLATILE,
accessOptions, NULL, &readKey, NULL);
if (rc == ERROR_SUCCESS) {
rc = RegDeleteValueW(readKey, NULL);
RegCloseKey(readKey);
}
}
if (rc == ERROR_SUCCESS) {
WINE_TRACE("HKCR Key '%s' deleted\n", wine_dbgstr_w(args));
} else if (rc != ERROR_FILE_NOT_FOUND) {
WCMD_print_error();
return_code = ERROR_FILE_NOT_FOUND;
} else {
WCHAR msgbuffer[MAXSTRING];
if (assoc) {
LoadStringW(hinst, WCMD_NOASSOC, msgbuffer, ARRAY_SIZE(msgbuffer));
} else {
LoadStringW(hinst, WCMD_NOFTYPE, msgbuffer, ARRAY_SIZE(msgbuffer));
}
WCMD_output_stderr(msgbuffer, args);
return_code = ERROR_FILE_NOT_FOUND;
}
} else {
rc = RegCreateKeyExW(key, subkey, 0, NULL, REG_OPTION_NON_VOLATILE,
accessOptions, NULL, &readKey, NULL);
if (rc == ERROR_SUCCESS) {
rc = RegSetValueExW(readKey, NULL, 0, REG_SZ,
(LPBYTE)newValue,
sizeof(WCHAR) * (lstrlenW(newValue) + 1));
RegCloseKey(readKey);
}
if (rc != ERROR_SUCCESS) {
WCMD_print_error();
return_code = ERROR_FILE_NOT_FOUND;
} else {
WCMD_output_asis(args);
WCMD_output_asis(L"=");
WCMD_output_asis(newValue);
WCMD_output_asis(L"\r\n");
}
}
}
}
RegCloseKey(key);
return WCMD_is_in_context(L".bat") && return_code == NO_ERROR ?
return_code : (errorlevel = return_code);
}
RETURN_CODE WCMD_color(void)
{
RETURN_CODE return_code = ERROR_INVALID_FUNCTION;
CONSOLE_SCREEN_BUFFER_INFO consoleInfo;
HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
if (param1[0] != 0x00 && lstrlenW(param1) > 2)
{
WCMD_output_stderr(WCMD_LoadMessage(WCMD_ARGERR));
}
else if (GetConsoleScreenBufferInfo(hStdOut, &consoleInfo))
{
COORD topLeft = {0, 0};
DWORD screenSize;
DWORD color;
screenSize = consoleInfo.dwSize.X * (consoleInfo.dwSize.Y + 1);
color = wcstoul(param1, NULL, 16);
if (((color & 0xF0) >> 4) != (color & 0x0F))
{
FillConsoleOutputAttribute(hStdOut, color, screenSize, topLeft, &screenSize);
SetConsoleTextAttribute(hStdOut, color);
return_code = NO_ERROR;
}
}
return errorlevel = return_code;
}
RETURN_CODE WCMD_mklink(WCHAR *args)
{
int argno = 0;
WCHAR *argN = args;
BOOL isdir = FALSE;
BOOL junction = FALSE;
BOOL hard = FALSE;
BOOL ret = FALSE;
WCHAR file1[MAX_PATH];
WCHAR file2[MAX_PATH];
file1[0] = 0;
while (argN) {
WCHAR *thisArg = WCMD_parameter (args, argno++, &argN, FALSE, FALSE);
if (!argN) break;
WINE_TRACE("mklink: Processing arg '%s'\n", wine_dbgstr_w(thisArg));
if (lstrcmpiW(thisArg, L"/D") == 0)
isdir = TRUE;
else if (lstrcmpiW(thisArg, L"/H") == 0)
hard = TRUE;
else if (lstrcmpiW(thisArg, L"/J") == 0)
junction = TRUE;
else if (*thisArg == L'/')
{
return errorlevel = ERROR_INVALID_FUNCTION;
}
else
{
if(!file1[0])
lstrcpyW(file1, thisArg);
else
lstrcpyW(file2, thisArg);
}
}
if (*file1 && *file2)
{
if (hard)
ret = CreateHardLinkW(file1, file2, NULL);
else if(!junction)
ret = CreateSymbolicLinkW(file1, file2, isdir);
else
TRACE("Junction links currently not supported.\n");
}
if (ret) return errorlevel = NO_ERROR;
WCMD_output_stderr(WCMD_LoadMessage(WCMD_READFAIL), file1);
return errorlevel = ERROR_INVALID_FUNCTION;
}
RETURN_CODE WCMD_change_drive(WCHAR drive)
{
WCHAR envvar[4];
WCHAR dir[MAX_PATH];
envvar[0] = L'=';
envvar[1] = drive;
envvar[2] = L':';
envvar[3] = L'\0';
if (GetEnvironmentVariableW(envvar, dir, ARRAY_SIZE(dir)) == 0)
wcscpy(dir, envvar + 1);
WINE_TRACE("Got directory for %lc: as %s\n", drive, wine_dbgstr_w(dir));
if (!SetCurrentDirectoryW(dir))
{
WCMD_print_error();
return errorlevel = ERROR_INVALID_FUNCTION;
}
return NO_ERROR;
}