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