commit 6ef3d9174bbfaeb955e32c59b40b4451e1b0b396
parent 273f150c842e3daeb95f398ec29ed36c264e4ccf
Author: Connor Lane Smith <cls@lubutu.com>
Date:   Thu, 26 May 2011 04:01:20 +0100
add ls; simpler pwd
Diffstat:
9 files changed, 268 insertions(+), 24 deletions(-)
diff --git a/Makefile b/Makefile
@@ -1,8 +1,8 @@
 include config.mk
 
-LIB = util/afgets.o util/enmasse.o util/eprintf.o util/recurse.o
+LIB = util/afgets.o util/agetcwd.o util/enmasse.o util/eprintf.o util/recurse.o
 SRC = basename.c cat.c chown.c date.c dirname.c echo.c false.c grep.c head.c \
-      ln.c mkfifo.c pwd.c rm.c sleep.c tee.c touch.c true.c wc.c
+      ln.c ls.c mkfifo.c pwd.c rm.c sleep.c tee.c touch.c true.c wc.c
 OBJ = $(SRC:.c=.o) $(LIB)
 BIN = $(SRC:.c=)
 MAN = $(SRC:.c=.1)
@@ -11,7 +11,7 @@ all: $(BIN)
 
 $(OBJ): util.h
 $(BIN): util.a
-grep: text.h
+grep.o: text.h
 
 .o:
 	@echo CC -o $@
diff --git a/echo.c b/echo.c
@@ -21,9 +21,9 @@ main(int argc, char *argv[])
 	for(; optind < argc; optind++) {
 		fputs(argv[optind], stdout);
 		if(optind+1 < argc)
-			fputc(' ', stdout);
+			putchar(' ');
 	}
 	if(!nflag)
-		fputc('\n', stdout);
+		putchar('\n');
 	return EXIT_SUCCESS;
 }
