Merge git-tools repository under "tools" subdirectory

Thanks to Ryan Anderson for setting me up to do this.  I'd have used his
work, but I wanted to clean up the old git-tools repository before
merging it: it had old-style file modes etc that needed a round of
git-convert-cache to fix up.
This commit is contained in:
Linus Torvalds 2005-07-16 10:12:06 -07:00
commit 98e031f0bb
5 changed files with 527 additions and 0 deletions

14
tools/Makefile Normal file
View File

@ -0,0 +1,14 @@
CC=gcc
CFLAGS=-Wall -O2
HOME=$(shell echo $$HOME)
PROGRAMS=mailsplit mailinfo
SCRIPTS=applymbox applypatch
all: $(PROGRAMS)
install: $(PROGRAMS) $(SCRIPTS)
cp -f $(PROGRAMS) $(SCRIPTS) $(HOME)/bin/
clean:
rm -f $(PROGRAMS) *.o

35
tools/applymbox Executable file
View File

@ -0,0 +1,35 @@
#!/bin/sh
##
## "dotest" is my stupid name for my patch-application script, which
## I never got around to renaming after I tested it. We're now on the
## second generation of scripts, still called "dotest".
##
## Update: Ryan Anderson finally shamed me into naming this "applymbox".
##
## You give it a mbox-format collection of emails, and it will try to
## apply them to the kernel using "applypatch"
##
## dotest [ -q ] mail_archive [Signoff_file]
##
rm -rf .dotest
mkdir .dotest
case $1 in
-q) touch .dotest/.query_apply
shift;;
esac
mailsplit $1 .dotest || exit 1
for i in .dotest/*
do
mailinfo .dotest/msg .dotest/patch < $i > .dotest/info || exit 1
git-stripspace < .dotest/msg > .dotest/msg-clean
applypatch .dotest/msg-clean .dotest/patch .dotest/info "$2"
ret=$?
if [ $ret -ne 0 ]; then
# 2 is a special exit code from applypatch to indicate that
# the patch wasn't applied, but continue anyway
[ $ret -ne 2 ] && exit $ret
fi
done
# return to pristine
rm -fr .dotest

64
tools/applypatch Executable file
View File

@ -0,0 +1,64 @@
#!/bin/sh
##
## applypatch takes four file arguments, and uses those to
## apply the unpacked patch (surprise surprise) that they
## represent to the current tree.
##
## The arguments are:
## $1 - file with commit message
## $2 - file with the actual patch
## $3 - "info" file with Author, email and subject
## $4 - optional file containing signoff to add
##
signoff="$4"
final=.dotest/final-commit
##
## If this file exists, we ask before applying
##
query_apply=.dotest/.query_apply
MSGFILE=$1
PATCHFILE=$2
INFO=$3
EDIT=${VISUAL:-$EDITOR}
EDIT=${EDIT:-vi}
export GIT_AUTHOR_NAME="$(sed -n '/^Author/ s/Author: //p' .dotest/info)"
export GIT_AUTHOR_EMAIL="$(sed -n '/^Email/ s/Email: //p' .dotest/info)"
export GIT_AUTHOR_DATE="$(sed -n '/^Date/ s/Date: //p' .dotest/info)"
export SUBJECT="$(sed -n '/^Subject/ s/Subject: //p' .dotest/info)"
if [ -n "$signoff" -a -f "$signoff" ]; then
cat $signoff >> $MSGFILE
fi
(echo "[PATCH] $SUBJECT" ; if [ -s $MSGFILE ]; then echo ; cat $MSGFILE; fi ) > $final
f=0
[ -f $query_apply ] || f=1
while [ $f -eq 0 ]; do
echo "Commit Body is:"
echo "--------------------------"
cat $final
echo "--------------------------"
echo -n "Apply? [y]es/[n]o/[e]dit/[a]ccept all "
read reply
case $reply in
y|Y) f=1;;
n|N) exit 2;; # special value to tell dotest to keep going
e|E) $EDIT $final;;
a|A) rm -f $query_apply
f=1;;
esac
done
echo
echo Applying "'$SUBJECT'"
echo
git-apply --index $PATCHFILE || exit 1
tree=$(git-write-tree) || exit 1
echo Wrote tree $tree
commit=$(git-commit-tree $tree -p $(cat .git/HEAD) < $final) || exit 1
echo Committed: $commit
echo $commit > .git/HEAD

270
tools/mailinfo.c Normal file
View File

@ -0,0 +1,270 @@
/*
* Another stupid program, this one parsing the headers of an
* email to figure out authorship and subject
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
static FILE *cmitmsg, *patchfile;
static char line[1000];
static char date[1000];
static char name[1000];
static char email[1000];
static char subject[1000];
static char *sanity_check(char *name, char *email)
{
int len = strlen(name);
if (len < 3 || len > 60)
return email;
if (strchr(name, '@') || strchr(name, '<') || strchr(name, '>'))
return email;
return name;
}
static int handle_from(char *line)
{
char *at = strchr(line, '@');
char *dst;
if (!at)
return 0;
/*
* If we already have one email, don't take any confusing lines
*/
if (*email && strchr(at+1, '@'))
return 0;
while (at > line) {
char c = at[-1];
if (isspace(c) || c == '<')
break;
at--;
}
dst = email;
for (;;) {
unsigned char c = *at;
if (!c || c == '>' || isspace(c))
break;
*at++ = ' ';
*dst++ = c;
}
*dst++ = 0;
at = line + strlen(line);
while (at > line) {
unsigned char c = *--at;
if (isalnum(c))
break;
*at = 0;
}
at = line;
for (;;) {
unsigned char c = *at;
if (!c)
break;
if (isalnum(c))
break;
at++;
}
at = sanity_check(at, email);
strcpy(name, at);
return 1;
}
static void handle_date(char *line)
{
strcpy(date, line);
}
static void handle_subject(char *line)
{
strcpy(subject, line);
}
static void add_subject_line(char *line)
{
while (isspace(*line))
line++;
*--line = ' ';
strcat(subject, line);
}
static void check_line(char *line, int len)
{
static int cont = -1;
if (!memcmp(line, "From:", 5) && isspace(line[5])) {
handle_from(line+6);
cont = 0;
return;
}
if (!memcmp(line, "Date:", 5) && isspace(line[5])) {
handle_date(line+6);
cont = 0;
return;
}
if (!memcmp(line, "Subject:", 8) && isspace(line[8])) {
handle_subject(line+9);
cont = 1;
return;
}
if (isspace(*line)) {
switch (cont) {
case 0:
fprintf(stderr, "I don't do 'Date:' or 'From:' line continuations\n");
break;
case 1:
add_subject_line(line);
return;
default:
break;
}
}
cont = -1;
}
static char * cleanup_subject(char *subject)
{
for (;;) {
char *p;
int len, remove;
switch (*subject) {
case 'r': case 'R':
if (!memcmp("e:", subject+1, 2)) {
subject +=3;
continue;
}
break;
case ' ': case '\t': case ':':
subject++;
continue;
case '[':
p = strchr(subject, ']');
if (!p) {
subject++;
continue;
}
len = strlen(p);
remove = p - subject;
if (remove <= len *2) {
subject = p+1;
continue;
}
break;
}
return subject;
}
}
static void cleanup_space(char *buf)
{
unsigned char c;
while ((c = *buf) != 0) {
buf++;
if (isspace(c)) {
buf[-1] = ' ';
c = *buf;
while (isspace(c)) {
int len = strlen(buf);
memmove(buf, buf+1, len);
c = *buf;
}
}
}
}
static void handle_rest(void)
{
char *sub = cleanup_subject(subject);
cleanup_space(name);
cleanup_space(date);
cleanup_space(email);
cleanup_space(sub);
printf("Author: %s\nEmail: %s\nSubject: %s\nDate: %s\n\n", name, email, sub, date);
FILE *out = cmitmsg;
do {
if (!memcmp("diff -", line, 6) ||
!memcmp("---", line, 3) ||
!memcmp("Index: ", line, 7))
out = patchfile;
fputs(line, out);
} while (fgets(line, sizeof(line), stdin) != NULL);
if (out == cmitmsg) {
fprintf(stderr, "No patch found\n");
exit(1);
}
fclose(cmitmsg);
fclose(patchfile);
}
static int eatspace(char *line)
{
int len = strlen(line);
while (len > 0 && isspace(line[len-1]))
line[--len] = 0;
return len;
}
static void handle_body(void)
{
int has_from = 0;
/* First line of body can be a From: */
while (fgets(line, sizeof(line), stdin) != NULL) {
int len = eatspace(line);
if (!len)
continue;
if (!memcmp("From:", line, 5) && isspace(line[5])) {
if (!has_from && handle_from(line+6)) {
has_from = 1;
continue;
}
}
line[len] = '\n';
handle_rest();
break;
}
}
static void usage(void)
{
fprintf(stderr, "mailinfo msg-file path-file < email\n");
exit(1);
}
int main(int argc, char ** argv)
{
if (argc != 3)
usage();
cmitmsg = fopen(argv[1], "w");
if (!cmitmsg) {
perror(argv[1]);
exit(1);
}
patchfile = fopen(argv[2], "w");
if (!patchfile) {
perror(argv[2]);
exit(1);
}
while (fgets(line, sizeof(line), stdin) != NULL) {
int len = eatspace(line);
if (!len) {
handle_body();
break;
}
check_line(line, len);
}
return 0;
}

