/*
 *  Copyright (C) 2013-2019 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
 *  Copyright (C) 2009-2013 Sourcefire, Inc.
 *
 *  Authors: aCaB <acab@clamav.net>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License version 2 as
 *  published by the Free Software Foundation.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 *  MA 02110-1301, USA.
 */

#include <errno.h>

#include "clamav.h"
#include "others.h"
#include "dirent.h"
#include "w32_stat.h"
#include "shared/misc.h"

DIR *opendir(const char *name)
{
    DIR *d;
    DWORD attrs;
    int len;
    struct stat sb;
    wchar_t *wpath;

    if (stat(name, &sb) < 0)
        return NULL;

    if (!S_ISDIR(sb.st_mode)) {
        errno = ENOTDIR;
        return NULL;
    }
    if (!(d = cli_malloc(sizeof(*d)))) {
        errno = ENOMEM;
        return NULL;
    }
    wpath = uncpath(name);
    if (!wpath)
        return NULL;
    wcsncpy(d->entry, wpath, sizeof(d->entry) / sizeof(d->entry[0]));
    free(wpath);
    d->entry[sizeof(d->entry) / sizeof(d->entry[0])] = L'\0';
    len                                              = wcslen(d->entry);

    if (len >= sizeof(d->entry) / sizeof(d->entry[0]) - 4) {
        free(d);
        errno = ENAMETOOLONG;
        return NULL;
    }
    while (len--) {
        if (d->entry[len] == L'\\')
            d->entry[len] = L'\0';
        else
            break;
    }

    wcsncat(d->entry, L"\\*.*", 4);
    d->dh = INVALID_HANDLE_VALUE;
    return d;
}

struct dirent *readdir(DIR *dirp)
{
    while (1) {
        if (dirp->dh == INVALID_HANDLE_VALUE) {
            if ((dirp->dh = FindFirstFileW(dirp->entry, &dirp->wfd)) == INVALID_HANDLE_VALUE) {
                errno = ENOENT;
                return NULL;
            }
        } else {
            if (!(FindNextFileW(dirp->dh, &dirp->wfd))) {
                errno = (GetLastError() == ERROR_NO_MORE_FILES) ? 0 : ENOENT;
                return NULL;
            }
        }
        if (!WideCharToMultiByte(CP_UTF8, 0, dirp->wfd.cFileName, -1, dirp->ent.d_name, sizeof(dirp->ent.d_name), NULL, NULL))
            continue; /* FIXME: WARN HERE ! */
        dirp->ent.d_ino = dirp->wfd.ftCreationTime.dwLowDateTime ^ dirp->wfd.nFileSizeLow;
        if (!dirp->ent.d_ino) dirp->ent.d_ino = 0x1337;
        dirp->ent.d_type = (dirp->wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? DT_DIR : DT_REG;
        break;
    }
    return &dirp->ent;
}

void rewinddir(DIR *dirp)
{
    if (dirp->dh != INVALID_HANDLE_VALUE)
        FindClose(dirp->dh);
    dirp->dh = INVALID_HANDLE_VALUE;
}

int closedir(DIR *dirp)
{
    rewinddir(dirp);
    free(dirp);
    return 0;
}