Main Page   Compound List   File List   Compound Members   File Members  

local.vtc

Go to the documentation of this file.
00001 /* -*- C -*- */
00009 load_path += ";/afs/athena/user/c/a/cat/project/vt/hacks;/mit/dmaze/vtc";
00010 Load("rmtio");
00011 Load("disp");
00012 Load("cquote");
00013 Load("fquote");
00014 Load("filter");
00015 Load("shell");
00016 Load("bimap");
00017 Load("color");
00018 line_numbers = 1;
00019 default_pager_state = 0;
00020 zfieldregex = regcomp("^([0-9]+): (.*)$");
00021 rndseed(time);
00022 
00023 Load("zephyr-core");
00024 Load("zaway");
00025 Load("zclassmap");
00026 Load("zdefout");
00027 Load("zformat");
00028 Load("zpunt");
00029 Load("zreplay");
00030 Load("zsend");
00031 Load("zsend-zcrypt");
00032 Load("zsend-zwrite");
00033 Load("zsig-random");
00034 Load("zsig-responses");
00035 Load("zsig-t");
00036 Load("zwindow");
00037 
00045 func zauth_letter(z)
00046 {
00047   if (is_webzephyr(z))
00048     return '&';
00049   else if (stricmp(z->auth, "yes") == 0)
00050   {
00051     if (stricmp(z->opcode, "crypt") == 0)
00052       return '*';
00053     else
00054       return'+';
00055   }
00056   else if (stricmp(z->auth, "no") == 0)
00057     return '-';
00058   else if (stricmp(z->auth, "forged") == 0)
00059     return '?';
00060   else
00061     return ' ';
00062 }
00063 
00071 func should_wrap(body) [count, indent, do_wrap, s]
00072 {
00073   indent = -1;
00074   do_wrap = 1;
00075   for (s = explode(body, "\n") + 1; *s; s++)
00076   {
00077     count = 0;
00078     for (; **s == ' ' || **s == '\t'; (*s)++)
00079       count++;
00080     if (indent == -1)
00081     {
00082       indent = count;
00083     }
00084     else if (count - indent > 1 || count - indent < -1)
00085     {
00086       do_wrap = 0;
00087       break;
00088     }
00089   }
00090   /* Hack to not wrap messages with equals signs.  This can be annoying. */
00091   /*
00092   if (body[strcspn(body, "=")] != 0)
00093     do_wrap = 0;
00094   */
00095 
00096   return do_wrap;
00097 }
00098 
00106 func zephyr_output() [z, line, fields, i, s, p, ival, cval, do_wrap,
00107                       annot, win, body, sig, pipe, style, auth2, front,
00108                       wz]
00109 {
00110   /* Extract fields from the current zephyr and initialize a new one. */
00111   z = cur_rmt->zephyr;
00112   cur_rmt->zephyr = new_zgram();
00113 
00114   /* Drop trailing newlines from the zephyr. */
00115   zapply(.chop, z);
00116 
00117   /* Pull the fields out of the zephyr. */
00118   fields = alloc(z->numfields);
00119   for (i = 0; i < z->numfields; ++i)
00120   {
00121     fields[i] = chop(z->fields[i] ?: "");
00122   }
00123 
00124   /* Create a history line. */
00125   line = bprintf("%s\n%s\n%s\n{%s}\n<%s>\n[%s]\n%s:",
00126                  z->fromhost, z->ztime, z->auth, z->opcode,
00127                  z->class, z->instance, z->sender);
00128   for (i = 0; i < z->numfields; ++i)
00129   {
00130     line += "\n/\n" + fields[i];
00131   }
00132   add_hist(line, S_NORM);
00133 
00134   /* "Normal" zephyrgrams have two fields: a body and a signature. 
00135    * For abnormal zephyrs, set the body to the concatenated list of
00136    * fields, and set the signature to empty. */
00137   if (z->numfields == 2)
00138   {
00139     sig = fields[0];
00140     body = fields[1];
00141   }
00142   else
00143   {
00144     sig = "";
00145     body = "";
00146     for(i = 0; i < z->numfields; ++i)
00147     {
00148       body += fields[i];
00149       if (i != z->numfields - 1)
00150         body += " / ";
00151     }
00152   }
00153 
00154   /* Fold newlines in the zephyr and other things we're tracking. */
00155   zapply(.fold_newlines, z);
00156   zapply(.clean_string, z);
00157   sig = fold_newlines(sig);
00158   sig = clean_string(sig);
00159 
00160   /* Reduce the things we know about the zephyr's authenticity to a
00161    * single letter. */
00162   auth2 = zauth_letter(z);
00163 
00164   /* Notice if the current signature is something we want to respond to. */
00165   note_zsig_responses(z, sig);
00166 
00167   /* Find the right window to display the message in. */
00168   win = find_win(z);
00169 
00170   /* Use heuristics to determine whether we should fold newlines in
00171    * the body of the message. */
00172   do_wrap = should_wrap(body);
00173 
00174   /* Look for abbreviations for the class or class and instance. */
00175   style = style_of_zephyr(z);
00176 
00177   /* special-case login/logout messages and personals */
00178   if (stricmp(z->opcode, "user_login") == 0)
00179     style = find_style("green");
00180   else if (stricmp(z->opcode, "user_logout") == 0)
00181     style = find_style("red");
00182   if (z->personal && (stricmp(z->class, "mail") != 0))
00183     style = S_HILITE;
00184 
00185         /* A few translations on the instance for aesthetic purposes. */
00186     /*
00187     if ((!personal && stricmp(instance, "personal") == 0))
00188         instance = "";
00189         */
00190 
00191   /* Construct the class/instance field of the displayed message, using
00192    * abbreviations if we have them. */
00193   cval = mapped_class(z->class);
00194   ival = mapped_instance(z->class, z->instance);
00195   
00196   if (cval)
00197     s = bprintf("%s/%s", cval, z->instance);
00198   else
00199     s = bprintf("<%s>/%s", z->class, z->instance);
00200     
00201   /* Notice if this is a cross-realm zephyr.  If it is, display the full
00202    * username (with Zephyr realm) instead of user/host. */
00203   xrealm = 0;
00204   if (z->sender[strcspn(z->sender, "@")] != 0)
00205     xrealm = 1;
00206   
00207   /* Start building the line, starting with the sender and class/instance
00208    * field, then appending the body (either folded or raw, depending on
00209    * what we decided up above. */
00210   if (is_noc(z))    /* Special case for the NOC */
00211   {
00212     front = bprintf("%c%22s %5s %12s ", auth2, z->sender,
00213                     z->ztime, fields[1]);
00214     body = z->instance + ":";
00215     for (i = 2; i < z->numfields; i++)
00216     {
00217       body += " " + fields[i];
00218     }
00219     do_wrap = 1;
00220   }
00221   else if (is_wz_personal(z)) /* Special case for Webzephyr personals */
00222   {
00223     front = bprintf("%c%22s %5s %12s ", auth2, z->instance, z->ztime,
00224                     "wz/...");
00225   }
00226   else
00227   {
00228     if (xrealm || is_rcmd(z))
00229       front = bprintf("%c%22s %5s ", auth2, z->sender, z->ztime);
00230     else
00231       front = bprintf("%c%8s %13s %5s ", auth2,
00232                       z->sender, z->fromhost, z->ztime);
00233     /* Pad out class/instance to at least 12 characters */
00234     if (strlen(s) <= 12)
00235       front += bprintf("%12s ", s);
00236     else
00237       front += s + " ";
00238   }
00239   
00240   /* Ignore this all as a special case for -c login... */
00241   if (stricmp(z->class, "login") == 0)
00242   {
00243     front = bprintf("%c%8s %13s %5s %12s ", auth2,
00244                    z->sender, z->fromhost, z->ztime, "");
00245     if (stricmp(z->opcode, "user_login") == 0)
00246     {
00247       body = "Logged in on ";
00248     }
00249     else if (stricmp(z->opcode, "user_logout") == 0)
00250     {
00251       body = "Logged out from ";
00252     }
00253     else
00254     {
00255       body = opcode + ": ";
00256     }
00257     body += fields[0];
00258     if (fields[2])
00259       body += " (" + fields[2] + ")";
00260     do_wrap = 1;
00261     sig = "";
00262     opcode = "";
00263   }
00264     
00265   /* Finish the line by appending the zsig and opcode. */
00266   if (is_noc(z))
00267     opcode = "";
00268   annot = ((*sig) ? "-- " + sig : "") + ((*sig && *(z->opcode)) ? " " : "") +
00269           ((*(z->opcode)) ? "{" + z->opcode + "}" : "");
00270   annot += ((*annot) ? " " : "") + bprintf("(%d)", lineno - 1);
00271 
00272   line = make_line(front, body, annot, do_wrap);
00273 
00274   /* Possibly respond to zephyrs if we're away. */
00275   zaway_respond(z, sig);
00276   
00277   /* Log the displayed message if we have a logfile open. */
00278   if (cur_rmt->logfile)
00279   {
00280     fwrite(cur_rmt->logfile, line);
00281     fflush(cur_rmt->logfile);
00282   }
00283 
00284   /* Don't print anything if we've punted the message. */
00285   if (is_zpunted(z))
00286     return;
00287     
00288   /* Ignore certain cases of -c mail personals too. */
00289   if ((stricmp(z->class, "mail") == 0) && z->personal)
00290   {
00291     if (stristr(fields[1], "debian") && !stristr(fields[1], "dmaze@debian"))
00292     {
00293       return;
00294     }
00295   }
00296 
00297   output(line, style, win);
00298 }
00299 
00307 func is_rcmd(z) [sender2, n]
00308 {
00309   if (stricmp(z->sender, "rcmd.", 5) == 0)
00310   {
00311     sender2 = z->sender;
00312     sender2 += 5;
00313     n = strlen(sender2);
00314     if (stricmp(z->fromhost, sender2, n-1) == 0)
00315     {
00316       return 1;
00317     }
00318   }
00319   
00320   return 0;
00321 }
00322 
00331 func is_noc(z)
00332 {
00333   if (is_rcmd(z) &&
00334       stricmp(z->class, "noc") == 0 &&
00335       stricmp(z->opcode, "echo") == 0)
00336     return 1;
00337   return 0;
00338 }
00339 
00346 func is_webzephyr(z)
00347 {
00348   if (stricmp(z->sender, "daemon.webzephyr") == 0)
00349     return 1;
00350   return 0;
00351 }
00352 
00360 func is_wz_personal(z)
00361 {
00362   if (is_webzephyr(z) &&
00363       stricmp(z->class, "webzephyr") == 0 &&
00364       z->personal)
00365     return 1;
00366   return 0;
00367 }
00368 
00380 func make_line(front, body, annot, do_wrap) [line, fbody, s, p, col, spaces]
00381 {
00382   line = front;
00383   /* Add the body.  If do_wrap is set, wrap lines (easy).  If not,
00384    * print one line in body per line in output, subject to wrapping
00385    * constraints. */
00386   body = clean_string(body);
00387   if (do_wrap)
00388   {
00389     fbody = fold_newlines(body);
00390     line += wrap(fbody, cols - 1, 43, strlen(line));
00391   }
00392   else
00393   {
00394     s = explode(body, "\n") + 1;
00395     if (*s)
00396     {
00397       /* If the front matter is short enough that we don't need
00398        * an extra line break, put the first line after it
00399        * (with a possibly shorter line). */
00400       if (strlen(front) <= 43)
00401       {
00402         line += wrap(*s, cols - 1, 43, strlen(front));
00403         if (s[1])
00404           line += bprintf("%43s", "");
00405         s++;
00406       }
00407       for (; *s; s++)
00408       {
00409         line += wrap(*s, cols - 1, 43, 43);
00410         if (s[1])
00411           line += bprintf("%43s", "");
00412       }
00413     }
00414   }
00415   /* Add the annotation.  Right-justify it if that makes sense. */
00416   line[strlen(line) - 1] = 0;
00417   p = strrchr(line, '\n');
00418   col = (p) ? line + strlen(line) - (p + 1) : strlen(line);
00419   if (col + 1 + strlen(annot) > cols - 1)
00420   {
00421     line = bprintf("%s\n%43s%s", line, "",
00422                    wrap(annot, cols - 1, 46, 43));
00423   }
00424   else
00425   {
00426     spaces = cols - 1 - (col + strlen(annot));
00427     line += cfield("", spaces) + annot + "\n";
00428   }
00429   return line;
00430 }
00431 
00438 func zephyr_outbound(rmt, line) [zout]
00439 {
00440   /* Check: if we're in the middle of a multi-line zephyrgram, either
00441    * send it or append the current line to it. */
00442   if (zout_pipe)
00443   {
00444     zout = zout_pipe;
00445     if (strcmp(line, ".") != 0)
00446     {
00447       /* Just more text; append to the body. */
00448       zout->body += line + "\n";
00449       return;
00450     }
00451     /* Else fall through to the sending code */
00452     zout_pipe = NULL;
00453   }
00454   else
00455   {
00456     zout = outbound_parse(rmt, line);
00457     if (!zout) return;
00458     zout->body = softwrap(zout->body, 60);
00459   }
00460   
00461   /* Don't send if we've just started a multiline message. */
00462   if (zout && !zout_pipe)
00463   {
00464     generate_signature(zout);
00465     zout_send(zout);
00466   }
00467 }
00468 ZEPHYR->outbound = .zephyr_outbound;
00469 
00473 func Nopipe()
00474 {
00475   zout_pipe = NULL;
00476 }
00477 add_cmd("nopipe", 0, .Nopipe, "/nopipe");
00478 
00485 func generate_signature(zout)
00486 {
00487   /* Use a fixed zsig for <message,6.035*,*>. */
00488   if (((!zout->class || (stricmp(zout->class, "message") == 0))) &&
00489       (zout->instance && (stricmp(zout->instance, "6.035", 5) == 0)))
00490     zout->sig = "Give me the brain...I forgot what lattice is.";
00491   /* Check for a timely zsig response. */
00492   if (!zout->sig)
00493     zout->sig = get_zsig_response(zout->class, zout->instance);
00494   /* Use a Red Line zsig. */
00495   /* if (!zout->sig)
00496      zout->sig = red_line_zsig(); */
00497   /* Use a random zsig from a file. */
00498   /* if (!zout->sig)
00499      zout->sig = random_zsig(); */
00500   /* Use a fixed zsig. */
00501   if (!zout->sig)
00502     zout->sig = "Quantum singularity on the front stairs";
00503 }
00504 
00505 /* >recipient message */
00506 zreg1 = regcomp("^(>([^ ]+)) *(.*)$");
00507 
00508 /* -instance message */
00509 zreg2 = regcomp("^(-([^ -][^ ]*)) *(.*)$");
00510 
00511 /* <class>/instance message, or <class>instance message */
00512 zreg3 = regcomp("^(<([^>]+)>/?([^ ]*)) *(.*)$");
00513 
00514 /* class/instance message, or class/ message, or /instance message */
00515 zreg4 = regcomp("^(([^/ ]*)/([^ ]*)) *(.*)$");
00516 
00517 /* abbrev message */
00518 zreg5 = regcomp("^(([^/ ]*)) *(.*)$");
00519 
00520 /* &recip message, for Webzephyr */
00521 zreg6 = regcomp("^(&([^ ]+)) +(.*)$");
00522 
00532 func outbound_parse(rmt, line) [multi, crypt, a, zout, ival,
00533                                 used_default_instance, default]
00534 {
00535   /* Initial setup */
00536   multi = 0;
00537   crypt = 0;
00538   
00539   /* Strip leading whitespace. */
00540   while (*line == ' ')
00541     line++;
00542 
00543   /* Use default recipient if '>', '.', or '-' followed by space. */
00544   if (strchr(">-", *line) && line[1] == ' ')
00545   {
00546     default = get_default_recip(rmt);
00547     if (!default)
00548     {
00549       printf("No default set.\n");
00550       return;
00551     }
00552     if (*line == '>' && *default != '>')
00553     {
00554       printf("Default is not a personal.\n");
00555       return;
00556     }
00557     line = bprintf("%s%s", default, line + 1);
00558   }
00559 
00560   /* Look for leading modifiers. */
00561   while(*line)
00562   {
00563     if (*line == '|')
00564       multi = 1;
00565     else if (*line == '*')
00566       crypt = 1;
00567     else
00568       break;
00569     line++;
00570   }
00571 
00572   /* Ignore empty lines. */
00573   if (!*line)
00574     return;
00575 
00576   /* Set up the zout structure.  Use zcrypt if appropriate. */
00577   zout = new_zoutgoing();
00578   if (crypt)
00579     zout->sender = .zsend_zcrypt;
00580   else
00581     zout->sender = .zsend_zwrite;
00582 
00583   /* Check line against the various command formats.  After a successful
00584    * match of any command format, a[1] is always the part of the line
00585    * that specifies the recipient, so we can use it for the default. */
00586   if (regexec(zreg1, line))
00587   {
00588     a = regmatch_array(zreg1);
00589     zout->recip = a[2];
00590     zout->body = a[3];
00591   }
00592   else if (regexec(zreg2, line))
00593   {
00594     a = regmatch_array(zreg2);
00595     zout->instance = a[2];
00596     zout->body = a[3];
00597   }
00598   else if (regexec(zreg3, line))
00599   {
00600     a = regmatch_array(zreg3);
00601     zout->class = a[2];
00602     zout->instance = a[3];
00603     zout->body = a[4];
00604   }
00605   else if (regexec(zreg6, line))
00606   {
00607     a = regmatch_array(zreg6);
00608     zout->class = "webzephyr";
00609     zout->instance = a[2];
00610     zout->recip = "daemon.webzephyr";
00611     zout->body = a[3];
00612   }
00613   else if (regexec(zreg4, line))
00614   {
00615     a = regmatch_array(zreg4);
00616     zout->class = unmapped_class(a[2]);
00617     if (!zout->class) zout->class = a[2];
00618     zout->instance = a[3];
00619     zout->body = a[4];
00620   }
00621   else if (regexec(zreg5, line))
00622   {
00623     a = regmatch_array(zreg5);
00624     /* Could be a class/instance abbreviation. */
00625     ival = unmapped_instance(a[2]);
00626     if (ival)
00627       {
00628         zout->class = ival[0];
00629         zout->instance = ival[1];
00630       }
00631     if (!zout->class)
00632       zout->class = unmapped_class(a[2]);
00633     if (!zout->class)
00634     {
00635       printf("Unknown class \"%s\".\n", a[2]);
00636       return;
00637     }
00638     zout->body = a[3];
00639   }
00640   else
00641   {
00642     printf("Illegal zephyr input.\n");
00643     return;
00644   }
00645 
00646   /* Update or use the default instance for the class. */
00647   used_default_instance = use_default_instance(rmt, zout);
00648   
00649   /* Update the default recipient and status bar. */
00650   if (cur_win)
00651     {
00652       default = bprintf("%s%s", crypt ? "*" : "", a[1]);
00653       while (*default && default[strlen(default) - 1] == ' ')
00654         default[strlen(default) - 1] = 0;
00655       if (zout->class && *zout->class && used_default_instance)
00656           strcat(default, bprintf("/%s", zout->instance));
00657       set_default_recip(rmt, default);
00658     }
00659 
00660   /* Save zout in zout_pipe if appropriate.  Do other assorted late-stage
00661    * processing, too. */
00662   if (multi)
00663     {
00664       if (zout->body && *zout->body)
00665         zout->body += "\n";
00666       else
00667         zout->body = "";
00668       zout_pipe = zout;
00669       return zout;
00670     }
00671 
00672   /* Punt if no body. */
00673   if (!zout->body || !*zout->body)
00674     return;
00675 
00676   return zout;
00677 }
00678 
00679 /* ----- Output windows for specific classes ----- */
00680 
00681 [win] if (!windows_inited) {
00682     windows_inited = 1;
00683     win = std_split(active, 3);
00684     set_class_output("noc", win);
00685     set_class_output("filsrv", win);
00686     set_class_output("moira", win);
00687 }
00688 
00689 /* ----- Miscellaneous stuff ----- */
00690 
00691 /* Key bindings: M-< and M-> to shrink and enlarge the input window,
00692  * M-backspace to delete a word backwards, ^b and ^f to switch windows. */
00693 func shrink_active() { resize(active, win_bottom(active)); }
00694 func enlarge_active() { resize(active, win_bottom(active) + 2); }
00695 bind(ctrl("^[<"), .shrink_active);
00696 bind(ctrl("^[>"), .enlarge_active);
00697 bind(ctrl("^[^H"), K_BWORD);
00698 bind(ctrl("^b"), .prev_win);
00699 bind(ctrl("^f"), .next_win);
00700 
00701 bind(ctrl("^[[A") , .prevline);
00702 bind(ctrl("^[[B") , .nextline);
00703 bind(ctrl("^[OA") , .prevline);
00704 bind(ctrl("^[OB") , .nextline);
00705 bind(ctrl("^[^H"), K_BWORD);
00706 bind(ctrl("^[[C") , K_CRIGHT);
00707 bind(ctrl("^[[D") , K_CLEFT);
00708 bind(ctrl("^[OC") , K_CRIGHT);
00709 bind(ctrl("^[OD") , K_CLEFT);
00710 bind(ctrl("^[[E") , K_CWRIGHT);
00711 bind(ctrl("^[^[[C") , K_CWRIGHT);
00712 bind(ctrl("^[^[OC") , K_CWRIGHT);
00713 bind(ctrl("^[[F") , K_CWLEFT);
00714 bind(ctrl("^[^B") , K_CWLEFT);
00715 bind(ctrl("^[^[[D") , K_CWLEFT);
00716 bind(ctrl("^[^[OD") , K_CWLEFT);
00717 bind(ctrl("^[^[^[") , K_MODE);
00718 bind(ctrl("^[[G") , K_CUP);
00719 bind(ctrl("^[[H") , K_CDOWN);
00720 bind(ctrl("^[^[[A") , K_CUP);
00721 bind(ctrl("^[^[[B") , K_CDOWN);
00722 bind(ctrl("^[^[OA") , K_CUP);
00723 bind(ctrl("^[^[OB") , K_CDOWN);
00724 
00725 [fp, line] if (!zephyr_initialized) {
00726     do_mailcheck =  0;
00727     zephyr_initialized = 1;
00728     resize(active, win_bottom(active) + 2);
00729     Load("~/.settings");
00730     /* Load_zsigs(defzsigfile); */
00731     Load_zsigs("~/Private/zsigs-thesis");
00732     World("Zephyr");
00733     More("on");
00734     /* default_pager_state = 1; */
00735 }
00736 
00737 /* vim:set syntax=c: */

Generated at Mon Aug 13 16:45:52 2001 for dzm-vtc by doxygen1.2.8.1 written by Dimitri van Heesch, © 1997-2001