| 124 | } |
| 125 | |
| 126 | int cbm_path_info_utf8(const char *path, cbm_path_info_t *out) { |
| 127 | if (!path || !out) { |
| 128 | return CBM_PATH_INFO_UNAVAILABLE; |
| 129 | } |
| 130 | wchar_t *wpath = cbm_path_to_wide(path); |
| 131 | if (!wpath) { |
| 132 | return CBM_PATH_INFO_UNAVAILABLE; |
| 133 | } |
| 134 | WIN32_FILE_ATTRIBUTE_DATA data; |
| 135 | BOOL ok = GetFileAttributesExW(wpath, GetFileExInfoStandard, &data); |
| 136 | DWORD path_error = ok ? ERROR_SUCCESS : GetLastError(); |
| 137 | free(wpath); |
| 138 | if (!ok) { |
| 139 | return path_error == ERROR_FILE_NOT_FOUND || path_error == ERROR_PATH_NOT_FOUND |
| 140 | ? CBM_PATH_INFO_ABSENT |
| 141 | : CBM_PATH_INFO_UNAVAILABLE; |
| 142 | } |
| 143 | memset(out, 0, sizeof(*out)); |
| 144 | out->is_directory = (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; |
| 145 | out->is_symlink = (data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0; |
| 146 | out->is_regular = !out->is_directory && !out->is_symlink; |
| 147 | /* Compose the 64-bit values arithmetically rather than through |
| 148 | * ULARGE_INTEGER. Writing .LowPart/.HighPart and reading .QuadPart is |
| 149 | * correct -- it is a union -- but cppcheck does not model that aliasing and |
| 150 | * reports all four halves as assigned-but-never-read. This form says the |
| 151 | * same thing without the union, so the checker needs no exception. */ |
| 152 | uint64_t file_size = ((uint64_t)data.nFileSizeHigh << 32) | (uint64_t)data.nFileSizeLow; |
| 153 | out->size = (int64_t)file_size; |
| 154 | uint64_t written = ((uint64_t)data.ftLastWriteTime.dwHighDateTime << 32) | |
| 155 | (uint64_t)data.ftLastWriteTime.dwLowDateTime; |
| 156 | enum { NANOSECONDS_PER_WINDOWS_TICK = 100 }; |
| 157 | const uint64_t windows_to_unix_ticks = UINT64_C(116444736000000000); |
| 158 | out->mtime_ns = |
| 159 | written >= windows_to_unix_ticks |
| 160 | ? (int64_t)((written - windows_to_unix_ticks) * NANOSECONDS_PER_WINDOWS_TICK) |
| 161 | : 0; |
| 162 | return CBM_PATH_INFO_OK; |
| 163 | } |
| 164 | |
| 165 | void cbm_closedir(cbm_dir_t *d) { |
| 166 | if (d) { |