1/* Read the next entry of a directory.
2   Copyright (C) 2011-2022 Free Software Foundation, Inc.
3
4   This file is free software: you can redistribute it and/or modify
5   it under the terms of the GNU Lesser General Public License as
6   published by the Free Software Foundation; either version 2.1 of the
7   License, or (at your option) any later version.
8
9   This file is distributed in the hope that it will be useful,
10   but WITHOUT ANY WARRANTY; without even the implied warranty of
11   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12   GNU Lesser General Public License for more details.
13
14   You should have received a copy of the GNU Lesser General Public License
15   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
16
17#include <config.h>
18
19/* Specification.  */
20#include <dirent.h>
21
22#include <errno.h>
23#include <stddef.h>
24
25#include "dirent-private.h"
26
27/* Don't assume that UNICODE is not defined.  */
28#undef FindNextFile
29#define FindNextFile FindNextFileA
30
31struct dirent *
32readdir (DIR *dirp)
33{
34  char type;
35  struct dirent *result;
36
37  /* There is no need to add code to produce entries for "." and "..".
38     According to the POSIX:2008 section "4.12 Pathname Resolution"
39     <https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html>
40     "." and ".." are syntactic entities.
41     POSIX also says:
42       "If entries for dot or dot-dot exist, one entry shall be returned
43        for dot and one entry shall be returned for dot-dot; otherwise,
44        they shall not be returned."  */
45
46  switch (dirp->status)
47    {
48    case -2:
49      /* End of directory already reached.  */
50      return NULL;
51    case -1:
52      break;
53    case 0:
54      if (!FindNextFile (dirp->current, &dirp->entry))
55        {
56          switch (GetLastError ())
57            {
58            case ERROR_NO_MORE_FILES:
59              dirp->status = -2;
60              return NULL;
61            default:
62              errno = EIO;
63              return NULL;
64            }
65        }
66      break;
67    default:
68      errno = dirp->status;
69      return NULL;
70    }
71
72  dirp->status = 0;
73
74  if (dirp->entry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
75    type = DT_DIR;
76  else if (dirp->entry.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
77    type = DT_LNK;
78  else if ((dirp->entry.dwFileAttributes
79            & ~(FILE_ATTRIBUTE_READONLY
80                | FILE_ATTRIBUTE_HIDDEN
81                | FILE_ATTRIBUTE_SYSTEM
82                | FILE_ATTRIBUTE_ARCHIVE
83                | FILE_ATTRIBUTE_NORMAL
84                | FILE_ATTRIBUTE_TEMPORARY
85                | FILE_ATTRIBUTE_SPARSE_FILE
86                | FILE_ATTRIBUTE_COMPRESSED
87                | FILE_ATTRIBUTE_NOT_CONTENT_INDEXED
88                | FILE_ATTRIBUTE_ENCRYPTED)) == 0)
89    /* Devices like COM1, LPT1, NUL would also have the attributes 0x20 but
90       they cannot occur here.  */
91    type = DT_REG;
92  else
93    type = DT_UNKNOWN;
94
95  /* Reuse the memory of dirp->entry for the result.  */
96  result =
97    (struct dirent *)
98    ((char *) dirp->entry.cFileName - offsetof (struct dirent, d_name[0]));
99  result->d_type = type;
100
101  return result;
102}
103