int aws_directory_traverse()

in source/windows/file.c [213:375]


int aws_directory_traverse(
    struct aws_allocator *allocator,
    const struct aws_string *path,
    bool recursive,
    aws_on_directory_entry *on_entry,
    void *user_data) {
    struct aws_wstring *w_path_wchar = aws_string_convert_to_wstring(allocator, path);
    struct aws_wstring *long_path_wchar = s_to_long_path(allocator, w_path_wchar);
    aws_wstring_destroy(w_path_wchar);

    /* windows doesn't fail in FindFirstFile if it's not a directory. Do the check here. We don't call the perfectly
       good function for this check because the string is already converted to utf-16 and it's trivial to reuse it. */
    DWORD attributes = GetFileAttributesW(aws_wstring_c_str(long_path_wchar));
    if (!(attributes & FILE_ATTRIBUTE_DIRECTORY)) {
        aws_wstring_destroy(long_path_wchar);
        return aws_raise_error(AWS_ERROR_FILE_INVALID_PATH);
    }

    WIN32_FIND_DATAW ffd;
    HANDLE find_handle = FindFirstFileW(aws_wstring_c_str(long_path_wchar), &ffd);

    if (find_handle == INVALID_HANDLE_VALUE) {
        aws_wstring_destroy(long_path_wchar);

        int error = GetLastError();

        if (error == ERROR_FILE_NOT_FOUND) {
            return aws_raise_error(AWS_ERROR_FILE_INVALID_PATH);
        }

        return aws_raise_error(AWS_ERROR_UNKNOWN);
    }

    FindClose(find_handle);

    /* create search path string */
    struct aws_byte_cursor path_wchar_cur =
        aws_byte_cursor_from_array(aws_wstring_c_str(long_path_wchar), aws_wstring_size_bytes(long_path_wchar));
    struct aws_byte_buf search_wchar_buf;
    aws_byte_buf_init_copy_from_cursor(&search_wchar_buf, allocator, path_wchar_cur);

    wchar_t search_wchar_pattern[] = L"\\*";
    struct aws_byte_cursor search_char_wchar =
        aws_byte_cursor_from_array((uint8_t *)search_wchar_pattern, sizeof(search_wchar_pattern));

    aws_byte_buf_append_dynamic(&search_wchar_buf, &search_char_wchar);
    struct aws_byte_cursor search_wchar_cur = aws_byte_cursor_from_buf(&search_wchar_buf);
    /* it's already converted to wide string */
    struct aws_wstring *search_wchar_string = aws_wstring_new_from_cursor(allocator, &search_wchar_cur);

    find_handle = FindFirstFileW(aws_wstring_c_str(search_wchar_string), &ffd);
    aws_wstring_destroy(search_wchar_string);
    aws_byte_buf_clean_up(&search_wchar_buf);

    int ret_val = AWS_OP_SUCCESS;

    /* iterate each entry in the directory. Do a bunch of utf-16 conversions. Figure out the paths etc....
       invoke the visitor, and continue recursing if the flag was set. */
    do {
        struct aws_string *name_component_multi_char_str =
            aws_string_convert_from_wchar_c_str(allocator, ffd.cFileName);
        struct aws_byte_cursor name_component_multi_char = aws_byte_cursor_from_string(name_component_multi_char_str);

        /* disgard . and .. */
        char *ascend_mark = "..";
        char *cd_mark = ".";
        struct aws_byte_cursor ascend_mark_cur = aws_byte_cursor_from_c_str(ascend_mark);
        struct aws_byte_cursor cd_mark_cur = aws_byte_cursor_from_c_str(cd_mark);
        if (aws_byte_cursor_eq(&name_component_multi_char, &ascend_mark_cur) ||
            aws_byte_cursor_eq(&name_component_multi_char, &cd_mark_cur)) {
            aws_string_destroy(name_component_multi_char_str);
            continue;
        }

        /* get the relative path as utf-16, so we can talk to windows. */
        struct aws_byte_buf relative_path_wchar;
        aws_byte_buf_init_copy_from_cursor(&relative_path_wchar, allocator, path_wchar_cur);

        wchar_t unicode_delim[] = L"\\";
        struct aws_byte_cursor delimiter_cur =
            aws_byte_cursor_from_array((uint8_t *)unicode_delim, sizeof(unicode_delim) - 2);
        aws_byte_buf_append_dynamic(&relative_path_wchar, &delimiter_cur);
        struct aws_byte_cursor name_str =
            aws_byte_cursor_from_array(ffd.cFileName, wcsnlen(ffd.cFileName, sizeof(ffd.cFileName)) * sizeof(wchar_t));
        aws_byte_buf_append_dynamic(&relative_path_wchar, &name_str);
        aws_byte_buf_append_byte_dynamic(&relative_path_wchar, 0);
        aws_byte_buf_append_byte_dynamic(&relative_path_wchar, 0);

        relative_path_wchar.len -= 2;

        /* now get the absolute path from the relative path we just computed. */
        DWORD path_res = GetFullPathNameW((wchar_t *)relative_path_wchar.buffer, 0, NULL, NULL);

        AWS_FATAL_ASSERT(path_res > 0);
        struct aws_byte_buf full_path_wchar_buf;
        aws_byte_buf_init(&full_path_wchar_buf, allocator, (size_t)path_res * sizeof(wchar_t) + 2);

        full_path_wchar_buf.len = full_path_wchar_buf.capacity - 2;
        path_res = GetFullPathNameW(
            (wchar_t *)relative_path_wchar.buffer, (DWORD)path_res + 1, (wchar_t *)full_path_wchar_buf.buffer, NULL);
        AWS_FATAL_ASSERT(path_res > 0);

        aws_byte_buf_append_byte_dynamic(&full_path_wchar_buf, 0);
        aws_byte_buf_append_byte_dynamic(&full_path_wchar_buf, 0);

        /* now we have the data, convert the utf-16 strings we used to communicate with windows back to
           utf-8 for the user to actually consume. */
        struct aws_string *full_path_name_multi_char =
            aws_string_convert_from_wchar_c_str(allocator, (wchar_t *)full_path_wchar_buf.buffer);
        aws_byte_buf_clean_up(&full_path_wchar_buf);

        struct aws_string *relative_path_multi_char =
            aws_string_convert_from_wchar_c_str(allocator, (wchar_t *)relative_path_wchar.buffer);

        struct aws_directory_entry entry;
        AWS_ZERO_STRUCT(entry);
        entry.relative_path = aws_byte_cursor_from_string(relative_path_multi_char);
        entry.path = aws_byte_cursor_from_string(full_path_name_multi_char);

        LARGE_INTEGER file_size;
        file_size.HighPart = ffd.nFileSizeHigh;
        file_size.LowPart = ffd.nFileSizeLow;
        entry.file_size = (int64_t)file_size.QuadPart;

        if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
            entry.file_type |= AWS_FILE_TYPE_DIRECTORY;
        } else {
            entry.file_type |= AWS_FILE_TYPE_FILE;
        }

        if (recursive && entry.file_type & AWS_FILE_TYPE_DIRECTORY) {
            ret_val = aws_directory_traverse(allocator, relative_path_multi_char, recursive, on_entry, user_data);
        }

        /* post order traversal, if a node below us ended the traversal, don't call the visitor again. */
        if (ret_val && aws_last_error() == AWS_ERROR_OPERATION_INTERUPTED) {
            goto cleanup;
        }

        if (!on_entry(&entry, user_data)) {
            ret_val = aws_raise_error(AWS_ERROR_OPERATION_INTERUPTED);
            goto cleanup;
        }

        if (ret_val) {
            goto cleanup;
        }

    cleanup:
        aws_string_destroy(relative_path_multi_char);
        aws_string_destroy(full_path_name_multi_char);
        aws_byte_buf_clean_up(&relative_path_wchar);
        aws_string_destroy(name_component_multi_char_str);

    } while (ret_val == AWS_OP_SUCCESS && FindNextFileW(find_handle, &ffd));

    aws_wstring_destroy(long_path_wchar);
    if (find_handle != INVALID_HANDLE_VALUE) {
        FindClose(find_handle);
    }

    return ret_val;
}