📄 nedmail.c
字号:
p = seprint(buf, e, "%-64s %-6d ", typeid, m->len); if(m->filename && *m->filename) p = seprint(p, e, "(file,%s)", m->filename); if(m->from && *m->from) p = seprint(p, e, "(from,%s)", m->from); if(m->subject && *m->subject) seprint(p, e, "(subj,%s)", m->subject); s_free(id);}char sstring[256];// cmd := range cmd ' ' arg-list ; // range := address// | address ',' address// | 'g' search ;// address := msgno// | search ;// msgno := number// | number '/' msgno ;// search := '/' string '/'// | '%' string '%' ;//Reprog*parsesearch(char **pp){ char *p, *np; int c, n; p = *pp; c = *p++; np = strchr(p, c); if(np != nil){ *np++ = 0; *pp = np; } else { n = strlen(p); *pp = p + n; } if(*p == 0) p = sstring; else{ strncpy(sstring, p, sizeof(sstring)); sstring[sizeof(sstring)-1] = 0; } return regcomp(p);}char*parseaddr(char **pp, Message *first, Message *cur, Message *unspec, Message **mp){ int n; Message *m; char *p; Reprog *prog; int c, sign; char buf[256]; *mp = nil; p = *pp; if(*p == '+'){ sign = 1; p++; *pp = p; } else if(*p == '-'){ sign = -1; p++; *pp = p; } else sign = 0; switch(*p){ default: if(sign){ n = 1; goto number; } *mp = unspec; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': n = strtoul(p, pp, 10); if(n == 0){ if(sign) *mp = cur; else *mp = ⊤ break; } number: m = nil; switch(sign){ case 0: for(m = first; m != nil; m = m->next) if(m->id == n) break; break; case -1: if(cur != &top) for(m = cur; m != nil && n > 0; n--) m = m->prev; break; case 1: if(cur == &top){ n--; cur = first; } for(m = cur; m != nil && n > 0; n--) m = m->next; break; } if(m == nil) return "address"; *mp = m; break; case '%': case '/': case '?': c = *p; prog = parsesearch(pp); if(prog == nil) return "badly formed regular expression"; m = nil; switch(c){ case '%': for(m = cur == &top ? first : cur->next; m != nil; m = m->next){ if(rawsearch(m, prog)) break; } break; case '/': for(m = cur == &top ? first : cur->next; m != nil; m = m->next){ snprintheader(buf, sizeof(buf), m); if(regexec(prog, buf, nil, 0)) break; } break; case '?': for(m = cur == &top ? nil : cur->prev; m != nil; m = m->prev){ snprintheader(buf, sizeof(buf), m); if(regexec(prog, buf, nil, 0)) break; } break; } if(m == nil) return "search"; *mp = m; free(prog); break; case '$': for(m = first; m != nil && m->next != nil; m = m->next) ; *mp = m; *pp = p+1; break; case '.': *mp = cur; *pp = p+1; break; case ',': *mp = first; *pp = p; break; } if(*mp != nil && **pp == '.'){ (*pp)++; if((*mp)->child == nil) return "no sub parts"; return parseaddr(pp, (*mp)->child, (*mp)->child, (*mp)->child, mp); } if(**pp == '+' || **pp == '-' || **pp == '/' || **pp == '%') return parseaddr(pp, first, *mp, *mp, mp); return nil;}//// search a message for a regular expression match//intrawsearch(Message *m, Reprog *prog){ char buf[4096+1]; int i, fd, rv; String *path; path = extendpath(m->path, "raw"); fd = open(s_to_c(path), OREAD); if(fd < 0) return 0; // march through raw message 4096 bytes at a time // with a 128 byte overlap to chain the re search. rv = 0; for(;;){ i = read(fd, buf, sizeof(buf)-1); if(i <= 0) break; buf[i] = 0; if(regexec(prog, buf, nil, 0)){ rv = 1; break; } if(i < sizeof(buf)-1) break; if(seek(fd, -128LL, 1) < 0) break; } close(fd); s_free(path); return rv;}char*parsecmd(char *p, Cmd *cmd, Message *first, Message *cur){ Reprog *prog; Message *m, *s, *e, **l, *last; char buf[256]; char *err; int i, c; char *q; static char errbuf[Errlen]; cmd->delete = 0; l = &cmd->msgs; *l = nil; // eat white space while(*p == ' ') p++; // null command is a special case (advance and print) if(*p == 0){ if(cur == &top){ // special case m = first; } else { // walk to the next message even if we have to go up m = cur->next; while(m == nil && cur->parent != nil){ cur = cur->parent; m = cur->next; } } if(m == nil) return "address"; *l = m; m->cmd = nil; cmd->an = 0; cmd->f = pcmd; return nil; } // global search ? if(*p == 'g'){ p++; // no search string means all messages if(*p != '/' && *p != '%'){ for(m = first; m != nil; m = m->next){ *l = m; l = &m->cmd; *l = nil; } } else { // mark all messages matching this search string c = *p; prog = parsesearch(&p); if(prog == nil) return "badly formed regular expression"; if(c == '%'){ for(m = first; m != nil; m = m->next){ if(rawsearch(m, prog)){ *l = m; l = &m->cmd; *l = nil; } } } else { for(m = first; m != nil; m = m->next){ snprintheader(buf, sizeof(buf), m); if(regexec(prog, buf, nil, 0)){ *l = m; l = &m->cmd; *l = nil; } } } free(prog); } } else { // parse an address s = e = nil; err = parseaddr(&p, first, cur, cur, &s); if(err != nil) return err; if(*p == ','){ // this is an address range if(s == &top) s = first; p++; for(last = s; last != nil && last->next != nil; last = last->next) ; err = parseaddr(&p, first, cur, last, &e); if(err != nil) return err; // select all messages in the range for(; s != nil; s = s->next){ *l = s; l = &s->cmd; *l = nil; if(s == e) break; } if(s == nil) return "null address range"; } else { // single address if(s != &top){ *l = s; s->cmd = nil; } } } // insert a space after '!'s and '|'s for(q = p; *q; q++) if(*q != '!' && *q != '|') break; if(q != p && *q != ' '){ memmove(q+1, q, strlen(q)+1); *q = ' '; } cmd->an = getfields(p, cmd->av, nelem(cmd->av) - 1, 1, " \t\r\n"); if(cmd->an == 0 || *cmd->av[0] == 0) cmd->f = pcmd; else { // hack to allow all messages to start with 'd' if(*(cmd->av[0]) == 'd' && *(cmd->av[0]+1) != 0){ cmd->delete = 1; cmd->av[0]++; } // search command table for(i = 0; cmdtab[i].cmd != nil; i++) if(strcmp(cmd->av[0], cmdtab[i].cmd) == 0) break; if(cmdtab[i].cmd == nil) return "illegal command"; if(cmdtab[i].args == 0 && cmd->an > 1){ snprint(errbuf, sizeof(errbuf), "%s doesn't take an argument", cmdtab[i].cmd); return errbuf; } cmd->f = cmdtab[i].f; } return nil; }// inefficient read from standard inputchar*readline(char *prompt, char *line, int len){ char *p, *e; int n;retry: interrupted = 0; Bprint(&out, "%s", prompt); Bflush(&out); e = line + len; for(p = line; p < e; p++){ n = read(0, p, 1); if(n < 0){ if(interrupted) goto retry; return nil; } if(n == 0) return nil; if(*p == '\n') break; } *p = 0; return line;}voidmessagecount(Message *m){ int i; i = 0; for(; m != nil; m = m->next) i++; Bprint(&out, "%d message%s\n", i, plural(i));}Message*aichcmd(Message *m, int indent){ char hdr[256]; if(m == &top) return nil; snprintHeader(hdr, sizeof(hdr), indent, m); Bprint(&out, "%s\n", hdr); for(m = m->child; m != nil; m = m->next) aichcmd(m, indent+1); return nil;}Message*Hcmd(Cmd*, Message *m){ if(m == &top) return nil; aichcmd(m, 0); return nil;}Message*hcmd(Cmd*, Message *m){ char hdr[256]; if(m == &top) return nil; snprintheader(hdr, sizeof(hdr), m); Bprint(&out, "%s\n", hdr); return nil;}Message*bcmd(Cmd*, Message *m){ int i; Message *om = m; if(m == &top) m = top.child; for(i = 0; i < 10 && m != nil; i++){ hcmd(nil, m); om = m; m = m->next; } return om;}Message*ncmd(Cmd*, Message *m){ if(m == &top) return m->child; return m->next;}intprintpart(String *s, char *part){ char buf[4096]; int n, fd, tot; String *path; path = extendpath(s, part); fd = open(s_to_c(path), OREAD); s_free(path); if(fd < 0){ fprint(2, "!message dissappeared\n"); return 0; } tot = 0; while((n = read(fd, buf, sizeof(buf))) > 0){ if(interrupted) break; if(Bwrite(&out, buf, n) <= 0) break; tot += n; } close(fd); return tot;}intprinthtml(Message *m){ Cmd c; c.an = 3; c.av[1] = "/bin/htmlfmt"; c.av[2] = "-l 40 -cutf-8"; Bprint(&out, "!%s\n", c.av[1]); Bflush(&out); pipecmd(&c, m); return 0;}Message*Pcmd(Cmd*, Message *m){ if(m == &top) return ⊤ if(m->parent == &top) printpart(m->path, "unixheader"); printpart(m->path, "raw"); return m;}voidcompress(char *p){ char *np; int last; last = ' '; for(np = p; *p; p++){ if(*p != ' ' || last != ' '){ last = *p; *np++ = last; } } *np = 0;}Message*pcmd(Cmd*, Message *m){ Message *nm; Ctype *cp; String *s; char buf[128]; if(m == &top) return ⊤ if(m->parent == &top) printpart(m->path, "unixheader"); if(printpart(m->path, "header") > 0) Bprint(&out, "\n"); cp = findctype(m); if(cp->display){ if(strcmp(m->type, "text/html") == 0) printhtml(m); else printpart(m->path, "body"); } else if(strcmp(m->type, "multipart/alternative") == 0){ for(nm = m->child; nm != nil; nm = nm->next){ cp = findctype(nm); if(cp->ext != nil && strncmp(cp->ext, "txt", 3) == 0) break; } if(nm == nil) for(nm = m->child; nm != nil; nm = nm->next){ cp = findctype(nm); if(cp->display) break; } if(nm != nil) pcmd(nil, nm); else hcmd(nil, m); } else if(strncmp(m->type, "multipart/", 10) == 0){ nm = m->child; if(nm != nil){ // always print first part pcmd(nil, nm); for(nm = nm->next; nm != nil; nm = nm->next){ s = rooted(s_clone(nm->path)); cp = findctype(nm); snprintHeader(buf, sizeof buf, -1, nm); compress(buf); if(strcmp(nm->disposition, "inline") == 0){ if(cp->ext != nil) Bprint(&out, "\n--- %s %s/body.%s\n\n", buf, s_to_c(s), cp->ext); else Bprint(&out, "\n--- %s %s/body\n\n", buf, s_to_c(s)); pcmd(nil, nm); } else { if(cp->ext != nil) Bprint(&out, "\n!--- %s %s/body.%s\n", buf, s_to_c(s), cp->ext); else Bprint(&out, "\n!--- %s %s/body\n", buf, s_to_c(s)); } s_free(s); } } else { hcmd(nil, m); } } else if(strcmp(m->type, "message/rfc822") == 0){ pcmd(nil, m->child); } else if(plumb(m, cp) >= 0) Bprint(&out, "\n!--- using plumber to display message of type %s\n", m->type); else Bprint(&out, "\n!--- cannot display messages of type %s\n", m->type); return m;}voidprintpartindented(String *s, char *part, char *indent){ char *p; String *path; Biobuf *b; path = extendpath(s, part); b = Bopen(s_to_c(path), OREAD); s_free(path); if(b == nil){ fprint(2, "!message dissappeared\n"); return; } while((p = Brdline(b, '\n')) != nil){ if(interrupted) break; p[Blinelen(b)-1] = 0; if(Bprint(&out, "%s%s\n", indent, p) < 0) break; } Bprint(&out, "\n"); Bterm(b);}Message*quotecmd(Cmd*, Message *m){ Message *nm; Ctype *cp; if(m == &top) return ⊤ Bprint(&out, "\n"); if(m->from != nil && *m->from) Bprint(&out, "On %s, %s wrote:\n", m->date, m->from); cp = findctype(m); if(cp->display){ printpartindented(m->path, "body", "> "); } else if(strcmp(m->type, "multipart/alternative") == 0){ for(nm = m->child; nm != nil; nm = nm->next){ cp = findctype(nm); if(cp->ext != nil && strncmp(cp->ext, "txt", 3) == 0) break; } if(nm == nil) for(nm = m->child; nm != nil; nm = nm->next){ cp = findctype(nm); if(cp->display) break; } if(nm != nil) quotecmd(nil, nm); } else if(strncmp(m->type, "multipart/", 10) == 0){ nm = m->child; if(nm != nil){ cp = findctype(nm); if(cp->display || strncmp(m->type, "multipart/", 10) == 0) quotecmd(nil, nm); } } return m;}// really delete messagesMessage*flushdeleted(Message *cur){ Message *m, **l; char buf[1024], *p, *e, *msg; int deld, n, fd; int i; doflush = 0; deld = 0; fd = open("/mail/fs/ctl", ORDWR); if(fd < 0){ fprint(2, "!can't delete mail, opening /mail/fs/ctl: %r\n"); exitfs(0); } e = &buf[sizeof(buf)]; p = seprint(buf, e, "delete %s", mbname); n = 0; for(l = &top.child; *l != nil;){ m = *l; if(!m->deleted){ l = &(*l)->next; continue; } // don't return a pointer to a deleted message if(m == cur) cur = m->next; deld++; msg = strrchr(s_to_c(m->path), '/'); if(msg == nil) msg = s_to_c(m->path); else msg++; if(e-p < 10){ write(fd, buf, p-buf); n = 0; p = seprint(buf, e, "delete %s", mbname); } p = seprint(p, e, " %s", msg); n++; // unchain and free *l = m->next; if(m->next) m->next->prev = m->prev; freemessage(m); } if(n) write(fd, buf, p-buf); close(fd); if(deld) Bprint(&out, "!%d message%s deleted\n", deld, plural(deld)); // renumber i = 1; for(m = top.child; m != nil; m = m->next) m->id = natural ? m->fileno : i++; // if we're out of messages, go back to first // if no first, return the fake first if(cur == nil){ if(top.child) return top.child; else return ⊤ } return cur;}Message*qcmd(Cmd*, Message*){ flushdeleted(nil); if(didopen) closemb(); Bflush(&out); exitfs(0); return nil; // not reached}Message*ycmd(Cmd*, Message *m){ doflush = 1; return icmd(nil, m);}Message*xcmd(Cmd*, Message*){ exitfs(0); return nil; // not reached}Message*eqcmd(Cmd*, Message *m){ if(m == &top) Bprint(&out, "0\n"); else Bprint(&out, "%d\n", m->id); return nil;}Message*dcmd(Cmd*, Message *m){ if(m == &top){ Bprint(&out, "!address\n"); return nil; } while(m->parent != &top) m = m->parent; m->deleted = 1; return m;}Message*ucmd(Cmd*, Message *m){ if(m == &top) return nil; while(m->parent != &top) m = m->parent; if(m->deleted < 0) Bprint(&out, "!can't undelete, already flushed\n"); m->deleted = 0; return m;}Message*icmd(Cmd*, Message *m){ int n; n = dir2message(&top, reverse); if(n > 0) Bprint(&out, "%d new message%s\n", n, plural(n)); return m;}Message*
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -