// New ls command by Rust@TMI-2 on 23-4-94 // This is the ls from hell. Supports every flag anyone // has asked me for. I don 't feel so hot about -r support, but oh well. // if there's some odd flag you' d like to see implemented, let me know. // Val@TMI-2 maded the following fixes (July 2, 1994) : //Fixed: ls /doc/??? (used to turn into /doc/???/*) //Fixed: ls -t didn't strip all . begining files //Fixed: ls -F didn't add * to any files if the argument wasn't a directory // for example "ls -F /std/c*" //Fixed: ls -s used '-' as size for 0 bytes long files. now it is 0. // Blue@TMI-2, 940905. Changed the default argument (when // you just type "ls") from " " to "". Resolv_path had been // changed (anonymously) so that the space didn't work any // more. // Leto 941109 Changed status to int. // Val 950124 removed extra empty line, allowed -Fr with -l, // added flags -1 and -A // Leto 950211 made is use new range (v20.26+) // leto 111195 Fixed a stupid bug pointed out by Hanzou that // used getuid instead of geteuid, resulting in root uid when using // the 'call' command ;) #define L_LINE sprintf("%-30s %-8s %s", "Last Modified", "Size", "File") #define DASHES "-----------------------------------------------------------------" #define F_FLAG 1 #define S_FLAG 2 #define L_FLAG 4 #define A_FLAG 8 #define T_FLAG 16 #define R_FLAG 32 #define X_FLAG 64 #define ONE_FLAG 128 #define CAP_A_FLAG 256 private mixed internal_ls(string path, int active, int internal_call); mixed map_junk(mixed junk, int index); int sort_by_time(mixed a, mixed b); int sort_by_ext(mixed a, mixed b); int filter_directories(mixed junk); int filter_no_period(mixed junk); int cmd_ls(string arg) { int i; int active; string flags; string *output; seteuid(geteuid(previous_object())); if (!arg) arg = ""; else arg += " "; while (sscanf(arg, "-%s %s", flags, arg) > 1) { i = strlen(flags); while (i--) switch (flags[i]) { case 'F': active |= F_FLAG; break; case 's': active |= S_FLAG; break; case 'l': active |= L_FLAG; break; case 'a': active |= A_FLAG; break; case 't': active |= T_FLAG; break; case 'r': active |= R_FLAG; break; case 'x': active |= X_FLAG; break; case '1': active |= ONE_FLAG; break; case 'A': active |= CAP_A_FLAG; break; } } if (arg[<1..<1] == " ") arg = arg[0..<2]; if (arg == "/..") { write("ls: no such directory.\n"); return 1; } // Remove impossible flag combinations. if (active & L_FLAG) active &= ~S_FLAG; arg = resolv_path(this_player()->query("cwd"), arg); if (output = internal_ls(arg, active, 0)) { this_player()->more(output); } return 1; } private mixed internal_ls(string arg, int active, int internal_call) { mixed *junk; string *files; int *times; int *sizes; string *dirs; int first_period; int i, j; string *output; mixed tmp; string path; output = ({ sprintf("Path: [%s]", arg) }); if (internal_call) { path = arg; arg += "*"; } else { if (arg[<1..< 1] != "/" && file_size(arg + "/") == -2) arg += "/"; path = arg; if (arg[<1..<1] == "/") { arg += "*"; } else { for (i = strlen(path); path[--i] != '/';); first_period = path[i+1] == '.'; path = path[0..i]; } } junk = get_dir(arg, -1); if (!sizeof(junk)) { write("ls: no such file or directory.\n"); return 0; } if (!(active & A_FLAG) && !first_period) { junk = filter_array(junk, (active & CAP_A_FLAG)?"filter_limit_period": "filter_no_period", this_object()); if (!sizeof(junk)) { if (internal_call) return ({ sprintf("%s: Directory is empty.", path) }); write("Directory is empty.\n"); return 0; } } if (active & T_FLAG) junk = sort_array(junk, "sort_by_time", this_object()); if (active & X_FLAG) junk = sort_array(junk, "sort_by_ext", this_object()); if (active & R_FLAG) dirs = map_array( filter_array(junk, "filter_directories", this_object()), "map_junk", this_object(), 0); files = map_array(junk, "map_junk", this_object(), 0); sizes = map_array(junk, "map_junk", this_object(), 1); times = map_array(junk, "map_junk", this_object(), 2); if (active & (F_FLAG | S_FLAG)) { for (i = sizeof(sizes); i--;) { if (active & S_FLAG) { tmp = (sizes[i] >= 0) ? sprintf("%3d %s", (sizes[i] + 1023) / 1024, files[i]) : sprintf(" - %s", files[i]); } else { tmp = files[i]; } if (active & F_FLAG) { if (sizes[i] == -2) { tmp += "/"; } else if (files[i][<2..<1] == ".c" && find_object(path + files[i])) { tmp += "*"; } } files[i] = tmp; } } if (active & L_FLAG) { #ifndef UNIX_LOOK int out_idx, max; out_idx = sizeof(output += ({L_LINE,DASHES})); output += allocate(max = sizeof(files)); for (i = 0; i < max; i++) { output[out_idx++] = (sizes[i] >= 0) ? sprintf("%-30s %-8d %s", ctime(times[i]), sizes[i], files[i]) : sprintf("%-30s %-8s %s", ctime(times[i]), "Dir", files[i]); } #else int out_idx, max, time_lim; time_lim = time() - 178 * 24 * 3600; out_idx = sizeof(output); output += allocate(max = sizeof(files)); for (i = 0; i < max; i++) { tmp = ctime( times[i] ); tmp = (times[i] < time_lim) ? (tmp[4..10] + tmp[19..23]) : tmp[4..15]; output[out_idx++] = (sizes[i] >= 0) ? sprintf("%6d %s %s", sizes[i], tmp, files[i]) : sprintf(" (dir) %s %s", tmp, files[i]); } #endif } else if (!(active & ONE_FLAG) ) { // !L_FLAG !ONE_FLAG for (i = 0; i < sizeof(files); i += 300) { output += explode(sprintf("%-79#s\n", implode(files[i..(i + 299)], "\n")), "\n"); } } else { // ONE_FLAG output += files; } if (active & R_FLAG) { for (i = 0; i < sizeof(dirs); i++) output += ({ "" }) + internal_ls(path + dirs[i] + "/", active, 1); } return output; } string help() { return(wrap( "Usage: ls [ -aslFrtx1 ] [ Directory ]\n" "The ls command shows the user the files in the specified " "directory, or if there is none given, to his current working " "directory.\nFlags:\n" " -a shows files with a . as the first character, which are " "normally excluded.\n" " -A same as -a, except that '.' and '..' are not listed.\n" " -s shows an approximate file size to the next highest kilobyte.\n" " -F postpends a '/' to directory names and '*' to loaded files.\n" " -l gives the long display. The l flag negates the s flag.\n" " -r is recursive, and should not be used on directories such as " "the root directory, etc... as the eval cost would top out.\n" " -t sorts by date last modified.\n" " -x sorts by file extension.\n" " -1 show only one file per line.\n" "Unsupported flags are ignored.\n" )); } mixed map_junk(mixed junk, int index) { return junk[index]; } int filter_no_period(mixed junk) { if (junk[0][0] != '.') return 1; } int filter_limit_period(mixed junk) { if (junk[0] != "." && junk[0] != "..") return 1; } int filter_directories(mixed junk) { if (junk[1] == -2 && junk[0] != "." && junk[0] != "..") return 1; } int sort_by_time(mixed file_a, mixed file_b) { return file_a[2] > file_b[2] ? 1 : -1; } int sort_by_ext(mixed file_a, mixed file_b) { file_a = " ." + file_a[0]; file_b = " ." + file_b[0]; file_a = explode(file_a, "."); file_b = explode(file_b, "."); if (sizeof(file_a) == 2 || sizeof(file_b) == 2) return sizeof(file_a) > sizeof(file_b) ? 1 : -1; return file_a[sizeof(file_a) - 1] > file_b[sizeof(file_b) - 1] ? 1 : -1; }