commit - /dev/null
commit + b79428b4aec5e99d9c0f1dcc07fe677c2679cf4a
blob - /dev/null
blob + b5e9a61d525eb0d72c5e28cc95b1817549a69aa9 (mode 644)
--- /dev/null
+++ Makefile
+include config.mk
+
+.SUFFIXES: .o .c
+
+BIN = rene
+OBJ = $(BIN:=.o)
+SRC = $(BIN:=.c)
+MAN = $(BIN:=.1)
+
+all: $(BIN)
+
+$(BIN): $(OBJ)
+ $(CC) -o $@ $(OBJ) $(LDFLAGS)
+
+$(OBJ): config.mk
+
+.c.o:
+ $(CC) $(CFLAGS) $(CPPFLAGS) -c $<
+
+install: all
+ mkdir -p $(DESTDIR)$(PREFIX)/bin
+ install -m 755 $(BIN) $(DISTDIR)$(PREFIX)/bin/
+ mkdir -p $(DESTDIR)$(MANPREFIX)/man1
+ install -m 644 $(MAN) $(DISTDIR)$(MANPREFIX)/man1
+
+uninstall:
+ cd $(DESTDIR)$(PREFIX)/bin && rm -f $(BIN)
+ cd $(DESTDIR)$(MANPREFIX)/man1 && rm -f $(MAN)
+
+dist: clean
+ mkdir -p rene-$(VERSION)
+ cp -rf TODO README Makefile config.mk t $(SRC) $(MAN) rene-$(VERSION)
+ tar -czf rene-$(VERSION).tar.gz rene-$(VERSION)
+ rm -rf rene-$(VERSION)
+
+clean:
+ -rm -f $(BIN) $(OBJ)
+ -rm -f rene-$(VERSION).tag.gz
+ -rm -f *.core
+ -rm -f vgcore.*
+
+test:
+ -sh ./t/t.sh
+
+.PHONY: all install uninstall clean dist test
blob - /dev/null
blob + 9e4493647aac5f594b374a9e6bad12654eee5b4c (mode 644)
--- /dev/null
+++ README
+rene is a batch-renaming tool similar to rename from util-linux, but for all
+Unix-likes. To install, edit config.mk if necessary, and then run make and
+make install:
+
+ $ $EDITOR config.mk
+ $ make
+ # make install
+
+
+COPYING
+
+Copyright (c) 2022 Alexander Arkhipov <scm@mineeyes.cyou>
+
+Permission to use, copy, modify, and distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
blob - /dev/null
blob + a6d21f954682c09856c6a4342e6a35d6ba0ff935 (mode 644)
--- /dev/null
+++ TODO
+- add options -b and -e for basic and extended regex;
+
+- add support for pledge(2);
blob - /dev/null
blob + 2241ed7407971cea3182a2144cc599bce75de4c3 (mode 644)
--- /dev/null
+++ config.mk
+VERSION = 0.1
+
+PREFIX = /usr/local
+MANPREFIX = $(PREFIX)/man
+
+CC = cc
+CFLAGS = -std=c99 -Wall -pedantic
+CPPFLAGS = -D_DEFAULT_SOURCE
blob - /dev/null
blob + f047179ba2369365a2e2d012e784a519dfb8fe65 (mode 755)
Binary files /dev/null and rene differ
blob - /dev/null
blob + 600a65df3c46936110b851538688167a1e4aa638 (mode 644)
--- /dev/null
+++ rene.1
+.Dd July 07, 2022
+.Dt RENE 1
+.Os
+.Sh NAME
+.Nm rene
+.Op Fl ailnov
+.Nd rename files
+.Sh SYNOPSIS
+.Nm
+.Ar from to file ...
+.Sh DESCRIPTION
+The utility
+.Nm
+renames the supplied
+.Ar files
+by replacing the first occurence of
+.Ar from
+with
+.Ar to .
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl a
+Replace all occurences of
+.Ar from
+instead of only the first one. Overrides
+.Fl l .
+.It Fl i
+Prompt to stderr before overriding existing files. A renaming operation is only
+attempted if the response form stdin begins with the character
+.Dq y .
+.It Fl l
+Replace the last occurence of
+.Ar from
+instead of the first one.
+.It Fl n
+Don't actually rename any files. Useful with
+.Fl v .
+.It Fl o
+Don't override existing files. Disables
+.Fl i .
+.It Fl v
+Display the old and new names of each renamed file.
+.El
+.Sh EXIT STATUS
+.Ex -std
+.Sh SEE ALSO
+.Xr mv 1 ,
+.Xr rename 2
+.Sh AUTHORS
+.An Alexander Arkhipov Aq Mt src@mineeyes.cyou .
blob - /dev/null
blob + 2ab6b9e65216392e7300fb04a6f2f76a3c4d1ba2 (mode 644)
--- /dev/null
+++ rene.c
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+int interactive = 0;
+int noact = 0;
+int nooverride = 0;
+int replaceall = 0;
+int replacelast = 0;
+int verbose = 0;
+
+void
+usage(void)
+{
+ fprintf(stderr, "usage: rene [-ailnov] from to file ...\n");
+ exit(EXIT_FAILURE);
+}
+
+char *rene;
+
+void
+err(const char *fmt, ...)
+{
+ fprintf(stderr, "%s: ", rene);
+ if (fmt != NULL) {
+ va_list argp;
+ va_start(argp, fmt);
+ vfprintf(stderr, fmt, argp);
+ va_end(argp);
+ }
+ fprintf(stderr, "\n");
+}
+
+int
+strrep(char *from, char *to, char *s, char **new)
+{
+ char *p = strrchr(s, '/');
+ if (p) {
+ if (*++p == '\0')
+ return 0;
+ } else
+ p = s;
+ p = strstr(p, from);
+ if (!p)
+ return 0;
+ int fromlen = strlen(from);
+ int count = 1;
+ if (replacelast || replaceall) {
+ char *temp = p;
+ while (temp) {
+ temp = strstr(temp+fromlen, from);
+ count = (replaceall && temp) ? count + 1 : count;
+ p = (replacelast && temp) ? temp : p;
+ }
+ }
+
+ if (!(*new = malloc(strlen(s) + strlen(to)*count - fromlen*count + 1))) {
+ err("%s: couldn't allocate memory", s);
+ return 0;
+ }
+
+ char *newp = *new;
+ char *top = to;
+ while (p) {
+ for (; s != p; *newp++ = *s++)
+ ;
+ for (; *top != '\0'; *newp++ = *top++)
+ ;
+ s += fromlen;
+ p = replaceall ? strstr(s, from) : strchr(s, '\0');
+ p = p ? p : strchr(s, '\0');
+ for (; s != p; *newp++ = *s++)
+ ;
+ p = (*p == '\0') ? NULL : p;
+ top = to;
+ }
+ *newp = '\0';
+ return 1;
+}
+
+int
+ask(char *from, char *to)
+{
+ fprintf(stderr, "replace %s with %s? ", from, to);
+ return getchar() == 'y';
+}
+
+void
+ren(char *from, char *to, char *f)
+{
+ int yes = 1;
+ char *new = NULL;
+ if (!strrep(from, to, f, &new))
+ return;
+ if ((nooverride || interactive) && access(new, F_OK) == 0)
+ yes = nooverride ? 0 : ask(f, new);
+ if (yes && !noact)
+ yes -= rename(f, new);
+ if (verbose && yes)
+ printf("%s -> %s\n", f, new);
+ free(new);
+}
+
+int
+main(int argc, char *argv[])
+{
+ char *from, *to;
+ rene = argv[0];
+
+ int c;
+ while ((c = getopt(argc, argv, "ailnov")) != -1) {
+ switch (c) {
+ case 'a':
+ replaceall = 1;
+ break;
+ case 'i':
+ interactive = 1;
+ break;
+ case 'l':
+ replacelast = 1;
+ break;
+ case 'n':
+ noact = 1;
+ break;
+ case 'o':
+ nooverride = 1;
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 3)
+ usage();
+ from = argv[0];
+ to = argv[1];
+ for (int i = 2; i < argc; i++)
+ ren(from, to, argv[i]);
+ return 0;
+}
blob - /dev/null
blob + 2a7024a24d46c0da802241eb1ca2902e03ce2cca (mode 644)
Binary files /dev/null and rene.o differ
blob - /dev/null
blob + e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 (mode 644)
blob - /dev/null
blob + b68c9f68791cd8985f27447a53d9bc52ae055b73 (mode 644)
--- /dev/null
+++ t/all.exp
+foobar -> barbar
+barfoo -> barbar
+barfoobar -> barbarbar
+foobarfoo -> barbarbar
blob - /dev/null
blob + 0b3927a595e3669652fc5f2f017d22350fc8fc5a (mode 644)
--- /dev/null
+++ t/all.t
+./rene -nva foo bar foobar barfoo barfoobar foobarfoo foo/ bar
blob - /dev/null
blob + c3e4f5385af30cf0e58e43911bcb19eeb8b585f6 (mode 644)
--- /dev/null
+++ t/base.sh
+#!/bin/sh
+
+tests="$(find . -name '*.t')"
+
+for t in $tests; do
+ base=${t%%.t}
+ printf '%-10s ' "$(basename $base)"
+ sh $t >$base.out 2>$base.err
+ cmp $base.out $base.exp >/dev/null 2>&1
+ res=$?
+ cmp $base.err $base.err.exp >/dev/null 2>&1
+ eres=$?
+ if [ "$res" = 0 ] && [ "$eres" = 0 ]; then
+ echo OK
+ else
+ echo FAIL
+ fi
+done
blob - /dev/null
blob + e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 (mode 644)
blob - /dev/null
blob + adb838938baad969374ccf30dca0f50da9f17ca2 (mode 644)
--- /dev/null
+++ t/last.exp
+foobar -> barbar
+barfoo -> barbar
+barfoobar -> barbarbar
+foobarfoo -> foobarbar
blob - /dev/null
blob + 30987e736f62901ccff5cd09a8bebe56a383d259 (mode 644)
--- /dev/null
+++ t/last.t
+./rene -nvl foo bar foobar barfoo barfoobar foobarfoo foo/ bar
blob - /dev/null
blob + e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 (mode 644)
blob - /dev/null
blob + e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 (mode 644)
blob - /dev/null
blob + e704573b5fa887e354882c0e77b799d393fe9993 (mode 644)
--- /dev/null
+++ t/nooverride.t
+tmp=/tmp/.rene.tes
+mkdir -p $tmp
+trap 'rm -rf $tmp' EXIT HUP INT
+touch $tmp/bar
+./rene -nvo foo bar $tmp/foo
blob - /dev/null
blob + e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 (mode 644)
blob - /dev/null
blob + ac6dd9e1f817c246cdef9be77b681b21c42ec6da (mode 644)
--- /dev/null
+++ t/norm.exp
+foobar -> barbar
+barfoo -> barbar
+barfoobar -> barbarbar
+foobarfoo -> barbarfoo
blob - /dev/null
blob + c85b4d4bbb6a39f698e4f1b5fb4fe68dedce850f (mode 644)
--- /dev/null
+++ t/norm.t
+./rene -nv foo bar foobar barfoo barfoobar foobarfoo foo/ bar
blob - /dev/null
blob + 9dae219cfe88e8e6dd00b95724eb31b8cf0d87cf (mode 644)
--- /dev/null
+++ t/t.sh
+dir="$(dirname $0)"
+
+echo ---- BASE ----
+sh "$dir"/base.sh
+
+echo
+echo -- VALGRIND --
+sh "$dir"/val.sh
blob - /dev/null
blob + 671cb90afbfd5a0edaf9b236945f07774252ce56 (mode 644)
--- /dev/null
+++ t/val.sh
+res() {
+ printf '%-10s ' "$1"
+ if [ $2 = $err ]; then
+ echo FAIL
+ else
+ echo OK
+ fi
+}
+err=1
+
+echo AVERAGE
+set -- foo bar foobar barfoo barfoobar foobarfoo foofoofoo foo bar
+valgrind --error-exitcode=$err ./rene "$@" >/dev/null 2>&1
+res normal $?
+valgrind --error-exitcode=$err ./rene -l "$@" >/dev/null 2>&1
+res last $?
+valgrind --error-exitcode=$err ./rene -a "$@" >/dev/null 2>&1
+res all $?
+
+echo
+echo SLASH
+set -- foo bar foobar/ barfoo/ barfoobar/ foobarfoo/ foofoofoo/ foo/ bar/
+valgrind --error-exitcode=$err ./rene "$@" >/dev/null 2>&1
+res normal $?
+valgrind --error-exitcode=$err ./rene -l "$@" >/dev/null 2>&1
+res last $?
+valgrind --error-exitcode=$err ./rene -a "$@" >/dev/null 2>&1
+res all $?
+
+echo
+echo NOFROM
+set -- foo bar bar abracadebra lol abcd
+valgrind --error-exitcode=$err ./rene "$@" >/dev/null 2>&1
+res normal $?
+valgrind --error-exitcode=$err ./rene -l "$@" >/dev/null 2>&1
+res last $?
+valgrind --error-exitcode=$err ./rene -a "$@" >/dev/null 2>&1
+res all $?