diff -r 5a597358bf4c chmod.c
--- a/chmod.c	Thu Jun 09 00:40:23 2011 +0100
+++ b/chmod.c	Fri Jun 10 00:17:46 2011 +0200
@@ -10,42 +10,85 @@
 
 static bool rflag = false;
 static mode_t mode = 0;
+static mode_t ormode = 0;
+static mode_t andmode = (mode_t)-1;
+
+/* format is [ugoa][+-=][rwxstX] */
+static int
+parsemode(const char *m)
+{
+	int guoa = 0;
+	int set = 0;
+	while(*m) {
+		switch(*m) {
+		case 'g': guoa |= 00070; break;
+		case 'u': guoa |= 00700; break;
+		case 'o': guoa |= 00007; break;
+		case 'a': guoa = 00777; break;
+		/* */
+		case '+': 
+		case '-':
+		case '=':
+			if (!guoa)
+				guoa = 00777;
+			set = *m;
+			break;
+		case 'X': /* TODO only if directory add -x */
+		case 't': ormode |= 001000; break; /* sticky bit */
+		case 's': ormode |= 006000; break; /* set group and user suid bit */
+		case 'r': ormode |= (guoa&00444); break;
+		case 'w': ormode |= (guoa&00222); break;
+		case 'x': ormode |= (guoa&00111); break;
+		default: return 0;
+		}
+		m++;
+	}
+	switch(set) {
+	case '-': andmode = ~ormode ; ormode = 0; break;
+	case '=': andmode = 0; break;
+	case '+': andmode = (mode_t)-1; break;
+	default: eprintf("chmod: invalid mode: %s\n", m);
+	}
+
+	return 1;
+}
 
 int
 main(int argc, char *argv[])
 {
-	char c, *end;
-	int octal;
+	char *end;
+	int octal = 0;
 
-	while((c = getopt(argc, argv, "Rr")) != -1)
-		switch(c) {
+	optind = 1;
+	if(argc<3)
+		eprintf("usage: %s [-Rr] [mode|[guoa][+-=][rwxstX]] [file...]\n", argv[0]);
+	if(argv[1][0]=='-') {
+		switch(argv[1][1]) {
+		case 'r':
 		case 'R':
-		case 'r':
 			rflag = true;
-			break;
-		default:
-			exit(EXIT_FAILURE);
+			optind = 2;
 		}
-	if(optind == argc)
-		eprintf("usage: %s [-Rr] mode [file...]\n", argv[0]);
-	octal = strtol(argv[optind++], &end, 8);
-	if(*end != '\0')
-		eprintf("%s: not an octal number\n", argv[optind-1]);
+	}
+	if(!parsemode(argv[optind])) {
+		octal = strtol(argv[optind], &end, 8);
+		if(*end != '\0')
+			eprintf("%s: not an valid mode\n", argv[optind]);
+		/* posix doesn't specify modal bits */
+		if(octal & 04000) mode |= S_ISUID;
+		if(octal & 02000) mode |= S_ISGID;
+		if(octal & 00400) mode |= S_IRUSR;
+		if(octal & 00200) mode |= S_IWUSR;
+		if(octal & 00100) mode |= S_IXUSR;
+		if(octal & 00040) mode |= S_IRGRP;
+		if(octal & 00020) mode |= S_IWGRP;
+		if(octal & 00010) mode |= S_IXGRP;
+		if(octal & 00004) mode |= S_IROTH;
+		if(octal & 00002) mode |= S_IWOTH;
+		if(octal & 00001) mode |= S_IXOTH;
+	}
 
-	/* posix doesn't specify modal bits */
-	if(octal & 04000) mode |= S_ISUID;
-	if(octal & 02000) mode |= S_ISGID;
-	if(octal & 00400) mode |= S_IRUSR;
-	if(octal & 00200) mode |= S_IWUSR;
-	if(octal & 00100) mode |= S_IXUSR;
-	if(octal & 00040) mode |= S_IRGRP;
-	if(octal & 00020) mode |= S_IWGRP;
-	if(octal & 00010) mode |= S_IXGRP;
-	if(octal & 00004) mode |= S_IROTH;
-	if(octal & 00002) mode |= S_IWOTH;
-	if(octal & 00001) mode |= S_IXOTH;
-
-	for(; optind < argc; optind++)
+	for(++optind; optind < argc; optind++)
 		chmodr(argv[optind]);
 	return EXIT_SUCCESS;
 }
@@ -53,7 +96,15 @@
 void
 chmodr(const char *path)
 {
-	if(chmod(path, mode) == -1)
+	int fmode;
+	struct stat st;
+
+	if(ormode||!andmode) {
+		if(stat (path, &st) == -1)
+			eprintf("stat %s:", path);
+		fmode = (st.st_mode &= andmode) | ormode;
+	} else fmode = mode;
+	if(chmod(path, fmode) == -1)
 		eprintf("chmod %s:", path);
 	if(rflag)
 		recurse(path, chmodr);
diff -r 5a597358bf4c sleep.c
--- a/sleep.c	Thu Jun 09 00:40:23 2011 +0100
+++ b/sleep.c	Fri Jun 10 00:17:46 2011 +0200
@@ -9,15 +9,11 @@
 	char *end;
 	unsigned int seconds;
 
-	if(getopt(argc, argv, "") != -1)
-		exit(EXIT_FAILURE);
-	if(optind != argc-1)
+	if(argc!=2)
 		eprintf("usage: %s seconds\n", argv[0]);
+	seconds = (unsigned int)strtol(argv[1], &end, 0);
+	if(*end != '\0')
+		eprintf("%s: not a number\n", argv[1]);
 
-	seconds = strtol(argv[optind], &end, 0);
-	if(*end != '\0')
-		eprintf("%s: not a number\n", argv[optind]);
-	while((seconds = sleep(seconds)) > 0)
-		;
-	return EXIT_SUCCESS;
+	return sleep(seconds)==0? EXIT_SUCCESS: EXIT_FAILURE;
 }
diff -r 5a597358bf4c tail.c
--- a/tail.c	Thu Jun 09 00:40:23 2011 +0100
+++ b/tail.c	Fri Jun 10 00:17:46 2011 +0200
@@ -60,7 +60,7 @@
 {
 	char **ring;
 	long i, j;
-	size_t *size;
+	size_t *size = NULL;
 
 	if(!(ring = calloc(n, sizeof *ring)) || !(size = calloc(n, sizeof *size)))
 		eprintf("calloc:");
