ls.c 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349
  1. /**
  2. * Licensed to the Apache Software Foundation (ASF) under one or more
  3. * contributor license agreements. See the NOTICE file distributed with this
  4. * work for additional information regarding copyright ownership. The ASF
  5. * licenses this file to you under the Apache License, Version 2.0 (the
  6. * "License"); you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  13. * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  14. * License for the specific language governing permissions and limitations under
  15. * the License.
  16. */
  17. #include "winutils.h"
  18. //----------------------------------------------------------------------------
  19. // Function: GetMaskString
  20. //
  21. // Description:
  22. // Get the mask string that are used for output to the console.
  23. //
  24. // Returns:
  25. // TRUE: on success
  26. //
  27. // Notes:
  28. // The function only sets the existed permission in the mask string. If the
  29. // permission does not exist, the corresponding character in mask string is not
  30. // altered. The caller need to initilize the mask string to be all '-' to get
  31. // the correct mask string.
  32. //
  33. static BOOL GetMaskString(__in INT accessMask, __in_ecount(10) LPWSTR maskString)
  34. {
  35. if(wcslen(maskString) != 10)
  36. return FALSE;
  37. if ((accessMask & UX_DIRECTORY) == UX_DIRECTORY)
  38. maskString[0] = L'd';
  39. else if ((accessMask & UX_SYMLINK) == UX_SYMLINK)
  40. maskString[0] = L'l';
  41. if ((accessMask & UX_U_READ) == UX_U_READ)
  42. maskString[1] = L'r';
  43. if ((accessMask & UX_U_WRITE) == UX_U_WRITE)
  44. maskString[2] = L'w';
  45. if ((accessMask & UX_U_EXECUTE) == UX_U_EXECUTE)
  46. maskString[3] = L'x';
  47. if ((accessMask & UX_G_READ) == UX_G_READ)
  48. maskString[4] = L'r';
  49. if ((accessMask & UX_G_WRITE) == UX_G_WRITE)
  50. maskString[5] = L'w';
  51. if ((accessMask & UX_G_EXECUTE) == UX_G_EXECUTE)
  52. maskString[6] = L'x';
  53. if ((accessMask & UX_O_READ) == UX_O_READ)
  54. maskString[7] = L'r';
  55. if ((accessMask & UX_O_WRITE) == UX_O_WRITE)
  56. maskString[8] = L'w';
  57. if ((accessMask & UX_O_EXECUTE) == UX_O_EXECUTE)
  58. maskString[9] = L'x';
  59. return TRUE;
  60. }
  61. //----------------------------------------------------------------------------
  62. // Function: LsPrintLine
  63. //
  64. // Description:
  65. // Print one line of 'ls' command given all the information needed
  66. //
  67. // Returns:
  68. // None
  69. //
  70. // Notes:
  71. // if useSeparator is false, separates the output tokens with a space
  72. // character, otherwise, with a pipe character
  73. //
  74. static BOOL LsPrintLine(
  75. const INT mask,
  76. const DWORD hardlinkCount,
  77. LPCWSTR ownerName,
  78. LPCWSTR groupName,
  79. const FILETIME *lpFileWritetime,
  80. const LARGE_INTEGER fileSize,
  81. LPCWSTR path,
  82. BOOL useSeparator)
  83. {
  84. // 'd' + 'rwx' for user, group, other
  85. static const size_t ck_ullMaskLen = 1 + 3 * 3;
  86. LPWSTR maskString = NULL;
  87. SYSTEMTIME stFileWriteTime;
  88. BOOL ret = FALSE;
  89. maskString = (LPWSTR)LocalAlloc(LPTR, (ck_ullMaskLen+1)*sizeof(WCHAR));
  90. if (maskString == NULL)
  91. {
  92. ReportErrorCode(L"LocalAlloc", GetLastError());
  93. return FALSE;
  94. }
  95. // Build mask string from mask mode
  96. if (FAILED(StringCchCopyW(maskString, (ck_ullMaskLen+1), L"----------")))
  97. {
  98. goto LsPrintLineEnd;
  99. }
  100. if (!GetMaskString(mask, maskString))
  101. {
  102. goto LsPrintLineEnd;
  103. }
  104. // Convert file time to system time
  105. if (!FileTimeToSystemTime(lpFileWritetime, &stFileWriteTime))
  106. {
  107. goto LsPrintLineEnd;
  108. }
  109. if (useSeparator)
  110. {
  111. fwprintf(stdout, L"%10s|%d|%s|%s|%lld|%3s|%2d|%4d|%s\n",
  112. maskString, hardlinkCount, ownerName, groupName, fileSize.QuadPart,
  113. MONTHS[stFileWriteTime.wMonth-1], stFileWriteTime.wDay,
  114. stFileWriteTime.wYear, path);
  115. }
  116. else
  117. {
  118. fwprintf(stdout, L"%10s %d %s %s %lld %3s %2d %4d %s\n",
  119. maskString, hardlinkCount, ownerName, groupName, fileSize.QuadPart,
  120. MONTHS[stFileWriteTime.wMonth-1], stFileWriteTime.wDay,
  121. stFileWriteTime.wYear, path);
  122. }
  123. ret = TRUE;
  124. LsPrintLineEnd:
  125. LocalFree(maskString);
  126. return ret;
  127. }
  128. // List of command line options supported by "winutils ls"
  129. enum CmdLineOption
  130. {
  131. CmdLineOptionFollowSymlink = 0x1, // "-L"
  132. CmdLineOptionSeparator = 0x2 // "-F"
  133. // options should be powers of 2 (aka next is 0x4)
  134. };
  135. static wchar_t* CurrentDir = L".";
  136. //----------------------------------------------------------------------------
  137. // Function: ParseCommandLine
  138. //
  139. // Description:
  140. // Parses the command line
  141. //
  142. // Returns:
  143. // TRUE on the valid command line, FALSE otherwise
  144. //
  145. BOOL ParseCommandLine(
  146. __in int argc,
  147. __in_ecount(argc) wchar_t *argv[],
  148. __deref_out PWSTR *path,
  149. __out int *optionsMask)
  150. {
  151. int MaxOptions = 2; // Should be equal to the number of elems in CmdLineOption
  152. int i = 0;
  153. assert(optionsMask != NULL);
  154. assert(argv != NULL);
  155. assert(path != NULL);
  156. *optionsMask = 0;
  157. if (argc == 1)
  158. {
  159. // no path specified, assume "."
  160. *path = CurrentDir;
  161. return TRUE;
  162. }
  163. if (argc == 2)
  164. {
  165. // only path specified, no other options
  166. *path = argv[1];
  167. return TRUE;
  168. }
  169. if (argc > 2 + MaxOptions)
  170. {
  171. // too many parameters
  172. return FALSE;
  173. }
  174. for (i = 1; i < argc - 1; ++i)
  175. {
  176. if (wcscmp(argv[i], L"-L") == 0)
  177. {
  178. // Check if this option was already specified
  179. BOOL alreadySet = *optionsMask & CmdLineOptionFollowSymlink;
  180. if (alreadySet)
  181. return FALSE;
  182. *optionsMask |= CmdLineOptionFollowSymlink;
  183. }
  184. else if (wcscmp(argv[i], L"-F") == 0)
  185. {
  186. // Check if this option was already specified
  187. BOOL alreadySet = *optionsMask & CmdLineOptionSeparator;
  188. if (alreadySet)
  189. return FALSE;
  190. *optionsMask |= CmdLineOptionSeparator;
  191. }
  192. else
  193. {
  194. return FALSE;
  195. }
  196. }
  197. *path = argv[argc - 1];
  198. return TRUE;
  199. }
  200. //----------------------------------------------------------------------------
  201. // Function: Ls
  202. //
  203. // Description:
  204. // The main method for ls command
  205. //
  206. // Returns:
  207. // 0: on success
  208. //
  209. // Notes:
  210. //
  211. int Ls(__in int argc, __in_ecount(argc) wchar_t *argv[])
  212. {
  213. LPWSTR pathName = NULL;
  214. LPWSTR longPathName = NULL;
  215. BY_HANDLE_FILE_INFORMATION fileInformation;
  216. LPWSTR ownerName = NULL;
  217. LPWSTR groupName = NULL;
  218. INT unixAccessMode = 0;
  219. DWORD dwErrorCode = ERROR_SUCCESS;
  220. LARGE_INTEGER fileSize;
  221. BOOL isSymlink = FALSE;
  222. int ret = EXIT_FAILURE;
  223. int optionsMask = 0;
  224. if (!ParseCommandLine(argc, argv, &pathName, &optionsMask))
  225. {
  226. fwprintf(stderr, L"Incorrect command line arguments.\n\n");
  227. LsUsage(argv[0]);
  228. return EXIT_FAILURE;
  229. }
  230. assert(pathName != NULL);
  231. if (wcsspn(pathName, L"/?|><:*\"") != 0)
  232. {
  233. fwprintf(stderr, L"Incorrect file name format: %s\n", pathName);
  234. return EXIT_FAILURE;
  235. }
  236. // Convert the path the the long path
  237. //
  238. dwErrorCode = ConvertToLongPath(pathName, &longPathName);
  239. if (dwErrorCode != ERROR_SUCCESS)
  240. {
  241. ReportErrorCode(L"ConvertToLongPath", dwErrorCode);
  242. goto LsEnd;
  243. }
  244. dwErrorCode = GetFileInformationByName(
  245. longPathName, optionsMask & CmdLineOptionFollowSymlink, &fileInformation);
  246. if (dwErrorCode != ERROR_SUCCESS)
  247. {
  248. ReportErrorCode(L"GetFileInformationByName", dwErrorCode);
  249. goto LsEnd;
  250. }
  251. dwErrorCode = SymbolicLinkCheck(longPathName, &isSymlink);
  252. if (dwErrorCode != ERROR_SUCCESS)
  253. {
  254. ReportErrorCode(L"IsSymbolicLink", dwErrorCode);
  255. goto LsEnd;
  256. }
  257. if (isSymlink)
  258. unixAccessMode |= UX_SYMLINK;
  259. else if (IsDirFileInfo(&fileInformation))
  260. unixAccessMode |= UX_DIRECTORY;
  261. dwErrorCode = FindFileOwnerAndPermission(longPathName,
  262. &ownerName, &groupName, &unixAccessMode);
  263. if (dwErrorCode != ERROR_SUCCESS)
  264. {
  265. ReportErrorCode(L"FindFileOwnerAndPermission", dwErrorCode);
  266. goto LsEnd;
  267. }
  268. fileSize.HighPart = fileInformation.nFileSizeHigh;
  269. fileSize.LowPart = fileInformation.nFileSizeLow;
  270. // Print output using the input path name (not the long one)
  271. //
  272. if (!LsPrintLine(unixAccessMode,
  273. fileInformation.nNumberOfLinks,
  274. ownerName, groupName,
  275. &fileInformation.ftLastWriteTime,
  276. fileSize,
  277. pathName,
  278. optionsMask & CmdLineOptionSeparator))
  279. goto LsEnd;
  280. ret = EXIT_SUCCESS;
  281. LsEnd:
  282. LocalFree(ownerName);
  283. LocalFree(groupName);
  284. LocalFree(longPathName);
  285. return ret;
  286. }
  287. void LsUsage(LPCWSTR program)
  288. {
  289. fwprintf(stdout, L"\
  290. Usage: %s [OPTIONS] [FILE]\n\
  291. List information about the FILE (the current directory by default).\n\
  292. Using long listing format and list directory entries instead of contents,\n\
  293. and do not dereference symbolic links.\n\
  294. Provides equivalent or similar function as 'ls -ld' on GNU/Linux.\n\
  295. \n\
  296. OPTIONS: -L dereference symbolic links\n\
  297. -F format the output by separating tokens with a pipe\n",
  298. program);
  299. }