ls.c 55 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332
  1. /*++
  2. Copyright (c) 2013 Minoca Corp. All Rights Reserved
  3. Module Name:
  4. ls.c
  5. Abstract:
  6. This module implements the ls utility.
  7. Author:
  8. Evan Green 25-Jun-2013
  9. Environment:
  10. POSIX
  11. --*/
  12. //
  13. // ------------------------------------------------------------------- Includes
  14. //
  15. #include <minoca/lib/types.h>
  16. #include <assert.h>
  17. #include <ctype.h>
  18. #include <dirent.h>
  19. #include <getopt.h>
  20. #include <errno.h>
  21. #include <stdlib.h>
  22. #include <string.h>
  23. #include <sys/stat.h>
  24. #include <sys/types.h>
  25. #include <unistd.h>
  26. #include "ls.h"
  27. #include "../swlib.h"
  28. //
  29. // ---------------------------------------------------------------- Definitions
  30. //
  31. #define LS_VERSION_MAJOR 1
  32. #define LS_VERSION_MINOR 0
  33. #define LS_USAGE \
  34. "usage: ls [-CFRacdilqrtu1][-H | -L][-fgmnopsx] [file...]\n\n" \
  35. "Options:\n" \
  36. " -a, --all -- Include names that begin with a period.\n" \
  37. " -C -- Display as multi-text-column output, sorted down the columns.\n" \
  38. " --color=[when] -- Display items in color. Arguments can be always, \n" \
  39. " auto, or never.\n" \
  40. " -c -- Show file status change time instead of modification time.\n" \
  41. " -d, --directory -- Treat directories specified as operands the same \n" \
  42. " as files are treated. Don't follow symbolic links unless -H or \n"\
  43. " -L is specified.\n" \
  44. " -F, --classify -- Write a '/' after directories, a '*' after \n" \
  45. " executables, a '|' fter FIFOs, and a '@' after symbolic links.\n" \
  46. " -f -- Disable sorting. Turns off -l, -t, -s, and -r, and turns on -a.\n"\
  47. " -g -- Same as -l but don't print the owner.\n" \
  48. " -H, --dereference-command-line -- Follow symbolic links found in \n" \
  49. " command line arguments.\n" \
  50. " -i, --inode -- Print file serial numbers.\n" \
  51. " -L, --dereference -- Always follow symbolic links.\n" \
  52. " -l -- Show the output in long format. Turns on -1, and does not\n" \
  53. " follow symlinks unless -H or -L is specified.\n" \
  54. " -m -- List results separated by commas.\n" \
  55. " -n, --numeric-uid-gid -- Write out the owner and group UID and GID, \n" \
  56. " instead of their associated character names.\n" \
  57. " -o -- Same as -l, but don't print the group.\n" \
  58. " -p -- Write a slash '/' after all directories.\n" \
  59. " -q, --hide-control-characters -- Print non-printable characters and \n" \
  60. " tabs as '?'.\n" \
  61. " -R, --recursive -- Recursively list subdirectories.\n" \
  62. " -r, --reverse -- Reverse the sort order.\n" \
  63. " -s, --size -- Print the file block count for each file.\n" \
  64. " -t -- Sort with the primary key as the modification (or creation, or \n"\
  65. " access) time, with a secondary key of the file name.\n" \
  66. " -u -- Use the last access time instead of modification time.\n" \
  67. " -x -- Sort entries across rather than down for column-based output.\n" \
  68. " -1 -- Display one entry per line.\n" \
  69. " --help -- Display this help text.\n" \
  70. " --version -- Display the version number and exit.\n"
  71. #define LS_OPTIONS_STRING "CFHLRacdfgilmnopqrstux1"
  72. #define LS_INITIAL_FILES_COUNT 16
  73. #define LS_INITIAL_TRAVERSED_DIRECTORIES_COUNT 16
  74. #define LS_DATE_STRING_SIZE 13
  75. #define LS_DEFAULT_MAX_WIDTH 80
  76. #define LS_COLUMN_PADDING 2
  77. //
  78. // ------------------------------------------------------ Data Type Definitions
  79. //
  80. typedef
  81. int
  82. (*PLS_SORT_COMPARE_FUNCTION) (
  83. const void *Item1,
  84. const void *Item2
  85. );
  86. /*++
  87. Routine Description:
  88. This routine compares two ls files.
  89. Arguments:
  90. Item1 - Supplies a pointer to the first element, which is a pointer to an
  91. ls file structure.
  92. Item2 - Supplies a pointer to the second element, which is a pointer to an
  93. ls file structure.
  94. Return Value:
  95. Less than zero if the first argument is less than the second.
  96. Zero if the first argument is equal to the second.
  97. Greater than zero if the first argument is greater than the second.
  98. --*/
  99. //
  100. // ----------------------------------------------- Internal Function Prototypes
  101. //
  102. INT
  103. LsCategorize (
  104. PLS_CONTEXT Context,
  105. PSTR Argument
  106. );
  107. INT
  108. LsList (
  109. PLS_CONTEXT Context
  110. );
  111. INT
  112. LsListDirectory (
  113. PLS_CONTEXT Context,
  114. PSTR DirectoryPath
  115. );
  116. INT
  117. LsListFiles (
  118. PLS_CONTEXT Context,
  119. PLS_FILE *Files,
  120. ULONG FileCount,
  121. BOOL PrintTotal
  122. );
  123. VOID
  124. LsListFile (
  125. PLS_CONTEXT Context,
  126. PLS_FILE File
  127. );
  128. VOID
  129. LsPrintPermissions (
  130. PLS_FILE File
  131. );
  132. VOID
  133. LsPrintDate (
  134. PLS_CONTEXT Context,
  135. time_t Date
  136. );
  137. VOID
  138. LsPrintFileName (
  139. PLS_CONTEXT Context,
  140. PLS_FILE File
  141. );
  142. PLS_CONTEXT
  143. LsCreateContext (
  144. );
  145. VOID
  146. LsDestroyContext (
  147. PLS_CONTEXT Context
  148. );
  149. PLS_FILE
  150. LsCreateFileInformation (
  151. PLS_CONTEXT Context,
  152. PSTR FileName,
  153. ULONG FileNameSize,
  154. PSTR LinkDestination,
  155. ULONG LinkDestinationSize,
  156. BOOL LinkBroken,
  157. struct stat *Stat
  158. );
  159. VOID
  160. LsDestroyFileInformation (
  161. PLS_FILE File
  162. );
  163. VOID
  164. LsAddTraversedDirectory (
  165. PLS_CONTEXT Context,
  166. ino_t Directory
  167. );
  168. BOOL
  169. LsHasDirectoryBeenTraversed (
  170. PLS_CONTEXT Context,
  171. ino_t Directory
  172. );
  173. ULONG
  174. LsGetCharacterCountForInteger (
  175. ULONGLONG Integer
  176. );
  177. //
  178. // -------------------------------------------------------------------- Globals
  179. //
  180. struct option LsLongOptions[] = {
  181. {"color", optional_argument, 0, '2'},
  182. {"classify", no_argument, 0, 'F'},
  183. {"dereference-command-line", no_argument, 0, 'H'},
  184. {"dereference", no_argument, 0, 'L'},
  185. {"recursive", no_argument, 0, 'R'},
  186. {"all", no_argument, 0, 'a'},
  187. {"directory", no_argument, 0, 'd'},
  188. {"inode", no_argument, 0, 'i'},
  189. {"numeric-uid-gid", no_argument, 0, 'n'},
  190. {"hide-control-characters", no_argument, 0, 'q'},
  191. {"reverse", no_argument, 0, 'r'},
  192. {"size", no_argument, 0, 's'},
  193. {"help", no_argument, 0, 'h'},
  194. {"version", no_argument, 0, 'V'},
  195. {NULL, 0, 0, 0},
  196. };
  197. //
  198. // ------------------------------------------------------------------ Functions
  199. //
  200. INT
  201. LsMain (
  202. INT ArgumentCount,
  203. CHAR **Arguments
  204. )
  205. /*++
  206. Routine Description:
  207. This routine implements the main entry point for the ls (list directory)
  208. utility.
  209. Arguments:
  210. ArgumentCount - Supplies the number of arguments on the command line.
  211. Arguments - Supplies an array of pointers to strings representing the
  212. arguments.
  213. Return Value:
  214. 0 on success.
  215. Non-zero on failure.
  216. --*/
  217. {
  218. PSTR Argument;
  219. ULONG ArgumentIndex;
  220. PLS_CONTEXT Context;
  221. ULONG ListCount;
  222. INT Option;
  223. INT ReturnValue;
  224. ReturnValue = ENOMEM;
  225. Context = LsCreateContext();
  226. if (Context == NULL) {
  227. goto MainEnd;
  228. }
  229. ListCount = 0;
  230. //
  231. // Loop through all the options.
  232. //
  233. while (TRUE) {
  234. Option = getopt_long(ArgumentCount,
  235. Arguments,
  236. LS_OPTIONS_STRING,
  237. LsLongOptions,
  238. NULL);
  239. if (Option == -1) {
  240. break;
  241. }
  242. if ((Option == '?') || (Option == ':')) {
  243. ReturnValue = 1;
  244. goto MainEnd;
  245. }
  246. switch (Option) {
  247. case 'C':
  248. Context->Flags |= LS_OPTION_COLUMN_OUTPUT;
  249. Context->Flags &= ~(LS_OPTION_ONE_ENTRY_PER_LINE |
  250. LS_OPTION_LONG_FORMAT |
  251. LS_OPTION_COMMA_SEPARATED);
  252. break;
  253. case 'F':
  254. Context->Flags |= LS_OPTION_DECORATE_NAMES |
  255. LS_OPTION_DECORATE_DIRECTORIES;
  256. break;
  257. case 'H':
  258. Context->Flags |= LS_OPTION_FOLLOW_LINKS_IN_OPERANDS;
  259. break;
  260. case 'L':
  261. Context->Flags |= LS_OPTION_FOLLOW_LINKS_IN_OPERANDS |
  262. LS_OPTION_FOLLOW_LINKS_IN_LIST;
  263. break;
  264. case 'R':
  265. Context->Flags |= LS_OPTION_RECURSIVE |
  266. LS_OPTION_PRINT_DIRECTORY_NAME;
  267. break;
  268. case 'a':
  269. Context->Flags |= LS_OPTION_LIST_ALL;
  270. break;
  271. case 'c':
  272. Context->Flags |= LS_OPTION_USE_STATUS_CHANGE_TIME;
  273. Context->Flags &= ~LS_OPTION_USE_ACCESS_TIME;
  274. break;
  275. case 'd':
  276. Context->Flags &= ~(LS_OPTION_FOLLOW_LINKS_IN_OPERANDS |
  277. LS_OPTION_FOLLOW_LINKS_IN_LIST);
  278. Context->Flags |= LS_OPTION_ALL_OPERANDS_AS_FILE;
  279. break;
  280. case 'f':
  281. Context->Flags |= LS_OPTION_LIST_ALL |
  282. LS_OPTION_COLUMN_OUTPUT |
  283. LS_OPTION_NO_SORTING;
  284. Context->Flags &= ~(LS_OPTION_LONG_FORMAT |
  285. LS_OPTION_SORT_BY_DATE |
  286. LS_OPTION_PRINT_BLOCK_COUNT |
  287. LS_OPTION_REVERSE_SORT |
  288. LS_OPTION_ONE_ENTRY_PER_LINE);
  289. break;
  290. case 'i':
  291. Context->Flags |= LS_OPTION_INCLUDE_SERIAL_NUMBERS;
  292. break;
  293. case 'o':
  294. case 'g':
  295. case 'l':
  296. Context->Flags |= LS_OPTION_ONE_ENTRY_PER_LINE |
  297. LS_OPTION_LONG_FORMAT;
  298. if (Option == 'o') {
  299. Context->Flags |= LS_OPTION_SKIP_GROUP;
  300. } else if (Option == 'g') {
  301. Context->Flags |= LS_OPTION_SKIP_OWNER;
  302. }
  303. Context->Flags &= ~LS_OPTION_COLUMN_OUTPUT;
  304. break;
  305. case 'm':
  306. Context->Flags |= LS_OPTION_COMMA_SEPARATED;
  307. Context->Flags &= ~LS_OPTION_COLUMN_OUTPUT;
  308. break;
  309. case 'n':
  310. Context->Flags |= LS_OPTION_PRINT_USER_GROUP_NUMBERS;
  311. break;
  312. case 'p':
  313. Context->Flags |= LS_OPTION_DECORATE_DIRECTORIES;
  314. break;
  315. case 'q':
  316. Context->Flags |= LS_OPTION_PRINT_QUESTION_MARKS;
  317. break;
  318. case 'r':
  319. Context->Flags |= LS_OPTION_REVERSE_SORT;
  320. break;
  321. case 's':
  322. Context->Flags |= LS_OPTION_PRINT_BLOCK_COUNT;
  323. break;
  324. case 't':
  325. Context->Flags |= LS_OPTION_SORT_BY_DATE;
  326. break;
  327. case 'u':
  328. Context->Flags |= LS_OPTION_USE_ACCESS_TIME;
  329. Context->Flags &= ~LS_OPTION_USE_STATUS_CHANGE_TIME;
  330. break;
  331. case 'x':
  332. Context->Flags |= LS_OPTION_SORT_COLUMNS_ACROSS;
  333. break;
  334. case '1':
  335. Context->Flags |= LS_OPTION_ONE_ENTRY_PER_LINE;
  336. break;
  337. case '2':
  338. Argument = optarg;
  339. if (Argument != NULL) {
  340. if (strcasecmp(Argument, "always") == 0) {
  341. Context->Flags |= LS_OPTION_COLOR;
  342. } else if (strcasecmp(Argument, "never") == 0) {
  343. Context->Flags &= ~LS_OPTION_COLOR;
  344. }
  345. }
  346. break;
  347. case 'V':
  348. SwPrintVersion(LS_VERSION_MAJOR, LS_VERSION_MINOR);
  349. return 1;
  350. case 'h':
  351. printf(LS_USAGE);
  352. return 1;
  353. default:
  354. assert(FALSE);
  355. ReturnValue = 1;
  356. goto MainEnd;
  357. }
  358. }
  359. ArgumentIndex = optind;
  360. if (ArgumentIndex > ArgumentCount) {
  361. ArgumentIndex = ArgumentCount;
  362. }
  363. ListCount = ArgumentCount - ArgumentIndex;
  364. //
  365. // Print directory names if there's more than one argument to list.
  366. //
  367. if (ListCount > 1) {
  368. Context->Flags |= LS_OPTION_PRINT_DIRECTORY_NAME;
  369. }
  370. //
  371. // Now that the options have been figured out, loop through again to
  372. // actually print the files and directories requested.
  373. //
  374. while (ArgumentIndex < ArgumentCount) {
  375. Argument = Arguments[ArgumentIndex];
  376. //
  377. // Categorize this as either a file or a directory. Errors are not
  378. // treated fatally.
  379. //
  380. LsCategorize(Context, Argument);
  381. ArgumentIndex += 1;
  382. }
  383. //
  384. // If nothing was printed, then categorize and print the current directory.
  385. //
  386. if (ListCount == 0) {
  387. LsCategorize(Context, ".");
  388. }
  389. //
  390. // Finally, list everything that's been built up.
  391. //
  392. ReturnValue = LsList(Context);
  393. MainEnd:
  394. if (Context != NULL) {
  395. LsDestroyContext(Context);
  396. }
  397. return ReturnValue;
  398. }
  399. //
  400. // --------------------------------------------------------- Internal Functions
  401. //
  402. INT
  403. LsCategorize (
  404. PLS_CONTEXT Context,
  405. PSTR Argument
  406. )
  407. /*++
  408. Routine Description:
  409. This routine categorizes the argument as either a file or a directory.
  410. Arguments:
  411. Context - Supplies a pointer to the current context.
  412. Argument - Supplies a pointer to the string containing the item to
  413. categorize.
  414. Return Value:
  415. 0 on success.
  416. Non-zero if there was an error.
  417. --*/
  418. {
  419. PLS_FILE File;
  420. BOOL FollowLinks;
  421. BOOL IsDirectory;
  422. BOOL LinkBroken;
  423. PSTR LinkDestination;
  424. ULONG LinkDestinationSize;
  425. struct stat LinkStat;
  426. INT Result;
  427. struct stat Stat;
  428. LinkBroken = FALSE;
  429. LinkDestination = NULL;
  430. LinkDestinationSize = 0;
  431. FollowLinks = FALSE;
  432. if ((Context->Flags & LS_OPTION_FOLLOW_LINKS_IN_OPERANDS) != 0) {
  433. FollowLinks = TRUE;
  434. }
  435. //
  436. // Figure out if this argument is a file or a directory.
  437. //
  438. Result = SwStat(Argument, FollowLinks, &Stat);
  439. if (Result != 0) {
  440. Result = errno;
  441. SwPrintError(Result, Argument, "Cannot stat");
  442. return Result;
  443. }
  444. IsDirectory = FALSE;
  445. if ((Context->Flags & LS_OPTION_ALL_OPERANDS_AS_FILE) == 0) {
  446. //
  447. // Figure out if this is a directory or a file. Follow a link if
  448. // requested.
  449. //
  450. if (S_ISLNK(Stat.st_mode)) {
  451. if (SwStat(Argument, TRUE, &LinkStat) != 0) {
  452. LinkBroken = TRUE;
  453. }
  454. Result = SwReadLink(Argument, &LinkDestination);
  455. if (Result != 0) {
  456. Result = errno;
  457. SwPrintError(Result, Argument, "Cannot read link");
  458. goto CategorizeEnd;
  459. }
  460. LinkDestinationSize = strlen(LinkDestination) + 1;
  461. }
  462. if (S_ISDIR(Stat.st_mode)) {
  463. IsDirectory = TRUE;
  464. }
  465. }
  466. //
  467. // Add it to the right array.
  468. //
  469. if (IsDirectory != FALSE) {
  470. if (Context->DirectoriesSize >= Context->DirectoriesCapacity) {
  471. if (Context->DirectoriesCapacity == 0) {
  472. Context->DirectoriesCapacity = LS_INITIAL_FILES_COUNT;
  473. } else {
  474. Context->DirectoriesCapacity *= 2;
  475. }
  476. Context->Directories = realloc(
  477. Context->Directories,
  478. Context->DirectoriesCapacity * sizeof(PSTR));
  479. if (Context->Directories == NULL) {
  480. Context->DirectoriesSize = 0;
  481. Context->DirectoriesCapacity = 0;
  482. Result = ENOMEM;
  483. goto CategorizeEnd;
  484. }
  485. }
  486. Context->Directories[Context->DirectoriesSize] = Argument;
  487. Context->DirectoriesSize += 1;
  488. } else {
  489. File = LsCreateFileInformation(Context,
  490. Argument,
  491. strlen(Argument) + 1,
  492. LinkDestination,
  493. LinkDestinationSize,
  494. LinkBroken,
  495. &Stat);
  496. if (File == NULL) {
  497. Result = ENOMEM;
  498. goto CategorizeEnd;
  499. }
  500. LinkDestination = NULL;
  501. if (Context->FilesSize >= Context->FilesCapacity) {
  502. if (Context->FilesCapacity == 0) {
  503. Context->FilesCapacity = LS_INITIAL_FILES_COUNT;
  504. } else {
  505. Context->FilesCapacity *= 2;
  506. }
  507. Context->Files = realloc(Context->Files,
  508. Context->FilesCapacity * sizeof(PLS_FILE));
  509. if (Context->Files == NULL) {
  510. Context->FilesSize = 0;
  511. Context->FilesCapacity = 0;
  512. Result = ENOMEM;
  513. goto CategorizeEnd;
  514. }
  515. }
  516. Context->Files[Context->FilesSize] = File;
  517. Context->FilesSize += 1;
  518. }
  519. CategorizeEnd:
  520. if (LinkDestination != NULL) {
  521. free(LinkDestination);
  522. }
  523. return Result;
  524. }
  525. INT
  526. LsList (
  527. PLS_CONTEXT Context
  528. )
  529. /*++
  530. Routine Description:
  531. This routine prints the listing for the files and directories in the
  532. given context.
  533. Arguments:
  534. Context - Supplies a pointer to the current context.
  535. Return Value:
  536. 0 on success.
  537. Non-zero if there was an error.
  538. --*/
  539. {
  540. ULONG DirectoryIndex;
  541. INT OverallResult;
  542. INT Result;
  543. OverallResult = 0;
  544. if (Context->FilesSize != 0) {
  545. OverallResult = LsListFiles(Context,
  546. Context->Files,
  547. Context->FilesSize,
  548. FALSE);
  549. }
  550. for (DirectoryIndex = 0;
  551. DirectoryIndex < Context->DirectoriesSize;
  552. DirectoryIndex += 1) {
  553. if ((Context->FilesSize != 0) || (DirectoryIndex != 0)) {
  554. printf("\n");
  555. }
  556. Result = LsListDirectory(Context, Context->Directories[DirectoryIndex]);
  557. if ((Result != 0) && (OverallResult == 0)) {
  558. OverallResult = Result;
  559. }
  560. }
  561. return OverallResult;
  562. }
  563. INT
  564. LsListDirectory (
  565. PLS_CONTEXT Context,
  566. PSTR DirectoryPath
  567. )
  568. /*++
  569. Routine Description:
  570. This routine prints the contents for the given directory.
  571. Arguments:
  572. Context - Supplies a pointer to the current context.
  573. DirectoryPath - Supplies a pointer to the path of the directory.
  574. Return Value:
  575. 0 on success.
  576. Non-zero if there was an error.
  577. --*/
  578. {
  579. DIR *Directory;
  580. ULONG DirectoryPathLength;
  581. struct dirent Entry;
  582. ULONG EntryNameLength;
  583. PLS_FILE File;
  584. PLS_FILE *FileArray;
  585. ULONG FileArrayCapacity;
  586. ULONG FileArraySize;
  587. ULONG FileIndex;
  588. BOOL FollowLinks;
  589. PSTR FullPath;
  590. ULONG FullPathSize;
  591. BOOL LinkBroken;
  592. PSTR LinkDestination;
  593. ULONG LinkDestinationSize;
  594. struct stat LinkStat;
  595. BOOL PrintTotal;
  596. INT Result;
  597. struct dirent *ReturnedPointer;
  598. struct stat Stat;
  599. struct stat *StatPointer;
  600. FileArray = NULL;
  601. FileArrayCapacity = 0;
  602. FileArraySize = 0;
  603. FullPath = NULL;
  604. LinkDestination = NULL;
  605. LinkDestinationSize = 0;
  606. PrintTotal = FALSE;
  607. if (((Context->Flags & LS_OPTION_LONG_FORMAT) != 0) ||
  608. ((Context->Flags & LS_OPTION_PRINT_BLOCK_COUNT) != 0)) {
  609. PrintTotal = TRUE;
  610. }
  611. FollowLinks = FALSE;
  612. if ((Context->Flags & LS_OPTION_FOLLOW_LINKS_IN_LIST) != 0) {
  613. FollowLinks = TRUE;
  614. }
  615. //
  616. // Add this directory as having been traversed.
  617. //
  618. Result = SwStat(DirectoryPath, FALSE, &Stat);
  619. if (Result == 0) {
  620. LsAddTraversedDirectory(Context, Stat.st_ino);
  621. }
  622. //
  623. // Open up the directory.
  624. //
  625. Directory = opendir(DirectoryPath);
  626. if (Directory == NULL) {
  627. Result = errno;
  628. SwPrintError(Result, DirectoryPath, "Unable to open directory");
  629. goto ListDirectoryEnd;
  630. }
  631. DirectoryPathLength = strlen(DirectoryPath);
  632. if ((Context->Flags & LS_OPTION_PRINT_DIRECTORY_NAME) != 0) {
  633. printf("%s:\n", DirectoryPath);
  634. }
  635. //
  636. // Loop through the entries in the directory.
  637. //
  638. while (TRUE) {
  639. Result = SwReadDirectory(Directory, &Entry, &ReturnedPointer);
  640. if (Result != 0) {
  641. SwPrintError(Result, DirectoryPath, "Unable to read directory");
  642. goto ListDirectoryEnd;
  643. }
  644. if (ReturnedPointer == NULL) {
  645. break;
  646. }
  647. //
  648. // If the entry begins with a dot, skip it unless otherwise specified.
  649. //
  650. if ((Entry.d_name[0] == '.') &&
  651. ((Context->Flags & LS_OPTION_LIST_ALL) == 0)) {
  652. continue;
  653. }
  654. //
  655. // Create the full path to the file so it can be statted.
  656. //
  657. EntryNameLength = strlen(Entry.d_name);
  658. Result = SwAppendPath(DirectoryPath,
  659. DirectoryPathLength + 1,
  660. Entry.d_name,
  661. EntryNameLength + 1,
  662. &FullPath,
  663. &FullPathSize);
  664. if (Result == FALSE) {
  665. Result = ENOMEM;
  666. goto ListDirectoryEnd;
  667. }
  668. LinkBroken = FALSE;
  669. Result = SwStat(FullPath, FollowLinks, &Stat);
  670. if (Result == 0) {
  671. StatPointer = &Stat;
  672. assert((FollowLinks != FALSE) || (Stat.st_ino == Entry.d_ino));
  673. //
  674. // Follow the link for the stat information.
  675. //
  676. if (S_ISLNK(Stat.st_mode)) {
  677. if (SwStat(FullPath, TRUE, &LinkStat) != 0) {
  678. LinkBroken = TRUE;
  679. }
  680. Result = SwReadLink(FullPath, &LinkDestination);
  681. if (Result != 0) {
  682. SwPrintError(Result, FullPath, "Failed to read link");
  683. }
  684. LinkDestinationSize = strlen(LinkDestination) + 1;
  685. }
  686. } else {
  687. StatPointer = NULL;
  688. SwPrintError(Result, FullPath, "Unable to stat");
  689. }
  690. free(FullPath);
  691. FullPath = NULL;
  692. //
  693. // Ensure there's enough room in the array for this upcoming entry.
  694. //
  695. if (FileArraySize == FileArrayCapacity) {
  696. if (FileArrayCapacity == 0) {
  697. FileArrayCapacity = LS_INITIAL_FILES_COUNT;
  698. } else {
  699. FileArrayCapacity *= 2;
  700. }
  701. FileArray = realloc(FileArray,
  702. FileArrayCapacity * sizeof(PLS_FILE));
  703. if (FileArray == NULL) {
  704. Result = ENOMEM;
  705. goto ListDirectoryEnd;
  706. }
  707. }
  708. //
  709. // Create the file information structure.
  710. //
  711. FileArray[FileArraySize] = LsCreateFileInformation(Context,
  712. Entry.d_name,
  713. EntryNameLength + 1,
  714. LinkDestination,
  715. LinkDestinationSize,
  716. LinkBroken,
  717. StatPointer);
  718. if (FileArray[FileArraySize] == NULL) {
  719. goto ListDirectoryEnd;
  720. }
  721. FileArraySize += 1;
  722. LinkDestination = NULL;
  723. LinkDestinationSize = 0;
  724. }
  725. //
  726. // List the files in here.
  727. //
  728. Result = LsListFiles(Context, FileArray, FileArraySize, PrintTotal);
  729. if (Result != 0) {
  730. goto ListDirectoryEnd;
  731. }
  732. //
  733. // Potentially recurse down subdirectories if requested
  734. //
  735. if ((Context->Flags & LS_OPTION_RECURSIVE) != 0) {
  736. for (FileIndex = 0; FileIndex < FileArraySize; FileIndex += 1) {
  737. File = FileArray[FileIndex];
  738. //
  739. // Skip the dot and dot-dot entries.
  740. //
  741. if (File->Name[0] == '.') {
  742. if (File->Name[1] == '\0') {
  743. continue;
  744. } else if ((File->Name[1] == '.') && (File->Name[2] == '\0')) {
  745. continue;
  746. }
  747. }
  748. if ((S_ISDIR(File->Stat.st_mode)) &&
  749. (LsHasDirectoryBeenTraversed(Context, File->Stat.st_ino) ==
  750. FALSE)) {
  751. Result = SwAppendPath(DirectoryPath,
  752. DirectoryPathLength + 1,
  753. File->Name,
  754. File->NameSize,
  755. &FullPath,
  756. &FullPathSize);
  757. if (Result == FALSE) {
  758. Result = ENOMEM;
  759. goto ListDirectoryEnd;
  760. }
  761. printf("\n");
  762. Result = LsListDirectory(Context, FullPath);
  763. free(FullPath);
  764. FullPath = NULL;
  765. if (Result != 0) {
  766. goto ListDirectoryEnd;
  767. }
  768. }
  769. }
  770. }
  771. ListDirectoryEnd:
  772. if (LinkDestination != NULL) {
  773. free(LinkDestination);
  774. }
  775. if (FullPath != NULL) {
  776. free(FullPath);
  777. }
  778. if (Directory != NULL) {
  779. closedir(Directory);
  780. }
  781. return Result;
  782. }
  783. INT
  784. LsListFiles (
  785. PLS_CONTEXT Context,
  786. PLS_FILE *Files,
  787. ULONG FileCount,
  788. BOOL PrintTotal
  789. )
  790. /*++
  791. Routine Description:
  792. This routine lists a group of files as a directory.
  793. Arguments:
  794. Context - Supplies a pointer to the application context.
  795. Files - Supplies an array of pointers to files to print.
  796. FileCount - Supplies the number of files in the array.
  797. PrintTotal - Supplies a boolean indicating whether the total number of
  798. 512 byte blocks used by these files should be displayed.
  799. Return Value:
  800. 0 on success.
  801. Returns an error number on failure.
  802. --*/
  803. {
  804. ULONGLONG BlockCount;
  805. ULONG ColumnCount;
  806. ULONG ColumnWidth;
  807. PLS_SORT_COMPARE_FUNCTION CompareFunction;
  808. PLS_FILE File;
  809. ULONG FileIndex;
  810. ULONG MaxBlocksLength;
  811. ULONG MaxFileLength;
  812. ULONG MaxFileNumberLength;
  813. ULONG MaxFileSizeLength;
  814. ULONG MaxGroupLength;
  815. ULONG MaxHardLinkLength;
  816. ULONG MaxOwnerLength;
  817. int MaxWidth;
  818. ULONG NumberWidth;
  819. INT Result;
  820. BOOL Reverse;
  821. ULONG RoundedCount;
  822. PLS_FILE *RoundedFiles;
  823. ULONG Row;
  824. ULONG RowCount;
  825. ULONGLONG TotalBlockCount;
  826. ULONG TotalWidth;
  827. Context->NameColumnSize = 0;
  828. Context->FileNumberColumnSize = 0;
  829. Context->FileBlocksColumnSize = 0;
  830. Context->FileSizeColumnSize = 0;
  831. Context->HardLinkColumnSize = 0;
  832. Context->OwnerColumnSize = 0;
  833. Context->GroupColumnSize = 0;
  834. RoundedFiles = NULL;
  835. TotalBlockCount = 0;
  836. //
  837. // Figure out how wide the terminal is.
  838. //
  839. Result = SwGetTerminalDimensions(&MaxWidth, NULL);
  840. if (Result != 0) {
  841. MaxWidth = LS_DEFAULT_MAX_WIDTH;
  842. }
  843. //
  844. // Calculate some column widths.
  845. //
  846. MaxBlocksLength = 0;
  847. MaxFileLength = 0;
  848. MaxFileNumberLength = 0;
  849. MaxFileSizeLength = 0;
  850. MaxGroupLength = 0;
  851. MaxHardLinkLength = 0;
  852. MaxOwnerLength = 0;
  853. TotalWidth = 0;
  854. for (FileIndex = 0; FileIndex < FileCount; FileIndex += 1) {
  855. File = Files[FileIndex];
  856. assert(File->NameSize - 1 == strlen(File->Name));
  857. if (File->NameSize > MaxFileLength) {
  858. MaxFileLength = File->NameSize;
  859. }
  860. if ((Context->Flags & LS_OPTION_INCLUDE_SERIAL_NUMBERS)) {
  861. NumberWidth = LsGetCharacterCountForInteger(File->Stat.st_ino);
  862. if (NumberWidth + 1 > MaxFileNumberLength) {
  863. MaxFileNumberLength = NumberWidth + 1;
  864. }
  865. }
  866. BlockCount = SwGetBlockCount(&(File->Stat));
  867. TotalBlockCount += (BlockCount * SwGetBlockSize(&(File->Stat))) / 512;
  868. if ((Context->Flags & LS_OPTION_PRINT_BLOCK_COUNT) != 0) {
  869. NumberWidth = LsGetCharacterCountForInteger(BlockCount);
  870. if (NumberWidth + 1 > MaxBlocksLength) {
  871. MaxBlocksLength = NumberWidth + 1;
  872. }
  873. }
  874. if ((Context->Flags & LS_OPTION_LONG_FORMAT) != 0) {
  875. NumberWidth = LsGetCharacterCountForInteger(File->Stat.st_size);
  876. if (NumberWidth + 1 > MaxFileSizeLength) {
  877. MaxFileSizeLength = NumberWidth + 1;
  878. }
  879. NumberWidth = LsGetCharacterCountForInteger(File->Stat.st_nlink);
  880. if (NumberWidth + 1 > MaxHardLinkLength) {
  881. MaxHardLinkLength = NumberWidth + 1;
  882. }
  883. if (File->OwnerName != NULL) {
  884. if (File->OwnerNameSize > MaxOwnerLength) {
  885. MaxOwnerLength = File->OwnerNameSize;
  886. }
  887. } else {
  888. NumberWidth = LsGetCharacterCountForInteger(File->Stat.st_uid);
  889. if (NumberWidth + 1 > MaxOwnerLength) {
  890. MaxOwnerLength = NumberWidth + 1;
  891. }
  892. }
  893. if (File->GroupName != NULL) {
  894. if (File->GroupNameSize > MaxGroupLength) {
  895. MaxGroupLength = File->GroupNameSize;
  896. }
  897. } else {
  898. NumberWidth = LsGetCharacterCountForInteger(File->Stat.st_gid);
  899. if (NumberWidth + 1 > MaxGroupLength) {
  900. MaxGroupLength = NumberWidth + 1;
  901. }
  902. }
  903. }
  904. TotalWidth += Files[FileIndex]->NameSize;
  905. }
  906. //
  907. // Remove the null terminator from the name length calculation.
  908. //
  909. if (MaxFileLength != 0) {
  910. MaxFileLength -= 1;
  911. }
  912. //
  913. // If decorating everything or just directories, add a character of
  914. // space for that.
  915. //
  916. if ((Context->Flags &
  917. (LS_OPTION_DECORATE_DIRECTORIES | LS_OPTION_DECORATE_NAMES)) != 0) {
  918. MaxFileLength += 1;
  919. }
  920. Context->FileNumberColumnSize = MaxFileNumberLength;
  921. Context->FileBlocksColumnSize = MaxBlocksLength;
  922. Context->FileSizeColumnSize = MaxFileSizeLength;
  923. Context->HardLinkColumnSize = MaxHardLinkLength;
  924. Context->OwnerColumnSize = MaxOwnerLength;
  925. Context->GroupColumnSize = MaxGroupLength;
  926. ColumnWidth = MaxFileLength + MaxFileNumberLength + MaxBlocksLength;
  927. if ((Context->Flags & LS_OPTION_COLUMN_OUTPUT) != 0) {
  928. if (MaxFileLength > MaxWidth) {
  929. Context->NameColumnSize = 0;
  930. Context->ColumnCount = 1;
  931. } else if ((TotalWidth + (FileCount * LS_COLUMN_PADDING)) < MaxWidth) {
  932. Context->NameColumnSize = 0;
  933. Context->ColumnCount = FileCount;
  934. } else {
  935. Context->NameColumnSize = MaxFileLength;
  936. Context->ColumnCount = (MaxWidth - 1) /
  937. (ColumnWidth + LS_COLUMN_PADDING);
  938. }
  939. }
  940. //
  941. // Sort the output if desired.
  942. //
  943. RoundedCount = FileCount;
  944. RoundedFiles = Files;
  945. if (((Context->Flags & LS_OPTION_NO_SORTING) == 0) && (FileCount > 1)) {
  946. Reverse = FALSE;
  947. if ((Context->Flags & LS_OPTION_REVERSE_SORT) != 0) {
  948. Reverse = TRUE;
  949. }
  950. if ((Context->Flags & LS_OPTION_SORT_BY_DATE) != 0) {
  951. if ((Context->Flags & LS_OPTION_USE_ACCESS_TIME) != 0) {
  952. if (Reverse != FALSE) {
  953. CompareFunction = LsCompareFilesByReverseAccessDate;
  954. } else {
  955. CompareFunction = LsCompareFilesByAccessDate;
  956. }
  957. } else if ((Context->Flags &
  958. LS_OPTION_USE_STATUS_CHANGE_TIME) != 0) {
  959. if (Reverse != FALSE) {
  960. CompareFunction = LsCompareFilesByReverseStatusChangeDate;
  961. } else {
  962. CompareFunction = LsCompareFilesByStatusChangeDate;
  963. }
  964. } else {
  965. if (Reverse != FALSE) {
  966. CompareFunction = LsCompareFilesByReverseModificationDate;
  967. } else {
  968. CompareFunction = LsCompareFilesByModificationDate;
  969. }
  970. }
  971. } else {
  972. if (Reverse != FALSE) {
  973. CompareFunction = LsCompareFilesByReverseName;
  974. } else {
  975. CompareFunction = LsCompareFilesByName;
  976. }
  977. }
  978. qsort(Files, FileCount, sizeof(PLS_FILE *), CompareFunction);
  979. //
  980. // Unless the user wanted the entries to go across, rotate the array
  981. // to make the list scan down each column.
  982. //
  983. if (((Context->Flags & LS_OPTION_COLUMN_OUTPUT) != 0) &&
  984. ((Context->Flags & LS_OPTION_SORT_COLUMNS_ACROSS) == 0)) {
  985. ColumnCount = Context->ColumnCount;
  986. assert(ColumnCount != 0);
  987. RowCount = FileCount / ColumnCount;
  988. if (FileCount % ColumnCount != 0) {
  989. RowCount += 1;
  990. }
  991. //
  992. // Re-allocate a rectangular array.
  993. //
  994. RoundedCount = RowCount * ColumnCount;
  995. if ((RoundedCount != 0) && (RoundedCount > FileCount)) {
  996. RoundedFiles = malloc(RoundedCount * sizeof(PLS_FILE));
  997. if (RoundedFiles == NULL) {
  998. return ENOMEM;
  999. }
  1000. memcpy(RoundedFiles, Files, FileCount * sizeof(PLS_FILE));
  1001. memset(RoundedFiles + FileCount,
  1002. 0,
  1003. (RoundedCount - FileCount) * sizeof(PLS_FILE));
  1004. } else {
  1005. RoundedFiles = Files;
  1006. }
  1007. SwRotatePointerArray((PVOID *)RoundedFiles, ColumnCount, RowCount);
  1008. }
  1009. }
  1010. //
  1011. // Override the column stuff if there's just one entry per line.
  1012. //
  1013. if ((Context->Flags & LS_OPTION_ONE_ENTRY_PER_LINE) != 0) {
  1014. Context->ColumnCount = 1;
  1015. }
  1016. if (PrintTotal != FALSE) {
  1017. printf("total %I64d\n", TotalBlockCount);
  1018. }
  1019. Row = 0;
  1020. ColumnCount = Context->ColumnCount;
  1021. Context->NextColumn = 0;
  1022. for (FileIndex = 0; FileIndex < RoundedCount; FileIndex += 1) {
  1023. File = RoundedFiles[FileIndex];
  1024. if (File == NULL) {
  1025. if ((Context->Flags & LS_OPTION_COLUMN_OUTPUT) != 0) {
  1026. Context->NextColumn = 0;
  1027. Row += 1;
  1028. }
  1029. //
  1030. // Print a newline unless the last one was null too.
  1031. //
  1032. if ((FileIndex == 0) || (RoundedFiles[FileIndex - 1] != NULL)) {
  1033. printf("\n");
  1034. }
  1035. continue;
  1036. }
  1037. LsListFile(Context, File);
  1038. //
  1039. // Print out all the junk that comes after a listing.
  1040. //
  1041. if ((Context->Flags & LS_OPTION_ONE_ENTRY_PER_LINE) != 0) {
  1042. printf("\n");
  1043. } else if ((Context->Flags & LS_OPTION_COLUMN_OUTPUT) != 0) {
  1044. Context->NextColumn += 1;
  1045. //
  1046. // Advance to the next row.
  1047. //
  1048. if ((Context->NextColumn >= ColumnCount) ||
  1049. (FileIndex == RoundedCount - 1)) {
  1050. Context->NextColumn = 0;
  1051. Row += 1;
  1052. printf("\n");
  1053. } else {
  1054. printf(" ");
  1055. }
  1056. } else if ((Context->Flags & LS_OPTION_COMMA_SEPARATED) != 0) {
  1057. if (FileIndex != FileCount - 1) {
  1058. printf(", ");
  1059. } else {
  1060. printf("\n");
  1061. }
  1062. }
  1063. }
  1064. if ((RoundedFiles != NULL) && (RoundedFiles != Files)) {
  1065. free(RoundedFiles);
  1066. }
  1067. return 0;
  1068. }
  1069. VOID
  1070. LsListFile (
  1071. PLS_CONTEXT Context,
  1072. PLS_FILE File
  1073. )
  1074. /*++
  1075. Routine Description:
  1076. This routine prints out file information.
  1077. Arguments:
  1078. Context - Supplies a pointer to the application context.
  1079. File - Supplies a pointer to the file to print.
  1080. Return Value:
  1081. None.
  1082. --*/
  1083. {
  1084. ULONGLONG Number;
  1085. //
  1086. // Print the file number if requested.
  1087. //
  1088. if ((Context->Flags & LS_OPTION_INCLUDE_SERIAL_NUMBERS) != 0) {
  1089. if (File->StatValid != FALSE) {
  1090. Number = File->Stat.st_ino;
  1091. printf("%*I64u ", Context->FileNumberColumnSize - 1, Number);
  1092. } else {
  1093. printf("%*s ", Context->FileNumberColumnSize - 1, "?");
  1094. }
  1095. }
  1096. //
  1097. // Print the block count if requested.
  1098. //
  1099. if ((Context->Flags & LS_OPTION_PRINT_BLOCK_COUNT) != 0) {
  1100. if (File->StatValid != FALSE) {
  1101. Number = SwGetBlockCount(&(File->Stat));
  1102. printf("%*I64u ", Context->FileBlocksColumnSize - 1, Number);
  1103. } else {
  1104. printf("%*s ", Context->FileBlocksColumnSize - 1, "?");
  1105. }
  1106. }
  1107. //
  1108. // Long format lines look something like this:
  1109. // drwxrwxr-x 3 evan evan 4096 1986-01-08 13:43 testdir
  1110. //
  1111. if ((Context->Flags & LS_OPTION_LONG_FORMAT) != 0) {
  1112. //
  1113. // Print the permissions and the hard link count.
  1114. //
  1115. LsPrintPermissions(File);
  1116. if (File->StatValid != FALSE) {
  1117. Number = File->Stat.st_nlink;
  1118. printf("%*I64u ", Context->HardLinkColumnSize - 1, Number);
  1119. } else {
  1120. printf("%*s ", Context->HardLinkColumnSize - 1, "?");
  1121. }
  1122. //
  1123. // Print the user and group if requested.
  1124. //
  1125. if ((Context->Flags & LS_OPTION_SKIP_OWNER) == 0) {
  1126. if (File->OwnerName != NULL) {
  1127. printf("%*s ", Context->OwnerColumnSize, File->OwnerName);
  1128. } else {
  1129. Number = File->Stat.st_uid;
  1130. printf("%*I64u ", Context->OwnerColumnSize, Number);
  1131. }
  1132. }
  1133. if ((Context->Flags & LS_OPTION_SKIP_GROUP) == 0) {
  1134. if (File->GroupName != NULL) {
  1135. printf("%*s ", Context->GroupColumnSize, File->GroupName);
  1136. } else {
  1137. Number = File->Stat.st_gid;
  1138. printf("%*I64u ", Context->GroupColumnSize, Number);
  1139. }
  1140. }
  1141. //
  1142. // Print the file size.
  1143. //
  1144. if (File->StatValid != FALSE) {
  1145. Number = File->Stat.st_size;
  1146. printf("%*I64u ", Context->FileSizeColumnSize, Number);
  1147. } else {
  1148. printf("%*s ", Context->FileSizeColumnSize, "?");
  1149. }
  1150. //
  1151. // Print the date.
  1152. //
  1153. if (File->StatValid != FALSE) {
  1154. if ((Context->Flags & LS_OPTION_USE_ACCESS_TIME) != 0) {
  1155. LsPrintDate(Context, File->Stat.st_atime);
  1156. } else if ((Context->Flags &
  1157. LS_OPTION_USE_STATUS_CHANGE_TIME) != 0) {
  1158. LsPrintDate(Context, File->Stat.st_ctime);
  1159. } else {
  1160. LsPrintDate(Context, File->Stat.st_mtime);
  1161. }
  1162. } else {
  1163. printf("%*s ", LS_DATE_STRING_SIZE - 1, "?");
  1164. }
  1165. //
  1166. // Finally, print the file name, followed by a newline.
  1167. //
  1168. LsPrintFileName(Context, File);
  1169. } else {
  1170. //
  1171. // Print out the file name, and then maybe a newline or something
  1172. // else.
  1173. //
  1174. LsPrintFileName(Context, File);
  1175. }
  1176. }
  1177. VOID
  1178. LsPrintPermissions (
  1179. PLS_FILE File
  1180. )
  1181. /*++
  1182. Routine Description:
  1183. This routine prints the standard permissions set.
  1184. Arguments:
  1185. File - Supplies a pointer to the file whose permissions should be printed.
  1186. Return Value:
  1187. None.
  1188. --*/
  1189. {
  1190. mode_t Mode;
  1191. CHAR String[13];
  1192. if (File->StatValid == FALSE) {
  1193. printf("?????????? ");
  1194. return;
  1195. }
  1196. memset(String, '-', sizeof(String) - 3);
  1197. String[12] = '\0';
  1198. //
  1199. // This space is just the separator between the permissions and the next
  1200. // field.
  1201. //
  1202. String[11] = ' ';
  1203. Mode = File->Stat.st_mode;
  1204. //
  1205. // Set up the file type.
  1206. //
  1207. if (S_ISDIR(Mode)) {
  1208. String[0] = 'd';
  1209. } else if (S_ISBLK(Mode)) {
  1210. String[0] = 'b';
  1211. } else if (S_ISCHR(Mode)) {
  1212. String[0] = 'c';
  1213. } else if (S_ISLNK(Mode)) {
  1214. String[0] = 'l';
  1215. } else if (S_ISFIFO(Mode)) {
  1216. String[0] = 'p';
  1217. } else if (S_ISSOCK(Mode)) {
  1218. String[0] = 's';
  1219. }
  1220. //
  1221. // Set up the user permissions.
  1222. //
  1223. if ((Mode & S_IRUSR) != 0) {
  1224. String[1] = 'r';
  1225. }
  1226. if ((Mode & S_IWUSR) != 0) {
  1227. String[2] = 'w';
  1228. }
  1229. if ((Mode & S_IXUSR) != 0) {
  1230. String[3] = 'x';
  1231. if ((Mode & S_ISUID) != 0) {
  1232. String[3] = 's';
  1233. }
  1234. } else {
  1235. if ((Mode & S_ISUID) != 0) {
  1236. String[3] = 'S';
  1237. }
  1238. }
  1239. //
  1240. // Set up group permissions.
  1241. //
  1242. if ((Mode & S_IRGRP) != 0) {
  1243. String[4] = 'r';
  1244. }
  1245. if ((Mode & S_IWGRP) != 0) {
  1246. String[5] = 'w';
  1247. }
  1248. if ((Mode & S_IXGRP) != 0) {
  1249. String[6] = 'x';
  1250. if ((Mode & S_ISGID) != 0) {
  1251. String[6] = 's';
  1252. }
  1253. } else {
  1254. if ((Mode & S_ISGID) != 0) {
  1255. String[6] = 'S';
  1256. }
  1257. }
  1258. //
  1259. // Set up other permissions.
  1260. //
  1261. if ((Mode & S_IROTH) != 0) {
  1262. String[7] = 'r';
  1263. }
  1264. if ((Mode & S_IWOTH) != 0) {
  1265. String[8] = 'w';
  1266. }
  1267. if ((Mode & S_IXOTH) != 0) {
  1268. String[9] = 'x';
  1269. if ((Mode & S_ISVTX) != 0) {
  1270. String[9] = 't';
  1271. }
  1272. } else {
  1273. if ((Mode & S_ISVTX) != 0) {
  1274. String[9] = 'T';
  1275. }
  1276. }
  1277. //
  1278. // The "alternate access method" flag is set to a space.
  1279. //
  1280. String[10] = ' ';
  1281. printf(String);
  1282. return;
  1283. }
  1284. VOID
  1285. LsPrintDate (
  1286. PLS_CONTEXT Context,
  1287. time_t Date
  1288. )
  1289. /*++
  1290. Routine Description:
  1291. This routine prints a file date.
  1292. Arguments:
  1293. Context - Supplies a pointer to the application context.
  1294. Date - Supplies the date to print.
  1295. Return Value:
  1296. None.
  1297. --*/
  1298. {
  1299. CHAR Buffer[LS_DATE_STRING_SIZE];
  1300. struct tm CurrentTime;
  1301. time_t CurrentTimeValue;
  1302. struct tm LocalTime;
  1303. struct tm *StaticPointer;
  1304. BOOL Within6Months;
  1305. CurrentTimeValue = time(NULL);
  1306. StaticPointer = localtime(&CurrentTimeValue);
  1307. if (StaticPointer == NULL) {
  1308. printf("%*s ", LS_DATE_STRING_SIZE - 1, "?");
  1309. return;
  1310. }
  1311. memcpy(&CurrentTime, StaticPointer, sizeof(struct tm));
  1312. StaticPointer = localtime(&Date);
  1313. if (StaticPointer == NULL) {
  1314. printf("%*s ", LS_DATE_STRING_SIZE - 1, "?");
  1315. return;
  1316. }
  1317. memcpy(&LocalTime, StaticPointer, sizeof(struct tm));
  1318. //
  1319. // Determine if the timestamp is within six months of the current time. If
  1320. // it's in the future, don't count it.
  1321. //
  1322. Within6Months = TRUE;
  1323. if (Date > CurrentTimeValue) {
  1324. Within6Months = FALSE;
  1325. } else if (LocalTime.tm_year == CurrentTime.tm_year) {
  1326. if (LocalTime.tm_mon + 6 <= CurrentTime.tm_mon) {
  1327. Within6Months = FALSE;
  1328. }
  1329. } else if (LocalTime.tm_year + 1 == CurrentTime.tm_year) {
  1330. if ((int)(LocalTime.tm_mon) - 12 + 6 <= CurrentTime.tm_mon) {
  1331. Within6Months = FALSE;
  1332. }
  1333. } else {
  1334. Within6Months = FALSE;
  1335. }
  1336. //
  1337. // If the timestamp is in the past from the last six months (approximately),
  1338. // then print the date and time. The format for the day of the month should
  1339. // really be %e, but Windows doesn't support that one, so %d is close
  1340. // enough.
  1341. //
  1342. if (Within6Months != FALSE) {
  1343. strftime(Buffer, LS_DATE_STRING_SIZE, "%b %d %H:%M", &LocalTime);
  1344. } else {
  1345. strftime(Buffer, LS_DATE_STRING_SIZE, "%b %d %Y", &LocalTime);
  1346. }
  1347. printf("%s ", Buffer);
  1348. return;
  1349. }
  1350. VOID
  1351. LsPrintFileName (
  1352. PLS_CONTEXT Context,
  1353. PLS_FILE File
  1354. )
  1355. /*++
  1356. Routine Description:
  1357. This routine prints the file name.
  1358. Arguments:
  1359. Context - Supplies a pointer to the application context.
  1360. File - Supplies a pointer to the file to print.
  1361. Return Value:
  1362. None.
  1363. --*/
  1364. {
  1365. CONSOLE_COLOR Background;
  1366. CHAR Decorator;
  1367. CONSOLE_COLOR Foreground;
  1368. ULONG NameIndex;
  1369. ULONG SpaceIndex;
  1370. ULONG Spaces;
  1371. Background = ConsoleColorDefault;
  1372. Foreground = ConsoleColorDefault;
  1373. Spaces = 0;
  1374. if (Context->NameColumnSize > File->NameSize - 1) {
  1375. Spaces = Context->NameColumnSize - (File->NameSize - 1);
  1376. }
  1377. Decorator = 0;
  1378. NameIndex = 0;
  1379. if ((Context->Flags & LS_OPTION_PRINT_QUESTION_MARKS) != 0) {
  1380. while (File->Name[NameIndex] != '\0') {
  1381. if (isprint(File->Name[NameIndex]) == FALSE) {
  1382. File->Name[NameIndex] = '?';
  1383. }
  1384. NameIndex += 1;
  1385. }
  1386. }
  1387. if ((Context->Flags & LS_OPTION_COLOR) != 0) {
  1388. if (S_ISDIR(File->Stat.st_mode)) {
  1389. Foreground = ConsoleColorBlue;
  1390. if ((File->Stat.st_mode & S_IWOTH) != 0) {
  1391. Background = ConsoleColorGreen;
  1392. }
  1393. } else if (S_ISLNK(File->Stat.st_mode)) {
  1394. if ((Context->Flags & LS_OPTION_ALL_OPERANDS_AS_FILE) == 0) {
  1395. Foreground = ConsoleColorCyan;
  1396. if (File->LinkBroken != FALSE) {
  1397. Foreground = ConsoleColorRed;
  1398. Background = ConsoleColorBlack;
  1399. }
  1400. }
  1401. } else if (S_ISSOCK(File->Stat.st_mode)) {
  1402. Foreground = ConsoleColorMagenta;
  1403. } else if ((S_ISBLK(File->Stat.st_mode)) ||
  1404. (S_ISCHR(File->Stat.st_mode)) ||
  1405. (S_ISFIFO(File->Stat.st_mode))) {
  1406. Foreground = ConsoleColorYellow;
  1407. Background = ConsoleColorBlack;
  1408. } else if ((File->Stat.st_mode & S_ISUID) != 0) {
  1409. Foreground = ConsoleColorWhite;
  1410. Background = ConsoleColorRed;
  1411. } else if ((File->Stat.st_mode & S_ISGID) != 0) {
  1412. Foreground = ConsoleColorBlack;
  1413. Background = ConsoleColorYellow;
  1414. } else if (File->Stat.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) {
  1415. Foreground = ConsoleColorGreen;
  1416. }
  1417. if (Foreground != ConsoleColorDefault) {
  1418. SwPrintInColor(Background, Foreground, File->Name);
  1419. } else {
  1420. printf("%s", File->Name);
  1421. }
  1422. } else {
  1423. printf("%s", File->Name);
  1424. }
  1425. if (((Context->Flags & LS_OPTION_DECORATE_DIRECTORIES) != 0) &&
  1426. (S_ISDIR(File->Stat.st_mode))) {
  1427. Decorator = '/';
  1428. } else if (((Context->Flags & LS_OPTION_DECORATE_NAMES) != 0) &&
  1429. ((Context->Flags & LS_OPTION_LONG_FORMAT) == 0)) {
  1430. if (S_ISFIFO(File->Stat.st_mode)) {
  1431. Decorator = '|';
  1432. } else if (S_ISLNK(File->Stat.st_mode)) {
  1433. Decorator = '@';
  1434. } else if ((File->Stat.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0) {
  1435. Decorator = '*';
  1436. }
  1437. }
  1438. if (((Context->Flags & LS_OPTION_LONG_FORMAT) != 0) &&
  1439. ((Context->Flags & LS_OPTION_ALL_OPERANDS_AS_FILE) == 0) &&
  1440. (S_ISLNK(File->Stat.st_mode))) {
  1441. printf(" -> ");
  1442. if (((Context->Flags & LS_OPTION_COLOR) != 0) &&
  1443. (File->LinkBroken != FALSE)) {
  1444. SwPrintInColor(ConsoleColorBlack,
  1445. ConsoleColorRed,
  1446. File->LinkDestination);
  1447. } else {
  1448. printf("%s", File->LinkDestination);
  1449. }
  1450. }
  1451. if (Decorator != 0) {
  1452. putchar(Decorator);
  1453. if (Spaces != 0) {
  1454. Spaces -= 1;
  1455. }
  1456. }
  1457. for (SpaceIndex = 0; SpaceIndex < Spaces; SpaceIndex += 1) {
  1458. putchar(' ');
  1459. }
  1460. return;
  1461. }
  1462. PLS_CONTEXT
  1463. LsCreateContext (
  1464. )
  1465. /*++
  1466. Routine Description:
  1467. This routine creates a new LS context instance.
  1468. Arguments:
  1469. None.
  1470. Return Value:
  1471. Returns a pointer to the new context on success.
  1472. NULL on allocation failure.
  1473. --*/
  1474. {
  1475. PLS_CONTEXT Context;
  1476. Context = malloc(sizeof(LS_CONTEXT));
  1477. if (Context == NULL) {
  1478. return NULL;
  1479. }
  1480. memset(Context, 0, sizeof(LS_CONTEXT));
  1481. //
  1482. // Set up some default flags depending on whether or not standard out is a
  1483. // terminal.
  1484. //
  1485. if (isatty(STDOUT_FILENO) != 0) {
  1486. Context->Flags = LS_DEFAULT_OPTIONS_TERMINAL;
  1487. } else {
  1488. Context->Flags = LS_DEFAULT_OPTIONS_NON_TERMINAL;
  1489. }
  1490. return Context;
  1491. }
  1492. VOID
  1493. LsDestroyContext (
  1494. PLS_CONTEXT Context
  1495. )
  1496. /*++
  1497. Routine Description:
  1498. This routine creates destroys an LS context.
  1499. Arguments:
  1500. Context - Supplies a pointer to the context to destroy.
  1501. Return Value:
  1502. None.
  1503. --*/
  1504. {
  1505. ULONG FileIndex;
  1506. if (Context->TraversedDirectories != NULL) {
  1507. free(Context->TraversedDirectories);
  1508. }
  1509. if (Context->Files != NULL) {
  1510. for (FileIndex = 0; FileIndex < Context->FilesSize; FileIndex += 1) {
  1511. LsDestroyFileInformation(Context->Files[FileIndex]);
  1512. }
  1513. free(Context->Files);
  1514. }
  1515. if (Context->Directories != NULL) {
  1516. free(Context->Directories);
  1517. }
  1518. free(Context);
  1519. return;
  1520. }
  1521. PLS_FILE
  1522. LsCreateFileInformation (
  1523. PLS_CONTEXT Context,
  1524. PSTR FileName,
  1525. ULONG FileNameSize,
  1526. PSTR LinkDestination,
  1527. ULONG LinkDestinationSize,
  1528. BOOL LinkBroken,
  1529. struct stat *Stat
  1530. )
  1531. /*++
  1532. Routine Description:
  1533. This routine creates a file information structure.
  1534. Arguments:
  1535. Context - Supplies a pointer to the application context.
  1536. FileName - Supplies a pointer to the file name. This string will be copied.
  1537. FileNameSize - Supplies the length of the file buffer in bytes including
  1538. the null terminator.
  1539. LinkDestination - Supplies an optional pointer to the link destination for
  1540. symbolic links, allocated from the heap. This routine will take over
  1541. the allocation, the caller should not subsequently free the memory
  1542. unless this routine fails.
  1543. LinkDestinationSize - Supplies the size of the link destination buffer in
  1544. bytes.
  1545. LinkBroken - Supplies a boolean indicating if the link destination is bad.
  1546. Stat - Supplies a pointer to the file details. If this is NULL, most
  1547. details will be printed as question marks.
  1548. Return Value:
  1549. Returns a pointer to a new file information structure on success.
  1550. NULL on allocation failure.
  1551. --*/
  1552. {
  1553. PLS_FILE NewFile;
  1554. BOOL Result;
  1555. Result = FALSE;
  1556. NewFile = malloc(sizeof(LS_FILE));
  1557. if (NewFile == NULL) {
  1558. goto CreateFileInformationEnd;
  1559. }
  1560. memset(NewFile, 0, sizeof(LS_FILE));
  1561. //
  1562. // Copy the name.
  1563. //
  1564. NewFile->Name = malloc(FileNameSize);
  1565. if (NewFile->Name == NULL) {
  1566. goto CreateFileInformationEnd;
  1567. }
  1568. memcpy(NewFile->Name, FileName, FileNameSize);
  1569. NewFile->Name[FileNameSize - 1] = '\0';
  1570. NewFile->NameSize = FileNameSize;
  1571. if (Stat != NULL) {
  1572. NewFile->StatValid = TRUE;
  1573. memcpy(&(NewFile->Stat), Stat, sizeof(struct stat));
  1574. }
  1575. NewFile->LinkDestination = LinkDestination;
  1576. NewFile->LinkDestinationSize = LinkDestinationSize;
  1577. if (LinkDestination != NULL) {
  1578. NewFile->LinkBroken = LinkBroken;
  1579. }
  1580. //
  1581. // Attempt to get the user and group names if they're going to be needed.
  1582. //
  1583. if (NewFile->StatValid != FALSE) {
  1584. if (((Context->Flags & LS_OPTION_LONG_FORMAT) != 0) &&
  1585. ((Context->Flags & LS_OPTION_PRINT_USER_GROUP_NUMBERS) == 0)) {
  1586. SwGetUserNameFromId(Stat->st_uid, &(NewFile->OwnerName));
  1587. SwGetGroupNameFromId(Stat->st_gid, &(NewFile->GroupName));
  1588. }
  1589. } else {
  1590. NewFile->OwnerName = strdup("?");
  1591. NewFile->GroupName = strdup("?");
  1592. }
  1593. if (NewFile->OwnerName != NULL) {
  1594. NewFile->OwnerNameSize = strlen(NewFile->OwnerName);
  1595. }
  1596. if (NewFile->GroupName != NULL) {
  1597. NewFile->GroupNameSize = strlen(NewFile->GroupName);
  1598. }
  1599. Result = TRUE;
  1600. CreateFileInformationEnd:
  1601. if (Result == FALSE) {
  1602. if (NewFile != NULL) {
  1603. if (NewFile->Name != NULL) {
  1604. free(NewFile->Name);
  1605. }
  1606. free(NewFile);
  1607. NewFile = NULL;
  1608. }
  1609. }
  1610. return NewFile;
  1611. }
  1612. VOID
  1613. LsDestroyFileInformation (
  1614. PLS_FILE File
  1615. )
  1616. /*++
  1617. Routine Description:
  1618. This routine creates destroys an LS file information structure.
  1619. Arguments:
  1620. File - Supplies a pointer to the file to destroy.
  1621. Return Value:
  1622. None.
  1623. --*/
  1624. {
  1625. if (File->Name != NULL) {
  1626. free(File->Name);
  1627. }
  1628. if (File->OwnerName != NULL) {
  1629. free(File->OwnerName);
  1630. }
  1631. if (File->GroupName != NULL) {
  1632. free(File->GroupName);
  1633. }
  1634. free(File);
  1635. return;
  1636. }
  1637. VOID
  1638. LsAddTraversedDirectory (
  1639. PLS_CONTEXT Context,
  1640. ino_t Directory
  1641. )
  1642. /*++
  1643. Routine Description:
  1644. This routine adds an element to the array of traversed directories.
  1645. Arguments:
  1646. Context - Supplies a pointer to the application context.
  1647. Directory - Supplies the serial number of the directory that is about to be
  1648. enumerated.
  1649. Return Value:
  1650. None. Failures are silently ignored.
  1651. --*/
  1652. {
  1653. ULONG NewCapacity;
  1654. if ((Context->Flags & LS_OPTION_RECURSIVE) == 0) {
  1655. return;
  1656. }
  1657. if (Directory == 0) {
  1658. return;
  1659. }
  1660. if (Context->TraversedDirectoriesSize >=
  1661. Context->TraversedDirectoriesCapacity) {
  1662. NewCapacity = Context->TraversedDirectoriesCapacity * 2;
  1663. if (NewCapacity == 0) {
  1664. NewCapacity = LS_INITIAL_TRAVERSED_DIRECTORIES_COUNT;
  1665. }
  1666. assert(NewCapacity > Context->TraversedDirectoriesSize);
  1667. Context->TraversedDirectories = realloc(Context->TraversedDirectories,
  1668. NewCapacity * sizeof(ino_t));
  1669. if (Context->TraversedDirectories == NULL) {
  1670. Context->TraversedDirectoriesSize = 0;
  1671. Context->TraversedDirectoriesCapacity = 0;
  1672. return;
  1673. }
  1674. Context->TraversedDirectoriesCapacity = NewCapacity;
  1675. }
  1676. Context->TraversedDirectories[Context->TraversedDirectoriesSize] =
  1677. Directory;
  1678. Context->TraversedDirectoriesSize += 1;
  1679. return;
  1680. }
  1681. BOOL
  1682. LsHasDirectoryBeenTraversed (
  1683. PLS_CONTEXT Context,
  1684. ino_t Directory
  1685. )
  1686. /*++
  1687. Routine Description:
  1688. This routine checks to see if the given directory has already been
  1689. traversed.
  1690. Arguments:
  1691. Context - Supplies a pointer to the application context.
  1692. Directory - Supplies the serial number of the directory that is about to be
  1693. enumerated.
  1694. Return Value:
  1695. TRUE if the directory has already been traversed.
  1696. FALSE if the directory has not been traversed.
  1697. --*/
  1698. {
  1699. ULONG Index;
  1700. for (Index = 0;
  1701. Index < Context->TraversedDirectoriesSize;
  1702. Index += 1) {
  1703. if (Context->TraversedDirectories[Index] == Directory) {
  1704. return TRUE;
  1705. }
  1706. }
  1707. return FALSE;
  1708. }
  1709. ULONG
  1710. LsGetCharacterCountForInteger (
  1711. ULONGLONG Integer
  1712. )
  1713. /*++
  1714. Routine Description:
  1715. This routine returns the number of characters required to represent the
  1716. given unsigned integer.
  1717. Arguments:
  1718. Integer - Supplies the integer that will get printed.
  1719. Return Value:
  1720. Returns the number of characters required to print a string of that
  1721. decimal integer.
  1722. --*/
  1723. {
  1724. ULONGLONG Comparison;
  1725. ULONG Count;
  1726. Count = 1;
  1727. Comparison = 10;
  1728. while ((Count < 24) && (Integer >= Comparison)) {
  1729. Count += 1;
  1730. Comparison *= 10;
  1731. }
  1732. return Count;
  1733. }