Browse Source
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.maint
Linus Torvalds
20 years ago
5 changed files with 527 additions and 0 deletions
@ -0,0 +1,14 @@
@@ -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 |
@ -0,0 +1,35 @@
@@ -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 |
@ -0,0 +1,64 @@
@@ -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 |
@ -0,0 +1,270 @@
@@ -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; |
||||
} |
@ -0,0 +1,144 @@
@@ -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; |
||||
} |
Loading…
Reference in new issue