diff --git a/ls.1 b/ls.1
@@ -0,0 +1,27 @@
+.TH LS 1 sbase\-VERSION
+.SH NAME
+ls \- list directory contents
+.SH SYNOPSIS
+.B ls
+.RB [ \-adlt ]
+.RI [ file ...]
+.SH DESCRIPTION
+.B ls
+lists each given file, and the contents of each given directory.  If no files
+are given the current directory is listed.
+.SH OPTIONS
+.TP
+.B \-a
+shows hidden files (those beginning with '.').
+.TP
+.B \-d
+lists directories themselves, not their contents.
+.TP
+.B \-l
+lists detailed information about each file, including their permissions, links,
+owner, group, size, and modification time.
+.TP
+.B \-t
+sorts files by modification time instead of by name.
+.SH SEE ALSO
+.IR stat (2)
diff --git a/ls.c b/ls.c
@@ -0,0 +1,212 @@
+/* See LICENSE file for copyright and license details. */
+#include <dirent.h>
+#include <errno.h>
+#include <grp.h>
+#include <pwd.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include "util.h"
+
+typedef struct {
+	char *name;
+	mode_t mode;
+	nlink_t nlink;
+	uid_t uid;
+	gid_t gid;
+	off_t size;
+	time_t mtime;
+} Entry;
+
+static int entcmp(Entry *, Entry *);
+static void ls(char *, bool);
+static void lsdir(const char *);
+static void mkent(Entry *, char *);
+static void output(Entry *);
+
+static bool aflag = false;
+static bool lflag = false;
+static bool tflag = false;
+static bool many;
+
+int
+main(int argc, char *argv[])
+{
+	bool dflag = false;
+	char c;
+
+	while((c = getopt(argc, argv, "adlt")) != -1)
+		switch(c) {
+		case 'a':
+			aflag = true;
+			break;
+		case 'd':
+			dflag = true;
+			break;
+		case 'l':
+			lflag = true;
+			break;
+		case 't':
+			tflag = true;
+			break;
+		default:
+			exit(EXIT_FAILURE);
+		}
+	many = (argc > optind+1);
+
+	if(optind == argc)
+		ls(".", !dflag);
+	else for(; optind < argc; optind++)
+		ls(argv[optind], !dflag);
+	return EXIT_SUCCESS;
+}
+
+int
+entcmp(Entry *a, Entry *b)
+{
+	if(tflag)
+		return b->mtime - a->mtime;
+	else
+		return strcmp(a->name, b->name);
+}
+
+void
+ls(char *path, bool expand)
+{
+	Entry ent;
+
+	mkent(&ent, path);
+	if(expand && S_ISDIR(ent.mode))
+		lsdir(path);
+	else
+		output(&ent);
+}
+
+void
+lsdir(const char *path)
+{
+	char *buf, *p;
+	long i, n = 0;
+	struct dirent *d;
+	DIR *dp;
+	Entry *ents = NULL;
+
+	buf = agetcwd();
+	if(!(dp = opendir(path)))
+		eprintf("opendir %s:", path);
+	if(chdir(path) != 0)
+		eprintf("chdir %s:", path);
+
+	while((d = readdir(dp))) {
+		if(d->d_name[0] == '.' && !aflag)
+			continue;
+		if(!(ents = realloc(ents, ++n * sizeof *ents)))
+			eprintf("realloc:");
+		if(!(p = strdup(d->d_name)))
+			eprintf("strdup:");
+		mkent(&ents[n-1], p);
+	}
+	closedir(dp);
+	qsort(ents, n, sizeof *ents, (int (*)(const void *, const void *))entcmp);
+
+	if(many)
+		printf("%s:\n", path);
+	for(i = 0; i < n; i++) {
+		output(&ents[i]);
+		free(ents[i].name);
+	}
+	if(chdir(buf) != 0)
+		eprintf("chdir %s:", buf);
+	free(ents);
+	free(buf);
+}
+
+void
+mkent(Entry *ent, char *path)
+{
+	struct stat st;
+
+	if(lstat(path, &st) != 0)
+		eprintf("lstat %s:", path);
+	ent->name   = path;
+	ent->mode   = st.st_mode;
+	ent->nlink  = st.st_nlink;
+	ent->uid    = st.st_uid;
+	ent->gid    = st.st_gid;
+	ent->size   = st.st_size;
+	ent->mtime  = st.st_mtime;
+}
+
+void
+output(Entry *ent)
+{
+	char buf[BUFSIZ], mode[11], *fmt;
+	struct group *gr;
+	struct passwd *pw;
+
+	if(!lflag) {
+		puts(ent->name);
+		return;
+	}
+	if(S_ISREG(ent->mode))
+		mode[0] = '-';
+	else if(S_ISBLK(ent->mode))
+		mode[0] = 'b';
+	else if(S_ISCHR(ent->mode))
+		mode[0] = 'c';
+	else if(S_ISDIR(ent->mode))
+		mode[0] = 'd';
+	else if(S_ISFIFO(ent->mode))
+		mode[0] = 'p';
+	else if(S_ISLNK(ent->mode))
+		mode[0] = 'l';
+	else if(S_ISSOCK(ent->mode))
+		mode[0] = 's';
+	else
+		mode[0] = '?';
+
+	mode[1] = (ent->mode & S_IRUSR) ? 'r' : '-';
+	mode[2] = (ent->mode & S_IWUSR) ? 'w' : '-';
+	if(ent->mode & S_ISUID)
+		mode[3] = (ent->mode & S_IXUSR) ? 's' : 'S';
+	else
+		mode[3] = (ent->mode & S_IXUSR) ? 'x' : '-';
+
+	mode[4] = (ent->mode & S_IRGRP) ? 'r' : '-';
+	mode[5] = (ent->mode & S_IWGRP) ? 'w' : '-';
+	if(ent->mode & S_ISGID)
+		mode[6] = (ent->mode & S_IXGRP) ? 's' : 'S';
+	else
+		mode[6] = (ent->mode & S_IXGRP) ? 'x' : '-';
+
+	mode[7] = (ent->mode & S_IROTH) ? 'r' : '-';
+	mode[8] = (ent->mode & S_IWOTH) ? 'w' : '-';
+	mode[9] = (ent->mode & S_IXOTH) ? 'x' : '-';
+	mode[10] = '\0';
+
+	errno = 0;
+	pw = getpwuid(ent->uid);
+	if(errno)
+		eprintf("getpwuid %d:", ent->uid);
+	else if(!pw)
+		eprintf("getpwuid %d: no such user\n", ent->uid);
+
+	errno = 0;
+	gr = getgrgid(ent->gid);
+	if(errno)
+		eprintf("getgrgid %d:", ent->gid);
+	else if(!gr)
+		eprintf("getgrgid %d: no such group\n", ent->gid);
+
+	if(time(NULL) > ent->mtime + (180*24*60*60)) /* 6 months ago? */
+		fmt = "%b %d %Y";
+	else
+		fmt = "%b %d %H:%M";
+
+	strftime(buf, sizeof buf, fmt, localtime(&ent->mtime));
+	printf("%s %2d %s %s %6lu %s %s\n", mode, ent->nlink, pw->pw_name, gr->gr_name, (unsigned long)ent->size, buf, ent->name);
+}
diff --git a/pwd.c b/pwd.c
@@ -7,16 +7,6 @@
 int
 main(void)
 {
-	char *buf;
-	long size;
-
-	if((size = pathconf(".", _PC_PATH_MAX)) < 0)
-		size = BUFSIZ;
-	if(!(buf = malloc(size)))
-		eprintf("malloc:");
-	if(!getcwd(buf, size))
-		eprintf("getcwd:");
-
-	puts(buf);
+	puts(agetcwd());
 	return EXIT_SUCCESS;
 }
