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