mailinfo.con commit Get AUTHOR_DATE from the email Date: line (62c1f6b)
   1/*
   2 * Another stupid program, this one parsing the headers of an
   3 * email to figure out authorship and subject
   4 */
   5#include <stdio.h>
   6#include <stdlib.h>
   7#include <string.h>
   8#include <ctype.h>
   9
  10static FILE *cmitmsg, *patchfile, *filelist;
  11
  12static char line[1000];
  13static char date[1000];
  14static char name[1000];
  15static char email[1000];
  16static char subject[1000];
  17
  18static char *sanity_check(char *name, char *email)
  19{
  20        int len = strlen(name);
  21        if (len < 3 || len > 60)
  22                return email;
  23        if (strchr(name, '@') || strchr(name, '<') || strchr(name, '>'))
  24                return email;
  25        return name;
  26}
  27
  28static int handle_from(char *line)
  29{
  30        char *at = strchr(line, '@');
  31        char *dst;
  32
  33        if (!at)
  34                return 0;
  35
  36        /*
  37         * If we already have one email, don't take any confusing lines
  38         */
  39        if (*email && strchr(at+1, '@'))
  40                return 0;
  41
  42        while (at > line) {
  43                char c = at[-1];
  44                if (isspace(c) || c == '<')
  45                        break;
  46                at--;
  47        }
  48        dst = email;
  49        for (;;) {
  50                unsigned char c = *at;
  51                if (!c || c == '>' || isspace(c))
  52                        break;
  53                *at++ = ' ';
  54                *dst++ = c;
  55        }
  56        *dst++ = 0;
  57
  58        at = line + strlen(line);
  59        while (at > line) {
  60                unsigned char c = *--at;
  61                if (isalnum(c))
  62                        break;
  63                *at = 0;
  64        }
  65
  66        at = line;
  67        for (;;) {
  68                unsigned char c = *at;
  69                if (!c)
  70                        break;
  71                if (isalnum(c))
  72                        break;
  73                at++;
  74        }
  75
  76        at = sanity_check(at, email);
  77        
  78        strcpy(name, at);
  79        return 1;
  80}
  81
  82static void handle_date(char *line)
  83{
  84        strcpy(date, line);
  85}
  86
  87static void handle_subject(char *line)
  88{
  89        strcpy(subject, line);
  90}
  91
  92static void add_subject_line(char *line)
  93{
  94        while (isspace(*line))
  95                line++;
  96        *--line = ' ';
  97        strcat(subject, line);
  98}
  99
 100static void check_line(char *line, int len)
 101{
 102        static int cont = -1;
 103        if (!memcmp(line, "From:", 5) && isspace(line[5])) {
 104                handle_from(line+6);
 105                cont = 0;
 106                return;
 107        }
 108        if (!memcmp(line, "Date:", 5) && isspace(line[5])) {
 109                handle_date(line+6);
 110                cont = 0;
 111                return;
 112        }
 113        if (!memcmp(line, "Subject:", 8) && isspace(line[8])) {
 114                handle_subject(line+9);
 115                cont = 1;
 116                return;
 117        }
 118        if (isspace(*line)) {
 119                switch (cont) {
 120                case 0:
 121                        fprintf(stderr, "I don't do 'Date:' or 'From:' line continuations\n");
 122                        break;
 123                case 1:
 124                        add_subject_line(line);
 125                        return;
 126                default:
 127                        break;
 128                }
 129        }
 130        cont = -1;
 131}
 132
 133static char * cleanup_subject(char *subject)
 134{
 135        for (;;) {
 136                char *p;
 137                int len, remove;
 138                switch (*subject) {
 139                case 'r': case 'R':
 140                        if (!memcmp("e:", subject+1, 2)) {
 141                                subject +=3;
 142                                continue;
 143                        }
 144                        break;
 145                case ' ': case '\t': case ':':
 146                        subject++;
 147                        continue;
 148
 149                case '[':
 150                        p = strchr(subject, ']');
 151                        if (!p) {
 152                                subject++;
 153                                continue;
 154                        }
 155                        len = strlen(p);
 156                        remove = p - subject;
 157                        if (remove <= len *2) {
 158                                subject = p+1;
 159                                continue;
 160                        }       
 161                        break;
 162                }
 163                return subject;
 164        }
 165}                       
 166
 167static void cleanup_space(char *buf)
 168{
 169        unsigned char c;
 170        while ((c = *buf) != 0) {
 171                buf++;
 172                if (isspace(c)) {
 173                        buf[-1] = ' ';
 174                        c = *buf;
 175                        while (isspace(c)) {
 176                                int len = strlen(buf);
 177                                memmove(buf, buf+1, len);
 178                                c = *buf;
 179                        }
 180                }
 181        }
 182}
 183
 184/*
 185 * Hacky hacky. This depends not only on -p1, but on
 186 * filenames not having some special characters in them,
 187 * like tilde.
 188 */
 189static void show_filename(char *line)
 190{
 191        int len;
 192        char *name = strchr(line, '/');
 193
 194        if (!name || !isspace(*line))
 195                return;
 196        name++;
 197        len = 0;
 198        for (;;) {
 199                unsigned char c = name[len];
 200                switch (c) {
 201                default:
 202                        len++;
 203                        continue;
 204
 205                case 0: case ' ':
 206                case '\t': case '\n':
 207                        break;
 208
 209                /* patch tends to special-case these things.. */
 210                case '~':
 211                        break;
 212                }
 213                break;
 214        }
 215        /* remove ".orig" from the end - common patch behaviour */
 216        if (len > 5 && !memcmp(name+len-5, ".orig", 5))
 217                len -=5;
 218        if (!len)
 219                return;
 220        fprintf(filelist, "%.*s\n", len, name);
 221}
 222
 223static void handle_rest(void)
 224{
 225        char *sub = cleanup_subject(subject);
 226        cleanup_space(name);
 227        cleanup_space(date);
 228        cleanup_space(email);
 229        cleanup_space(sub);
 230        printf("Author: %s\nEmail: %s\nSubject: %s\nDate: %s\n\n", name, email, sub, date);
 231        FILE *out = cmitmsg;
 232
 233        do {
 234                /* Track filename information from the patch.. */
 235                if (!memcmp("---", line, 3)) {
 236                        out = patchfile;
 237                        show_filename(line+3);
 238                }
 239
 240                if (!memcmp("+++", line, 3))
 241                        show_filename(line+3);
 242
 243                fputs(line, out);
 244        } while (fgets(line, sizeof(line), stdin) != NULL);
 245
 246        if (out == cmitmsg) {
 247                fprintf(stderr, "No patch found\n");
 248                exit(1);
 249        }
 250
 251        fclose(cmitmsg);
 252        fclose(patchfile);
 253}
 254
 255static int eatspace(char *line)
 256{
 257        int len = strlen(line);
 258        while (len > 0 && isspace(line[len-1]))
 259                line[--len] = 0;
 260        return len;
 261}
 262
 263static void handle_body(void)
 264{
 265        int has_from = 0;
 266
 267        /* First line of body can be a From: */
 268        while (fgets(line, sizeof(line), stdin) != NULL) {
 269                int len = eatspace(line);
 270                if (!len)
 271                        continue;
 272                if (!memcmp("From:", line, 5) && isspace(line[5])) {
 273                        if (!has_from && handle_from(line+6)) {
 274                                has_from = 1;
 275                                continue;
 276                        }
 277                }
 278                line[len] = '\n';
 279                handle_rest();
 280                break;
 281        }
 282}
 283
 284static void usage(void)
 285{
 286        fprintf(stderr, "mailinfo msg-file path-file filelist-file < email\n");
 287        exit(1);
 288}
 289
 290int main(int argc, char ** argv)
 291{
 292        if (argc != 4)
 293                usage();
 294        cmitmsg = fopen(argv[1], "w");
 295        if (!cmitmsg) {
 296                perror(argv[1]);
 297                exit(1);
 298        }
 299        patchfile = fopen(argv[2], "w");
 300        if (!patchfile) {
 301                perror(argv[2]);
 302                exit(1);
 303        }
 304        filelist = fopen(argv[3], "w");
 305        if (!filelist) {
 306                perror(argv[3]);
 307                exit(1);
 308        }
 309        while (fgets(line, sizeof(line), stdin) != NULL) {
 310                int len = eatspace(line);
 311                if (!len) {
 312                        handle_body();
 313                        break;
 314                }
 315                check_line(line, len);
 316        }
 317        return 0;
 318}