Merge git-tools repository under "tools" subdirectory
authorLinus Torvalds <torvalds@g5.osdl.org>
Sat, 16 Jul 2005 17:12:06 +0000 (10:12 -0700)
committerLinus Torvalds <torvalds@g5.osdl.org>
Sat, 16 Jul 2005 17:12:06 +0000 (10:12 -0700)
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.

tools/Makefile [new file with mode: 0644]
tools/applymbox [new file with mode: 0755]
tools/applypatch [new file with mode: 0755]
tools/mailinfo.c [new file with mode: 0644]
tools/mailsplit.c [new file with mode: 0644]

diff --git a/tools/Makefile b/tools/Makefile
new file mode 100644 (file)
index 0000000..8e7252e
--- /dev/null
@@ -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
diff --git a/tools/applymbox b/tools/applymbox
new file mode 100755 (executable)
index 0000000..5ac8d2b
--- /dev/null
@@ -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
diff --git a/tools/applypatch b/tools/applypatch
new file mode 100755 (executable)
index 0000000..5a3a44b
--- /dev/null
@@ -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
diff --git a/tools/mailinfo.c b/tools/mailinfo.c
new file mode 100644 (file)
index 0000000..ae279bf
--- /dev/null
@@ -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;
+}
diff --git a/tools/mailsplit.c b/tools/mailsplit.c
new file mode 100644 (file)
index 0000000..9379fbc
--- /dev/null
@@ -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;
+}