[KERNEL32_APITEST]: Internationalization console tests by Katayama Hirofumi MZ.
authorHermès Bélusca-Maïto <hermes.belusca-maito@reactos.org>
Thu, 4 May 2017 15:39:50 +0000 (15:39 +0000)
committerHermès Bélusca-Maïto <hermes.belusca-maito@reactos.org>
Thu, 4 May 2017 15:39:50 +0000 (15:39 +0000)
Passes on Win2k3 (either are skipped because either Russian or Japanese locales or codepages are absent, or are passed OK), but not on ReactOS yet. They are committed as reference for future work.
CORE-12451

svn path=/trunk/; revision=74475

rostests/apitests/kernel32/CMakeLists.txt
rostests/apitests/kernel32/Console.c [new file with mode: 0644]
rostests/apitests/kernel32/testlist.c

index 4fbde1f..c6e2f48 100644 (file)
@@ -2,6 +2,7 @@
 add_subdirectory(redirptest)
 
 list(APPEND SOURCE
 add_subdirectory(redirptest)
 
 list(APPEND SOURCE
+    Console.c
     DefaultActCtx.c
     DeviceIoControl.c
     dosdev.c
     DefaultActCtx.c
     DeviceIoControl.c
     dosdev.c
diff --git a/rostests/apitests/kernel32/Console.c b/rostests/apitests/kernel32/Console.c
new file mode 100644 (file)
index 0000000..3263291
--- /dev/null
@@ -0,0 +1,476 @@
+/*
+ * PROJECT:         ReactOS api tests
+ * LICENSE:         GPLv2+ - See COPYING in the top level directory
+ * PURPOSE:         Test for i18n console test
+ * PROGRAMMERS:     Katayama Hirofumi MZ
+ */
+
+#include <apitest.h>
+#include <wincon.h>
+#include <winnls.h>
+
+#define okCURSOR(hCon, c) do { \
+  CONSOLE_SCREEN_BUFFER_INFO __sbi; \
+  BOOL expect = GetConsoleScreenBufferInfo((hCon), &__sbi) && \
+                __sbi.dwCursorPosition.X == (c).X && __sbi.dwCursorPosition.Y == (c).Y; \
+  ok(expect, "Expected cursor at (%d,%d), got (%d,%d)\n", \
+     (c).X, (c).Y, __sbi.dwCursorPosition.X, __sbi.dwCursorPosition.Y); \
+} while (0)
+
+#define ATTR \
+    (FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED)
+
+static const WCHAR u0414[] = { 0x0414, 0 };             /* Д */
+static const WCHAR u9580[] = { 0x9580, 0 };             /* 門 */
+static const WCHAR ideograph_space = (WCHAR)0x3000;     /* fullwidth space */
+LCID lcidJapanese = MAKELCID(MAKELANGID(LANG_JAPANESE, SUBLANG_DEFAULT), SORT_DEFAULT);
+LCID lcidRussian  = MAKELCID(MAKELANGID(LANG_RUSSIAN , SUBLANG_DEFAULT), SORT_DEFAULT);
+
+/* Russian Code Page 855 */
+// NOTE that CP 866 can also be used
+static void test_cp855(HANDLE hConOut)
+{
+    BOOL ret;
+    DWORD oldcp;
+    int n;
+    DWORD len;
+    COORD c;
+    CONSOLE_SCREEN_BUFFER_INFO csbi;
+    int count;
+    WCHAR str[32];
+    WORD attr;
+
+    if (!IsValidCodePage(855))
+    {
+        skip("Codepage 855 not available\n");
+        return;
+    }
+
+    /* Set code page */
+    oldcp = GetConsoleOutputCP();
+    SetLastError(0xdeadbeef);
+    ret = SetConsoleOutputCP(855);
+    if (!ret)
+    {
+        skip("SetConsoleOutputCP failed with last error %lu\n", GetLastError());
+        return;
+    }
+
+    /* Get info */
+    ret = GetConsoleScreenBufferInfo(hConOut, &csbi);
+    ok(ret, "GetConsoleScreenBufferInfo failed\n");
+    trace("csbi.dwSize.X:%d, csbi.dwSize.Y:%d\n", csbi.dwSize.X, csbi.dwSize.Y);
+    count = csbi.dwSize.X * 3 / 2;
+    trace("count: %d\n", count);
+
+    /* "\u0414" */
+    {
+        /* Output u0414 "count" times at (0,0) */
+        c.X = c.Y = 0;
+        SetConsoleCursorPosition(hConOut, c);
+        okCURSOR(hConOut, c);
+        for (n = 0; n < count; ++n)
+        {
+            ret = WriteConsoleW(hConOut, u0414, lstrlenW(u0414), &len, NULL);
+            ok(ret && len == lstrlenW(u0414), "WriteConsoleW failed\n");
+        }
+
+        /* Check cursor */
+        len = count;        /* u0414 is normal width in Russian */
+        c.X = (SHORT)(len % csbi.dwSize.X);
+        c.Y = (SHORT)(len / csbi.dwSize.X);
+        okCURSOR(hConOut, c);
+
+        /* Read characters at (0,0) */
+        c.X = c.Y = 0;
+        ret = ReadConsoleOutputCharacterW(hConOut, str, 3 * sizeof(WCHAR), c, &len);
+        ok(ret, "ReadConsoleOutputCharacterW failed\n");
+        ok(len == 6, "len was: %ld\n", len);
+        ok(str[0] == 0x414, "str[0] was: 0x%04X\n", str[0]);
+        ok(str[1] == 0x414, "str[1] was: 0x%04X\n", str[1]);
+        ok(str[2] == 0x414, "str[2] was: 0x%04X\n", str[2]);
+
+        /* Check cursor */
+        c.X = 1;
+        c.Y = 0;
+        ret = SetConsoleCursorPosition(hConOut, c);
+        ok(ret, "SetConsoleCursorPosition failed\n");
+        okCURSOR(hConOut, c);
+
+        /* Fill by space */
+        c.X = c.Y = 0;
+        FillConsoleOutputCharacterW(hConOut, L' ', csbi.dwSize.X * csbi.dwSize.Y, c, &len);
+
+        /* Output u0414 "count" times at (1,0) */
+        c.X = 1;
+        c.Y = 0;
+        SetConsoleCursorPosition(hConOut, c);
+        okCURSOR(hConOut, c);
+        for (n = 0; n < count; ++n)
+        {
+            ret = WriteConsoleW(hConOut, u0414, lstrlenW(u0414), &len, NULL);
+            ok(ret && len == lstrlenW(u0414), "WriteConsoleW failed\n");
+        }
+
+        /* Check cursor */
+        len = 1 + count;
+        c.X = (SHORT)(len % csbi.dwSize.X);
+        c.Y = (SHORT)(len / csbi.dwSize.X);
+        okCURSOR(hConOut, c);
+
+        /* Read characters at (0,0) */
+        c.X = c.Y = 0;
+        ret = ReadConsoleOutputCharacterW(hConOut, str, 3 * sizeof(WCHAR), c, &len);
+        ok(ret, "ReadConsoleOutputCharacterW failed\n");
+        ok(len == 6, "len was: %ld\n", len);
+        ok(str[0] == L' ', "str[0] was: 0x%04X\n", str[0]);
+        ok(str[1] == 0x414, "str[1] was: 0x%04X\n", str[1]);
+        ok(str[2] == 0x414, "str[2] was: 0x%04X\n", str[2]);
+    }
+
+    /* "\u9580" */
+    {
+        /* Output u9580 "count" times at (0,0) */
+        c.X = c.Y = 0;
+        SetConsoleCursorPosition(hConOut, c);
+        okCURSOR(hConOut, c);
+        for (n = 0; n < count; ++n)
+        {
+            ret = WriteConsoleW(hConOut, u9580, lstrlenW(u9580), &len, NULL);
+            ok(ret && len == lstrlenW(u9580), "WriteConsoleW failed\n");
+        }
+
+        /* Check cursor */
+        len = count;        /* u9580 is normal width in Russian */
+        c.X = (SHORT)(len % csbi.dwSize.X);
+        c.Y = (SHORT)(len / csbi.dwSize.X);
+        okCURSOR(hConOut, c);
+
+        /* Check cursor */
+        c.X = 1;
+        c.Y = 0;
+        ret = SetConsoleCursorPosition(hConOut, c);
+        ok(ret, "SetConsoleCursorPosition failed\n");
+        okCURSOR(hConOut, c);
+
+        /* Fill by space */
+        c.X = c.Y = 0;
+        ret = FillConsoleOutputCharacterW(hConOut, L' ', csbi.dwSize.X * csbi.dwSize.Y, c, &len);
+        ok(ret, "FillConsoleOutputCharacterW failed\n");
+        ok(len == csbi.dwSize.X * csbi.dwSize.Y, "len was: %ld\n", len);
+
+        /* Output u9580 "count" times at (1,0) */
+        c.X = 1;
+        c.Y = 0;
+        SetConsoleCursorPosition(hConOut, c);
+        okCURSOR(hConOut, c);
+        for (n = 0; n < count; ++n)
+        {
+            ret = WriteConsoleW(hConOut, u9580, lstrlenW(u9580), &len, NULL);
+            ok(ret && len == lstrlenW(u9580), "WriteConsoleW failed\n");
+        }
+
+        /* Check cursor */
+        len = 1 + count;
+        c.X = (SHORT)(len % csbi.dwSize.X);
+        c.Y = (SHORT)(len / csbi.dwSize.X);
+        okCURSOR(hConOut, c);
+
+        /* Fill by ideograph space */
+        c.X = c.Y = 0;
+        ret = FillConsoleOutputCharacterW(hConOut, ideograph_space, csbi.dwSize.X * csbi.dwSize.Y, c, &len);
+        ok(ret, "FillConsoleOutputCharacterW failed\n");
+        ok(len == csbi.dwSize.X * csbi.dwSize.Y, "len was: %ld\n", len);
+
+        /* Read characters at (0,0) */
+        c.X = c.Y = 0;
+        ret = ReadConsoleOutputCharacterW(hConOut, str, 3 * sizeof(WCHAR), c, &len);
+        ok(ret, "ReadConsoleOutputCharacterW failed\n");
+        ok(len == 6, "len was: %ld\n", len);
+        ok(str[0] == ideograph_space || str[0] == L'?', "str[0] was: 0x%04X\n", str[0]);
+        ok(str[1] == ideograph_space || str[1] == L'?', "str[1] was: 0x%04X\n", str[1]);
+        ok(str[2] == ideograph_space || str[2] == L'?', "str[2] was: 0x%04X\n", str[2]);
+
+        /* Read attr at (0,0) */
+        c.X = c.Y = 0;
+        ret = ReadConsoleOutputAttribute(hConOut, &attr, 1, c, &len);
+        ok(ret, "ReadConsoleOutputAttribute failed\n");
+        ok(attr == ATTR, "attr was: %d\n", attr);
+        ok(len == 1, "len was %ld\n", len);
+
+        /* Read characters at (1,0) */
+        c.X = 1;
+        c.Y = 0;
+        ret = ReadConsoleOutputCharacterW(hConOut, str, 3 * sizeof(WCHAR), c, &len);
+        ok(ret, "ReadConsoleOutputCharacterW failed\n");
+        ok(len == 6, "len was: %ld\n", len);
+        ok(str[0] == ideograph_space || str[0] == L'?', "str[0] was: 0x%04X\n", str[0]);
+        ok(str[1] == ideograph_space || str[1] == L'?', "str[1] was: 0x%04X\n", str[1]);
+        ok(str[2] == ideograph_space || str[2] == L'?', "str[2] was: 0x%04X\n", str[2]);
+
+        /* Output u9580 "count" once at (1,0) */
+        c.X = 1;
+        c.Y = 0;
+        SetConsoleCursorPosition(hConOut, c);
+        okCURSOR(hConOut, c);
+        ret = WriteConsoleW(hConOut, u9580, lstrlenW(u9580), &len, NULL);
+        ok(ret && len == lstrlenW(u9580), "WriteConsoleW failed\n");
+
+        /* Read attr (1,0) */
+        c.X = 1;
+        c.Y = 0;
+        ret = ReadConsoleOutputAttribute(hConOut, &attr, 1, c, &len);
+        ok(ret, "ReadConsoleOutputAttribute failed\n");
+        ok(attr == ATTR, "attr was: %d\n", attr);
+        ok(len == 1, "len was %ld\n", len);
+
+        /* Check cursor */
+        c.X = 2;
+        c.Y = 0;
+        okCURSOR(hConOut, c);
+
+        /* Read characters at (0,0) */
+        c.X = c.Y = 0;
+        ret = ReadConsoleOutputCharacterW(hConOut, str, 3 * sizeof(WCHAR), c, &len);
+        ok(ret, "ReadConsoleOutputCharacterW failed\n");
+        ok(len == 6, "len was: %ld\n", len);
+        ok(str[0] == ideograph_space || str[0] == L'?', "str[0] was: 0x%04X\n", str[0]);
+        ok(str[1] == 0x9580 || str[1] == L'?', "str[1] was: 0x%04X\n", str[1]);
+        ok(str[2] == ideograph_space || str[2] == L'?', "str[2] was: 0x%04X\n", str[2]);
+    }
+
+    /* Restore code page */
+    SetConsoleOutputCP(oldcp);
+}
+
+/* Japanese Code Page 932 */
+static void test_cp932(HANDLE hConOut)
+{
+    BOOL ret;
+    DWORD oldcp;
+    int n;
+    DWORD len;
+    COORD c;
+    CONSOLE_SCREEN_BUFFER_INFO csbi;
+    int count;
+    WCHAR str[32];
+    WORD attr;
+
+    if (!IsValidCodePage(932))
+    {
+        skip("Codepage 932 not available\n");
+        return;
+    }
+
+    /* Set code page */
+    oldcp = GetConsoleOutputCP();
+    SetLastError(0xdeadbeef);
+    ret = SetConsoleOutputCP(932);
+    if (!ret)
+    {
+        skip("SetConsoleOutputCP failed with last error %lu\n", GetLastError());
+        return;
+    }
+
+    /* Get info */
+    ret = GetConsoleScreenBufferInfo(hConOut, &csbi);
+    ok(ret, "GetConsoleScreenBufferInfo failed\n");
+    trace("csbi.dwSize.X:%d, csbi.dwSize.Y:%d\n", csbi.dwSize.X, csbi.dwSize.Y);
+    count = csbi.dwSize.X * 3 / 2;
+    trace("count: %d\n", count);
+
+    /* "\u0414" */
+    {
+        /* Output u0414 "count" times at (0,0) */
+        c.X = c.Y = 0;
+        SetConsoleCursorPosition(hConOut, c);
+        okCURSOR(hConOut, c);
+        for (n = 0; n < count; ++n)
+        {
+            ret = WriteConsoleW(hConOut, u0414, lstrlenW(u0414), &len, NULL);
+            ok(ret && len == lstrlenW(u0414), "WriteConsoleW failed\n");
+        }
+
+        /* Check cursor */
+        GetConsoleScreenBufferInfo(hConOut, &csbi);
+        len = count * 2;     /* u0414 is fullwidth in Japanese */
+        c.X = (SHORT)(len % csbi.dwSize.X);
+        c.Y = (SHORT)(len / csbi.dwSize.X);
+        okCURSOR(hConOut, c);
+
+        /* Read characters at (0,0) */
+        c.X = c.Y = 0;
+        ret = ReadConsoleOutputCharacterW(hConOut, str, 3 * sizeof(WCHAR), c, &len);
+        ok(ret, "ReadConsoleOutputCharacterW failed\n");
+        ok(len == 3, "len was: %ld\n", len);
+        ok(str[0] == 0x414, "str[0] was: 0x%04X\n", str[0]);
+        ok(str[1] == 0x414, "str[1] was: 0x%04X\n", str[1]);
+        ok(str[2] == 0x414, "str[2] was: 0x%04X\n", str[2]);
+
+        /* Check cursor */
+        c.X = 1;
+        c.Y = 0;
+        ret = SetConsoleCursorPosition(hConOut, c);
+        ok(ret, "SetConsoleCursorPosition failed\n");
+        okCURSOR(hConOut, c);
+
+        /* Fill by space */
+        c.X = c.Y = 0;
+        FillConsoleOutputCharacterW(hConOut, L' ', csbi.dwSize.X * csbi.dwSize.Y, c, &len);
+
+        /* Output u0414 "count" times at (1,0) */
+        c.X = 1;
+        c.Y = 0;
+        SetConsoleCursorPosition(hConOut, c);
+        okCURSOR(hConOut, c);
+        for (n = 0; n < count; ++n)
+        {
+            ret = WriteConsoleW(hConOut, u0414, lstrlenW(u0414), &len, NULL);
+            ok(ret && len == lstrlenW(u0414), "WriteConsoleW failed\n");
+        }
+
+        /* Check cursor */
+        len = csbi.dwSize.X + (count - (csbi.dwSize.X - 1) / 2) * 2;
+        c.X = (SHORT)(len % csbi.dwSize.X);
+        c.Y = (SHORT)(len / csbi.dwSize.X);
+        okCURSOR(hConOut, c);
+
+        /* Read characters at (0,0) */
+        c.X = 0;
+        c.Y = 0;
+        ret = ReadConsoleOutputCharacterW(hConOut, str, 3 * sizeof(WCHAR), c, &len);
+        ok(ret, "ReadConsoleOutputCharacterW failed\n");
+        ok(len == 4, "len was: %ld\n", len);
+        ok(str[0] == L' ', "str[0] was: 0x%04X\n", str[0]);
+        ok(str[1] == 0x414, "str[1] was: 0x%04X\n", str[1]);
+        ok(str[2] == 0x414, "str[2] was: 0x%04X\n", str[2]);
+    }
+
+    /* "\u9580" */
+    {
+        /* Output u9580 "count" times at (0,0) */
+        c.X = c.Y = 0;
+        SetConsoleCursorPosition(hConOut, c);
+        okCURSOR(hConOut, c);
+        for (n = 0; n < count; ++n)
+        {
+            ret = WriteConsoleW(hConOut, u9580, lstrlenW(u9580), &len, NULL);
+            ok(ret && len == lstrlenW(u9580), "WriteConsoleW failed\n");
+        }
+
+        /* Check cursor */
+        len = count * 2;     /* u9580 is fullwidth in Japanese */
+        c.X = (SHORT)(len % csbi.dwSize.X);
+        c.Y = (SHORT)(len / csbi.dwSize.X);
+        okCURSOR(hConOut, c);
+
+        /* Check cursor */
+        c.X = 1;
+        c.Y = 0;
+        ret = SetConsoleCursorPosition(hConOut, c);
+        ok(ret, "SetConsoleCursorPosition failed\n");
+        okCURSOR(hConOut, c);
+
+        /* Fill by space */
+        c.X = c.Y = 0;
+        ret = FillConsoleOutputCharacterW(hConOut, L' ', csbi.dwSize.X * csbi.dwSize.Y, c, &len);
+        ok(ret, "FillConsoleOutputCharacterW failed\n");
+        ok(len == csbi.dwSize.X * csbi.dwSize.Y, "len was: %ld\n", len);
+
+        /* Output u9580 "count" times at (1,0) */
+        c.X = 1;
+        c.Y = 0;
+        SetConsoleCursorPosition(hConOut, c);
+        okCURSOR(hConOut, c);
+        for (n = 0; n < count; ++n)
+        {
+            ret = WriteConsoleW(hConOut, u9580, lstrlenW(u9580), &len, NULL);
+            ok(ret && len == lstrlenW(u9580), "WriteConsoleW failed\n");
+        }
+
+        /* Check cursor */
+        len = csbi.dwSize.X + (count - (csbi.dwSize.X - 1) / 2) * 2;
+        c.X = (SHORT)(len % csbi.dwSize.X);
+        c.Y = (SHORT)(len / csbi.dwSize.X);
+        okCURSOR(hConOut, c);
+
+        /* Fill by ideograph space */
+        c.X = c.Y = 0;
+        ret = FillConsoleOutputCharacterW(hConOut, ideograph_space, csbi.dwSize.X * csbi.dwSize.Y, c, &len);
+        ok(ret, "FillConsoleOutputCharacterW failed\n");
+        ok(len == csbi.dwSize.X * csbi.dwSize.Y, "len was: %ld\n", len);
+
+        /* Read characters at (0,0) */
+        c.X = c.Y = 0;
+        ret = ReadConsoleOutputCharacterW(hConOut, str, 3 * sizeof(WCHAR), c, &len);
+        ok(ret, "ReadConsoleOutputCharacterW failed\n");
+        ok(len == 3, "len was: %ld\n", len);
+        ok(str[0] == ideograph_space, "str[0] was: 0x%04X\n", str[0]);
+        ok(str[1] == ideograph_space, "str[1] was: 0x%04X\n", str[1]);
+        ok(str[2] == ideograph_space, "str[2] was: 0x%04X\n", str[2]);
+
+        /* Read attr */
+        ret = ReadConsoleOutputAttribute(hConOut, &attr, 1, c, &len);
+        ok(ret, "ReadConsoleOutputAttribute failed\n");
+        ok(attr == ATTR, "attr was: %d\n", attr);
+        ok(len == 1, "len was %ld\n", len);
+
+        /* Output u9580 "count" once at (1,0) */
+        c.X = 1;
+        c.Y = 0;
+        SetConsoleCursorPosition(hConOut, c);
+        okCURSOR(hConOut, c);
+        ret = WriteConsoleW(hConOut, u9580, lstrlenW(u9580), &len, NULL);
+        ok(ret && len == lstrlenW(u9580), "WriteConsoleW failed\n");
+
+        /* Read attr */
+        ret = ReadConsoleOutputAttribute(hConOut, &attr, 1, c, &len);
+        ok(ret, "ReadConsoleOutputAttribute failed\n");
+        ok(attr == ATTR, "attr was: %d\n", attr);
+        ok(len == 1, "len was %ld\n", len);
+
+        /* Check cursor */
+        c.X = 3;
+        c.Y = 0;
+        okCURSOR(hConOut, c);
+
+        /* Read characters */
+        c.X = c.Y = 0;
+        ret = ReadConsoleOutputCharacterW(hConOut, str, 3 * sizeof(WCHAR), c, &len);
+        ok(ret, "ReadConsoleOutputCharacterW failed\n");
+        ok(len == 4, "len was: %ld\n", len);
+        ok(str[0] == L' ', "str[0] was: 0x%04X\n", str[0]);
+        ok(str[1] == 0x9580, "str[1] was: 0x%04X\n", str[1]);
+        ok(str[2] == L' ', "str[2] was: 0x%04X\n", str[2]);
+    }
+
+    /* Restore code page */
+    SetConsoleOutputCP(oldcp);
+}
+
+START_TEST(Console)
+{
+    HANDLE hConIn, hConOut;
+    FreeConsole();
+    ok(AllocConsole(), "Couldn't alloc console\n");
+
+    hConIn = CreateFileA("CONIN$", GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0);
+    hConOut = CreateFileA("CONOUT$", GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0);
+    ok(hConIn != INVALID_HANDLE_VALUE, "Opening ConIn\n");
+    ok(hConOut != INVALID_HANDLE_VALUE, "Opening ConOut\n");
+
+    if (IsValidLocale(lcidRussian, LCID_INSTALLED))
+        test_cp855(hConOut);
+    else
+        skip("Russian locale is not installed\n");
+
+    if (IsValidLocale(lcidJapanese, LCID_INSTALLED))
+        test_cp932(hConOut);
+    else
+        skip("Japanese locale is not installed\n");
+
+    CloseHandle(hConIn);
+    CloseHandle(hConOut);
+    FreeConsole();
+    ok(AllocConsole(), "Couldn't alloc console\n");
+}
index f1beea8..69131f5 100644 (file)
@@ -3,6 +3,7 @@
 #define STANDALONE
 #include <apitest.h>
 
 #define STANDALONE
 #include <apitest.h>
 
+extern void func_Console(void);
 extern void func_DefaultActCtx(void);
 extern void func_DeviceIoControl(void);
 extern void func_dosdev(void);
 extern void func_DefaultActCtx(void);
 extern void func_DeviceIoControl(void);
 extern void func_dosdev(void);
@@ -28,6 +29,7 @@ extern void func_WideCharToMultiByte(void);
 
 const struct test winetest_testlist[] =
 {
 
 const struct test winetest_testlist[] =
 {
+    { "Console",                     func_Console },
     { "DefaultActCtx",               func_DefaultActCtx },
     { "DeviceIoControl",             func_DeviceIoControl },
     { "dosdev",                      func_dosdev },
     { "DefaultActCtx",               func_DefaultActCtx },
     { "DeviceIoControl",             func_DeviceIoControl },
     { "dosdev",                      func_dosdev },