diff --git a/clar.py b/clar.py index 4d10d85..1c33607 100644 --- a/clar.py +++ b/clar.py @@ -338,7 +338,7 @@ def _process_categories(self, suite_name, contents): "clar_print_tap.c" : r"""eJyNVE1vnDAQPcOvmGWFBAiQot6yaqr2HFU9tLdKyAGzscLayDbbVlX+e8cDJPbuJtsTzPObmTcfdmwss6KFoxIdtAPTzaiFtI2Qwmb4A5Yb27RqkrYEZ5tJWL4CrZLGQvvINBTzgWQHbvL4bxxlLmT+6r5bIY94gq08ktBnyffP3+DItRFKws2HnzLJd/FzHL8h2TxOtlO/5HXZDuBaKz0D/yM3xDznXRxHoodsEwSMXmrYwsiM4R2wYYC0I2GZybGY0hOJhUV8MDxw7JkY0BGd2EHJ/am3l7BEvyiMtoa5qeu0O8/2dhspLPVQTod1xMbqqbUzjQhQ0MdrHbJdL9a8AFVVzSPzMJy5YXsOt5Ca1yKqu7mWg9mHdMNx/ML+uaVenEWj0QCcRSM8pLri4QLV4SGzx6ZfYjo8ZA5CrszOZzq8wXY8cJ2v67Ecddy0WozWbfTmI3z9cX/vLwuARzgV4B3lYafrur52OZSk1fEvLO2Du4bzhZhNUj0D8/rRhNdUqXFLWC3CUPiyop8gkcqCekqwGQl+3Jkf8MXEdHFE8kmc5qPSy86Z7EoFNNbs8pvj33IhO/470L2FoihQNWTbtMudQY313X3X92WwB5QcyMC9Ld0QKOeRNYPAI6b3445MjIQOzi5hWfF+UWbwxZrwRUq+YCMBfzdAO348JVAKFyKfY3LZZYv5HP8D5Mbj9w==""", "clar_sandbox.c" : r"""eJydVWtP4kAU/dz+iism0gpKfWQ3G9YPm+gasioEMJgomdR2KhPplMwM7KLxv++dTqEP0DVrTKjcO+eec+6cKpWvWADBxBdAgqkvyMxXk/tT79uXcdu2pSkzrmwmycKfspCoeJY2OUHCpTJH9/UXrv1qW4PhjyEZglR42mIROBrC0eUm7Enlws4ZeK5tWYKqueDgrfp2BqQzOO/08cChVCROQupW+7Jnxw8CKmWGOiLdXy6cadi2/VbiHDFe5JsyfZxHERVNkOyFEgVTyp8M9V0W8ZBGQEadm5Nj28pwjMqse4EGBcmcKziD03alx+BTvkCjhLwfYw8aYtWG1z3UVWuCfko/Lszn7eCi3+t3f3auLmo2WG8oEaxsEtN6o0SAwxDHawOD7/n4NjQazE3hK7Ox+YkqfHDWRNgYjbGMyfilNlWfUozPqZ6SVjbXq1vNCJQpeDBbOivvsNRcOaehC0uyrDcbf22rtQ+dCNSE6m4mEh5TtC1MqOR19NNfgs+XasL4UxOUWIJKYC4ptHA+7Lfsd0jVdL2W8arSMsUSswIxJLVLp5Ia6EuqhjSe9TSocz7q9s9dc6wJBq5y+XYpD1lkdA0nTIJcSkXjtaApe6YooKRFiw/mQqTCmaCBSrD4gbjDd5UdfiRr9efBUTEAi4SFkEZ6zqXPw8fkj6O/S2OqCRTy7o11gOoPXj1XjVcDI1FMRDBBFcgSaRYMiSQRcQGsmkL0k01DklEwStc8CrdXF4jy2TRNTi3F09bcpT81nbZ1ZFcvjXLAcw4m3klUpOVigIpvHu2WbSEYTkO/8aEsoqr+FXD1PBExLu2FpnT1onvdQecOMKm/fRGCnPpyQmW65EKUrY0oaxF5iKv7YNk+HtJ9WFalBPVWfR219SIqGFrZARyN9RsX+82gcr3RyMH0PVpdu7wLGpppM1/ONmdxDDZllgF6xjgNHUKuOzeXo5NjQtyMXPyMkZmVjqLMm9urq4296P74Wd+34la9r5638S9EH8BkF0enKytPJfKf92ML7v8QWb1i8NQn5a5XmOe6HKEU4fMhhr29banbngCNYpJdJLrVixK9v7GvgW8=""", "clar_fixtures.c" : r"""eJyFUV1LwzAUfW5+xZU9rLUVJ4ggZQ9DFAUfZEwQSglZmrBAl5Qkk6n43236tWbKfMvNOfecc+81llhBgSppLNAN0XCOuNjbnWa4InYTjpE1MSzxuD1Vki2L0BcKTKfn0EYgu57d3uRpjYhPhi1opSwumUwRCvo3zMFYXT9C5xA5stWSVh9hI5FAa+wUFG//osgJCA5tmQ1SF3CVw9kcppfTCAWBj8ZxDg3UN4/zZ7MaHBrHSBw7vpcJ4mGS5Ijtai9qnannNqk1q7myXU+KvhGaCF4wDnfPiyV+eHpbvS7v8cti9YjGq6Yl7lzCkxfo1L0j/lJOwOtrUrwrUcDBBRsii7Xan3bjBlNVL2WUzuMkgGlJdLuIP21oyYjcVf/a6G3ozXTQPRqmsZkwWQiOfgAVGffP""", -"clar_fs.c" : r"""eJylVdtu20YQfSa/YkAD8TKWY8dJX6L0wXDEVqgsBhINN7UFhiGX1qIkl9hd+dLG/57ZCynJUWEkfZE0s7NnZufMGe2xsqAlpJfj6ZsT399DgzUUojhKo8npb3Mg+ud8PBlNE/hq/NP4LJ5G49n5aTKOp71zNJvFs4vx06DzPz6MZ6HvS5UplkO+zAS89EtWUd7KtM3UkuS8kcqdGE/o/+t71tYm/ArTi8lk6HuS/UNTBRVtbtRyAGzo+x4rgaQ2zMaFvucJqlaicdd8z15AHKkE/rbxIQI6+DqrKp4TF3YAJ2GH/AxwTeu8fTBRA0jtl0Xp0K+sucAsx9suzPPauX2v5AIIMxYweO9AhnBwwELAbvTFXLGFrmf/aF+X4/Uu2L++3scEjwjmitRnQ/+x7/0tZ0XXecIaBTUv6AC22i/5SuRPnQWVynAy/z3CSYg/zpPZxVkCJQLp4m2YvYqVbJHrEHU7bJgG+y7IZNBQf1HBz2nNxQN5oeEHoDnnJdlOHYa2aa18dRetmlxziI8ZOl8bCV5ruk3u3ptw9OlUnaeMquxGorOfd/OcKs2kpEKlBFuMibHUuKUCm8gbW1aoOTge4HFwyZqC30l4EgdlhmYR+J4tVVBK1q0wpnv0U4JkKmqygxTDQEdfFKcfRpNRMsKx6zgzM7oLL+c4oz9A80aSs/jjp40U6bpmA46t0vgVzZpVS7TLApg3lOwe55A6ivMqE04hwcsgtCB7tJK0KxdH0pdLWlUpXylii3IVZuLm9mphsPXg6gsrqeXECtwH+Kl7jF96sLj4m6z1i773cGw1VLYCb5dEqoIKodnzgvmDVLQGtLl4B5/t7c+Q40ZwFL66bgLNmUfvmSKHr0Onsg5eT4LFp/c0vyWm1uPFwBTdBd9lTGGwvjCAF7b+Ad4b9mq9HP05TubJaXIxJ/b8f3DZU2lNU9Ivi+G2VNcL1dopLh3dt17IuC0LpHVDwuvA9TLtT21LrHm1EXlo9ly/s/4rwC5C1z00g6MvrDnK22DovCYoOJz1jpPFpsaN6412udkJndTNwdtF/zdiFF6vpMJxlNKIfD12hjQj7MiwD4qD7jkovbfcSEvtlVlTfOH3uxX+rKg3NL3B0dvFrh6I+rselNtN6F68oxk/+2araVBLuv3SZ6RvZL5q3BVi9r52bTgeUfZNwUr/G9kaoSs=""", +"clar_fs.c" : r"""eJzdWW1v20YM/mz9Ci4BWjlznPRlQLEsGNzYaQ0kTuE484a20C7SKb5VlgTdyU62dr995J0k681J13UftiJoozvyyCMfPqTUXeF73AdnPp48e2pZu9mjcz6evJo/e+o4lnWwB7MFlxwkd9NEqLt9Hi5Y6HIP/DR0lYhCCSzhEEYK2IqJgF0H3II9ECGci/DVvAcygt9SqSDFY9SCw4qFIggYRCGXsHdg7aJVEXJYu9KN7xxpsx5c98DtZiu2zbo9sN1utyLKVF2UqZKotctDT/iANyjdiOxZUjElXPRQgeVLx/EiFSX4F/7Y85PXgynsOSpyF9z90LX+sDoJV2kSQr729vA9HB/D4/5jePTI6nTsYuOJ2Xh3+Bg+fsQdaGxpnc1JTwuFbvfI+lS4toqER64lS08kzoIHMU8K19YyShOXa9fM2nXq+zx5ez742XkzmL1+f2R1Xg8mw7MRYLQ8BxPmBRwXdaKd0/Fk6AwHs8HcbHtMMdykcJiDnDjBMN86AQ+PLKuDEbzkmL5YZ8+IAB4JLosxMCapKHujFhTeTpFHI9qD3K8eFL4fGTGdw6bY2c67dzsk0/AHjinT+Eum1c0cPMWLnIpEqlMRIBg59xCWsBaB57LEAxXBkil3Acs0UCJGEaH4UhbubvVjT7tRiiI6ULE1L9QeFcEkFTdwmJQ8UfZ48tPgbDx0TEYcfLgawTfH5dToW3gRYEIblzmgpwm/NTe7ESssmmvmfoC+zkG/TzjbAx6qROAWUyZL/EaEoQhv9BU7WAd2HemFu32Xjp6wJcey6XQ6bhQqEaacnKonE75tYmQTMdhv220zpM8mr07HGJPBbDYdv7yajZzheDo6mV1Mf4FHJT1vTYoDhVe8ThWX2s16eRSA6HR4gFRDwaRojn0kL9XTYfkWa0UoRAHS0TUHN+DIXR7+6keIY48HXBUxa3NvOhoMLyZnn+FdCQBYPFWBDWa2H4IW/txivGuiV7IwJMd5GY5dHYdPFv2sF4Qcu4yjuV1CXwW6eUGNQmlKGwG1RvpPXZdL6adBcAdutIzJoKdjysN0yRNGrUAHbuPWaDq9mDqTC+f8Yjpy6DaXRHevuDpjUo2SJErswuBJEGX9gZyBrNroQHJc79r1kkG1SbQ2TpImooG7iPA7EBL4MlZ3PUj4MlpRvdecm+r1Ya4xtwtu2sLENtYFwsZdsARJuMHBmXaFhIfzi+kQS1IlMnMXmyn2nThVEDOkS3Tzana6/6IPJ1G4QrfITySrtfC4tsRcxRNpUYUjRHULXQtUpNvOMRrRWsLgzbh2t3NiuZd3is+iOZ50ggfZJ28cNPWip8F5/tLB5Dg5NdEFLs1OdjHzsP+kR/1zjI0UKRJBObk6OwP0aCnCTcLxz7qslUegSO0wQlXymEgX+K2Q6kcqS5wZegSuBVvRAAHrKPlAt0cmpHN13EDjpV4/mzZi6TLNL1KtGI02fQqVpOniucY9vFNo1Bmm1LwKhslzSjcT1HN8dLUPl+hrXKacgoa3M0ph937uyGNt5B/miXaaKIEdOaKJdzeK77bPHj0oFjwuVXUUceqV0NOrJPh1RpTMQKXJ5BZqs0u5feXQ/ox5pEW0PJM0HajMJU4JnRUHyMGqeR29uvG6WMV07ZJVw9lxuuqwbHBCKOgZ2YMYJC9DSQM6PRhf+41RqDUEnzcOFar/dCSitpBwZB7tqWLJDd6pIPl+jfWMaIXRTSSJtbr/kRkri52Zs9qAXp212iTa561OE4qZkRqiGiYa+//eQFdjnTKWSvA2A16VKE9QswnATTXNplejjOz+twNRO4u3zS34ylxerHF4Y5TBLt1gbzPbZPDTragHGinZuPMF9N4yIZmPC7UZCWOy/JIpqU4YX3FMemgK+jpGNZSrJjdLjbHrhD4xlDhex0ZPXzx0DZLK6Xto0mrh7/q4hRRePrHpSEbhTUc2yNniRtHZ7hv30P7moG4xnxmj2qA8AhbHHOspwIozQ3jMEFORn0cJgWXczFj/CtFU/byAEqiTFac+QjeAe9viur0n/u2m2Nk06fXWOeEemW3NYftLVjWDIV9vz+JnQOT4nvQaen5wPq9AzGrrG0Wwi/kqaxht/aIqvOkU6ImmUdQhCwFnYRrbtKSp0rwTYk+kT2AOocBGIZboX3tAo5I5ZFfb3nxztCR6GThRqmxDvxkXY1BXb9/rs2nOJYWUeDQWnoNIFR4lA/9BbGHkP9hFNdDaD3Bo8O7HCWr7tlQeTxLNDDuXd1K/eFF/+R5+Ndq/gsuCAHyGQfD670IDHMynsveflDOhTR7n5/Nb7q5s7esh9gX6JRdeM6FQmBQQ5sb/HuodFV9Q56Ofx7PL2WB2dWmb/X/QtYqmZR61S99RZzLPRgUfpfidYwAz1GTvBVmtI1+pxMO0bkb2zkbQbNNsXXvdzPpjSXIfsg+8B48JktsE4JCOyKKHjzsH1yI8cOOdo2xVC+3sT4sF+kIMxV300jNayrDTpSjkG89pg4bdrOPQZy6Eo5SOTT4VsNNJI2jsnOrsE6FRzPHNHqObQVrSqkQauI5ud6yOvrufcL6JxYMfSUrfSEo5ev6+LQbJshEDvxqE/MYtwfjSO5uaNi2yfNMHSl+XeRpmKrbpjbhUWviU/feD9Rfp9RR5""", "clar_categorize.c" : r"""eJydVV1v0zAUfU5+xV3RKudj0/ZcOqkqAyGVIU3bA9omy01dsJQlJXYRY+K/c32ddE6WtMBLGzv365x77s2blVyrQsJ8Mbvm89nN5YfP11/4u8v3s9vFDYzwrdjmZhSG5mkj8QTaVNvMwHMYZGWhDWTfRAVxXIhHqSdhoAq8KreFqZ9FnpfZJPwNWS4qngkjv5bVE8+VRotQG2FU1vMOuH+nfkkuC7HM5erFiTK1HFVBvqwnXGx/U/BLRoMofHZVKqx2XVbAFEzhbAIK3oL1OLlwWCBJVBQGgVoDQwIyoWX2uGEYIq3tCP+deohgihGsbVBJs60KOMfY9eMZErGr/0epVh0AYrXipvwvEFhYPxm1D9rZolwhSCJ5eBDhYlojoY5FtsGBdwHJFM6x/uaS8CJZrKWCqJJkzSx+RgjjyHNI/RwQg8bOlutWjCjCJMiSn+fOKzRJHjAt4jnApdML65BF74ixYebHQ9ojGl2Ev0rOESazELkBvVVGckLQdLCeJPKk1xDTn6b6aj+rzBbFhLxbfg22d4gja94Vt1cOXiSJ1QbZYEi0cnWgnE8bFnC4LXoYj4lHOji3/lJImT5Ldsz4p9nHKz6fd9iiUSKMTjGiYcgdl8RHPUIn4M2fSJeHOrGpMHHTwJYaNhVuvp/+CgiD77qsDGuEO6SDU6dlxD5o4RqNFn0KT1/j723S/ui7pUQQ12x0rI/1fTFKwSFLh/13y6rboM4K0U6XHfqGdOvGyteqzsRu1xztR2OB/KOmKSplxtWwk+nLlhv4OljJ7hnxAzNkITUD4qedAKGFoylc3S4WE7AnNyDDu3lPGRQt6nxH2h+SPx6FmFM=""", "clar.h" : r"""eJy9VV1vmzAUfS6/wg17gAi1TR+7tlIUJWukqJvaVNueLGNMsQaG2mbNNO2/7xpICF/Juoe+JOZe33uOOecam4ciYCHCeLaaPuD1/HGN7zC2bAhywTpxy+aCxnnA0LXSQcz9s+jWsn6mPEA0JhJjohST2rFOuNCIpiLgmqfCs05grSASEYnGIY+ZV26JAaWVZVKmshULmKKSZ1UvU6iiNI8DTPxUavdjDwfMXnISY+Xs9/GGH6BpJwCNh/pyxxT0FfV12bbBimlMY0ZEnjlFzBlXj275PHY9VC7SjLzkrKaAQ9UoNW1tHhr5CpEWy2/rp4c5/jJd31n7HEwp3+hcMqepQhHDgiQNlCqsiAj8dPOWki27AyU2A0uE1s5gsxVe3uPZdD3/9PnhuwML17LOx2MLjdG0eN8gOUoIlalCr1xHiG2ymFOuUeETlDClyDOD/ee7pkApyZXGGSiGHSiQHjIOcpsmLTIuur1BFx44fbFczTE2q9XyvliNFrmgBQFK4hiFBHwbXKERsuueHpq4HWCz8zjw9SDufJMxqlmAwgYBnRYcjjCobHoU/nT43IAv4b0aYK6QSDXSMmd9uFutZhGjP/5DJ2rq3kmoC7eL/M5K9VF4B6Eujg2VSP9xnCpKfRN2/7Ra9Y9Cq2j/nXeKqqPvKppuLrcPm+7YOWq71QhdC3ZI1V5plx08S7GlXdF7kkUqqTERdIPL8vyVSMHFc5t9QaDHJ0PuWDO4hsthOBv1XxYV0lu6fi1LUJBL86cNCNswmhtXXY16PLf+lcHhSMt57dO1Pttq4qnLJqVdDpKu50Da2zHcERw96oJXwlVCNI2KYVAT+IU5MsvLgQtz912feLwfmDuQBGDeC2zzGoQfBvEdf+L5QyCnp5B2PfPXD+TXQP5hoMzJJl52uTdJDkRcdHODHAjvSWRUTJiO0gD0M7SIkaoU6cNvttFMCryf+WNtP+Z/AaQwXp0=""" } diff --git a/clar_fs.c b/clar_fs.c index 3a6c940..f37c44e 100644 --- a/clar_fs.c +++ b/clar_fs.c @@ -1,68 +1,197 @@ #ifdef _WIN32 -#define FOF_FLAGS (FOF_SILENT | FOF_NOCONFIRMATION | FOF_NOERRORUI | FOF_NOCONFIRMMKDIR) +#ifdef __MINGW32__ -static char * -fileops_path(const char *_path) -{ - char *path = NULL; - size_t length, i; - - if (_path == NULL) - return NULL; - - length = strlen(_path); - path = malloc(length + 2); +/* These security-enhanced functions are not available + * in MinGW, so just use the vanilla ones */ +#define wcscpy_s(a, b, c) wcscpy((a), (c)) +#define wcscat_s(a, b, c) wcscat((a), (c)) - if (path == NULL) - return NULL; - - memcpy(path, _path, length); - path[length] = 0; - path[length + 1] = 0; - - for (i = 0; i < length; ++i) { - if (path[i] == '/') - path[i] = '\\'; - } +#endif /* __MINGW32__ */ - return path; +static int +fs__dotordotdot(WCHAR *_tocheck) +{ + return _tocheck[0] == '.' && + (_tocheck[1] == '\0' || + (_tocheck[1] == '.' && _tocheck[2] == '\0')); } static void -fileops(int mode, const char *_source, const char *_dest) +fs_rmdir_helper(WCHAR *_wsource) { - SHFILEOPSTRUCT fops; - - char *source = fileops_path(_source); - char *dest = fileops_path(_dest); - - ZeroMemory(&fops, sizeof(SHFILEOPSTRUCT)); + WCHAR buffer[MAX_PATH]; + HANDLE find_handle; + WIN32_FIND_DATAW find_data; + int buffer_prefix_len; + + /* Set up the buffer and capture the length */ + wcscpy_s(buffer, MAX_PATH, _wsource); + wcscat_s(buffer, MAX_PATH, L"\\"); + buffer_prefix_len = wcslen(buffer); + + /* FindFirstFile needs a wildcard to match multiple items */ + wcscat_s(buffer, MAX_PATH, L"*"); + find_handle = FindFirstFileW(buffer, &find_data); + cl_assert(INVALID_HANDLE_VALUE != find_handle); + + do { + /* FindFirstFile/FindNextFile gives back . and .. + * entries at the beginning */ + if (fs__dotordotdot(find_data.cFileName)) + continue; + + wcscpy_s(buffer + buffer_prefix_len, MAX_PATH - buffer_prefix_len, find_data.cFileName); + + if (FILE_ATTRIBUTE_DIRECTORY & find_data.dwFileAttributes) + fs_rmdir_helper(buffer); + else { + /* If set, the +R bit must be cleared before deleting */ + if (FILE_ATTRIBUTE_READONLY & find_data.dwFileAttributes) + cl_assert(SetFileAttributesW(buffer, find_data.dwFileAttributes & ~FILE_ATTRIBUTE_READONLY)); + + cl_assert(DeleteFileW(buffer)); + } + } + while (FindNextFileW(find_handle, &find_data)); - fops.wFunc = mode; - fops.pFrom = source; - fops.pTo = dest; - fops.fFlags = FOF_FLAGS; + /* Ensure that we successfully completed the enumeration */ + cl_assert(ERROR_NO_MORE_FILES == GetLastError()); - cl_assert_( - SHFileOperation(&fops) == 0, - "Windows SHFileOperation failed" - ); + /* Close the find handle */ + FindClose(find_handle); - free(source); - free(dest); + /* Now that the directory is empty, remove it */ + cl_assert(RemoveDirectoryW(_wsource)); } static void fs_rm(const char *_source) { - fileops(FO_DELETE, _source, NULL); + WCHAR wsource[MAX_PATH]; + DWORD attrs; + + /* The input path is UTF-8. Convert it to wide characters + * for use with the Windows API */ + cl_assert(MultiByteToWideChar(CP_UTF8, + MB_ERR_INVALID_CHARS, + _source, + -1, /* Indicates NULL termination */ + wsource, + MAX_PATH)); + + /* Does the item exist? If not, we have no work to do */ + attrs = GetFileAttributesW(wsource); + + if (INVALID_FILE_ATTRIBUTES == attrs) + return; + + if (FILE_ATTRIBUTE_DIRECTORY & attrs) + fs_rmdir_helper(wsource); + else { + /* The item is a file. Strip the +R bit */ + if (FILE_ATTRIBUTE_READONLY & attrs) + cl_assert(SetFileAttributesW(wsource, attrs & ~FILE_ATTRIBUTE_READONLY)); + + cl_assert(DeleteFileW(wsource)); + } +} + +static void +fs_copydir_helper(WCHAR *_wsource, WCHAR *_wdest) +{ + WCHAR buf_source[MAX_PATH], buf_dest[MAX_PATH]; + HANDLE find_handle; + WIN32_FIND_DATAW find_data; + int buf_source_prefix_len, buf_dest_prefix_len; + + wcscpy_s(buf_source, MAX_PATH, _wsource); + wcscat_s(buf_source, MAX_PATH, L"\\"); + buf_source_prefix_len = wcslen(buf_source); + + wcscpy_s(buf_dest, MAX_PATH, _wdest); + wcscat_s(buf_dest, MAX_PATH, L"\\"); + buf_dest_prefix_len = wcslen(buf_dest); + + /* Get an enumerator for the items in the source. */ + wcscat_s(buf_source, MAX_PATH, L"*"); + find_handle = FindFirstFileW(buf_source, &find_data); + cl_assert(INVALID_HANDLE_VALUE != find_handle); + + /* Create the target directory. */ + cl_assert(CreateDirectoryW(_wdest, NULL)); + + do { + /* FindFirstFile/FindNextFile gives back . and .. + * entries at the beginning */ + if (fs__dotordotdot(find_data.cFileName)) + continue; + + wcscpy_s(buf_source + buf_source_prefix_len, MAX_PATH - buf_source_prefix_len, find_data.cFileName); + wcscpy_s(buf_dest + buf_dest_prefix_len, MAX_PATH - buf_dest_prefix_len, find_data.cFileName); + + if (FILE_ATTRIBUTE_DIRECTORY & find_data.dwFileAttributes) + fs_copydir_helper(buf_source, buf_dest); + else + cl_assert(CopyFileW(buf_source, buf_dest, TRUE)); + } + while (FindNextFileW(find_handle, &find_data)); + + /* Ensure that we successfully completed the enumeration */ + cl_assert(ERROR_NO_MORE_FILES == GetLastError()); + + /* Close the find handle */ + FindClose(find_handle); } static void fs_copy(const char *_source, const char *_dest) { - fileops(FO_COPY, _source, _dest); + WCHAR wsource[MAX_PATH], wdest[MAX_PATH]; + DWORD source_attrs, dest_attrs; + HANDLE find_handle; + WIN32_FIND_DATAW find_data; + + /* The input paths are UTF-8. Convert them to wide characters + * for use with the Windows API. */ + cl_assert(MultiByteToWideChar(CP_UTF8, + MB_ERR_INVALID_CHARS, + _source, + -1, + wsource, + MAX_PATH)); + + cl_assert(MultiByteToWideChar(CP_UTF8, + MB_ERR_INVALID_CHARS, + _dest, + -1, + wdest, + MAX_PATH)); + + /* Check the source for existence */ + source_attrs = GetFileAttributesW(wsource); + cl_assert(INVALID_FILE_ATTRIBUTES != source_attrs); + + /* Check the target for existence */ + dest_attrs = GetFileAttributesW(wdest); + + if (INVALID_FILE_ATTRIBUTES != dest_attrs) { + /* Target exists; append last path part of source to target. + * Use FindFirstFile to parse the path */ + find_handle = FindFirstFileW(wsource, &find_data); + cl_assert(INVALID_HANDLE_VALUE != find_handle); + wcscat_s(wdest, MAX_PATH, L"\\"); + wcscat_s(wdest, MAX_PATH, find_data.cFileName); + FindClose(find_handle); + + /* Check the new target for existence */ + cl_assert(INVALID_FILE_ATTRIBUTES == GetFileAttributesW(wdest)); + } + + if (FILE_ATTRIBUTE_DIRECTORY & source_attrs) + fs_copydir_helper(wsource, wdest); + else + cl_assert(CopyFileW(wsource, wdest, TRUE)); } void