commit cec53d14b182c7edc0c89544fda0ef2a639df278
parent 4192b13768ca86104ab0e2359a919443fec0a15b
Author: William Haddon <william@haddonthethird.net>
Date: Mon, 30 Jan 2012 22:41:33 +0000
implement cp and mv and improve rm
Diffstat:
16 files changed, 229 insertions(+), 42 deletions(-)
diff --git a/LICENSE b/LICENSE
@@ -7,6 +7,7 @@ MIT/X Consortium License
© 2011 Hiltjo Posthuma <hiltjo@codemadness.org>
© 2011 pancake <pancake@youterm.com>
© 2011 Random832 <random832@fastmail.us>
+© 2012 William Haddon <william@haddonthethird.net>
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
diff --git a/Makefile b/Makefile
@@ -1,17 +1,20 @@
include config.mk
-HDR = text.h util.h
+HDR = fs.h text.h util.h
LIB = \
util/afgets.o \
util/agetcwd.o \
util/apathmax.o \
util/concat.o \
+ util/cp.o \
util/enmasse.o \
util/eprintf.o \
util/enprintf.o \
util/estrtol.o \
+ util/fnck.o \
util/putword.o \
util/recurse.o \
+ util/rm.o \
util/venprintf.o
SRC = \
@@ -21,6 +24,7 @@ SRC = \
chown.c \
cksum.c \
cmp.c \
+ cp.c \
date.c \
dirname.c \
echo.c \
@@ -33,6 +37,7 @@ SRC = \
ls.c \
mkdir.c \
mkfifo.c \
+ mv.c \
nl.c \
nohup.c \
pwd.c \
@@ -56,7 +61,8 @@ all: $(BIN)
$(OBJ): util.h config.mk
$(BIN): util.a
-cat.o grep.o tail.o: text.h
+cat.o cp.o mv.o grep.o tail.o: text.h
+cp.o mv.o rm.o: fs.h
.o:
@echo LD $@
diff --git a/TODO b/TODO
@@ -1,17 +1,11 @@
comm [-123] file1 file2
-cp [-r] file [name]
-cp [-r] [file...] [directory]
-
cut [-bcfs] [-d delim] list [file...]
diff [-ru] file1 file2
id [-gnru] [user]
-mv file [name]
-mv [file...] directory
-
paste [-s] [-d list] [file...]
printf format [argument...]
diff --git a/config.mk b/config.mk
@@ -10,7 +10,7 @@ MANPREFIX = $(PREFIX)/share/man
LD = $(CC)
CPPFLAGS = -D_POSIX_C_SOURCE=200112L
CFLAGS = -Os -ansi -Wall -pedantic $(CPPFLAGS)
-LDFLAGS = -static #-s
+LDFLAGS =
#CC = tcc
#LD = $(CC)
diff --git a/cp.1 b/cp.1
@@ -0,0 +1,22 @@
+.TH CP 1 sbase\-VERSION
+.SH NAME
+cp \- copy files and directories
+.SH SYNOPSIS
+.B cp
+.RB [ \-r ]
+.I file
+.RI [ name ]
+.P
+.B cp
+.RB [ \-r ]
+.RI [ file ...]
+.RI [ directory ]
+.SH DESCRIPTION
+.B cp
+copies a given file, naming it the given name. If multiple files are listed
+they will be copied into the given directory.
+.SH OPTIONS
+.TP
+.B \-r
+copies directories recursively. If this flag is not specified, directories are
+not copied.
diff --git a/cp.c b/cp.c
@@ -0,0 +1,27 @@
+/* See LICENSE file for copyright and license details. */
+#include <stdbool.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include "fs.h"
+#include "util.h"
+
+int
+main(int argc, char *argv[])
+{
+ struct stat st;
+ char c;
+
+ while((c = getopt(argc, argv, "r")) != -1)
+ switch(c) {
+ case 'r':
+ cp_rflag = true;
+ break;
+ default:
+ exit(EXIT_FAILURE);
+ }
+ if(argc > 3 && !cp_rflag && !(stat(argv[argc-1], &st) == 0 && S_ISDIR(st.st_mode)))
+ eprintf("%s: not a directory\n", argv[argc-1]);
+ enmasse(argc - optind, &argv[optind], cp);
+ return EXIT_SUCCESS;
+}
diff --git a/fs.h b/fs.h
@@ -0,0 +1,9 @@
+/* See LICENSE file for copyright and license details. */
+#include <stdbool.h>
+
+extern bool cp_rflag;
+extern bool rm_fflag;
+extern bool rm_rflag;
+
+int cp(const char *, const char *);
+void rm(const char *);
diff --git a/mv.1 b/mv.1
@@ -0,0 +1,16 @@
+.TH MV 1 sbase\-VERSION
+.SH NAME
+mv \- move files and directories
+.SH SYNOPSIS
+.B mv
+.I file
+.RI [ name ]
+.P
+.B mv
+.RI [ file ...]
+.RI [ directory ]
+.SH DESCRIPTION
+.B mv
+moves or renames a given file or directory, naming it the given name. If
+multiple files and directories are listed they will be moved into the given
+directory.
diff --git a/mv.c b/mv.c
@@ -0,0 +1,39 @@
+/* See LICENSE file for copyright and license details. */
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include "fs.h"
+#include "util.h"
+
+int mv(const char *, const char *);
+
+int
+main(int argc, char *argv[])
+{
+ struct stat st;
+
+ if(getopt(argc, argv, "") != -1)
+ exit(EXIT_FAILURE);
+ if(argc > 3 && !(stat(argv[argc-1], &st) == 0 && S_ISDIR(st.st_mode)))
+ eprintf("%s: not a directory\n", argv[argc-1]);
+ enmasse(argc - optind, &argv[optind], mv);
+ return EXIT_SUCCESS;
+}
+
+int
+mv (const char *s1, const char *s2)
+{
+ if (rename(s1, s2) == 0)
+ return 0;
+ if (errno == EXDEV)
+ {
+ cp_rflag = true;
+ rm_rflag = true;
+ cp(s1, s2);
+ rm(s1);
+ return 0;
+ }
+ return -1;
+}
diff --git a/rm.1 b/rm.1
@@ -14,6 +14,7 @@ removes the given files and directories.
ignore files that cannot be removed.
.TP
.B \-r
-remove directories recursively.
+remove directories recursively. If this flag is not specified, directories are
+not removed.
.SH SEE ALSO
.IR remove (3)
diff --git a/rm.c b/rm.c
@@ -3,39 +3,33 @@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
+#include <sys/stat.h>
+#include "fs.h"
#include "util.h"
-static void rm(const char *);
-
-static bool fflag = false;
-static bool rflag = false;
-
int
main(int argc, char *argv[])
{
char c;
+ struct stat st;
while((c = getopt(argc, argv, "fr")) != -1)
switch(c) {
case 'f':
- fflag = true;
+ rm_fflag = true;
break;
case 'r':
- rflag = true;
+ rm_rflag = true;
break;
default:
exit(EXIT_FAILURE);
}
- for(; optind < argc; optind++)
- rm(argv[optind]);
+ for(; optind < argc; optind++) {
+ if(!rm_rflag && stat(argv[optind], &st) == 0 &&
+ S_ISDIR(st.st_mode))
+ fprintf(stderr, "%s: is a directory\n", argv[optind]);
+ else
+ rm(argv[optind]);
+ }
return EXIT_SUCCESS;
}
-
-void
-rm(const char *path)
-{
- if(rflag)
- recurse(path, rm);
- if(remove(path) == -1 && !fflag)
- eprintf("remove %s:", path);
-}
diff --git a/util.h b/util.h
@@ -8,5 +8,6 @@ void enmasse(int, char **, int (*)(const char *, const char *));
void eprintf(const char *, ...);
void enprintf(int, const char *, ...);
long estrtol(const char *, int);
+void fnck(const char *, const char *, int (*)(const char *, const char *));
void putword(const char *);
void recurse(const char *, void (*)(const char *));
diff --git a/util/cp.c b/util/cp.c
@@ -0,0 +1,59 @@
+/* See LICENSE file for copyright and license details. */
+#include <dirent.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include "../fs.h"
+#include "../text.h"
+#include "../util.h"
+
+bool cp_rflag = false;
+
+int
+cp(const char *s1, const char *s2)
+{
+ FILE *f1, *f2;
+ char *ns1, *ns2;
+ long size1, size2;
+ struct dirent *d;
+ struct stat st;
+ DIR *dp;
+
+ if (stat(s1, &st) == 0 && S_ISDIR(st.st_mode)) {
+ if (!cp_rflag) {
+ eprintf("%s: is a directory\n", s1);
+ }
+ else {
+ if(!(dp = opendir(s1)))
+ eprintf("opendir %s:", s1);
+ if (mkdir(s2, st.st_mode) == -1 && errno != EEXIST)
+ eprintf("mkdir %s:", s2);
+ apathmax(&ns1, &size1);
+ apathmax(&ns2, &size2);
+ while((d = readdir(dp)))
+ if(strcmp(d->d_name, ".") && strcmp(d->d_name, "..")) {
+ if(snprintf(ns1, size1, "%s/%s", s1, d->d_name) > size1)
+ eprintf("%s/%s: filename too long\n", s1, d->d_name);
+ if(snprintf(ns2, size2, "%s/%s", s2, d->d_name) > size2)
+ eprintf("%s/%s: filename too long\n", s2, d->d_name);
+ fnck(ns1, ns2, cp);
+ }
+ closedir(dp);
+ free(ns1);
+ free(ns2);
+ }
+ return 0;
+ }
+ if(!(f1 = fopen(s1, "r")))
+ eprintf("fopen %s:", s1);
+ if(!(f2 = fopen(s2, "w")))
+ eprintf("fopen %s:", s2);
+ concat(f1, s1, f2, s2);
+ fclose(f2);
+ fclose(f1);
+ return 0;
+}
diff --git a/util/enmasse.c b/util/enmasse.c
@@ -7,8 +7,6 @@
#include <sys/stat.h>
#include "../util.h"
-static void fnck(const char *, const char *, int (*)(const char *, const char *));
-
void
enmasse(int argc, char **argv, int (*fn)(const char *, const char *))
{
@@ -32,15 +30,3 @@ enmasse(int argc, char **argv, int (*fn)(const char *, const char *))
}
free(buf);
}
-
-void
-fnck(const char *a, const char *b, int (*fn)(const char *, const char *))
-{
- struct stat sta, stb;
-
- if(stat(a, &sta) == 0 && stat(b, &stb) == 0
- && sta.st_dev == stb.st_dev && sta.st_ino == stb.st_ino)
- eprintf("%s -> %s: same file\n", a, b);
- if(fn(a, b) == -1)
- eprintf("%s -> %s:", a, b);
-}
diff --git a/util/fnck.c b/util/fnck.c
@@ -0,0 +1,15 @@
+/* See LICENSE file for copyright and license details. */
+#include <sys/stat.h>
+#include "../util.h"
+
+void
+fnck(const char *a, const char *b, int (*fn)(const char *, const char *))
+{
+ struct stat sta, stb;
+
+ if(stat(a, &sta) == 0 && stat(b, &stb) == 0
+ && sta.st_dev == stb.st_dev && sta.st_ino == stb.st_ino)
+ eprintf("%s -> %s: same file\n", a, b);
+ if(fn(a, b) == -1)
+ eprintf("%s -> %s:", a, b);
+}
diff --git a/util/rm.c b/util/rm.c
@@ -0,0 +1,17 @@
+/* See LICENSE file for copyright and license details. */
+#include <stdbool.h>
+#include <stdio.h>
+#include "../fs.h"
+#include "../util.h"
+
+bool rm_fflag = false;
+bool rm_rflag = false;
+
+void
+rm(const char *path)
+{
+ if(rm_rflag)
+ recurse(path, rm);
+ if(remove(path) == -1 && !rm_fflag)
+ eprintf("remove %s:", path);
+}