ls.c revision 070ec8e02e12aafc8a636737bba3d5ead7cecd8e
1#include <stdio.h> 2#include <stdlib.h> 3#include <string.h> 4#include <sys/types.h> 5#include <dirent.h> 6#include <errno.h> 7 8#include <sys/stat.h> 9#include <unistd.h> 10#include <time.h> 11 12#include <pwd.h> 13#include <grp.h> 14 15#include <linux/kdev_t.h> 16#include <limits.h> 17 18#include "dynarray.h" 19 20// bits for flags argument 21#define LIST_LONG (1 << 0) 22#define LIST_ALL (1 << 1) 23#define LIST_RECURSIVE (1 << 2) 24#define LIST_DIRECTORIES (1 << 3) 25#define LIST_SIZE (1 << 4) 26#define LIST_LONG_NUMERIC (1 << 5) 27#define LIST_CLASSIFY (1 << 6) 28 29// fwd 30static int listpath(const char *name, int flags); 31 32static char mode2kind(unsigned mode) 33{ 34 switch(mode & S_IFMT){ 35 case S_IFSOCK: return 's'; 36 case S_IFLNK: return 'l'; 37 case S_IFREG: return '-'; 38 case S_IFDIR: return 'd'; 39 case S_IFBLK: return 'b'; 40 case S_IFCHR: return 'c'; 41 case S_IFIFO: return 'p'; 42 default: return '?'; 43 } 44} 45 46static void mode2str(unsigned mode, char *out) 47{ 48 *out++ = mode2kind(mode); 49 50 *out++ = (mode & 0400) ? 'r' : '-'; 51 *out++ = (mode & 0200) ? 'w' : '-'; 52 if(mode & 04000) { 53 *out++ = (mode & 0100) ? 's' : 'S'; 54 } else { 55 *out++ = (mode & 0100) ? 'x' : '-'; 56 } 57 *out++ = (mode & 040) ? 'r' : '-'; 58 *out++ = (mode & 020) ? 'w' : '-'; 59 if(mode & 02000) { 60 *out++ = (mode & 010) ? 's' : 'S'; 61 } else { 62 *out++ = (mode & 010) ? 'x' : '-'; 63 } 64 *out++ = (mode & 04) ? 'r' : '-'; 65 *out++ = (mode & 02) ? 'w' : '-'; 66 if(mode & 01000) { 67 *out++ = (mode & 01) ? 't' : 'T'; 68 } else { 69 *out++ = (mode & 01) ? 'x' : '-'; 70 } 71 *out = 0; 72} 73 74static void user2str(unsigned uid, char *out) 75{ 76 struct passwd *pw = getpwuid(uid); 77 if(pw) { 78 strcpy(out, pw->pw_name); 79 } else { 80 sprintf(out, "%d", uid); 81 } 82} 83 84static void group2str(unsigned gid, char *out) 85{ 86 struct group *gr = getgrgid(gid); 87 if(gr) { 88 strcpy(out, gr->gr_name); 89 } else { 90 sprintf(out, "%d", gid); 91 } 92} 93 94static int show_total_size(const char *dirname, DIR *d, int flags) 95{ 96 struct dirent *de; 97 char tmp[1024]; 98 struct stat s; 99 int sum = 0; 100 101 /* run through the directory and sum up the file block sizes */ 102 while ((de = readdir(d)) != 0) { 103 if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0) 104 continue; 105 if (de->d_name[0] == '.' && (flags & LIST_ALL) == 0) 106 continue; 107 108 if (strcmp(dirname, "/") == 0) 109 snprintf(tmp, sizeof(tmp), "/%s", de->d_name); 110 else 111 snprintf(tmp, sizeof(tmp), "%s/%s", dirname, de->d_name); 112 113 if (lstat(tmp, &s) < 0) { 114 fprintf(stderr, "stat failed on %s: %s\n", tmp, strerror(errno)); 115 rewinddir(d); 116 return -1; 117 } 118 119 sum += s.st_blocks / 2; 120 } 121 122 printf("total %d\n", sum); 123 rewinddir(d); 124 return 0; 125} 126 127static int listfile_size(const char *path, const char *filename, int flags) 128{ 129 struct stat s; 130 131 if (lstat(path, &s) < 0) { 132 fprintf(stderr, "lstat '%s' failed: %s\n", path, strerror(errno)); 133 return -1; 134 } 135 136 /* blocks are 512 bytes, we want output to be KB */ 137 if ((flags & LIST_SIZE) != 0) { 138 printf("%lld ", s.st_blocks / 2); 139 } 140 141 if ((flags & LIST_CLASSIFY) != 0) { 142 char filetype = mode2kind(s.st_mode); 143 if (filetype != 'l') { 144 printf("%c ", filetype); 145 } else { 146 struct stat link_dest; 147 if (!stat(path, &link_dest)) { 148 printf("l%c ", mode2kind(link_dest.st_mode)); 149 } else { 150 fprintf(stderr, "stat '%s' failed: %s\n", path, strerror(errno)); 151 printf("l? "); 152 } 153 } 154 } 155 156 printf("%s\n", filename); 157 158 return 0; 159} 160 161static int listfile_long(const char *path, int flags) 162{ 163 struct stat s; 164 char date[32]; 165 char mode[16]; 166 char user[16]; 167 char group[16]; 168 const char *name; 169 170 /* name is anything after the final '/', or the whole path if none*/ 171 name = strrchr(path, '/'); 172 if(name == 0) { 173 name = path; 174 } else { 175 name++; 176 } 177 178 if(lstat(path, &s) < 0) { 179 return -1; 180 } 181 182 mode2str(s.st_mode, mode); 183 if (flags & LIST_LONG_NUMERIC) { 184 sprintf(user, "%ld", s.st_uid); 185 sprintf(group, "%ld", s.st_gid); 186 } else { 187 user2str(s.st_uid, user); 188 group2str(s.st_gid, group); 189 } 190 191 strftime(date, 32, "%Y-%m-%d %H:%M", localtime((const time_t*)&s.st_mtime)); 192 date[31] = 0; 193 194// 12345678901234567890123456789012345678901234567890123456789012345678901234567890 195// MMMMMMMM UUUUUUUU GGGGGGGGG XXXXXXXX YYYY-MM-DD HH:MM NAME (->LINK) 196 197 switch(s.st_mode & S_IFMT) { 198 case S_IFBLK: 199 case S_IFCHR: 200 printf("%s %-8s %-8s %3d, %3d %s %s\n", 201 mode, user, group, 202 (int) MAJOR(s.st_rdev), (int) MINOR(s.st_rdev), 203 date, name); 204 break; 205 case S_IFREG: 206 printf("%s %-8s %-8s %8lld %s %s\n", 207 mode, user, group, s.st_size, date, name); 208 break; 209 case S_IFLNK: { 210 char linkto[256]; 211 int len; 212 213 len = readlink(path, linkto, 256); 214 if(len < 0) return -1; 215 216 if(len > 255) { 217 linkto[252] = '.'; 218 linkto[253] = '.'; 219 linkto[254] = '.'; 220 linkto[255] = 0; 221 } else { 222 linkto[len] = 0; 223 } 224 225 printf("%s %-8s %-8s %s %s -> %s\n", 226 mode, user, group, date, name, linkto); 227 break; 228 } 229 default: 230 printf("%s %-8s %-8s %s %s\n", 231 mode, user, group, date, name); 232 233 } 234 return 0; 235} 236 237static int listfile(const char *dirname, const char *filename, int flags) 238{ 239 if ((flags & (LIST_LONG | LIST_SIZE | LIST_CLASSIFY)) == 0) { 240 printf("%s\n", filename); 241 return 0; 242 } 243 244 char tmp[4096]; 245 const char* pathname = filename; 246 247 if (dirname != NULL) { 248 snprintf(tmp, sizeof(tmp), "%s/%s", dirname, filename); 249 pathname = tmp; 250 } else { 251 pathname = filename; 252 } 253 254 if ((flags & LIST_LONG) != 0) { 255 return listfile_long(pathname, flags); 256 } else /*((flags & LIST_SIZE) != 0)*/ { 257 return listfile_size(pathname, filename, flags); 258 } 259} 260 261static int listdir(const char *name, int flags) 262{ 263 char tmp[4096]; 264 DIR *d; 265 struct dirent *de; 266 strlist_t files = STRLIST_INITIALIZER; 267 268 d = opendir(name); 269 if(d == 0) { 270 fprintf(stderr, "opendir failed, %s\n", strerror(errno)); 271 return -1; 272 } 273 274 if ((flags & LIST_SIZE) != 0) { 275 show_total_size(name, d, flags); 276 } 277 278 while((de = readdir(d)) != 0){ 279 if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) continue; 280 if(de->d_name[0] == '.' && (flags & LIST_ALL) == 0) continue; 281 282 strlist_append_dup(&files, de->d_name); 283 } 284 285 strlist_sort(&files); 286 STRLIST_FOREACH(&files, filename, listfile(name, filename, flags)); 287 strlist_done(&files); 288 289 if (flags & LIST_RECURSIVE) { 290 strlist_t subdirs = STRLIST_INITIALIZER; 291 292 rewinddir(d); 293 294 while ((de = readdir(d)) != 0) { 295 struct stat s; 296 int err; 297 298 if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) 299 continue; 300 if (de->d_name[0] == '.' && (flags & LIST_ALL) == 0) 301 continue; 302 303 if (!strcmp(name, "/")) 304 snprintf(tmp, sizeof(tmp), "/%s", de->d_name); 305 else 306 snprintf(tmp, sizeof(tmp), "%s/%s", name, de->d_name); 307 308 /* 309 * If the name ends in a '/', use stat() so we treat it like a 310 * directory even if it's a symlink. 311 */ 312 if (tmp[strlen(tmp)-1] == '/') 313 err = stat(tmp, &s); 314 else 315 err = lstat(tmp, &s); 316 317 if (err < 0) { 318 perror(tmp); 319 closedir(d); 320 return -1; 321 } 322 323 if (S_ISDIR(s.st_mode)) { 324 strlist_append_dup(&subdirs, tmp); 325 } 326 } 327 strlist_sort(&subdirs); 328 STRLIST_FOREACH(&subdirs, path, { 329 printf("\n%s:\n", path); 330 listdir(path, flags); 331 }); 332 strlist_done(&subdirs); 333 } 334 335 closedir(d); 336 return 0; 337} 338 339static int listpath(const char *name, int flags) 340{ 341 struct stat s; 342 int err; 343 344 /* 345 * If the name ends in a '/', use stat() so we treat it like a 346 * directory even if it's a symlink. 347 */ 348 if (name[strlen(name)-1] == '/') 349 err = stat(name, &s); 350 else 351 err = lstat(name, &s); 352 353 if (err < 0) { 354 perror(name); 355 return -1; 356 } 357 358 if ((flags & LIST_DIRECTORIES) == 0 && S_ISDIR(s.st_mode)) { 359 if (flags & LIST_RECURSIVE) 360 printf("\n%s:\n", name); 361 return listdir(name, flags); 362 } else { 363 /* yeah this calls stat() again*/ 364 return listfile(NULL, name, flags); 365 } 366} 367 368int ls_main(int argc, char **argv) 369{ 370 int flags = 0; 371 int listed = 0; 372 373 if(argc > 1) { 374 int i; 375 int err = 0; 376 strlist_t files = STRLIST_INITIALIZER; 377 378 for (i = 1; i < argc; i++) { 379 if (argv[i][0] == '-') { 380 /* an option ? */ 381 const char *arg = argv[i]+1; 382 while (arg[0]) { 383 switch (arg[0]) { 384 case 'l': flags |= LIST_LONG; break; 385 case 'n': flags |= LIST_LONG | LIST_LONG_NUMERIC; break; 386 case 's': flags |= LIST_SIZE; break; 387 case 'R': flags |= LIST_RECURSIVE; break; 388 case 'd': flags |= LIST_DIRECTORIES; break; 389 case 'a': flags |= LIST_ALL; break; 390 case 'F': flags |= LIST_CLASSIFY; break; 391 default: 392 fprintf(stderr, "%s: Unknown option '-%c'. Aborting.\n", "ls", arg[0]); 393 exit(1); 394 } 395 arg++; 396 } 397 } else { 398 /* not an option ? */ 399 strlist_append_dup(&files, argv[i]); 400 } 401 } 402 403 if (files.count > 0) { 404 STRLIST_FOREACH(&files, path, { 405 if (listpath(path, flags) != 0) { 406 err = EXIT_FAILURE; 407 } 408 }); 409 strlist_done(&files); 410 return err; 411 } 412 } 413 414 // list working directory if no files or directories were specified 415 return listpath(".", flags); 416} 417