ls.c revision dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0
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 22// fwd 23static int listpath(const char *name, int flags); 24 25static char mode2kind(unsigned mode) 26{ 27 switch(mode & S_IFMT){ 28 case S_IFSOCK: return 's'; 29 case S_IFLNK: return 'l'; 30 case S_IFREG: return '-'; 31 case S_IFDIR: return 'd'; 32 case S_IFBLK: return 'b'; 33 case S_IFCHR: return 'c'; 34 case S_IFIFO: return 'p'; 35 default: return '?'; 36 } 37} 38 39static void mode2str(unsigned mode, char *out) 40{ 41 *out++ = mode2kind(mode); 42 43 *out++ = (mode & 0400) ? 'r' : '-'; 44 *out++ = (mode & 0200) ? 'w' : '-'; 45 if(mode & 04000) { 46 *out++ = (mode & 0100) ? 's' : 'S'; 47 } else { 48 *out++ = (mode & 0100) ? 'x' : '-'; 49 } 50 *out++ = (mode & 040) ? 'r' : '-'; 51 *out++ = (mode & 020) ? 'w' : '-'; 52 if(mode & 02000) { 53 *out++ = (mode & 010) ? 's' : 'S'; 54 } else { 55 *out++ = (mode & 010) ? 'x' : '-'; 56 } 57 *out++ = (mode & 04) ? 'r' : '-'; 58 *out++ = (mode & 02) ? 'w' : '-'; 59 if(mode & 01000) { 60 *out++ = (mode & 01) ? 't' : 'T'; 61 } else { 62 *out++ = (mode & 01) ? 'x' : '-'; 63 } 64 *out = 0; 65} 66 67static void user2str(unsigned uid, char *out) 68{ 69 struct passwd *pw = getpwuid(uid); 70 if(pw) { 71 strcpy(out, pw->pw_name); 72 } else { 73 sprintf(out, "%d", uid); 74 } 75} 76 77static void group2str(unsigned gid, char *out) 78{ 79 struct group *gr = getgrgid(gid); 80 if(gr) { 81 strcpy(out, gr->gr_name); 82 } else { 83 sprintf(out, "%d", gid); 84 } 85} 86 87static int listfile(const char *path, int flags) 88{ 89 struct stat s; 90 char date[32]; 91 char mode[16]; 92 char user[16]; 93 char group[16]; 94 const char *name; 95 96 /* name is anything after the final '/', or the whole path if none*/ 97 name = strrchr(path, '/'); 98 if(name == 0) { 99 name = path; 100 } else { 101 name++; 102 } 103 104 if(lstat(path, &s) < 0) { 105 return -1; 106 } 107 108 mode2str(s.st_mode, mode); 109 user2str(s.st_uid, user); 110 group2str(s.st_gid, group); 111 112 strftime(date, 32, "%Y-%m-%d %H:%M", localtime((const time_t*)&s.st_mtime)); 113 date[31] = 0; 114 115// 12345678901234567890123456789012345678901234567890123456789012345678901234567890 116// MMMMMMMM UUUUUUUU GGGGGGGGG XXXXXXXX YYYY-MM-DD HH:MM NAME (->LINK) 117 118 switch(s.st_mode & S_IFMT) { 119 case S_IFBLK: 120 case S_IFCHR: 121 printf("%s %-8s %-8s %3d, %3d %s %s\n", 122 mode, user, group, 123 (int) MAJOR(s.st_rdev), (int) MINOR(s.st_rdev), 124 date, name); 125 break; 126 case S_IFREG: 127 printf("%s %-8s %-8s %8d %s %s\n", 128 mode, user, group, (int) s.st_size, date, name); 129 break; 130 case S_IFLNK: { 131 char linkto[256]; 132 int len; 133 134 len = readlink(path, linkto, 256); 135 if(len < 0) return -1; 136 137 if(len > 255) { 138 linkto[252] = '.'; 139 linkto[253] = '.'; 140 linkto[254] = '.'; 141 linkto[255] = 0; 142 } else { 143 linkto[len] = 0; 144 } 145 146 printf("%s %-8s %-8s %s %s -> %s\n", 147 mode, user, group, date, name, linkto); 148 break; 149 } 150 default: 151 printf("%s %-8s %-8s %s %s\n", 152 mode, user, group, date, name); 153 154 } 155 return 0; 156} 157 158static int listdir(const char *name, int flags) 159{ 160 char tmp[4096]; 161 DIR *d; 162 struct dirent *de; 163 164 d = opendir(name); 165 if(d == 0) { 166 fprintf(stderr, "opendir failed, %s\n", strerror(errno)); 167 return -1; 168 } 169 170 while((de = readdir(d)) != 0){ 171 if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) continue; 172 if(de->d_name[0] == '.' && (flags & LIST_ALL) == 0) continue; 173 if ((flags & LIST_LONG) != 0) { 174 sprintf(tmp, "%s/%s", name, de->d_name); 175 listfile(tmp, flags); 176 } else { 177 printf("%s\n", de->d_name); 178 } 179 } 180 181 if (flags & LIST_RECURSIVE) { 182 rewinddir(d); 183 184 while ((de = readdir(d)) != 0) { 185 struct stat s; 186 int err; 187 188 if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) 189 continue; 190 if (de->d_name[0] == '.' && (flags & LIST_ALL) == 0) 191 continue; 192 193 if (!strcmp(name, "/")) sprintf(tmp, "/%s", de->d_name); 194 else sprintf(tmp, "%s/%s", name, de->d_name); 195 196 /* 197 * If the name ends in a '/', use stat() so we treat it like a 198 * directory even if it's a symlink. 199 */ 200 if (tmp[strlen(tmp)-1] == '/') 201 err = stat(tmp, &s); 202 else 203 err = lstat(tmp, &s); 204 205 if (err < 0) { 206 perror(tmp); 207 closedir(d); 208 return -1; 209 } 210 211 if (S_ISDIR(s.st_mode)) { 212 printf("\n%s:\n", tmp); 213 listdir(tmp, flags); 214 } 215 } 216 } 217 218 closedir(d); 219 return 0; 220} 221 222static int listpath(const char *name, int flags) 223{ 224 struct stat s; 225 int err; 226 227 /* 228 * If the name ends in a '/', use stat() so we treat it like a 229 * directory even if it's a symlink. 230 */ 231 if (name[strlen(name)-1] == '/') 232 err = stat(name, &s); 233 else 234 err = lstat(name, &s); 235 236 if (err < 0) { 237 perror(name); 238 return -1; 239 } 240 241 if (S_ISDIR(s.st_mode)) { 242 if (flags & LIST_RECURSIVE) 243 printf("\n%s:\n", name); 244 return listdir(name, flags); 245 } else { 246 if ((flags & LIST_LONG) != 0) { 247 /* yeah this calls stat() again*/ 248 return listfile(name, flags); 249 } else { 250 printf("%s\n", name); 251 return 0; 252 } 253 } 254} 255 256int ls_main(int argc, char **argv) 257{ 258 int flags = 0; 259 int listed = 0; 260 261 if(argc > 1) { 262 int i; 263 int err = 0; 264 265 for (i = 1; i < argc; i++) { 266 if(!strcmp(argv[i], "-l")) { 267 flags |= LIST_LONG; 268 } else if (!strcmp(argv[i], "-a")) { 269 flags |= LIST_ALL; 270 } else if (!strcmp(argv[i], "-R")) { 271 flags |= LIST_RECURSIVE; 272 } else { 273 listed++; 274 if(listpath(argv[i], flags) != 0) { 275 err = EXIT_FAILURE; 276 } 277 } 278 } 279 280 if (listed > 0) return err; 281 } 282 283 // list working directory if no files or directories were specified 284 return listpath(".", flags); 285} 286