ls.c revision 327e696808910f8e49370079f8d7f4db8cbe3820
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 25static int listpath(const char *name, int flags); 26 27static 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 41static 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 69static 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 79static 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 89static int listfile_size(const char *path, int flags) 90{ 91 struct stat s; 92 93 if (lstat(path, &s) < 0) 94 return -1; 95 96 /* blocks are 512 bytes, we want output to be KB */ 97 printf("%lld %s\n", s.st_blocks / 2, path); 98 return 0; 99} 100 101static int listfile_long(const char *path, int flags) 102{ 103 struct stat s; 104 char date[32]; 105 char mode[16]; 106 char user[16]; 107 char group[16]; 108 const char *name; 109 110 /* name is anything after the final '/', or the whole path if none*/ 111 name = strrchr(path, '/'); 112 if(name == 0) { 113 name = path; 114 } else { 115 name++; 116 } 117 118 if(lstat(path, &s) < 0) { 119 return -1; 120 } 121 122 mode2str(s.st_mode, mode); 123 user2str(s.st_uid, user); 124 group2str(s.st_gid, group); 125 126 strftime(date, 32, "%Y-%m-%d %H:%M", localtime((const time_t*)&s.st_mtime)); 127 date[31] = 0; 128 129// 12345678901234567890123456789012345678901234567890123456789012345678901234567890 130// MMMMMMMM UUUUUUUU GGGGGGGGG XXXXXXXX YYYY-MM-DD HH:MM NAME (->LINK) 131 132 switch(s.st_mode & S_IFMT) { 133 case S_IFBLK: 134 case S_IFCHR: 135 printf("%s %-8s %-8s %3d, %3d %s %s\n", 136 mode, user, group, 137 (int) MAJOR(s.st_rdev), (int) MINOR(s.st_rdev), 138 date, name); 139 break; 140 case S_IFREG: 141 printf("%s %-8s %-8s %8d %s %s\n", 142 mode, user, group, (int) s.st_size, date, name); 143 break; 144 case S_IFLNK: { 145 char linkto[256]; 146 int len; 147 148 len = readlink(path, linkto, 256); 149 if(len < 0) return -1; 150 151 if(len > 255) { 152 linkto[252] = '.'; 153 linkto[253] = '.'; 154 linkto[254] = '.'; 155 linkto[255] = 0; 156 } else { 157 linkto[len] = 0; 158 } 159 160 printf("%s %-8s %-8s %s %s -> %s\n", 161 mode, user, group, date, name, linkto); 162 break; 163 } 164 default: 165 printf("%s %-8s %-8s %s %s\n", 166 mode, user, group, date, name); 167 168 } 169 return 0; 170} 171 172static int listfile(const char *dirname, const char *filename, int flags) 173{ 174 if ((flags & (LIST_LONG | LIST_SIZE)) == 0) { 175 printf("%s\n", filename); 176 return 0; 177 } 178 179 char tmp[4096]; 180 const char* pathname = filename; 181 182 if (dirname != NULL) { 183 snprintf(tmp, sizeof(tmp), "%s/%s", dirname, filename); 184 pathname = tmp; 185 } else { 186 pathname = filename; 187 } 188 189 if ((flags & LIST_LONG) != 0) { 190 return listfile_long(pathname, flags); 191 } else /*((flags & LIST_SIZE) != 0)*/ { 192 return listfile_size(pathname, flags); 193 } 194} 195 196static int listdir(const char *name, int flags) 197{ 198 char tmp[4096]; 199 DIR *d; 200 struct dirent *de; 201 202 d = opendir(name); 203 if(d == 0) { 204 fprintf(stderr, "opendir failed, %s\n", strerror(errno)); 205 return -1; 206 } 207 208 while((de = readdir(d)) != 0){ 209 if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) continue; 210 if(de->d_name[0] == '.' && (flags & LIST_ALL) == 0) continue; 211 212 listfile(name, de->d_name, flags); 213 } 214 215 if (flags & LIST_RECURSIVE) { 216 rewinddir(d); 217 218 while ((de = readdir(d)) != 0) { 219 struct stat s; 220 int err; 221 222 if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) 223 continue; 224 if (de->d_name[0] == '.' && (flags & LIST_ALL) == 0) 225 continue; 226 227 if (!strcmp(name, "/")) 228 snprintf(tmp, sizeof(tmp), "/%s", de->d_name); 229 else 230 snprintf(tmp, sizeof(tmp), "%s/%s", name, de->d_name); 231 232 /* 233 * If the name ends in a '/', use stat() so we treat it like a 234 * directory even if it's a symlink. 235 */ 236 if (tmp[strlen(tmp)-1] == '/') 237 err = stat(tmp, &s); 238 else 239 err = lstat(tmp, &s); 240 241 if (err < 0) { 242 perror(tmp); 243 closedir(d); 244 return -1; 245 } 246 247 if (S_ISDIR(s.st_mode)) { 248 printf("\n%s:\n", tmp); 249 listdir(tmp, flags); 250 } 251 } 252 } 253 254 closedir(d); 255 return 0; 256} 257 258static int listpath(const char *name, int flags) 259{ 260 struct stat s; 261 int err; 262 263 /* 264 * If the name ends in a '/', use stat() so we treat it like a 265 * directory even if it's a symlink. 266 */ 267 if (name[strlen(name)-1] == '/') 268 err = stat(name, &s); 269 else 270 err = lstat(name, &s); 271 272 if (err < 0) { 273 perror(name); 274 return -1; 275 } 276 277 if ((flags & LIST_DIRECTORIES) == 0 && S_ISDIR(s.st_mode)) { 278 if (flags & LIST_RECURSIVE) 279 printf("\n%s:\n", name); 280 return listdir(name, flags); 281 } else { 282 /* yeah this calls stat() again*/ 283 return listfile(NULL, name, flags); 284 } 285} 286 287int ls_main(int argc, char **argv) 288{ 289 int flags = 0; 290 int listed = 0; 291 292 if(argc > 1) { 293 int i; 294 int err = 0; 295 296 for (i = 1; i < argc; i++) { 297 if (!strcmp(argv[i], "-l")) { 298 flags |= LIST_LONG; 299 } else if (!strcmp(argv[i], "-s")) { 300 flags |= LIST_SIZE; 301 } else if (!strcmp(argv[i], "-a")) { 302 flags |= LIST_ALL; 303 } else if (!strcmp(argv[i], "-R")) { 304 flags |= LIST_RECURSIVE; 305 } else if (!strcmp(argv[i], "-d")) { 306 flags |= LIST_DIRECTORIES; 307 } else { 308 listed++; 309 if(listpath(argv[i], flags) != 0) { 310 err = EXIT_FAILURE; 311 } 312 } 313 } 314 315 if (listed > 0) return err; 316 } 317 318 // list working directory if no files or directories were specified 319 return listpath(".", flags); 320} 321