| Elliott Hughes | 88f0f4b | 2015-05-16 06:28:10 +0000 | [diff] [blame] | 1 | #include <dirent.h> | 
 | 2 | #include <errno.h> | 
 | 3 | #include <grp.h> | 
 | 4 | #include <limits.h> | 
 | 5 | #include <pwd.h> | 
 | 6 | #include <stddef.h> | 
 | 7 | #include <stdio.h> | 
 | 8 | #include <stdlib.h> | 
 | 9 | #include <string.h> | 
 | 10 | #include <sys/stat.h> | 
 | 11 | #include <sys/sysmacros.h> | 
 | 12 | #include <sys/types.h> | 
 | 13 | #include <time.h> | 
 | 14 | #include <unistd.h> | 
 | 15 |  | 
 | 16 | #include <selinux/selinux.h> | 
 | 17 |  | 
 | 18 | // simple dynamic array of strings. | 
 | 19 | typedef struct { | 
 | 20 |     int count; | 
 | 21 |     int capacity; | 
 | 22 |     void** items; | 
 | 23 | } strlist_t; | 
 | 24 |  | 
 | 25 | #define STRLIST_INITIALIZER { 0, 0, NULL } | 
 | 26 |  | 
 | 27 | /* Used to iterate over a strlist_t | 
 | 28 |  * _list   :: pointer to strlist_t object | 
 | 29 |  * _item   :: name of local variable name defined within the loop with | 
 | 30 |  *            type 'char*' | 
 | 31 |  * _stmnt  :: C statement executed in each iteration | 
 | 32 |  * | 
 | 33 |  * This macro is only intended for simple uses. Do not add or remove items | 
 | 34 |  * to/from the list during iteration. | 
 | 35 |  */ | 
 | 36 | #define  STRLIST_FOREACH(_list,_item,_stmnt) \ | 
 | 37 |     do { \ | 
 | 38 |         int _nn_##__LINE__ = 0; \ | 
 | 39 |         for (;_nn_##__LINE__ < (_list)->count; ++ _nn_##__LINE__) { \ | 
 | 40 |             char* _item = (char*)(_list)->items[_nn_##__LINE__]; \ | 
 | 41 |             _stmnt; \ | 
 | 42 |         } \ | 
 | 43 |     } while (0) | 
 | 44 |  | 
 | 45 | static void dynarray_reserve_more( strlist_t *a, int count ) { | 
 | 46 |     int old_cap = a->capacity; | 
 | 47 |     int new_cap = old_cap; | 
 | 48 |     const int max_cap = INT_MAX/sizeof(void*); | 
 | 49 |     void** new_items; | 
 | 50 |     int new_count = a->count + count; | 
 | 51 |  | 
 | 52 |     if (count <= 0) | 
 | 53 |         return; | 
 | 54 |  | 
 | 55 |     if (count > max_cap - a->count) | 
 | 56 |         abort(); | 
 | 57 |  | 
 | 58 |     new_count = a->count + count; | 
 | 59 |  | 
 | 60 |     while (new_cap < new_count) { | 
 | 61 |         old_cap = new_cap; | 
 | 62 |         new_cap += (new_cap >> 2) + 4; | 
 | 63 |         if (new_cap < old_cap || new_cap > max_cap) { | 
 | 64 |             new_cap = max_cap; | 
 | 65 |         } | 
 | 66 |     } | 
 | 67 |     new_items = realloc(a->items, new_cap*sizeof(void*)); | 
 | 68 |     if (new_items == NULL) | 
 | 69 |         abort(); | 
 | 70 |  | 
 | 71 |     a->items = new_items; | 
 | 72 |     a->capacity = new_cap; | 
 | 73 | } | 
 | 74 |  | 
 | 75 | void strlist_init( strlist_t *list ) { | 
 | 76 |     list->count = list->capacity = 0; | 
 | 77 |     list->items = NULL; | 
 | 78 | } | 
 | 79 |  | 
 | 80 | // append a new string made of the first 'slen' characters from 'str' | 
 | 81 | // followed by a trailing zero. | 
 | 82 | void strlist_append_b( strlist_t *list, const void* str, size_t  slen ) { | 
 | 83 |     char *copy = malloc(slen+1); | 
 | 84 |     memcpy(copy, str, slen); | 
 | 85 |     copy[slen] = '\0'; | 
 | 86 |     if (list->count >= list->capacity) | 
 | 87 |         dynarray_reserve_more(list, 1); | 
 | 88 |     list->items[list->count++] = copy; | 
 | 89 | } | 
 | 90 |  | 
 | 91 | // append the copy of a given input string to a strlist_t. | 
 | 92 | void strlist_append_dup( strlist_t *list, const char *str) { | 
 | 93 |     strlist_append_b(list, str, strlen(str)); | 
 | 94 | } | 
 | 95 |  | 
 | 96 | // note: strlist_done will free all the strings owned by the list. | 
 | 97 | void strlist_done( strlist_t *list ) { | 
 | 98 |     STRLIST_FOREACH(list, string, free(string)); | 
 | 99 |     free(list->items); | 
 | 100 |     list->items = NULL; | 
 | 101 |     list->count = list->capacity = 0; | 
 | 102 | } | 
 | 103 |  | 
 | 104 | static int strlist_compare_strings(const void* a, const void* b) { | 
 | 105 |     const char *sa = *(const char **)a; | 
 | 106 |     const char *sb = *(const char **)b; | 
 | 107 |     return strcmp(sa, sb); | 
 | 108 | } | 
 | 109 |  | 
 | 110 | /* sort the strings in a given list (using strcmp) */ | 
 | 111 | void strlist_sort( strlist_t *list ) { | 
 | 112 |     if (list->count > 0) { | 
 | 113 |         qsort(list->items, (size_t)list->count, sizeof(void*), strlist_compare_strings); | 
 | 114 |     } | 
 | 115 | } | 
 | 116 |  | 
 | 117 |  | 
 | 118 | // bits for flags argument | 
 | 119 | #define LIST_LONG           (1 << 0) | 
 | 120 | #define LIST_ALL            (1 << 1) | 
 | 121 | #define LIST_RECURSIVE      (1 << 2) | 
 | 122 | #define LIST_DIRECTORIES    (1 << 3) | 
 | 123 | #define LIST_SIZE           (1 << 4) | 
 | 124 | #define LIST_LONG_NUMERIC   (1 << 5) | 
 | 125 | #define LIST_CLASSIFY       (1 << 6) | 
 | 126 | #define LIST_MACLABEL       (1 << 7) | 
 | 127 | #define LIST_INODE          (1 << 8) | 
 | 128 |  | 
 | 129 | // fwd | 
 | 130 | static int listpath(const char *name, int flags); | 
 | 131 |  | 
 | 132 | static char mode2kind(mode_t mode) | 
 | 133 | { | 
 | 134 |     switch(mode & S_IFMT){ | 
 | 135 |     case S_IFSOCK: return 's'; | 
 | 136 |     case S_IFLNK: return 'l'; | 
 | 137 |     case S_IFREG: return '-'; | 
 | 138 |     case S_IFDIR: return 'd'; | 
 | 139 |     case S_IFBLK: return 'b'; | 
 | 140 |     case S_IFCHR: return 'c'; | 
 | 141 |     case S_IFIFO: return 'p'; | 
 | 142 |     default: return '?'; | 
 | 143 |     } | 
 | 144 | } | 
 | 145 |  | 
 | 146 | void strmode(mode_t mode, char *out) | 
 | 147 | { | 
 | 148 |     *out++ = mode2kind(mode); | 
 | 149 |  | 
 | 150 |     *out++ = (mode & 0400) ? 'r' : '-'; | 
 | 151 |     *out++ = (mode & 0200) ? 'w' : '-'; | 
 | 152 |     if(mode & 04000) { | 
 | 153 |         *out++ = (mode & 0100) ? 's' : 'S'; | 
 | 154 |     } else { | 
 | 155 |         *out++ = (mode & 0100) ? 'x' : '-'; | 
 | 156 |     } | 
 | 157 |     *out++ = (mode & 040) ? 'r' : '-'; | 
 | 158 |     *out++ = (mode & 020) ? 'w' : '-'; | 
 | 159 |     if(mode & 02000) { | 
 | 160 |         *out++ = (mode & 010) ? 's' : 'S'; | 
 | 161 |     } else { | 
 | 162 |         *out++ = (mode & 010) ? 'x' : '-'; | 
 | 163 |     } | 
 | 164 |     *out++ = (mode & 04) ? 'r' : '-'; | 
 | 165 |     *out++ = (mode & 02) ? 'w' : '-'; | 
 | 166 |     if(mode & 01000) { | 
 | 167 |         *out++ = (mode & 01) ? 't' : 'T'; | 
 | 168 |     } else { | 
 | 169 |         *out++ = (mode & 01) ? 'x' : '-'; | 
 | 170 |     } | 
 | 171 |     *out = 0; | 
 | 172 | } | 
 | 173 |  | 
 | 174 | static void user2str(uid_t uid, char *out, size_t out_size) | 
 | 175 | { | 
 | 176 |     struct passwd *pw = getpwuid(uid); | 
 | 177 |     if(pw) { | 
 | 178 |         strlcpy(out, pw->pw_name, out_size); | 
 | 179 |     } else { | 
 | 180 |         snprintf(out, out_size, "%d", uid); | 
 | 181 |     } | 
 | 182 | } | 
 | 183 |  | 
 | 184 | static void group2str(gid_t gid, char *out, size_t out_size) | 
 | 185 | { | 
 | 186 |     struct group *gr = getgrgid(gid); | 
 | 187 |     if(gr) { | 
 | 188 |         strlcpy(out, gr->gr_name, out_size); | 
 | 189 |     } else { | 
 | 190 |         snprintf(out, out_size, "%d", gid); | 
 | 191 |     } | 
 | 192 | } | 
 | 193 |  | 
 | 194 | static int show_total_size(const char *dirname, DIR *d, int flags) | 
 | 195 | { | 
 | 196 |     struct dirent *de; | 
 | 197 |     char tmp[1024]; | 
 | 198 |     struct stat s; | 
 | 199 |     int sum = 0; | 
 | 200 |  | 
 | 201 |     /* run through the directory and sum up the file block sizes */ | 
 | 202 |     while ((de = readdir(d)) != 0) { | 
 | 203 |         if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0) | 
 | 204 |             continue; | 
 | 205 |         if (de->d_name[0] == '.' && (flags & LIST_ALL) == 0) | 
 | 206 |             continue; | 
 | 207 |  | 
 | 208 |         if (strcmp(dirname, "/") == 0) | 
 | 209 |             snprintf(tmp, sizeof(tmp), "/%s", de->d_name); | 
 | 210 |         else | 
 | 211 |             snprintf(tmp, sizeof(tmp), "%s/%s", dirname, de->d_name); | 
 | 212 |  | 
 | 213 |         if (lstat(tmp, &s) < 0) { | 
 | 214 |             fprintf(stderr, "stat failed on %s: %s\n", tmp, strerror(errno)); | 
 | 215 |             rewinddir(d); | 
 | 216 |             return -1; | 
 | 217 |         } | 
 | 218 |  | 
 | 219 |         sum += s.st_blocks / 2; | 
 | 220 |     } | 
 | 221 |  | 
 | 222 |     printf("total %d\n", sum); | 
 | 223 |     rewinddir(d); | 
 | 224 |     return 0; | 
 | 225 | } | 
 | 226 |  | 
 | 227 | static int listfile_size(const char *path, const char *filename, struct stat *s, | 
 | 228 |                          int flags) | 
 | 229 | { | 
 | 230 |     if(!s || !path) { | 
 | 231 |         return -1; | 
 | 232 |     } | 
 | 233 |  | 
 | 234 |     /* blocks are 512 bytes, we want output to be KB */ | 
 | 235 |     if ((flags & LIST_SIZE) != 0) { | 
 | 236 |         printf("%lld ", (long long)s->st_blocks / 2); | 
 | 237 |     } | 
 | 238 |  | 
 | 239 |     if ((flags & LIST_CLASSIFY) != 0) { | 
 | 240 |         char filetype = mode2kind(s->st_mode); | 
 | 241 |         if (filetype != 'l') { | 
 | 242 |             printf("%c ", filetype); | 
 | 243 |         } else { | 
 | 244 |             struct stat link_dest; | 
 | 245 |             if (!stat(path, &link_dest)) { | 
 | 246 |                 printf("l%c ", mode2kind(link_dest.st_mode)); | 
 | 247 |             } else { | 
 | 248 |                 fprintf(stderr, "stat '%s' failed: %s\n", path, strerror(errno)); | 
 | 249 |                 printf("l? "); | 
 | 250 |             } | 
 | 251 |         } | 
 | 252 |     } | 
 | 253 |  | 
 | 254 |     printf("%s\n", filename); | 
 | 255 |  | 
 | 256 |     return 0; | 
 | 257 | } | 
 | 258 |  | 
 | 259 | static int listfile_long(const char *path, struct stat *s, int flags) | 
 | 260 | { | 
 | 261 |     char date[32]; | 
 | 262 |     char mode[16]; | 
 | 263 |     char user[32]; | 
 | 264 |     char group[32]; | 
 | 265 |     const char *name; | 
 | 266 |  | 
 | 267 |     if(!s || !path) { | 
 | 268 |         return -1; | 
 | 269 |     } | 
 | 270 |  | 
 | 271 |     /* name is anything after the final '/', or the whole path if none*/ | 
 | 272 |     name = strrchr(path, '/'); | 
 | 273 |     if(name == 0) { | 
 | 274 |         name = path; | 
 | 275 |     } else { | 
 | 276 |         name++; | 
 | 277 |     } | 
 | 278 |  | 
 | 279 |     strmode(s->st_mode, mode); | 
 | 280 |     if (flags & LIST_LONG_NUMERIC) { | 
 | 281 |         snprintf(user, sizeof(user), "%u", s->st_uid); | 
 | 282 |         snprintf(group, sizeof(group), "%u", s->st_gid); | 
 | 283 |     } else { | 
 | 284 |         user2str(s->st_uid, user, sizeof(user)); | 
 | 285 |         group2str(s->st_gid, group, sizeof(group)); | 
 | 286 |     } | 
 | 287 |  | 
 | 288 |     strftime(date, 32, "%Y-%m-%d %H:%M", localtime((const time_t*)&s->st_mtime)); | 
 | 289 |     date[31] = 0; | 
 | 290 |  | 
 | 291 | // 12345678901234567890123456789012345678901234567890123456789012345678901234567890 | 
 | 292 | // MMMMMMMM UUUUUUUU GGGGGGGGG XXXXXXXX YYYY-MM-DD HH:MM NAME (->LINK) | 
 | 293 |  | 
 | 294 |     switch(s->st_mode & S_IFMT) { | 
 | 295 |     case S_IFBLK: | 
 | 296 |     case S_IFCHR: | 
 | 297 |         printf("%s %-8s %-8s %3d, %3d %s %s\n", | 
 | 298 |                mode, user, group, | 
 | 299 |                major(s->st_rdev), minor(s->st_rdev), | 
 | 300 |                date, name); | 
 | 301 |         break; | 
 | 302 |     case S_IFREG: | 
 | 303 |         printf("%s %-8s %-8s %8lld %s %s\n", | 
 | 304 |                mode, user, group, (long long)s->st_size, date, name); | 
 | 305 |         break; | 
 | 306 |     case S_IFLNK: { | 
 | 307 |         char linkto[256]; | 
 | 308 |         ssize_t len; | 
 | 309 |  | 
 | 310 |         len = readlink(path, linkto, 256); | 
 | 311 |         if(len < 0) return -1; | 
 | 312 |  | 
 | 313 |         if(len > 255) { | 
 | 314 |             linkto[252] = '.'; | 
 | 315 |             linkto[253] = '.'; | 
 | 316 |             linkto[254] = '.'; | 
 | 317 |             linkto[255] = 0; | 
 | 318 |         } else { | 
 | 319 |             linkto[len] = 0; | 
 | 320 |         } | 
 | 321 |  | 
 | 322 |         printf("%s %-8s %-8s          %s %s -> %s\n", | 
 | 323 |                mode, user, group, date, name, linkto); | 
 | 324 |         break; | 
 | 325 |     } | 
 | 326 |     default: | 
 | 327 |         printf("%s %-8s %-8s          %s %s\n", | 
 | 328 |                mode, user, group, date, name); | 
 | 329 |  | 
 | 330 |     } | 
 | 331 |     return 0; | 
 | 332 | } | 
 | 333 |  | 
 | 334 | static int listfile_maclabel(const char *path, struct stat *s) | 
 | 335 | { | 
 | 336 |     char mode[16]; | 
 | 337 |     char user[32]; | 
 | 338 |     char group[32]; | 
 | 339 |     char *maclabel = NULL; | 
 | 340 |     const char *name; | 
 | 341 |  | 
 | 342 |     if(!s || !path) { | 
 | 343 |         return -1; | 
 | 344 |     } | 
 | 345 |  | 
 | 346 |     /* name is anything after the final '/', or the whole path if none*/ | 
 | 347 |     name = strrchr(path, '/'); | 
 | 348 |     if(name == 0) { | 
 | 349 |         name = path; | 
 | 350 |     } else { | 
 | 351 |         name++; | 
 | 352 |     } | 
 | 353 |  | 
 | 354 |     lgetfilecon(path, &maclabel); | 
 | 355 |     if (!maclabel) { | 
 | 356 |         return -1; | 
 | 357 |     } | 
 | 358 |  | 
 | 359 |     strmode(s->st_mode, mode); | 
 | 360 |     user2str(s->st_uid, user, sizeof(user)); | 
 | 361 |     group2str(s->st_gid, group, sizeof(group)); | 
 | 362 |  | 
 | 363 |     switch(s->st_mode & S_IFMT) { | 
 | 364 |     case S_IFLNK: { | 
 | 365 |         char linkto[256]; | 
 | 366 |         ssize_t len; | 
 | 367 |  | 
 | 368 |         len = readlink(path, linkto, sizeof(linkto)); | 
 | 369 |         if(len < 0) return -1; | 
 | 370 |  | 
 | 371 |         if((size_t)len > sizeof(linkto)-1) { | 
 | 372 |             linkto[sizeof(linkto)-4] = '.'; | 
 | 373 |             linkto[sizeof(linkto)-3] = '.'; | 
 | 374 |             linkto[sizeof(linkto)-2] = '.'; | 
 | 375 |             linkto[sizeof(linkto)-1] = 0; | 
 | 376 |         } else { | 
 | 377 |             linkto[len] = 0; | 
 | 378 |         } | 
 | 379 |  | 
 | 380 |         printf("%s %-8s %-8s          %s %s -> %s\n", | 
 | 381 |                mode, user, group, maclabel, name, linkto); | 
 | 382 |         break; | 
 | 383 |     } | 
 | 384 |     default: | 
 | 385 |         printf("%s %-8s %-8s          %s %s\n", | 
 | 386 |                mode, user, group, maclabel, name); | 
 | 387 |  | 
 | 388 |     } | 
 | 389 |  | 
 | 390 |     free(maclabel); | 
 | 391 |  | 
 | 392 |     return 0; | 
 | 393 | } | 
 | 394 |  | 
 | 395 | static int listfile(const char *dirname, const char *filename, int flags) | 
 | 396 | { | 
 | 397 |     struct stat s; | 
 | 398 |  | 
 | 399 |     if ((flags & (LIST_LONG | LIST_SIZE | LIST_CLASSIFY | LIST_MACLABEL | LIST_INODE)) == 0) { | 
 | 400 |         printf("%s\n", filename); | 
 | 401 |         return 0; | 
 | 402 |     } | 
 | 403 |  | 
 | 404 |     char tmp[4096]; | 
 | 405 |     const char* pathname = filename; | 
 | 406 |  | 
 | 407 |     if (dirname != NULL) { | 
 | 408 |         snprintf(tmp, sizeof(tmp), "%s/%s", dirname, filename); | 
 | 409 |         pathname = tmp; | 
 | 410 |     } else { | 
 | 411 |         pathname = filename; | 
 | 412 |     } | 
 | 413 |  | 
 | 414 |     if(lstat(pathname, &s) < 0) { | 
 | 415 |         fprintf(stderr, "lstat '%s' failed: %s\n", pathname, strerror(errno)); | 
 | 416 |         return -1; | 
 | 417 |     } | 
 | 418 |  | 
 | 419 |     if(flags & LIST_INODE) { | 
 | 420 |         printf("%8llu ", (unsigned long long)s.st_ino); | 
 | 421 |     } | 
 | 422 |  | 
 | 423 |     if ((flags & LIST_MACLABEL) != 0) { | 
 | 424 |         return listfile_maclabel(pathname, &s); | 
 | 425 |     } else if ((flags & LIST_LONG) != 0) { | 
 | 426 |         return listfile_long(pathname, &s, flags); | 
 | 427 |     } else /*((flags & LIST_SIZE) != 0)*/ { | 
 | 428 |         return listfile_size(pathname, filename, &s, flags); | 
 | 429 |     } | 
 | 430 | } | 
 | 431 |  | 
 | 432 | static int listdir(const char *name, int flags) | 
 | 433 | { | 
 | 434 |     char tmp[4096]; | 
 | 435 |     DIR *d; | 
 | 436 |     struct dirent *de; | 
 | 437 |     strlist_t  files = STRLIST_INITIALIZER; | 
 | 438 |  | 
 | 439 |     d = opendir(name); | 
 | 440 |     if(d == 0) { | 
 | 441 |         fprintf(stderr, "opendir failed, %s\n", strerror(errno)); | 
 | 442 |         return -1; | 
 | 443 |     } | 
 | 444 |  | 
 | 445 |     if ((flags & LIST_SIZE) != 0) { | 
 | 446 |         show_total_size(name, d, flags); | 
 | 447 |     } | 
 | 448 |  | 
 | 449 |     while((de = readdir(d)) != 0){ | 
 | 450 |         if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) continue; | 
 | 451 |         if(de->d_name[0] == '.' && (flags & LIST_ALL) == 0) continue; | 
 | 452 |  | 
 | 453 |         strlist_append_dup(&files, de->d_name); | 
 | 454 |     } | 
 | 455 |  | 
 | 456 |     strlist_sort(&files); | 
 | 457 |     STRLIST_FOREACH(&files, filename, listfile(name, filename, flags)); | 
 | 458 |     strlist_done(&files); | 
 | 459 |  | 
 | 460 |     if (flags & LIST_RECURSIVE) { | 
 | 461 |         strlist_t subdirs = STRLIST_INITIALIZER; | 
 | 462 |  | 
 | 463 |         rewinddir(d); | 
 | 464 |  | 
 | 465 |         while ((de = readdir(d)) != 0) { | 
 | 466 |             struct stat s; | 
 | 467 |             int err; | 
 | 468 |  | 
 | 469 |             if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) | 
 | 470 |                 continue; | 
 | 471 |             if (de->d_name[0] == '.' && (flags & LIST_ALL) == 0) | 
 | 472 |                 continue; | 
 | 473 |  | 
 | 474 |             if (!strcmp(name, "/")) | 
 | 475 |                 snprintf(tmp, sizeof(tmp), "/%s", de->d_name); | 
 | 476 |             else | 
 | 477 |                 snprintf(tmp, sizeof(tmp), "%s/%s", name, de->d_name); | 
 | 478 |  | 
 | 479 |             /* | 
 | 480 |              * If the name ends in a '/', use stat() so we treat it like a | 
 | 481 |              * directory even if it's a symlink. | 
 | 482 |              */ | 
 | 483 |             if (tmp[strlen(tmp)-1] == '/') | 
 | 484 |                 err = stat(tmp, &s); | 
 | 485 |             else | 
 | 486 |                 err = lstat(tmp, &s); | 
 | 487 |  | 
 | 488 |             if (err < 0) { | 
 | 489 |                 perror(tmp); | 
 | 490 |                 closedir(d); | 
 | 491 |                 return -1; | 
 | 492 |             } | 
 | 493 |  | 
 | 494 |             if (S_ISDIR(s.st_mode)) { | 
 | 495 |                 strlist_append_dup(&subdirs, tmp); | 
 | 496 |             } | 
 | 497 |         } | 
 | 498 |         strlist_sort(&subdirs); | 
 | 499 |         STRLIST_FOREACH(&subdirs, path, { | 
 | 500 |             printf("\n%s:\n", path); | 
 | 501 |             listdir(path, flags); | 
 | 502 |         }); | 
 | 503 |         strlist_done(&subdirs); | 
 | 504 |     } | 
 | 505 |  | 
 | 506 |     closedir(d); | 
 | 507 |     return 0; | 
 | 508 | } | 
 | 509 |  | 
 | 510 | static int listpath(const char *name, int flags) | 
 | 511 | { | 
 | 512 |     struct stat s; | 
 | 513 |     int err; | 
 | 514 |  | 
 | 515 |     /* | 
 | 516 |      * If the name ends in a '/', use stat() so we treat it like a | 
 | 517 |      * directory even if it's a symlink. | 
 | 518 |      */ | 
 | 519 |     if (name[strlen(name)-1] == '/') | 
 | 520 |         err = stat(name, &s); | 
 | 521 |     else | 
 | 522 |         err = lstat(name, &s); | 
 | 523 |  | 
 | 524 |     if (err < 0) { | 
 | 525 |         perror(name); | 
 | 526 |         return -1; | 
 | 527 |     } | 
 | 528 |  | 
 | 529 |     if ((flags & LIST_DIRECTORIES) == 0 && S_ISDIR(s.st_mode)) { | 
 | 530 |         if (flags & LIST_RECURSIVE) | 
 | 531 |             printf("\n%s:\n", name); | 
 | 532 |         return listdir(name, flags); | 
 | 533 |     } else { | 
 | 534 |         /* yeah this calls stat() again*/ | 
 | 535 |         return listfile(NULL, name, flags); | 
 | 536 |     } | 
 | 537 | } | 
 | 538 |  | 
 | 539 | int ls_main(int argc, char **argv) | 
 | 540 | { | 
 | 541 |     int flags = 0; | 
 | 542 |  | 
 | 543 |     if(argc > 1) { | 
 | 544 |         int i; | 
 | 545 |         int err = 0; | 
 | 546 |         strlist_t  files = STRLIST_INITIALIZER; | 
 | 547 |  | 
 | 548 |         for (i = 1; i < argc; i++) { | 
 | 549 |             if (argv[i][0] == '-') { | 
 | 550 |                 /* an option ? */ | 
 | 551 |                 const char *arg = argv[i]+1; | 
 | 552 |                 while (arg[0]) { | 
 | 553 |                     switch (arg[0]) { | 
 | 554 |                     case 'l': flags |= LIST_LONG; break; | 
 | 555 |                     case 'n': flags |= LIST_LONG | LIST_LONG_NUMERIC; break; | 
 | 556 |                     case 's': flags |= LIST_SIZE; break; | 
 | 557 |                     case 'R': flags |= LIST_RECURSIVE; break; | 
 | 558 |                     case 'd': flags |= LIST_DIRECTORIES; break; | 
 | 559 |                     case 'Z': flags |= LIST_MACLABEL; break; | 
 | 560 |                     case 'a': flags |= LIST_ALL; break; | 
 | 561 |                     case 'F': flags |= LIST_CLASSIFY; break; | 
 | 562 |                     case 'i': flags |= LIST_INODE; break; | 
 | 563 |                     default: | 
 | 564 |                         fprintf(stderr, "%s: Unknown option '-%c'. Aborting.\n", "ls", arg[0]); | 
 | 565 |                         exit(1); | 
 | 566 |                     } | 
 | 567 |                     arg++; | 
 | 568 |                 } | 
 | 569 |             } else { | 
 | 570 |                 /* not an option ? */ | 
 | 571 |                 strlist_append_dup(&files, argv[i]); | 
 | 572 |             } | 
 | 573 |         } | 
 | 574 |  | 
 | 575 |         if (files.count > 0) { | 
 | 576 |             STRLIST_FOREACH(&files, path, { | 
 | 577 |                 if (listpath(path, flags) != 0) { | 
 | 578 |                     err = EXIT_FAILURE; | 
 | 579 |                 } | 
 | 580 |             }); | 
 | 581 |             strlist_done(&files); | 
 | 582 |             return err; | 
 | 583 |         } | 
 | 584 |     } | 
 | 585 |  | 
 | 586 |     // list working directory if no files or directories were specified | 
 | 587 |     return listpath(".", flags); | 
 | 588 | } |