/*1* Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2008, 20092* The President and Fellows of Harvard College.3*4* Redistribution and use in source and binary forms, with or without5* modification, are permitted provided that the following conditions6* are met:7* 1. Redistributions of source code must retain the above copyright8* notice, this list of conditions and the following disclaimer.9* 2. Redistributions in binary form must reproduce the above copyright10* notice, this list of conditions and the following disclaimer in the11* documentation and/or other materials provided with the distribution.12* 3. Neither the name of the University nor the names of its contributors13* may be used to endorse or promote products derived from this software14* without specific prior written permission.15*16* THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND17* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE18* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE19* ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE20* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL21* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS22* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)23* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT24* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY25* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF26* SUCH DAMAGE.27*/2829#include <sys/types.h>30#include <sys/stat.h>31#include <stdio.h>32#include <unistd.h>33#include <string.h>34#include <errno.h>35#include <err.h>3637/*38* ls - list files.39* Usage: ls [-adlRs] [files]40* -a Show files whose names begin with a dot.41* -d Don't list contents of directories specified on the command line.42* -l Long format listing.43* -R Recurse into subdirectories found.44* -s (with -l) Show block counts.45*/4647/* Flags for which options we're using. */48static int aopt=0;49static int dopt=0;50static int lopt=0;51static int Ropt=0;52static int sopt=0;5354/* Process an option character. */55static56void57option(int ch)58{59switch (ch) {60case 'a': aopt=1; break;61case 'd': dopt=1; break;62case 'l': lopt=1; break;63case 'R': Ropt=1; break;64case 's': sopt=1; break;65default:66errx(1, "Unknown option -%c", ch);67}68}6970/*71* Utility function to find the non-directory part of a pathname.72*/73static74const char *75basename(const char *path)76{77const char *s;7879s = strrchr(path, '/');80if (s) {81return s+1;82}83return path;84}8586/*87* Utility function to check if a name refers to a directory.88*/89static90int91isdir(const char *path)92{93struct stat buf;94int fd;9596/* Assume stat() may not be implemented; use fstat */97fd = open(path, O_RDONLY);98if (fd<0) {99err(1, "%s", path);100}101if (fstat(fd, &buf)<0) {102err(1, "%s: fstat", path);103}104close(fd);105106return S_ISDIR(buf.st_mode);107}108109/*110* When listing one of several subdirectories, show the name of the111* directory.112*/113static114void115printheader(const char *file)116{117/* No blank line before the first header */118static int first=1;119if (first) {120first = 0;121}122else {123printf("\n");124}125printf("%s:\n", file);126}127128/*129* Show a single file.130* We don't do the neat multicolumn listing that Unix ls does.131*/132static133void134print(const char *path)135{136struct stat statbuf;137const char *file;138int typech;139140if (lopt || sopt) {141int fd;142143fd = open(path, O_RDONLY);144if (fd<0) {145err(1, "%s", path);146}147if (fstat(fd, &statbuf)<0) {148err(1, "%s: fstat", path);149}150close(fd);151}152153file = basename(path);154155if (sopt) {156printf("%3d ", statbuf.st_blocks);157}158159if (lopt) {160if (S_ISREG(statbuf.st_mode)) {161typech = '-';162}163else if (S_ISDIR(statbuf.st_mode)) {164typech = 'd';165}166else if (S_ISLNK(statbuf.st_mode)) {167typech = 'l';168}169else if (S_ISCHR(statbuf.st_mode)) {170typech = 'c';171}172else if (S_ISBLK(statbuf.st_mode)) {173typech = 'b';174}175else {176typech = '?';177}178179printf("%crwx------ %2d root %-8llu",180typech,181statbuf.st_nlink,182statbuf.st_size);183}184printf("%s\n", file);185}186187/*188* List a directory.189*/190static191void192listdir(const char *path, int showheader)193{194int fd;195char buf[1024];196char newpath[1024];197int len;198199if (showheader) {200printheader(path);201}202203/*204* Open it.205*/206fd = open(path, O_RDONLY);207if (fd<0) {208err(1, "%s", path);209}210211/*212* List the directory.213*/214while ((len = getdirentry(fd, buf, sizeof(buf)-1)) > 0) {215buf[len] = 0;216217/* Assemble the full name of the new item */218snprintf(newpath, sizeof(newpath), "%s/%s", path, buf);219220if (aopt || buf[0]!='.') {221/* Print it */222print(newpath);223}224}225if (len<0) {226err(1, "%s: getdirentry", path);227}228229/* Done */230close(fd);231}232233static234void235recursedir(const char *path)236{237int fd;238char buf[1024];239char newpath[1024];240int len;241242/*243* Open it.244*/245fd = open(path, O_RDONLY);246if (fd<0) {247err(1, "%s", path);248}249250/*251* List the directory.252*/253while ((len = getdirentry(fd, buf, sizeof(buf)-1)) > 0) {254buf[len] = 0;255256/* Assemble the full name of the new item */257snprintf(newpath, sizeof(newpath), "%s/%s", path, buf);258259if (!aopt && buf[0]=='.') {260/* skip this one */261continue;262}263264if (!strcmp(buf, ".") || !strcmp(buf, "..")) {265/* always skip these */266continue;267}268269if (!isdir(newpath)) {270continue;271}272273listdir(newpath, 1 /*showheader*/);274if (Ropt) {275recursedir(newpath);276}277}278if (len<0) {279err(1, "%s", path);280}281282close(fd);283}284285static286void287listitem(const char *path, int showheader)288{289if (!dopt && isdir(path)) {290listdir(path, showheader || Ropt);291if (Ropt) {292recursedir(path);293}294}295else {296print(path);297}298}299300int301main(int argc, char *argv[])302{303int i,j, items=0;304305/*306* Go through the arguments and count how many non-option args.307*/308for (i=1; i<argc; i++) {309if (argv[i][0]!='-') {310items++;311}312}313314/*315* Now go through the options for real, processing them.316*/317for (i=1; i<argc; i++) {318if (argv[i][0]=='-') {319/*320* This word is an option.321* Process all the option characters in it.322*/323for (j=1; argv[i][j]; j++) {324option(argv[i][j]);325}326}327else {328/*329* This word isn't an option; list it.330*/331listitem(argv[i], items>1);332}333}334335/*336* If no filenames were specified to list, list the current337* directory.338*/339if (items==0) {340listitem(".", 0);341}342343return 0;344}345346347