1: static char rcsid[] 2: = "$Header: /usr/src/ucb/sysline/RCS/sysline.c,v 1.2 83/02/08 16:20:50 jkf Exp $"; 3: 4: /* 5: * sysline - system status display on 25th line of terminal 6: * j.k.foderaro 7: * 8: * Prints a variety of information on the special status line of terminals 9: * that have a status display capability. Cursor motions, status commands, 10: * etc. are gleamed from /etc/termcap. 11: * By default, all information is printed, and flags are given on the command 12: * line to disable the printing of information. The information and 13: * disabling flags are: 14: * 15: * flag what 16: * ----- ---- 17: * time of day 18: * load average and change in load average in the last 5 mins 19: * number of user logged on 20: * -p # of processes the users owns which are runnable and the 21: * number which are suspended. Processes whose parent is 1 22: * are not counted. 23: * -l users who've logged on and off. 24: * -m summarize new mail which has arrived 25: * 26: * <other flags> 27: * -r use non reverse video 28: * -c turn off 25th line for 5 seconds before redisplaying. 29: * -b beep once one the half hour, twice on the hour 30: * +N refresh display every N seconds. 31: * -i print pid first thing 32: * -e do simple print designed for an emacs buffer line 33: * -h print hostname between time and load average 34: * -D print day/date before time of day 35: * -d debug mode - print status line data in human readable format 36: * -q quiet mode - don't output diagnostic messages 37: * -s print Short (left-justified) line if escapes not allowed 38: * -j Print left Justified line regardless 39: * 40: */ 41: 42: /* turn this on always */ 43: #define WHO 44: 45: /* turn this on if you are on running 4.1a or greater (i.e. a system 46: with the gethostname() function */ 47: #define HOSTNAME 48: 49: /* turn this on if you are running on vmunix */ 50: /* #define VMUNIX */ 51: 52: /* turn this on if you are running on 2.9 BSD */ 53: #define TWO_NINE 54: 55: /* turn this on if you are running on 4.1c or greater */ 56: /* # define NEW_BOOTTIME */ 57: 58: #include <sys/param.h> 59: #include <signal.h> 60: #include <stdio.h> 61: #include <utmp.h> 62: #include <ctype.h> 63: #include <unctrl.h> 64: #include <time.h> 65: #include <sys/stat.h> 66: #ifdef VMUNIX 67: #include <nlist.h> 68: #include <sys/vtimes.h> 69: #include <sys/proc.h> 70: #endif 71: #ifdef pdp11 72: #include <a.out.h> 73: #include <sys/proc.h> 74: #define strcmpn strncmp 75: #endif 76: #ifdef TERMINFO 77: #include <curses.h> 78: #include <term.h> 79: #endif 80: 81: #ifdef VMUNIX 82: #define MAXUSERS 100 83: #else 84: #define MAXUSERS 40 85: #endif 86: #define DEFDELAY 60 /* update status once per minute */ 87: #define MAILDIR "/usr/spool/mail" 88: 89: /* if MaxLoad is defined, then if the load average exceeded MaxLoad 90: * then the process table will not be scanned and the log in/out data 91: * will not be checked. The purpose of this is to reduced the load 92: * on the system when it is loaded. 93: */ 94: #define MaxLoad 6.0 95: 96: 97: struct nlist nl[] = 98: #ifdef NEW_BOOTTIME 99: { { "_boottime" }, /* After 4.1a the label changed to "boottime" */ 100: #else 101: { { "_bootime" }, /* Under 4.1a and earlier it is "bootime" */ 102: #endif 103: #define NL_BOOT 0 104: { "_proc" }, 105: #define NL_PROC 1 106: { "_avenrun" }, 107: #define NL_AVEN 2 108: #ifdef VMUNIX 109: { "_nproc" }, 110: #define NL_NPROC 3 111: #endif 112: #ifdef TWO_NINE 113: { "_nproc" }, 114: #define NL_NPROC 3 115: #endif 116: { 0 }}; 117: 118: struct proc *pr; 119: int nproc; 120: unsigned procadr; 121: 122: double avenrun[3]; /* used for storing load averages */ 123: 124: int kmem; /* file pointers for memory */ 125: int ut; 126: int users, nentries; 127: 128: #ifdef WHO 129: char whofilename[100]; 130: #endif 131: 132: #ifdef HOSTNAME 133: char hostname[32]; 134: #endif 135: 136: char lockfilename[100]; /* if exists, will prevent us from running */ 137: 138: /* flags which determine which info is printed */ 139: int mailcheck = 1; /* m - do biff like checking of mail */ 140: int proccheck = 1; /* p - give information on processes */ 141: int logcheck = 1; /* l - tell who logs in and out */ 142: int hostprint = 0; /* h - print out hostname */ 143: int dateprint = 0; /* h - print out day/date */ 144: int quiet = 0; /* q - hush diagnostic messages */ 145: 146: /* flags which determine how things are printed */ 147: int clr_bet_ref = 0; /* c - clear line between refeshes */ 148: int reverse = 1; /* r - use reverse video */ 149: int shortline = 0; /* s - short (left-justified) if escapes not allowed */ 150: int leftline = 0; /* j - left-justified even if escapes allowed */ 151: int sawmail; /* remember mail was seen to print bells */ 152: 153: /* flags which have terminal do random things */ 154: int beep = 0; /* b - beep every half hour and twice every hour */ 155: int synch = 1; /* synchronize with clock */ 156: int printid = 0; /* print pid of this process at startup */ 157: 158: /* 159: * used to turn off reverse video every REVOFF times 160: * in an attempt to not wear out the phospher. 161: */ 162: #define REVOFF 5 163: int revtime = 1; 164: 165: /* select output device (status display or straight output (emacs window)) */ 166: int emacs = 0; /* assume status display */ 167: int dbug = 0; 168: 169: /* used by mail checker */ 170: off_t mailsize = 0; 171: off_t linebeg = 0; /* place where we last left off reading */ 172: 173: /* globals */ 174: int mailprocessed; 175: char *username; 176: struct stat stbuf, mstbuf; /* mstbuf for mail check only */ 177: char *ourtty,*ttyname(); /* keep track of what tty we're on */ 178: char *getenv(); 179: char *tparm(), *tgoto(); 180: unsigned delay = DEFDELAY; 181: int chars; 182: short uid; 183: double loadavg = 0.0; /* current load average */ 184: int fullprocess; 185: int users = 0; 186: 187: /* strings which control status line display */ 188: #ifdef TERMINFO 189: 190: char *rev_out, *rev_end, *arrows; 191: 192: #else /* TERMCAP */ 193: 194: char to_status_line[30]; 195: char from_status_line[20]; 196: char dis_status_line[20]; 197: char rev_out[20], rev_end[20]; 198: char *arrows, *bell = "\007"; 199: int eslok; /* escapes on status line okay (reverse, cursor addressing) */ 200: int columns; 201: #endif 202: 203: /* 204: * In order to determine how many people are logged on and who has 205: * logged in or out, we read in the /etc/utmp file. We also keep track of 206: * the previous utmp file. 207: */ 208: struct utmp uts[2][MAXUSERS]; 209: 210: outc(c) 211: char c; 212: { 213: if (dbug) 214: printf("%s", unctrl(c)); 215: else putchar(c); 216: } 217: 218: erroutc(c) 219: char c; 220: { 221: if (dbug) 222: fprintf(stderr,"%s", unctrl(c)); 223: else fputc(c,stderr); 224: } 225: 226: main(argc,argv) 227: char **argv; 228: { 229: register new,old,tmp; 230: int clearbotl(); 231: char *cp; 232: extern char _sobuf[]; 233: 234: 235: setbuf(stdout, _sobuf); 236: signal(SIGINT,SIG_IGN); 237: signal(SIGQUIT,SIG_IGN); 238: signal(SIGALRM,SIG_IGN); 239: #ifdef VMUNIX 240: signal(SIGTTOU,SIG_IGN); 241: #endif 242: /* 243: * When we logoff, init will do a "vhangup()" on this 244: * tty which turns off I/O access and sends a SIGHUP 245: * signal. We catch this and thereby clear the status 246: * display. Note that a bug in 4.1bsd caused the SIGHUP 247: * signal to be sent to the wrong process, so you had to 248: * `kill -HUP' yourself in your .logout file. 249: */ 250: signal(SIGHUP,clearbotl); 251: 252: argv++; 253: while(--argc > 0) { 254: switch(argv[0][0]) { 255: case '-': for(cp = &argv[0][1]; *cp ; cp++) 256: { 257: switch(*cp) { 258: case 'r' : reverse = 0; /* turn off reverse video */ 259: break; 260: case 'c': clr_bet_ref = 1; 261: break; 262: case 'h': hostprint = 1; 263: break; 264: case 'D': dateprint = 1; 265: break; 266: case 'm': mailcheck = 0; 267: break; 268: case 'p': proccheck = 0; 269: break; 270: case 'l': logcheck = 0; 271: break; 272: case 'b': beep = 1; 273: break; 274: case 'i': printid = 1; 275: break; 276: case 'e': emacs = 1; 277: break; 278: case 'd': dbug = 1; 279: signal(SIGINT, SIG_DFL); 280: signal(SIGQUIT, SIG_DFL); 281: break; 282: case 'q': quiet = 1; 283: break; 284: case 's': shortline = 1; 285: break; 286: case 'j': leftline = 1; 287: break; 288: default: fprintf(stderr,"sysline: bad flag: %c\n",*cp); 289: } 290: } 291: break; 292: case '+': delay = atoi(&argv[0][1]); 293: if((delay <= 10) || (delay > 500)) delay = DEFDELAY; 294: synch = 0; /* no more sync */ 295: break; 296: default: fprintf(stderr,"sysline: illegal argument %s\n",argv[0]); 297: } 298: argv++; 299: } 300: if(emacs) { 301: columns = 80; 302: } else { 303: /* if not to emacs window, initialize terminal dependent info */ 304: initterm(); 305: } 306: 307: /* immediately fork and let the parent die if not emacs mode */ 308: if(!emacs && !dbug && fork()) exit(0); 309: uid = getuid(); 310: 311: ourtty = ttyname(2); /* remember what tty we are on */ 312: if(printid) { printf("%d\n",getpid()); fflush(stdout);} 313: close(1); 314: dup2(2, 1); 315: 316: strcpy(whofilename,getenv("HOME")); 317: strcat(whofilename,"/.who"); 318: 319: strcpy(lockfilename,getenv("HOME")); 320: strcat(lockfilename,"/.syslinelock"); 321: 322: #ifdef HOSTNAME 323: if( hostprint ) gethostname(hostname,sizeof(hostname)); 324: #endif 325: 326: if((ut = open("/etc/utmp",0)) < 0) 327: { 328: fprintf(stderr,"Can't open utmp"); 329: exit(1); 330: } 331: 332: if((kmem = open("/dev/kmem",0)) < 0) 333: { 334: fprintf(stderr,"Can't open kmem"); 335: exit(1); 336: } 337: 338: /* read in namelist in order to get location of symbols */ 339: readnamelist(); 340: 341: if(proccheck) initprocread(); 342: 343: if(mailcheck) 344: { 345: chdir(MAILDIR); 346: username = getenv("USER"); 347: if(stat(username,&mstbuf) != -1) 348: { 349: mailsize = mstbuf.st_size; 350: } 351: else mailsize = 0; 352: } 353: 354: old = 0; 355: new = 1; 356: 357: while(emacs || isloggedin()) 358: { 359: if(access(lockfilename,0)) 360: { 361: mailprocessed = 0; 362: prtinfo(old,new); 363: sleep(delay); 364: if(clr_bet_ref) 365: { 366: tputs(dis_status_line, 1, outc); 367: fflush(stdout); 368: sleep(5); 369: } 370: revtime = (1 + revtime) % REVOFF; 371: 372: /* 373: * if we have processed mail, then dont switch utmp pointers 374: * since we havent printed the people whove logged in and out 375: */ 376: if(!mailprocessed || !fullprocess) 377: { 378: tmp = old; 379: old = new; 380: new = tmp; 381: } 382: } else sleep(60); 383: } 384: clearbotl(); 385: /* NOTREACHED */ 386: } 387: 388: isloggedin() 389: { 390: /* 391: * you can tell if a person has logged out if the owner of 392: * the tty has changed 393: */ 394: struct stat statbuf; 395: if(fstat(2,&statbuf) == 0) 396: { 397: if(statbuf.st_uid == uid) return(1); 398: } 399: return(0); /* not logged in */ 400: } 401: 402: 403: readnamelist() 404: { 405: time_t bootime, clock, nintv, time(); 406: 407: #ifdef pdp11 408: nlist("/unix",nl); 409: #else 410: nlist("/vmunix",nl); 411: #endif 412: if(nl[0].n_value == 0) { 413: if (!quiet) 414: fprintf(stderr, "No namelist\n"); 415: return; 416: } 417: lseek(kmem, (long)nl[NL_BOOT].n_value, 0); 418: read(kmem, &bootime, sizeof(bootime)); 419: (void) time(&clock); 420: nintv = clock - bootime; 421: if (nintv <= 0L || nintv > 60L*60L*24L*365L) { 422: if (!quiet) 423: fprintf(stderr, "Time makes no sense... namelist must be wrong\n"); 424: nl[NL_PROC].n_value = nl[NL_AVEN].n_value = 0; 425: } 426: } 427: 428: readutmp(n) 429: { 430: lseek(ut,0L,0); 431: nentries = read(ut,&uts[n][0],MAXUSERS*sizeof(struct utmp)) 432: / sizeof(struct utmp); 433: } 434: 435: /* 436: * read in the process table locations and sizes, and allocate space 437: * for storing the process table. This is done only once. 438: */ 439: initprocread() 440: { 441: if (nl[NL_PROC].n_value == 0) return; 442: #ifdef VMUNIX 443: lseek(kmem,(long)nl[NL_PROC].n_value,0); 444: read(kmem,&procadr,sizeof(procadr)); 445: lseek(kmem,(long)nl[NL_NPROC].n_value,0); 446: read(kmem,&nproc,sizeof(nproc)); 447: #endif 448: #ifdef pdp11 449: procadr = nl[NL_PROC].n_value; 450: #ifdef TWO_NINE 451: lseek(kmem,(long)nl[NL_NPROC].n_value,0); 452: read(kmem,&nproc,sizeof(nproc)); 453: #else 454: nproc = NPROC; /* from param.h */ 455: #endif 456: #endif 457: pr = (struct proc *) calloc(nproc,sizeof(struct proc)); 458: if (pr == (struct proc *) NULL) { 459: if (!quiet) 460: fprintf(stderr,"Not enough memory for proc search\n"); 461: nl[NL_PROC].n_value = 0; 462: } 463: } 464: 465: /* 466: * read in the process table. This assumes that initprocread has alread been 467: * called to set up storage. 468: */ 469: readproctab() 470: { 471: if (nl[NL_PROC].n_value == 0) return(0); 472: /* printf("There are %d entries beginning at %x\n",nproc,procadr); */ 473: lseek(kmem,(long)procadr,0); 474: read(kmem,pr,nproc * sizeof(struct proc)); 475: return(1); 476: } 477: 478: /* 479: * codes to say what has happened to a particular entry in utmp 480: * NOCH means no change, ON means new person logged on, 481: * OFF means person logged off. 482: */ 483: #define NOCH 0; 484: #define ON 0x1 485: #define OFF 0x2 486: 487: prtinfo(old,new) 488: int old,new; 489: { 490: int procrun,procstop; 491: int on,off; 492: int status[MAXUSERS]; 493: register int i; 494: double diff; 495: 496: stringinit(); 497: 498: #ifdef WHO 499: /* check for file named .who in the home directory */ 500: whocheck(); 501: #endif 502: 503: timeprint(); 504: 505: /* 506: * if mail is seen, don't print rest of info, just the mail 507: * reverse new and old so that next time we run, we won't lose log 508: * in and out information 509: */ 510: if(mailcheck && (sawmail = mailseen()) ) 511: { 512: mailprocessed = 1; 513: goto bottom; 514: } 515: 516: #ifdef HOSTNAME 517: /* 518: * print hostname info if requested 519: */ 520: if(hostprint) 521: { 522: stringspace(); 523: stringcat(hostname,strlen(hostname),1); 524: } 525: #endif 526: 527: /* 528: * print load average and difference between current load average 529: * and the load average 5 minutes ago 530: */ 531: if (nl[NL_AVEN].n_value != 0) { 532: stringspace(); 533: #ifdef VMUNIX 534: lseek(kmem,(long)nl[NL_AVEN].n_value,0); 535: read(kmem,avenrun,sizeof(avenrun)); 536: #endif 537: #ifdef pdp11 538: loadav(avenrun); 539: #endif 540: stringprt("%.1f ",avenrun[0]); 541: if((diff = avenrun[0] - avenrun[1]) < 0.0) 542: stringprt("%.1f",diff); 543: else stringprt("+%.1f",diff); 544: loadavg = avenrun[0]; /* remember load average */ 545: } 546: 547: /* 548: * print log on and off information 549: */ 550: stringspace(); 551: 552: fullprocess = 1; 553: 554: #ifdef MaxLoad 555: if(loadavg > MaxLoad) fullprocess = 0; /* too loaded to run */ 556: #endif 557: /* read utmp file (logged in data) only if we are doing a full 558: process or if this is the first time and we are calculating 559: the number of users 560: */ 561: if(fullprocess || (users == 0)) readutmp(new); 562: 563: 564: /* 565: * make a pass through utmp, checking if person has logged off 566: * or on. Results are stored in status[] 567: */ 568: on = off = 0; 569: /* only do this if it hasn't been done yet (users == 0) or 570: * if the load average is low enough to permit it 571: */ 572: if(fullprocess || (users == 0 )) 573: { 574: users = 0; 575: for(i=0 ; i < nentries ; i++) 576: { 577: if(strcmpn(uts[old][i].ut_name,uts[new][i].ut_name,8) != 0) 578: { 579: if(uts[old][i].ut_name[0] == '\0') 580: { 581: status[i] = ON; 582: on++; 583: } 584: else if (uts[new][i].ut_name[0] == '\0') 585: { 586: status[i] = OFF; 587: off++; 588: } 589: else { 590: status[i] = ON | OFF; 591: on++; 592: off++; 593: } 594: } 595: else status[i] = NOCH; 596: 597: if(uts[new][i].ut_name[0]) users++; 598: } 599: } 600: 601: /* at this point we know how many users there are */ 602: stringprt("%du",users); 603: 604: /* if there is any unread mail, put out a star */ 605: /* fprintf(stderr,"mailsz:%d,mtime:%d,atime:%d\n", 606: mailsize,mstbuf.st_mtime,mstbuf.st_atime); */ 607: if((mailsize > 0) && (mstbuf.st_mtime >= mstbuf.st_atime)) 608: stringcat("*",1,1); 609: 610: /* if the load is too high, then we indicate that with a - sign */ 611: if(!fullprocess && (proccheck || logcheck)) stringcat("-",1,1); 612: 613: /* if we are to check on number of process running, do so now */ 614: if(fullprocess && proccheck && readproctab()) 615: { 616: /* 617: * we are only interested in processes which have the same 618: * uid as us, and whose parent process id is not 1. 619: */ 620: procrun = procstop = 0; 621: for(i=0; i < nproc ; i++) 622: { 623: if((pr[i].p_stat == 0) || (pr[i].p_pgrp == 0)) continue; 624: if((pr[i].p_uid == uid) && (pr[i].p_ppid != 1)) 625: { 626: /* printf("found pid %d, stat=%o\n", pr[i].p_pid, pr[i].p_stat); */ 627: switch (pr[i].p_stat) { 628: 629: case SSTOP: 630: procstop++; 631: break; 632: 633: case SSLEEP: 634: /* 635: * sleep can mean waiting for a signal or just 636: * in a disk or page wait queue ready to run. 637: * We can tell if it is the later by the pri being 638: * negative 639: */ 640: if (pr[i].p_pri < PZERO) procrun++; 641: break; 642: 643: case SWAIT: 644: case SRUN: 645: case SIDL: 646: procrun++; 647: 648: } 649: } 650: } 651: 652: if((procrun > 0) || (procstop > 0)) 653: { 654: stringspace(); 655: if((procrun > 0) && (procstop > 0)) 656: { 657: stringprt("%dr",procrun); 658: stringprt(" %ds",procstop); 659: } 660: else if(procrun > 0) stringprt("%dr",procrun); 661: else stringprt("%ds",procstop); 662: } 663: } 664: 665: /* 666: * if anyone has logged on or off, and we are interested in it, 667: * print it out 668: */ 669: if(logcheck && on) 670: { 671: stringspace(); 672: stringprt("on:",on); 673: for(i = 0 ; i < nentries ; i++) 674: { 675: if(status[i] & ON) 676: { 677: stringprt(" %.8s",uts[new][i].ut_name); 678: ttyprint(uts[new][i].ut_line); 679: } 680: } 681: } 682: 683: /* 684: * check for people logging off if we are intereste 685: */ 686: if(logcheck && off) 687: { 688: stringspace(); 689: stringprt("off:",off); 690: for(i = 0 ; i < nentries ; i++) 691: { 692: if(status[i] & OFF) 693: { 694: stringprt(" %.8s",uts[old][i].ut_name); 695: ttyprint(uts[old][i].ut_line); 696: } 697: } 698: } 699: bottom: 700: /* dump out what we know */ 701: stringdump(); 702: } 703: 704: timeprint() 705: { 706: long curtime; 707: struct tm *tp, *localtime(); 708: static int beepable=0; 709: 710: /* always print time */ 711: time(&curtime); 712: tp = localtime(&curtime); 713: 714: if (dateprint) 715: stringprt("%.11s", ctime(&curtime)); 716: stringprt("%d:",(tp->tm_hour > 12 ? tp->tm_hour - 12 717: : (tp->tm_hour == 0 ? 12 : tp->tm_hour))); 718: stringprt("%02d",tp->tm_min); 719: 720: if(synch) delay = 60 - tp->tm_sec; /* sync with clock */ 721: 722: /* beepable is used to insure that we get at most one set of beeps 723: every half hour */ 724: if(beep && beepable && (tp->tm_min == 30)) 725: { 726: tputs(bell, 1, outc); fflush(stdout); 727: beepable = 0; 728: } 729: else if(beep && beepable && (tp->tm_min == 00)) 730: { 731: tputs(bell, 1, outc); fflush(stdout); 732: sleep(2); 733: tputs(bell, 1, outc); fflush(stdout); 734: beepable = 0; 735: } 736: else if(beep && ((tp->tm_min != 00) || (tp->tm_min != 30))) 737: beepable = 1; 738: 739: } 740: 741: /* 742: * whocheck -- check for file named .who and print it on the who line first 743: */ 744: whocheck() 745: { 746: register wf,i,chss; 747: char buff[81]; 748: 749: if((wf = open(whofilename,0)) >= 0) 750: { 751: chss = read(wf,buff,80); 752: if(chss == 0) 753: { 754: close(wf); 755: return; 756: } 757: buff[chss] = '\0'; 758: /* 759: * remove all line feeds, and replace by spaces if they are within 760: * the message, else replace them by nulls. 761: */ 762: for(i = chss; i >= 0; i--) 763: { 764: if(buff[i] == '\n') 765: { 766: if(buff[i+1]) buff[i] = ' '; 767: else buff[i] = '\0'; 768: } 769: } 770: stringprt("%s",buff); 771: stringspace(); 772: close(wf); 773: } 774: } 775: 776: /* 777: * ttyprint -- given the name of a tty, print in the string buffer its 778: * short name surrounded by parenthesis. 779: * ttyxx is printed as (xx) 780: * console is printed as (cty) 781: */ 782: ttyprint(name) 783: char *name; 784: { 785: char buff[10]; 786: 787: if(strcmpn(name,"tty",3)==0) 788: { 789: sprintf(buff,"(%s)",name+3); 790: } 791: else if(strcmp(name,"console")== 0) 792: { 793: sprintf(buff,"(cty)"); 794: } 795: else sprintf(buff,"(%s)",name); 796: 797: stringcat(buff,strlen(buff),0); 798: } 799: 800: /* 801: * mail checking function 802: * returns 0 if no mail seen 803: */ 804: mailseen() 805: { 806: FILE *mfd; 807: int retval = 0; 808: int chs,initchs; 809: register char *rp,*cp; 810: char lbuf[100], sendbuf[100]; 811: int toprint,seenspace = 0; 812: 813: if(stat(username,&mstbuf) != -1) 814: { 815: if((mstbuf.st_size > mailsize) && ((mfd=fopen(username,"r")) != NULL)) 816: { 817: /* fprintf(stderr,"Mail gotten was %db, now %db\n", 818: mailsize,stbuf.st_size); */ 819: fseek(mfd,mailsize,0); 820: while((initchs = readline(mfd, lbuf, sizeof(lbuf))) != EOF) 821: { 822: if(strcmpn(lbuf,"From",4) == 0) 823: { 824: cp = lbuf+5; /* start after the From */ 825: while(*cp && (*++cp != ' ')); /* skip to blank */ 826: *cp = '\0'; /* terminate name */ 827: stringspace(); 828: /* if(!emacs) stringcat(bell,0,0); BELL MOVED */ 829: sprintf(sendbuf,"Mail from %s ",lbuf+5); 830: stringcat(sendbuf,strlen(sendbuf),0); 831: /* print message preceeded by little arrow */ 832: /* skip over the headers and look for blank line */ 833: while(((chs = readline(mfd, lbuf, sizeof(lbuf))) != EOF) && (chs != 0)) 834: if(strcmpn(lbuf,"Subject",7)==0) 835: { 836: sprintf(sendbuf,"on %s",lbuf+9); 837: stringcat(sendbuf,strlen(sendbuf),1); 838: } 839: if(!emacs) stringcat(arrows,2,0); 840: else stringcat(" : ",3,0); 841: 842: if(chs != EOF) 843: { 844: cp = sendbuf; 845: toprint = columns - chars; /* space left on line */ 846: lbuf[0] = '\0'; 847: while((chs = readline(mfd, lbuf, sizeof(lbuf))) != EOF) 848: { 849: if(toprint > 0) 850: { 851: *cp++ = ' '; /* space before lines */ 852: toprint--; 853: } 854: rp = lbuf; 855: if(strcmpn(lbuf,"From",4) == 0) break; 856: while(*rp && (toprint > 0)) 857: { 858: if(isspace(*rp)) 859: { 860: if(!seenspace) 861: { 862: *cp++ = ' '; 863: seenspace = 1; 864: toprint--; 865: } 866: } 867: else { 868: *cp++ = *rp; 869: seenspace = 0; 870: toprint--; 871: } 872: rp++; 873: } 874: } 875: *cp = '\0'; 876: stringcat(sendbuf,strlen(sendbuf),1); 877: /* if(!emacs) stringcat(bell,0,0); BELL MOVED */ 878: retval = 1; 879: } 880: break; 881: } 882: } 883: if(initchs == EOF) 884: { 885: stringprt("Mail has just arrived",chs); 886: } 887: /* 888: * want to update write time so a star will 889: * appear after the number of users until the 890: * user reads his mail 891: */ 892: mailsize = linebeg; 893: touch(username,mfd); 894: fclose(mfd); 895: } 896: else mailsize = mstbuf.st_size; 897: } 898: else mailsize = 0; 899: return(retval); 900: } 901: 902: /* 903: * readline -- read a line from fp and store it in buf. 904: * return the number of characters read. 905: */ 906: readline(fp, buf, maxch) 907: FILE *fp; 908: char *buf; 909: { 910: register char *cp, ch; 911: int size = maxch; 912: long ftell(); 913: 914: linebeg = ftell(fp); /* remember loc where line begins */ 915: cp = buf; 916: while(((ch=getc(fp)) != EOF) && (ch != '\n') && (size-- > 0)) *cp++ = ch; 917: *cp = '\0'; 918: if((size == maxch) && (ch == EOF)) return (EOF); 919: else return(maxch - size); 920: } 921: 922: 923: /* string hacking functions */ 924: 925: int eol; /* non zero when we have hit the end of line */ 926: char *sp; 927: char strarr[120]; 928: 929: stringinit() 930: { 931: sp = strarr; 932: chars = 0; 933: eol = 0; 934: } 935: 936: stringprt(format,value) 937: char *format; 938: { 939: char tempbuf[150]; 940: int bufsiz; 941: 942: sprintf(tempbuf,format,value); 943: bufsiz = strlen(tempbuf); 944: stringcat(tempbuf,bufsiz,0); 945: } 946: 947: stringdump() 948: { 949: char bigbuf[200]; 950: register char *cp; 951: register int i; 952: char blanks[80]; 953: 954: *sp = '\0'; 955: bigbuf[0] = 0; 956: if(!emacs) { 957: if (sawmail) strcat(bigbuf, bell); 958: if (eslok) { 959: if(!leftline) 960: cp = tparm(to_status_line, columns - chars); 961: else 962: cp = tparm(to_status_line, 0); 963: strcat(bigbuf, cp); 964: } else { 965: strcat(bigbuf, to_status_line); 966: if (!shortline & !leftline) { 967: for (i=0; i < (columns-chars); i++) 968: blanks[i] = ' '; 969: blanks[columns-chars] = '\0'; 970: strcat(bigbuf, blanks); 971: } 972: } 973: if(reverse && !(revtime == 0)) strcat(bigbuf, rev_out); 974: } 975: strcat(bigbuf,strarr); 976: if (!emacs) { 977: if (reverse) strcat(bigbuf, rev_end); 978: strcat(bigbuf, from_status_line); 979: if (sawmail) { 980: strcat(bigbuf, bell); 981: strcat(bigbuf, bell); 982: } 983: tputs(bigbuf, 1, outc); 984: if (dbug) putchar('\n'); 985: fflush(stdout); 986: } else 987: write(2,bigbuf,strlen(bigbuf)); 988: } 989: 990: stringspace() 991: { 992: if(!emacs && reverse && !(revtime == 0)) { 993: #ifdef TERMINFO 994: stringcat(rev_end, magic_cookie_glitch<=0?0:magic_cookie_glitch, 0); 995: stringcat(" ",1,0); 996: stringcat(rev_out, magic_cookie_glitch<=0?0:magic_cookie_glitch, 0); 997: #else 998: stringcat(rev_end,0,0); 999: stringcat(" ",1,0); 1000: stringcat(rev_out,0,0); 1001: #endif 1002: } else stringcat(" ",1,0); 1003: } 1004: 1005: /* 1006: * stringcat :: concatenate the characters in string str to the list we are 1007: * building to send out. 1008: * 1009: * the three args are 1010: * str - the string to print. may contain funny (terminal control) chars. 1011: * chrs - the number of printable characters in the string 1012: * trunc - a flag which is non zero if we should truncate strings which 1013: * don't fit. If this is 0 then if a string doesn't completely 1014: * fit it wont' be printed, this prevents us from getting 1/2 1015: * way through an escape sequence. 1016: */ 1017: stringcat(str,chrs,trunc) 1018: char *str; 1019: { 1020: 1021: if((chrs == 0) || (!eol && chars + chrs <= columns) 1022: || (!eol && trunc && (chars < columns))) 1023: { 1024: while(*sp++ = *str++) 1025: if(trunc) 1026: { 1027: if(++chars >= columns) /* check for trunc */ 1028: { 1029: eol = 1; 1030: return; 1031: } 1032: } 1033: sp--; 1034: if(!trunc) chars += chrs; 1035: } 1036: else eol = 1; 1037: } 1038: 1039: /* 1040: * touch :: update the modify time of a file. 1041: */ 1042: touch(name,filedes) 1043: char *name; /* name of file */ 1044: FILE *filedes; /* already open for read file descriptor */ 1045: { 1046: register fd; 1047: char buf[1]; 1048: 1049: lseek(fileno(filedes),0L,0); 1050: read(fileno(filedes),buf,1); /* get first byte */ 1051: if((fd = open(name,2)) >= 0) /* open in append mode */ 1052: { 1053: lseek(fd,0L,0); /* go to beginning */ 1054: write(fd,buf,1); /* and rewrite first byte */ 1055: close(fd); 1056: } 1057: } 1058: 1059: 1060: /* 1061: * clearbotl :: clear bottom line. 1062: * called when process quits or is killed. 1063: * it clears the bottom line of the terminal. 1064: */ 1065: clearbotl() 1066: { 1067: register int fd; 1068: int exit(); 1069: 1070: signal(SIGALRM,exit); 1071: alarm(30); /* if can't open in 30 secs, just die */ 1072: if( !emacs && (fd = open(ourtty,1)) >=0) 1073: { 1074: write(fd,dis_status_line,strlen(dis_status_line)); 1075: close(fd); 1076: } 1077: exit(0); 1078: } 1079: 1080: #ifdef TERMINFO 1081: initterm() 1082: { 1083: static char standbuf[40]; 1084: 1085: setupterm(0, 1, 0); 1086: if (!has_status_line) { 1087: /* not an appropriate terminal */ 1088: if (!quiet) 1089: fprintf(stderr, "sysline: no status capability for %s\n", 1090: getenv("TERM")); 1091: exit(1); 1092: } 1093: if (status_line_esc_ok) { 1094: if (set_attributes) { 1095: /* reverse video mode */ 1096: strcpy(standbuf, tparm(set_attributes,0,0,1,0,0,0,0,0,0)); 1097: rev_out = standbuf; 1098: rev_end = exit_attribute_mode; 1099: } else if (enter_standout_mode && exit_standout_mode) { 1100: rev_out = enter_standout_mode; 1101: rev_end = exit_standout_mode; 1102: } else { 1103: rev_out = rev_end = ""; 1104: } 1105: } else 1106: rev_out = rev_end = ""; 1107: columns--; /* avoid cursor wraparound */ 1108: } 1109: 1110: #else /* TERMCAP */ 1111: 1112: initterm() 1113: { 1114: char *term, *cp; 1115: char tbuf[1024], is2[40]; 1116: extern char *UP; 1117: 1118: if ((term=getenv("TERM")) == NULL) { 1119: if (!quiet) 1120: fprintf(stderr, "sysline: No TERM variable in enviroment\n"); 1121: exit(1); 1122: } 1123: if (tgetent(tbuf, term) <= 0) { 1124: if (!quiet) 1125: fprintf(stderr, "sysline: Unknown terminal type: %s\n", term); 1126: exit(1); 1127: } 1128: if (tgetflag("hs") <= 0) { 1129: if (!strcmpn(term, "h19", 3)) { 1130: /* for upward compatability with h19sys */ 1131: strcpy(to_status_line, "\033j\033x5\033x1\033Y8%+ \033o"); 1132: strcpy(from_status_line, "\033k\033y5"); 1133: strcpy(dis_status_line, "\033y1"); 1134: strcpy(rev_out, "\033p"); 1135: strcpy(rev_end, "\033q"); 1136: arrows = "\033Fhh\033G"; 1137: columns = 80; 1138: UP = "\b"; 1139: return; 1140: } 1141: if (!quiet) 1142: fprintf(stderr, "sysline: No status capability for %s\n", term); 1143: exit(1); 1144: } 1145: cp = is2; 1146: if (tgetstr("i2", &cp) != NULL) { 1147: /* someday tset will do this */ 1148: tputs(is2, 1, erroutc); 1149: fflush(stdout); 1150: } 1151: 1152: /* the "-1" below is to avoid cursor wraparound problems */ 1153: columns = tgetnum("co") - 1; 1154: cp = to_status_line; 1155: tgetstr("ts", &cp); 1156: cp = from_status_line; 1157: tgetstr("fs", &cp); 1158: cp = dis_status_line; 1159: tgetstr("ds", &cp); 1160: eslok = tgetflag("es"); 1161: if (eslok) { 1162: cp = rev_out; 1163: tgetstr("so", &cp); 1164: cp = rev_end; 1165: tgetstr("se", &cp); 1166: } else { 1167: reverse = 0; /* turn off reverse video */ 1168: }; 1169: UP = "\b"; 1170: if (!strcmpn(term, "h19", 3)) 1171: arrows = "\033Fhh\033G"; /* "two tiny graphic arrows" */ 1172: else 1173: arrows = "->"; 1174: } 1175: 1176: char * 1177: tparm(cap, parm) 1178: char *cap; 1179: int parm; 1180: { 1181: return tgoto(cap, 0, parm); 1182: } 1183: #endif