144
tools/mailsplit.c Normal file
View File

@ -0,0 +1,144 @@
/*
* Totally braindamaged mbox splitter program.
*
* It just splits a mbox into a list of files: "0001" "0002" ..
* so you can process them further from there.
*/
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include <assert.h>
static int usage(void)
{
fprintf(stderr, "mailsplit <mbox> <directory>\n");
exit(1);
}
static int linelen(const char *map, unsigned long size)
{
int len = 0, c;
do {
c = *map;
map++;
size--;
len++;
} while (size && c != '\n');
return len;
}
static int is_from_line(const char *line, int len)
{
const char *colon;
if (len < 20 || memcmp("From ", line, 5))
return 0;
colon = line + len - 2;
line += 5;
for (;;) {
if (colon < line)
return 0;
if (*--colon == ':')
break;
}
if (!isdigit(colon[-4]) ||
!isdigit(colon[-2]) ||
!isdigit(colon[-1]) ||
!isdigit(colon[ 1]) ||
!isdigit(colon[ 2]))
return 0;
/* year */
if (strtol(colon+3, NULL, 10) <= 90)
return 0;
/* Ok, close enough */
return 1;
}
static int parse_email(const void *map, unsigned long size)
{
unsigned long offset;
if (size < 6 || memcmp("From ", map, 5))
goto corrupt;
/* Make sure we don't trigger on this first line */
map++; size--; offset=1;
/*
* Search for a line beginning with "From ", and
* having smething that looks like a date format.
*/
do {
int len = linelen(map, size);
if (is_from_line(map, len))
return offset;
map += len;
size -= len;
offset += len;
} while (size);
return offset;
corrupt:
fprintf(stderr, "corrupt mailbox\n");
exit(1);
}
int main(int argc, char **argv)
{
int fd, nr;
struct stat st;
unsigned long size;
void *map;
if (argc != 3)
usage();
fd = open(argv[1], O_RDONLY);
if (fd < 0) {
perror(argv[1]);
exit(1);
}
if (chdir(argv[2]) < 0)
usage();
if (fstat(fd, &st) < 0) {
perror("stat");
exit(1);
}
size = st.st_size;
map = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
if (-1 == (int)(long)map) {
perror("mmap");
exit(1);
}
close(fd);
nr = 0;
do {
char name[10];
unsigned long len = parse_email(map, size);
assert(len <= size);
sprintf(name, "%04d", ++nr);
fd = open(name, O_WRONLY | O_CREAT | O_EXCL, 0600);
if (fd < 0) {
perror(name);
exit(1);
}
if (write(fd, map, len) != len) {
perror("write");
exit(1);
}
close(fd);
map += len;
size -= len;
} while (size > 0);
return 0;
}