/* subjadd.c Adds a "label", such as [XYZ] to the subject line of an email * on stdin and sends it to stdout. * * Tab = 4 * cc subjadd.c -o subjadd * * Slight change to indentation in April 2008. Don't worry if the compiler * complains about: * * warning: incompatible implicit declaration of built-in function 'exit' * */ #include #include /*--------------------------------------------------------------------------------*/ int main(int argc, char *argv[]) { /* Flag to say we must add a Subject: line. * and a temp for when we are looking for the * end of the headers. */ int addsubjectline = 0; char temph = 0; /* For storing the four chars after "Subject: " * which may contain "Re: " and for counting them in case * we reach the end of the file before then. */ char t0 = 0; char t1 = 0; char t2 = 0; char t3 = 0; int recount = 0; /* Temp for the loop which passes the main body of the message. */ char tempo = 0; if (argc == 1) { fprintf(stderr, " subjadd was called with no arguments. Usage: \n"); fprintf(stderr, " \n"); fprintf(stderr, " Adds a label such as [XYZ] to the subject line of an email on \n"); fprintf(stderr, " stdin and sends it to stdout. The label is the only argument. \n"); fprintf(stderr, " \n"); fprintf(stderr, " subjadd [XYZ] results in: \n"); fprintf(stderr, " \n"); fprintf(stderr, " Subject: Foo Subject: Re: Bar \n"); fprintf(stderr, " Subject: [XYZ] Foo Subject: Re: [XYZ] Bar \n"); fprintf(stderr, " \n"); fprintf(stderr, " Always passes the file, but only writes the label if it is the \n"); fprintf(stderr, " sole argument. If there was no subject line, then an empty one \n"); fprintf(stderr, " is added, preceded by an informative header line: \n"); fprintf(stderr, " \n"); fprintf(stderr, " X-Subjadd: No subject line, so this one was added by subjadd. \n"); fprintf(stderr, " \n"); fprintf(stderr, " \"subjadd\" primarily means \"adding to the subject line\" \n"); fprintf(stderr, " but if there was no subject line, then its function is to \n"); fprintf(stderr, " \"add a subject line\" too. \n"); fprintf(stderr, " \n"); fprintf(stderr, " Always returns 0. Intended to be used with the xfilter \n"); fprintf(stderr, " command of Sam Varshavchik's Maildrop mail filtering program. \n"); fprintf(stderr, " \n"); fprintf(stderr, " Public domain. Robin Whittle www.firstpr.com.au 8 July 2001 \n"); fprintf(stderr, " Control C to exit! \n"); } /* Look for the following sequence: * "\nSubject: " * whilst passing all bytes to stdout. * * We can't assume there is a Subject * line. Netscape will send an email * without a Subject: line if you * really have no characters in your * subject. * * We want to add a Subject: line if * we reach the end of the headers * without finding one. * * So we want to simultaneously look * for "Subject: " and pass it, but * also look for two consecutive * newlines and instead of passing the * second one, add the X-Subjadd: line * and then the blank new Subject: * line. Then we need to add the * label and remember to add an extra * newlines after that to end that * line and then to end the headers. * * This approach is a bit gumpy . . . * * Check we are not at the end of the * file, and then call getchar() to * read bytes from stdin. If we * reach the end of the file without * first finding the two consecutive * newlines, then it wasn't a valid * RFC822 email anyway. At end of * file, getchar() returns an EOF * character. * * Then keep reading bytes and * starting the search again if * we don't find a double newline * or "Subject: ". * * But the first test is to break * out of the while statement if * previous iterations have * found a double newline, indicating * end of header, and so setting * addsubjectline to 1. * * So the while statement exits on * an OR of any one of three tests * being false: * * 1 - (addsubjectline == 0) means * exit when it is = 1. * * 2 - (not end of file) means * exit on end of file. * * 3 - (not found "Subject: ") means * exit on finding "Subject". */ while ( (addsubjectline == 0) && (!feof(stdin)) && !( (putchar(getchar()) == '\n') /* Arrive here because we found * a newline, and we can assume * that the char before it, if any, * was not a newline. * * Now, take special action if the * next character is a newline. * * If it was a newline: * * 1 - Set addsubjectline to 1. * * 2 - Do not pass it to the output. * * 3 - Return untrue. * * Otherwise, pass it and compare * it with "S". */ && ( ( (temph = getchar()) == '\n') ? (0 * (addsubjectline = 1)) : (putchar(temph) == 'S') ) && (putchar(getchar()) == 'u') && (putchar(getchar()) == 'b') && (putchar(getchar()) == 'j') && (putchar(getchar()) == 'e') && (putchar(getchar()) == 'c') && (putchar(getchar()) == 't') && (putchar(getchar()) == ':') && (putchar(getchar()) == ' ') ) ) /* End of big while statement. * Do nothing and execute it again * if it is true, which means we * only leave the loop when: * * 1 - End of file - it was not * a valid RFC822 email. * * 2 - We found a double newline. * * 3 - We found "Subject". */ { } /*---------------------------------*/ /* Exit if we hit the end of file. * This should not happen with a * proper RFC822 email. */ if (feof(stdin)) { fprintf(stderr, "subjadd abnormal termination! No double newline found for end of header.\n"); exit(0); } if (addsubjectline == 1) { /* Add the subject line, preceded by * an informative X- header. * * puts() always adds a newline. */ puts("X-Subjadd: No subject line, so this one was added by subjadd."); fprintf(stdout, "Subject: "); } else { /* Arrive here because we have found * the "Subject: " line in the * header. * * Look for "Re: " and pass it if it * is there. * * If not, then don't pass it yet. * * This would be easy if we knew there * were 4 more chars in the email, but * maybe there are not. Maybe the * "Subject: " line has no text on it * and this is the last line of the * header, and there is no body - just * the two newlines which terminate * the header. We still have to pass * each character and add the label. * * Proceed one char at a time and * record how many characters we * found before reaching the end * of file. */ if (!feof(stdin)) { t0 = (char)getchar(); recount = 1; if (!feof(stdin)) { t1 = (char)getchar(); recount = 2; if (!feof(stdin)) { t2 = (char)getchar(); recount = 3; if (!feof(stdin)) { t3 = (char)getchar(); recount = 4; } } } } /* We have between 0 and 4 characters * stored. recount tells us how many. */ if ( ((t0 == 'R') || (t0 == 'r')) && ((t1 == 'E') || (t1 == 'e')) && (t2 == ':') && (t3 == ' ') ) { /* There was a "Re: ". So pass it now. * * Set recount to 0 to show we have no * characters stored which we need to * pass after the label. */ putchar(t0); putchar(t1); putchar(t2); putchar(t3); recount = 0; } } /*---------------------------------*/ /* Whether we found a Subject line or * made our own, and if there was a * Subject line, whether we found a * "Re: " and passed it, or not . . . * * Write the label - the first * argument - with a space after it. * If there is no first argument, or * if there is more than one then do * not write anything. * * puts() would be good to write to * stdio, except that it always * writes a \n too. */ if (argc == 2) { fprintf(stdout, argv[1]); putchar(' '); } /* Now, pass as many chars as we have * stored. These will be 0 to 4 chars * which were not "Re: ", which followed * "Subject: " and which now will follow * the label which we have just written. * * There are only chars stored if there * was a subject line, not followed by a * "Re: ". */ if (recount >= 1) putchar(t0); if (recount >= 2) putchar(t1); if (recount >= 3) putchar(t2); if (recount >= 4) putchar(t3); /* If we added a Subject line, then * we are now at the end of the * headers - so we add a newline * to end the subject line and then * another one to end the headers. * * When searching for the end of the * headers, we found and passed the * newline at the end of the last * header and we found but did not * pass the header-ending newline * which followed. */ if (addsubjectline == 1) { putchar('\n'); putchar('\n'); } /* We have done all the searching and * changing so now simply pass the * rest of the bytes. * * Copy stdin to stdout and exit when * there are no more chars to read. * * feof(stdin) is only valid after the * getchar() which reads to the end of * file. So read, and then test if * the byte read is valid. */ while (1) { tempo = getchar(); if (!feof(stdin)) { putchar(tempo); } else { exit(0); } } }