diff --git a/util.h b/util.h
@@ -1,5 +1,6 @@
 /* See LICENSE file for copyright and license details. */
 
+char *agetcwd(void);
 void enmasse(int, char **, int (*)(const char *, const char *));
 void eprintf(const char *, ...);
 void recurse(const char *, void (*)(const char *));
diff --git a/util/agetcwd.c b/util/agetcwd.c
@@ -0,0 +1,20 @@
+/* See LICENSE file for copyright and license details. */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "../util.h"
+
+char *
+agetcwd(void)
+{
+	char *buf;
+	size_t size;
+
+	if((size = pathconf(".", _PC_PATH_MAX)) < 0)
+		size = BUFSIZ;
+	if(!(buf = malloc(size)))
+		eprintf("malloc:");
+	if(!getcwd(buf, size))
+		eprintf("getcwd:");
+	return buf;
+}
diff --git a/util/recurse.c b/util/recurse.c
@@ -11,7 +11,6 @@ void
 recurse(const char *path, void (*fn)(const char *))
 {
 	char *buf;
-	long size;
 	struct dirent *d;
 	DIR *dp;
 
@@ -21,12 +20,7 @@ recurse(const char *path, void (*fn)(const char *))
 		else
 			eprintf("opendir %s:", path);
 	}
-	if((size = pathconf(".", _PC_PATH_MAX)) < 0)
-		size = BUFSIZ;
-	if(!(buf = malloc(size)))
-		eprintf("malloc:");
-	if(!getcwd(buf, size))
-		eprintf("getcwd:");
+	buf = agetcwd();
 	if(chdir(path) != 0)
 		eprintf("chdir %s:", path);
 	while((d = readdir(dp)))
diff --git a/wc.c b/wc.c
@@ -64,7 +64,7 @@ output(const char *str, long nc, long nl, long nw)
 		printf(" %5ld", nc);
 	if(str)
 		printf(" %s", str);
-	fputc('\n', stdout);
+	putchar('\n');
 }
 
 void