mblaze/mseq.c
2016-07-28 13:49:00 +02:00

337 lines
5.8 KiB
C

#include <dirent.h>
#include <errno.h>
#include <limits.h>
#include <search.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "blaze822.h"
static int fflag;
static int nflag;
static int rflag;
static int Aflag;
static char *Cflag;
static int Sflag;
struct name {
char *id;
char *file;
};
static void *names;
int
nameorder(const void *a, const void *b)
{
struct name *ia = (struct name *)a;
struct name *ib = (struct name *)b;
return strcmp(ia->id, ib->id);
}
char *
namefind(char *id)
{
struct name key, **result;
key.id = id;
if (!(result = tfind(&key, &names, nameorder)))
return 0;
return (*result)->file;
}
void
namescan(char *dir)
{
DIR *fd;
struct dirent *d;
fd = opendir(dir);
if (!fd)
return;
while ((d = readdir(fd))) {
#if defined(DT_REG) && defined(DT_UNKNOWN)
if (d->d_type != DT_REG && d->d_type != DT_UNKNOWN)
continue;
#endif
if (d->d_name[0] == '.')
continue;
char file[PATH_MAX];
snprintf(file, sizeof file, "%s/%s", dir, d->d_name);
char *e;
if ((e = strstr(d->d_name, ":2,")))
*e = 0;
struct name *c = malloc(sizeof (struct name));
c->id = strdup(d->d_name);
c->file = strdup(file);
tsearch(c, &names, nameorder);
}
closedir(fd);
// add dir so know we scanned it already
struct name *c = malloc(sizeof (struct name));
c->id = c->file = strdup(dir);
tsearch(c, &names, nameorder);
}
char *
search(char *file)
{
char dir[PATH_MAX];
char *e;
if ((e = strrchr(file, '/'))) {
snprintf(dir, sizeof dir, "%.*s", (int)(e-file), file);
file = e+1;
} else {
snprintf(dir, sizeof dir, ".");
}
if (!namefind(dir))
namescan(dir);
return namefind(file);
}
/* strategy to find a Maildir file name:
- if file exists, all good
- try a few common different flags
- index the containing dir, try to search for the id
- if its in new/, try the same in cur/
*/
int
fix(FILE *out, char *file)
{
int i;
for (i = 0; *file == ' '; i++, file++)
;
char buf[PATH_MAX];
char *bufptr = buf;
if (*file == '<' || access(file, F_OK) == 0) {
bufptr = file;
goto ok;
}
char *e;
char *sep;
if ((e = strstr(file, ":2,"))) {
sep = "";
e[3] = 0;
} else {
sep = ":2,";
}
snprintf(buf, sizeof buf, "%s%s", file, sep);
if (access(buf, F_OK) == 0) goto ok;
snprintf(buf, sizeof buf, "%s%sS", file, sep);
if (access(buf, F_OK) == 0) goto ok;
snprintf(buf, sizeof buf, "%s%sRS", file, sep);
if (access(buf, F_OK) == 0) goto ok;
snprintf(buf, sizeof buf, "%s%sFS", file, sep);
if (access(buf, F_OK) == 0) goto ok;
if ((bufptr = search(file))) goto ok;
char *ee = strrchr(file, '/');
if (ee >= file + 3 && ee[-3] == 'n' && ee[-2] == 'e' && ee[-1] == 'w') {
ee[-3] = 'c'; ee[-2] = 'u'; ee[-1] = 'r';
return fix(out, file-i);
}
return 0;
ok:
while(i--)
putc(' ', out);
fprintf(out, "%s\n", bufptr);
return 1;
}
void
cat(FILE *src, FILE *dst)
{
char buf[4096];
size_t rd;
rewind(src);
while ((rd = fread(buf, 1, sizeof buf, src)) > 0)
fwrite(buf, 1, rd, dst);
if (!feof(src)) {
perror("fread");
exit(2);
}
}
int
stdinmode()
{
char *line = 0;
char *l;
size_t linelen = 0;
ssize_t rd;
long i = 0;
FILE *outfile;
char tmpfile[PATH_MAX];
char oldfile[PATH_MAX];
char *seqfile = 0;
if (Sflag) {
// XXX locking?
seqfile = getenv("MAILSEQ");
if (!seqfile)
seqfile = blaze822_home_file(".santoku/seq");
snprintf(tmpfile, sizeof tmpfile, "%s-", seqfile);
snprintf(oldfile, sizeof oldfile, "%s.old", seqfile);
outfile = fopen(tmpfile, "w+");
if (!outfile) {
perror("fopen");
exit(2);
}
if (Aflag) {
FILE *seq = fopen(seqfile, "r");
if (seq) {
cat(seq, outfile);
fclose(seq);
}
}
} else {
outfile = stdout;
}
while ((rd = getline(&line, &linelen, stdin)) != -1) {
if (line[rd-1] == '\n')
line[rd-1] = 0;
if (nflag) {
printf("%ld\n", ++i); // always stdout
continue;
}
l = line;
if (rflag)
while (*l == ' ' || *l == '\t')
l++;
if (fflag)
fix(outfile, l);
else
fprintf(outfile, "%s\n", l);
}
if (Sflag) {
fflush(outfile);
if (rename(seqfile, oldfile) < 0 && errno != ENOENT) {
perror("rename");
exit(2);
}
if (rename(tmpfile, seqfile) < 0) {
perror("rename");
exit(2);
}
if (!isatty(1))
cat(outfile, stdout);
fclose(outfile);
}
free(line);
return 0;
}
void
setcur(char *file)
{
while (*file == ' ')
file++;
blaze822_seq_setcur(file);
}
int
main(int argc, char *argv[])
{
int c;
while ((c = getopt(argc, argv, "fnrAC:S")) != -1)
switch(c) {
case 'f': fflag = 1; break;
case 'n': nflag = 1; break;
case 'r': rflag = 1; break;
case 'A': Sflag = Aflag = 1; break;
case 'C': Cflag = optarg; break;
case 'S': Sflag = 1; break;
default:
usage:
fprintf(stderr,
"Usage: mseq [-fnr] [msgs...]\n"
" mseq -S [-fr] < sequence\n"
" mseq -A [-fr] < sequence\n"
" mseq -C msg\n"
);
exit(1);
}
if (Cflag) {
blaze822_loop1(Cflag, setcur);
return 0;
}
if (nflag && Sflag) {
fprintf(stderr, "error: -n and -S/-A doesn't make sense.\n");
goto usage;
}
if (Sflag && optind != argc) {
fprintf(stderr, "error: -S/-A doesn't take arguments.\n");
goto usage;
}
if (optind == argc && !isatty(0))
return stdinmode();
char *seq = blaze822_seq_open(0);
if (!seq)
return 1;
int i;
char *f;
char *a;
struct blaze822_seq_iter iter = { 0 };
if (optind == argc) {
a = ":";
i = argc;
goto hack;
}
for (i = optind; i < argc; i++) {
a = argv[i];
hack:
if (strchr(a, '/')) {
printf("%s\n", a);
continue;
}
while ((f = blaze822_seq_next(seq, a, &iter))) {
if (nflag) {
printf("%ld\n", iter.line-1);
} else {
char *s = f;
if (rflag)
while (*s == ' ' || *s == '\t')
s++;
if (fflag)
fix(stdout, s);
else
printf("%s\n", s);
}
free(f);
}
}
return 0;
}