home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / unix / volume3 / ttyuse < prev    next >
Internet Message Format  |  1986-11-30  |  25KB

  1. From: genrad!panda!talcott!elsie!ado
  2. Subject: ttyuse
  3. Newsgroups: mod.sources
  4. Approved: jpn@panda.UUCP
  5.  
  6. Mod.sources:  Volume 3, Issue 21
  7. Submitted by: harvard!seismo!elsie!ado
  8.  
  9.  
  10. : Run this file through sh, not csh.
  11. : It contains a manual page and source code for 'ttyuse';
  12. : you will also need some implementation of 'getopt' on your system.
  13. : To date, 'ttyuse' has only been used on 4.1bsd and 4.2bsd systems.
  14. sed 's/^X//' << 'EOF' > ttyuse.n
  15. X.TH TTYUSE 1
  16. X.SH NAME
  17. Xttyuse \- show terminal usage
  18. X.SH SYNOPSIS
  19. X.B ttyuse
  20. X[
  21. X.B \-l
  22. X] [
  23. X.B \-R
  24. X] [
  25. X.B \-w
  26. Xwtmp ]
  27. X.I time
  28. X.IR "user " ...
  29. X.IR "/dev/* " ...
  30. X.br
  31. X.BR "ttyuse =" "     (to get a `how to use it' message)"
  32. X.SH DESCRIPTION
  33. X.I Ttyuse
  34. Xwrites (to the standard output) a summary of daily terminal usage.
  35. XIf one or more
  36. X.IR user s
  37. Xare named on the command line,
  38. Xthe report is limited to those users.
  39. XIf one or more device names are given,
  40. Xthe report is limited to those devices.
  41. X(All--and only--command line arguments that begin with
  42. X.RB ` /dev/ '
  43. Xare taken to be device names.)
  44. XAnd if a
  45. X.I time
  46. Xargument is given, the report is limited to given date(s); the
  47. Xtime argument may take these forms:
  48. X.nf
  49. X.in +.5i
  50. X.ta \w'm/d-m/d  'u
  51. Xm/d    a month and day (example:  5/15);
  52. Xm/d-d    a month, starting day, and stopping day;
  53. Xm/d-m/d    starting and stopping months and days;
  54. Xm/d-    starting month and day
  55. X    (the current day is used as the ending day)
  56. X.fi
  57. X.in -.5i
  58. XIn the absence of a
  59. X.I time
  60. Xargument,
  61. Xoutput for the current day is produced.
  62. X.PP
  63. XThe leftmost of the 79 columns of output look like this:
  64. X.in +.5i
  65. X.nf
  66. XMon Sep 23 1985
  67. XTT USE 12-1--2--3--4--5--6--7--8--~--10-11-12-1--
  68. X01 5.0                       Snorri              
  69. X02 8.7                         Mark  Mark::::::::
  70. X03 7.7                           MRoot::::::::Mar
  71. X05 0.8                         Root              
  72. X07 12. ado                        ~  Ado:::::::::
  73. X09 0.4                            ~   Ma         
  74. X13 3.9 UU 2U 2U UU22U UU UUUUU UU U2 U UUucUucp::
  75. X  39.3 <   2><   1><   1><   2><   5><   5><   4>
  76. X.in -.5i
  77. X.fi
  78. XThe first line gives the date the report is for.
  79. XThe second line provides headings for the lines that follow.
  80. XIn those following lines, the
  81. X.RB ` TT '
  82. Xcolumns give the
  83. X.IR ps (1)-style
  84. Xabbreviation of the terminal the line of output is about,
  85. Xand the
  86. X.RB ` USE '
  87. Xcolumns give the total hours of use for the terminal on the
  88. Xdate the report is for.
  89. XEach other column represents a twenty-minute time period.
  90. XA capital letter in one of these columns means that a
  91. Xuser whose name begins with that letter logged in during
  92. Xthe time period; the user's name continues in lower case into
  93. Xfollowing columns for as long as they remain logged in
  94. X(followed by colons if they stay logged in for a long time).
  95. XIf more than one login occurs during a time period, the
  96. Xnumber of logins appears in the time period's column;
  97. Xan asterisk appears if there were ten or more logins.
  98. X.PP
  99. XThe final line for a date's report shows the total
  100. Xterminal-hours for the day,
  101. Xand the total number of terminals used during each two-hour period.
  102. X.PP
  103. XReboots are shown by a
  104. X.RB ` ~ '
  105. Xcharacter in the relevant column of output;
  106. Xtime changes are shown by a
  107. X.RB ` | '
  108. Xcharacter in the `time-changed-from' column and a
  109. X.RB ` { '
  110. Xcharacter in the `time-changed-to' column.
  111. XThese characters appear in the headings line and in lines
  112. Xof output for terminals that were idle during the time
  113. Xperiod when the reboot or time change occurred.
  114. X.PP
  115. XThe
  116. X.RB ` USE '
  117. Xcolumns are absent from reports for days with 25 hours
  118. X(Daylight Saving Time transition days).
  119. X.PP
  120. XThese options are available:
  121. X.TP
  122. X.B \-l
  123. XProduce long lines of output (up to 131 columns) where each column
  124. Xrepresents twelve minutes.  The final line of a date's report
  125. Xshows the total number of terminals used during each one-hour
  126. Xperiod.
  127. X.TP
  128. X.B \-R
  129. XInterpret the entire accounting file.
  130. X.TP
  131. X.BI "\-w " wtmp
  132. XUse the file whose name is given by
  133. X.I wtmp
  134. Xas the accounting file,
  135. Xrather than using the default name shown below.
  136. X.SH FILES
  137. X/usr/adm/wtmp
  138. X.SH SEE ALSO
  139. Xps(1)
  140. X.. @(#)ttyuse.n    1.1
  141. EOF
  142. sed 's/^X//' << 'EOF' > ttyuse.c
  143. X/*
  144. X** Still to handle:  do days with no logins or logouts but tty use!
  145. X*/
  146. X
  147. X#include "stdio.h"
  148. X#include "ctype.h"
  149. X#include "utmp.h"
  150. X#include "time.h"
  151. X
  152. X#ifdef OBJECTID
  153. Xstatic char    sccsid[] = "@(#)ttyuse.c    1.1";
  154. X#endif
  155. X
  156. X#ifdef USG
  157. X/* Uncompatible Software Glitch */
  158. X#define index    strchr
  159. X#endif
  160. X
  161. X#ifndef TRUE
  162. X#define TRUE    (1)
  163. X#define FALSE    (0)
  164. X#endif
  165. X
  166. X#ifndef arg4alloc
  167. X#define arg4alloc    unsigned
  168. X#endif
  169. X
  170. Xextern char *        calloc();
  171. Xextern char *        ctime();
  172. Xextern char *        index();
  173. Xextern struct tm *    localtime();
  174. Xextern long        lseek();
  175. Xextern char *        realloc();
  176. Xextern char *        sprintf();
  177. Xextern char *        strcat();
  178. Xextern char *        strcpy();
  179. Xextern long        time();
  180. X
  181. Xstatic long        midnight();
  182. Xstatic long        mdy2t();
  183. X
  184. X#define MAXDAYHOURS    25
  185. X#define HOURSLOTS    3
  186. X#define LONGHOURSLOTS    5
  187. X
  188. X#define MINUTE        60
  189. X#define HOUR        (60*MINUTE)
  190. X
  191. Xstruct entry {
  192. X    struct utmp    u;
  193. X    int        marked;
  194. X    int        daysecs;
  195. X    char        l[MAXDAYHOURS * LONGHOURSLOTS + 1];
  196. X};
  197. X
  198. Xstatic struct entry *    entries;
  199. Xstatic struct entry *    ebeyond;
  200. X
  201. Xstatic int        dayhours;
  202. Xstatic int        dayslots;
  203. Xstatic int        hourslots;
  204. X
  205. X#define NAMESIZE    (sizeof entries[0].u.ut_name)
  206. X#define LINESIZE    (sizeof entries[0].u.ut_line)
  207. X#define namecmp(name1, name2) strncmp(name1, name2, NAMESIZE)
  208. X#define linecmp(line1, line2) strncmp(line1, line2, LINESIZE)
  209. X#define namecpy(to, from) strncpy(to, from, NAMESIZE)
  210. X#define linecpy(to, from) strncpy(to, from, LINESIZE)
  211. X
  212. X#define BOOTCHAR    '~'
  213. X#define OLDTCHAR    '|'
  214. X/*
  215. X** 4.[12]bsd writes '{' to the file, but the documentation says that '}' is
  216. X** the character.  We'll take either.
  217. X**
  218. X** System V does things differently, of course.
  219. X*/
  220. X#define NEWTCHAR    '{'
  221. X#define OTHTCHAR    '}'
  222. X
  223. X#define BOOTLINE(s) (*(s) == BOOTCHAR && (s)[1] == '\0' || \
  224. X            strcmp((s), "reboot") == 0)
  225. X#define OLDTLINE(s) (*(s) == OLDTCHAR && (s)[1] == '\0' || \
  226. X            strncmp((s), "old time", 8) == 0)
  227. X#define NEWTLINE(s) (*(s) == NEWTCHAR && (s)[1] == '\0' || \
  228. X            *(s) == OTHTCHAR && (s)[1] == '\0' || \
  229. X            strncmp((s), "new time", 8) == 0)
  230. X#define TIMELINE(s) (OLDTLINE(s) || NEWTLINE(s))
  231. X#define SPCLLINE(s) (TIMELINE(s) || BOOTLINE(s))
  232. X
  233. Xstatic struct entry *
  234. Xlookup(up, doenter)
  235. Xregister struct utmp *    up;
  236. X{
  237. X    register struct entry *    ep;
  238. X    register char *        cp;
  239. X    register int        i;
  240. X
  241. X    cp = up->ut_line;
  242. X    if (SPCLLINE(cp))
  243. X        return NULL;
  244. X    for (ep = entries; ep < ebeyond; ++ep)
  245. X        if (linecmp(cp, ep->u.ut_line) == 0)
  246. X            return ep;
  247. X    if (!doenter)
  248. X        return NULL;
  249. X    i = ebeyond - entries;
  250. X    if (i == 0)
  251. X        ep = (struct entry *) calloc(1, sizeof *ep);
  252. X    else    ep = (struct entry *) realloc((char *) entries,
  253. X            (arg4alloc) ((i + 1) * sizeof *ep));
  254. X    if (ep == NULL)
  255. X        for ( ; ; )
  256. X            wildrexit("alloc");
  257. X    entries = ep;
  258. X    ebeyond = ep + i + 1;
  259. X    ep = ebeyond - 1;
  260. X    (void) linecpy(ep->u.ut_line, up->ut_line);
  261. X    (void) sprintf(ep->l, "%*s", dayslots, "");
  262. X    ep->u.ut_time = 0;
  263. X    ep->u.ut_name[0] = '\0';
  264. X    ep->marked = FALSE;
  265. X    ep->daysecs = 0;
  266. X    return ep;
  267. X}
  268. X
  269. Xstatic char        headline[MAXDAYHOURS * LONGHOURSLOTS + 1];
  270. X
  271. Xstatic int        lflag;    /* long lines */
  272. Xstatic char *        wname = "/usr/adm/wtmp";
  273. X
  274. Xstatic char *        name;
  275. X
  276. Xstatic char **        argusers;
  277. Xstatic int        cntusers;
  278. X
  279. Xstatic long        starttime;
  280. Xstatic long        stoptime;
  281. X
  282. Xstatic
  283. Xdodate(string)
  284. Xregister char *    string;
  285. X{
  286. X    struct tm    local;
  287. X    int        bmon, bday, byear, emon, eday, eyear, i;
  288. X    long        now;
  289. X    char        c;
  290. X
  291. X    (void) time(&now);
  292. X    local = *localtime(&now);
  293. X    c = '\0';
  294. X    i = sscanf(string, "%d/%d-%d/%d%c", &bmon, &bday, &emon, &eday, &c);
  295. X    if (i != 4 || c != '\0') {
  296. X        c = '\0';
  297. X        i = sscanf(string, "%d/%d-%d%c", &bmon, &bday, &eday, &c);
  298. X        if (i == 3 && c == '\0') {
  299. X            emon = bmon;
  300. X            if (bday > eday)
  301. X                for ( ; ; )
  302. X                    wildexit("date");
  303. X        } else {
  304. X            c = '\0';
  305. X            i = sscanf(string, "%d/%d%c", &bmon, &bday, &c);
  306. X            if (i == 2 && c == '\0') {
  307. X                emon = bmon;
  308. X                eday = bday;
  309. X            } else {
  310. X                c = '\0';
  311. X                i = sscanf(string, "%d/%d-%c",
  312. X                    &bmon, &bday, &c);
  313. X                if (i == 2 && c == '\0') {
  314. X                    emon = local.tm_mon + 1;
  315. X                    eday = local.tm_mday;
  316. X                } else for ( ; ; )
  317. X                    wildexit("date");
  318. X            }
  319. X        }
  320. X    }
  321. X    byear = eyear = local.tm_year + 1900;
  322. X    if (bmon > emon || bmon == emon && bday > eday)
  323. X        --byear;
  324. X    starttime = mdy2t(bmon, bday, byear);
  325. X    stoptime = mdy2t(emon, eday, eyear);
  326. X}
  327. X
  328. Xstatic char *
  329. Xttyabbr(string)
  330. Xregister char *    string;
  331. X{
  332. X    static char    buf[3];
  333. X
  334. X    if (strncmp(string, "tty", 3) == 0)
  335. X        (void) strncpy(buf, string + 3, 2);
  336. X    else    (void) strncpy(buf, string, 2);
  337. X    buf[2] = '\0';
  338. X    return buf;
  339. X}
  340. X
  341. Xstatic struct utmp *    utmp;
  342. Xstatic struct utmp *    ubeyond;
  343. Xstatic struct utmp *     unext;
  344. Xstatic struct utmp *    ubase;
  345. X
  346. Xstatic int
  347. Xqtime(up1, up2)
  348. Xchar *    up1;
  349. Xchar *    up2;
  350. X{
  351. X    register long    diff;
  352. X
  353. X    diff = ((struct utmp *) up1)->ut_time - ((struct utmp *) up2)->ut_time;
  354. X    if (diff == 0)
  355. X        return 0;
  356. X    else if (diff > 0)
  357. X        return 1;
  358. X    else    return -1;
  359. X}
  360. X
  361. Xmain(argc, argv)
  362. Xint    argc;
  363. Xchar *    argv[];
  364. X{
  365. X    register FILE *        fp;
  366. X    register struct entry *    ep;
  367. X    register int        i;
  368. X    register long        t;
  369. X    register long        lastm, m;
  370. X    register int        slot;
  371. X    register int        didttys;
  372. X    register long        filesize;
  373. X    static int        Rflag;
  374. X    static int        diddate;
  375. X    char            buf[134];
  376. X    extern int        optind;
  377. X    extern char *        optarg;
  378. X
  379. X    for (name = argv[0]; *name != '\0'; ++name)
  380. X        if (name[0] == '/' && name[1] != '\0' && name[1] != '/')
  381. X            argv[0] = ++name;
  382. X    name = argv[0];
  383. X    if (argc == 2 && strcmp(argv[1], "=") == 0) {
  384. X        (void) printf(
  385. X"%s: usage is %s [-lR] [-w wtmp] [m/d[-[[m/]d]]] [user...] [/dev/*...]\n",
  386. X            name, name);
  387. X        for ( ; ; )
  388. X            exit(1);
  389. X    }
  390. X    while ((i = getopt(argc, argv, "lRw:")) != EOF)
  391. X        switch (i) {
  392. X            case 'l':
  393. X                lflag = TRUE;
  394. X                break;
  395. X            case 'R':
  396. X                Rflag = TRUE;
  397. X                starttime = 0;
  398. X                (void) time(&stoptime);
  399. X                break;
  400. X            case 'w':
  401. X                wname = optarg;
  402. X                break;
  403. X            default:
  404. X                for ( ; ; )
  405. X                    wildexit("usage");
  406. X        }
  407. X    hourslots = lflag ? LONGHOURSLOTS : HOURSLOTS;
  408. X    /*
  409. X    ** Parse arguments
  410. X    */
  411. X    i = (argc - optind) + 1;
  412. X    argusers = (char **) calloc((arg4alloc) i, sizeof *argusers);
  413. X    if (argusers == NULL)
  414. X        for ( ; ; )
  415. X            wildrexit("calloc");
  416. X    cntusers = 0;
  417. X    for (i = optind; i < argc; ++i)
  418. X        if (strncmp(argv[i], "/dev/", 5) == 0) {
  419. X            struct utmp    fake;
  420. X
  421. X            (void) linecpy(fake.ut_line, &argv[i][5]);
  422. X            (void) namecpy(fake.ut_name, "fake");
  423. X            if (lookup(&fake, FALSE) != NULL)
  424. X                for ( ; ; )
  425. X                    wildrexit("duplicate /dev argument");
  426. X            if (lookup(&fake, TRUE) == NULL) /* "cannot happen */
  427. X                for ( ; ; )
  428. X                    wildrexit("lookup");
  429. X        } else if (index(argv[i], '/') == 0)
  430. X            argusers[cntusers++] = argv[i];
  431. X        else if (Rflag)
  432. X            for ( ; ; )
  433. X                wildexit("combination of date and -R");
  434. X        else if (diddate)
  435. X            for ( ; ; )
  436. X                wildexit("multiple dates");
  437. X        else {
  438. X            dodate(argv[i]);
  439. X            diddate = TRUE;
  440. X        }
  441. X    argusers[cntusers] = NULL;
  442. X    didttys = entries != NULL;
  443. X    if (!Rflag && !diddate) {
  444. X        (void) time(&starttime);
  445. X        stoptime = starttime;
  446. X    }
  447. X    starttime = midnight(starttime);
  448. X    stoptime = midnight(stoptime);
  449. X    stoptime = midnight(stoptime + MAXDAYHOURS * HOUR);
  450. X    /*
  451. X    ** And now the real work begins.
  452. X    */
  453. X    if ((fp = fopen(wname, "r")) == NULL)
  454. X        for ( ; ; )
  455. X            wildrexit("opening wtmp");
  456. X    (void) fseek(fp, 0L, 2);
  457. X    filesize = ftell(fp);
  458. X    if (filesize == 0)
  459. X        for ( ; ; )
  460. X            tameexit();
  461. X    if (filesize < 0 || (filesize % sizeof *utmp) != 0)
  462. X        for ( ; ; )
  463. X            wildexit("wtmp size");
  464. X    i = filesize / sizeof *utmp;
  465. X    utmp = (struct utmp *) calloc((arg4alloc) i, sizeof *utmp);
  466. X    if (utmp == NULL)
  467. X        for ( ; ; )
  468. X            wildrexit("calloc");
  469. X    ubeyond = utmp + i;
  470. X    /*
  471. X    ** Step 1--read it in.
  472. X    */
  473. X#ifdef fileno
  474. X    (void) lseek(fileno(fp), 0L, 0);
  475. X    i = read(fileno(fp), (char *) utmp, (int) filesize);
  476. X#endif
  477. X#ifndef fileno
  478. X    (void) fseek(fp, 0L, 0);
  479. X    i = fread((char *) utmp, sizeof *utmp, (int) i, fp) * sizeof *utmp);
  480. X#endif
  481. X    if (i != filesize || ferror(fp) || fclose(fp))
  482. X        for ( ; ; )
  483. X            wildrexit("reading wtmp");
  484. X    /*
  485. X    ** Step 2--eliminate records up to and including last reboot before
  486. X    ** start time.
  487. X    */
  488. X    for (unext = utmp; unext<ubeyond && unext->ut_time<starttime; ++unext)
  489. X        if (BOOTLINE(unext->ut_line))
  490. X            utmp = unext + 1;
  491. X    /*
  492. X    ** Step 3--eliminate trailing records for times past stoptime.
  493. X    */
  494. X    while ((ubeyond - 1) > utmp && (ubeyond - 1)->ut_time >= stoptime)
  495. X        --ubeyond;
  496. X    /*
  497. X    ** Step 4--eliminate bogus records.
  498. X    */
  499. X    for (unext = ubase = utmp; unext < ubeyond; ++unext)
  500. X        if (unext->ut_time == 0 || unext->ut_line[0] == '\0')
  501. X            continue;
  502. X        else if (ubase == unext)
  503. X            ++ubase;
  504. X        else    *ubase++ = *unext;
  505. X    /*
  506. X    ** Step 5--eliminate leading logouts, reboots, and time changes.
  507. X    */
  508. X    while (utmp < ubeyond && SPCLLINE(utmp->ut_line))
  509. X        ++utmp;
  510. X    /*
  511. X    ** Step 6--eliminate trailing reboots and time changes.
  512. X    */
  513. X    while ((ubeyond - 1) > utmp && SPCLLINE((ubeyond - 1)->ut_line))
  514. X        --ubeyond;
  515. X    /*
  516. X    ** Step 7--if ttys were specified, eliminate records for other ttys.
  517. X    */
  518. X    if (didttys)
  519. X        for (ubase = unext = utmp; unext < ubeyond; ++unext)
  520. X            if (SPCLLINE(unext->ut_line) ||
  521. X                lookup(unext, FALSE) != NULL)
  522. X                    if (ubase == unext)
  523. X                        ++ubase;
  524. X                    else    *ubase++ = *unext;
  525. X    /*
  526. X    ** Final step--sort records between time changes.
  527. X    */
  528. X    for (ubase = unext = utmp; unext < ubeyond; ++unext)
  529. X        if (TIMELINE(unext->ut_line)) {
  530. X            if (unext != ubase)
  531. X                (void) qsort((char *) ubase, unext - ubase,
  532. X                    sizeof *ubase, qtime);
  533. X            ubase = unext + 1;
  534. X        }
  535. X    if (ubeyond > ubase)
  536. X        (void) qsort((char *) ubase, ubeyond - ubase,
  537. X            sizeof *ubase, qtime);
  538. X    if (Rflag)
  539. X        unext = utmp;
  540. X    else {
  541. X        /*
  542. X        ** Find first record that's for after the start time.
  543. X        */
  544. X        for (unext = utmp; unext < ubeyond; ++unext)
  545. X            if (unext->ut_time >= starttime)
  546. X                break;
  547. X        if (unext >= ubeyond)    /* easy enough! */
  548. X            for ( ; ; )
  549. X                tameexit();
  550. X        ubase = unext;
  551. X        /*
  552. X        ** Scan back to a reboot or to the first record
  553. X        ** (or, if we didttys, until all ttys are accounted for).
  554. X        */
  555. X        for ( ; unext > utmp; --unext) {
  556. X            if (BOOTLINE(unext->ut_line))
  557. X                break;
  558. X            if ((ep = lookup(unext, !didttys)) == NULL)
  559. X                continue;
  560. X            if (ep->marked)
  561. X                continue;
  562. X            ep->marked = TRUE;
  563. X            (void) namecpy(ep->u.ut_name, unext->ut_name);
  564. X            if (didttys) {
  565. X                for (ep = entries; ep < ebeyond; ++ep)
  566. X                    if (!ep->marked)
  567. X                        break;
  568. X                if (ep >= ebeyond)
  569. X                    break;
  570. X            }
  571. X        }
  572. X        /*
  573. X        ** If users were specified, zap the records for anybody else.
  574. X        */
  575. X        if (cntusers > 0)
  576. X            for (ep = entries; ep < ebeyond; ++ep) {
  577. X                for (i = 0; i < cntusers; ++i)
  578. X                    if (namecmp(argusers[i],
  579. X                        ep->u.ut_name) == 0)
  580. X                            break;
  581. X                if (i >= cntusers)
  582. X                    ep->u.ut_name[0] = '\0';
  583. X            }
  584. X        /*
  585. X        ** We're ready!
  586. X        */
  587. X        utmp = unext = ubase;
  588. X    }
  589. X    lastm = starttime - 1;
  590. X    for ( ; unext < ubeyond; ++unext, lastm = m) {
  591. X        t = unext->ut_time;
  592. X        m = midnight(t);
  593. X        if (m != lastm) {
  594. X            if (lastm >= starttime && lastm < stoptime) {
  595. X                for (ep = entries; ep < ebeyond; ++ep) {
  596. X                    if (ep->u.ut_name[0] == '\0')
  597. X                        continue;
  598. X                    ep->daysecs += (lastm + dayhours *
  599. X                        HOUR) - ep->u.ut_time;
  600. X                    if (ep->daysecs == 0)
  601. X                        ++(ep->daysecs);
  602. X                    colonize(ep->l, dayslots - 1);
  603. X                }
  604. X                dump(lastm);
  605. X            }
  606. X            newday(m, 0L, '\0');
  607. X        }
  608. X        slot = (unext->ut_time - m) / (HOUR / hourslots);
  609. X        if (BOOTLINE(unext->ut_line)) {
  610. X            headline[slot] = BOOTCHAR;
  611. X            for (ep = entries; ep < ebeyond; ++ep) {
  612. X                if (ep->u.ut_name[0] != '\0') {
  613. X                    ep->daysecs += unext->ut_time -
  614. X                        ep->u.ut_time;
  615. X                    if (ep->daysecs == 0)
  616. X                        ++(ep->daysecs);
  617. X                    colonize(ep->l, slot);
  618. X                    ep->u.ut_name[0] = '\0';
  619. X                    ep->u.ut_time = 0;
  620. X                }
  621. X            }
  622. X            continue;
  623. X        }
  624. X        if (NEWTLINE(unext->ut_line))
  625. X            for ( ; ; )
  626. X                wildexit("new time without old time");
  627. X        if (OLDTLINE(unext->ut_line)) {
  628. X            if (unext == ubeyond || !NEWTLINE((unext + 1)->ut_line))
  629. X                for ( ; ; )
  630. X                    wildexit("old time without new time");
  631. X            headline[slot] = OLDTCHAR;
  632. X            for (ep = entries; ep < ebeyond; ++ep) {
  633. X                if (ep->u.ut_name[0] == '\0')
  634. X                    ep->l[slot] = OLDTCHAR;
  635. X                else {
  636. X                    ep->daysecs += unext->ut_time -
  637. X                        ep->u.ut_time;
  638. X                    if (ep->daysecs == 0)
  639. X                        ++(ep->daysecs);
  640. X                    colonize(ep->l, slot);
  641. X                }
  642. X            }
  643. X            dump(lastm);
  644. X            ++unext;
  645. X            if (unext->ut_line[0] == NEWTCHAR ||
  646. X                unext->ut_line[0] == OTHTCHAR)
  647. X                    newday(m, unext->ut_time,
  648. X                        unext->ut_line[0]);
  649. X            else    newday(m, unext->ut_time, NEWTCHAR);
  650. X            continue;
  651. X        }
  652. X        if ((ep = lookup(unext, !didttys)) == NULL)
  653. X            continue;
  654. X        /*
  655. X        ** If a login is in progress, either a login or
  656. X        ** logout terminates it.
  657. X        */
  658. X        if (ep->u.ut_name[0] != '\0') {
  659. X            ep->daysecs += unext->ut_time - ep->u.ut_time;
  660. X            if (ep->daysecs == 0)
  661. X                ++(ep->daysecs);
  662. X            colonize(ep->l, slot);
  663. X            ep->u.ut_name[0] = '\0';
  664. X            ep->u.ut_time = 0;
  665. X        }
  666. X        if (unext->ut_name[0] == '\0')
  667. X            continue;
  668. X        if (cntusers > 0) {
  669. X            for (i = 0; i < cntusers; ++i)
  670. X                if (namecmp(argusers[i], unext->ut_name) == 0)
  671. X                    break;
  672. X            if (i == cntusers)
  673. X                continue;
  674. X        }
  675. X        ep->u.ut_time = unext->ut_time;
  676. X        if (isupper(ep->l[slot])) {
  677. X            buf[0] = '2';
  678. X            i = 1;
  679. X        } else {
  680. X            register char *    cp;
  681. X
  682. X            cp = index("23456789**", ep->l[slot]);
  683. X            if (cp != 0) {
  684. X                buf[0] = *(cp + 1);
  685. X                i = 1;
  686. X            } else    i = 0;
  687. X        }
  688. X        fudge(unext->ut_name);
  689. X        (void) namecpy(ep->u.ut_name, unext->ut_name);
  690. X        (void) namecpy(&buf[i], unext->ut_name);
  691. X        /*
  692. X        ** Ensure termination.
  693. X        */
  694. X        buf[NAMESIZE + i] = '\0';
  695. X        if (i == 0 && islower(buf[0]))
  696. X            buf[0] = toupper(buf[0]);
  697. X        i = strlen(buf);
  698. X        if (i > (dayslots - slot))
  699. X            i = dayslots - slot;
  700. X        (void) strncpy(&ep->l[slot], buf, i);
  701. X    }
  702. X    for (ep = entries; ep < ebeyond; ++ep) {
  703. X        if (ep->u.ut_name[0] == '\0')
  704. X            continue;
  705. X        ep->daysecs += t - ep->u.ut_time;
  706. X        if (ep->daysecs == 0)
  707. X            ++(ep->daysecs);
  708. X        colonize(ep->l, slot);
  709. X    }
  710. X    dump(lastm);
  711. X    for ( ; ; )
  712. X        tameexit();
  713. X}
  714. X
  715. Xstatic int
  716. Xqline(ep1, ep2)
  717. Xchar *    ep1;
  718. Xchar *    ep2;
  719. X{
  720. X    return linecmp(((struct entry *) ep1)->u.ut_line,
  721. X        ((struct entry *) ep2)->u.ut_line);
  722. X}
  723. X
  724. Xstatic int
  725. Xttysused(base, count)
  726. Xregister int    base;
  727. Xregister int    count;
  728. X{
  729. X    register struct entry *    ep;
  730. X    register int        i;
  731. X    register int        result;
  732. X
  733. X    result = 0;
  734. X    for (ep = entries; ep < ebeyond; ++ep)
  735. X        for (i = 0; i < count; ++i) {
  736. X            switch (ep->l[base + i]) {
  737. X                case ' ':
  738. X                case BOOTCHAR:
  739. X                case OLDTCHAR:
  740. X                case NEWTCHAR:
  741. X                case OTHTCHAR:
  742. X                    continue;
  743. X            }
  744. X            ++result;
  745. X            break;
  746. X        }
  747. X    return result;
  748. X}
  749. X
  750. Xstatic
  751. Xdump(m)
  752. Xlong    m;    /* midnight */
  753. X{
  754. X    register struct entry *    ep;
  755. X    register char *        cp;
  756. X    register int        i, j, k, lines;
  757. X    register int        width, prec;
  758. X    register double        d;
  759. X    struct tm        tm;
  760. X    long            grandtot;
  761. X    char            buf[20];
  762. X
  763. X    tm = *localtime(&m);
  764. X    grandtot = 0;
  765. X    for (ep = entries; ep < ebeyond; ++ep)
  766. X        grandtot += ep->daysecs;
  767. X    if (grandtot == 0)
  768. X        return;
  769. X    (void) qsort((char *) entries, ebeyond - entries,
  770. X        sizeof entries[0], qline);
  771. X    lines = 0;
  772. X    width = (lflag ? 131 : 79) - dayslots;
  773. X    prec = (width >= 7) ? width : 3;
  774. X    for (ep = entries; ep < ebeyond; ++ep) {
  775. X        if (ep->daysecs == 0)
  776. X            continue;
  777. X        if (lines == 0) {
  778. X            cp = ctime(&m);
  779. X            (void) printf("%.11s%s", cp, cp + 20);
  780. X            (void) printf("%-*.*s", width, prec, "TT USE");
  781. X            (void) puts(headline);
  782. X        }
  783. X        d = ep->daysecs / (double) HOUR;
  784. X        (void) sprintf(buf, "%2.2s%4.1f", ttyabbr(ep->u.ut_line), d);
  785. X        if (buf[2] != ' ')
  786. X            (void) sprintf(buf, "%2.2s%4.0f",
  787. X                ttyabbr(ep->u.ut_line), d);
  788. X        (void) printf("%-*.*s", width, prec, buf);
  789. X        for (i = 0; i < dayslots; ++i)
  790. X            if (headline[i] == BOOTCHAR && ep->l[i] == ' ')
  791. X                ep->l[i] = BOOTCHAR;
  792. X        cp = ep->l + dayslots;
  793. X        while (cp > ep->l && *(cp - 1) == ' ')
  794. X            --cp;
  795. X        (void) printf("%.*s\n", cp - ep->l, ep->l);
  796. X        ++lines;
  797. X    }
  798. X    if (lines <= 1)
  799. X        return;
  800. X    if (prec > 6)
  801. X        (void) sprintf(buf, "%6.1f", grandtot / (double) HOUR);
  802. X    else    buf[0] = '\0';
  803. X    (void) printf("%-*.*s", width, prec, buf);
  804. X    for (i = 0; i < dayslots; i += j) {
  805. X        j = lflag ? hourslots : (2 * hourslots);
  806. X        if (i == 0 && (dayslots % j) != 0)
  807. X            j += dayslots % j;
  808. X        if ((k = ttysused(i, j)) == 0)
  809. X            (void) printf("%*s", j, "");
  810. X        else    (void) printf("<%*d>", j - 2, k);
  811. X    }
  812. X    (void) printf("\n");
  813. X}
  814. X
  815. Xstatic
  816. Xcolonize(line, slot)
  817. Xregister char *    line;
  818. Xregister int    slot;
  819. X{
  820. X    register int    i;
  821. X
  822. X    for (i = slot + 1; i < dayslots && line[slot] != ' '; ++i)
  823. X        line[i] = ' ';
  824. X    while (slot >= 0 && line[slot] == ' ') {
  825. X        line[slot] = ':';
  826. X        --slot;
  827. X    }
  828. X}
  829. X
  830. Xstatic
  831. Xwildrexit(string)
  832. Xchar *    string;
  833. X{
  834. X    (void) fprintf(stderr, "%s: wild result from %s\n", name, string);
  835. X    for ( ; ; )
  836. X        exit(1);
  837. X}
  838. X
  839. Xstatic
  840. Xwildexit(string)
  841. Xchar *    string;
  842. X{
  843. X    (void) fprintf(stderr, "%s: wild %s\n", name, string);
  844. X    for ( ; ; )
  845. X        exit(1);
  846. X}
  847. X
  848. Xstatic
  849. Xtameexit()
  850. X{
  851. X    for ( ; ; )
  852. X        exit(0);
  853. X}
  854. X
  855. Xstatic
  856. Xfudge(buf)
  857. Xregister char *    buf;
  858. X{
  859. X    register int    i;
  860. X
  861. X    for (i = 0; i < NAMESIZE; ++i)
  862. X        switch (buf[i]) {
  863. X            case '\0':
  864. X                return;
  865. X            case BOOTCHAR:
  866. X            case OLDTCHAR:
  867. X            case NEWTCHAR:
  868. X            case OTHTCHAR:
  869. X            case ':':
  870. X            case '*':
  871. X            case ' ':
  872. X            case '2': case '3': case '4': case '5':
  873. X            case '6': case '7': case '8': case '9':
  874. X                buf[i] = '?';
  875. X                break;
  876. X            default:
  877. X                if (!isascii(buf[i]))
  878. X                    buf[i] = '?';
  879. X                else if (isupper(buf[i]))
  880. X                    buf[i] = tolower(buf[i]);
  881. X                else if (!isprint(buf[i]))
  882. X                    buf[i] = '?';
  883. X                break;
  884. X        }
  885. X}
  886. X
  887. Xstatic long
  888. Xmdy2t(m, d, y)
  889. X{
  890. X    register struct tm *    tmp;
  891. X    register int        diff;
  892. X    register int        lastdiff;
  893. X    long            guess;
  894. X
  895. X    if (y < 1969 || m <= 0 || m > 12 || d <= 0 || d >= 32)
  896. X        for ( ; ; )
  897. X            wildrexit("date");
  898. X    guess = d + (m - 1) * (365.25 / 12.) + (y - 1970) * 365.25;
  899. X    guess = guess * HOUR * 24;
  900. X    y -= 1900;
  901. X    --m;
  902. X    lastdiff = 0;
  903. X    for ( ; ; ) {
  904. X        tmp = localtime(&guess);
  905. X        if ((diff = tmp->tm_year - y) == 0 &&
  906. X            (diff = tmp->tm_mon - m) == 0)
  907. X                diff = tmp->tm_mday - d;
  908. X        if (diff == 0)
  909. X            return midnight(guess);
  910. X        if (lastdiff != 0)
  911. X            if ((diff > 0) != (lastdiff > 0))
  912. X                for ( ; ; )
  913. X                    wildrexit("date");
  914. X        if (diff > 0)
  915. X            guess -= 12 * HOUR;
  916. X        else    guess += 12 * HOUR;
  917. X        lastdiff = diff;
  918. X    }
  919. X}
  920. X
  921. Xstatic long
  922. Xmidnight(t)
  923. Xlong    t;
  924. X{
  925. X    register int    diff, lastdiff;
  926. X    struct tm     tm;
  927. X    struct tm    mtm;
  928. X    long        m;
  929. X
  930. X    tm = *localtime(&t);
  931. X    if (tm.tm_hour == 0 && tm.tm_min == 0 && tm.tm_sec == 0)
  932. X        return t;
  933. X    m = t;
  934. X    m -= tm.tm_hour * HOUR;
  935. X    m -= tm.tm_min * MINUTE;
  936. X    m -= tm.tm_sec;
  937. X    lastdiff = 0;
  938. X    for ( ; ; ) {
  939. X        mtm = *localtime(&m);
  940. X        if ((diff = mtm.tm_year - tm.tm_year) == 0 &&
  941. X            (diff = mtm.tm_mon - tm.tm_mon) == 0 &&
  942. X            (diff = mtm.tm_mday - tm.tm_mday) == 0 &&
  943. X            (diff = mtm.tm_hour) == 0)
  944. X                if (mtm.tm_min == 0 && mtm.tm_sec == 0)
  945. X                    return m;
  946. X                else    for ( ; ; )
  947. X                        wildrexit("finding midnight");
  948. X        if (lastdiff != 0)
  949. X            if ((diff > 0) != (lastdiff > 0))
  950. X                for ( ; ; )
  951. X                    wildrexit("finding midnight");
  952. X        if (diff > 0)
  953. X            m -= HOUR;
  954. X        else    m += HOUR;
  955. X        lastdiff = diff;
  956. X    }
  957. X}
  958. X
  959. Xstatic
  960. Xnewday(m, t, headchar)
  961. Xlong    m, t;
  962. X{
  963. X    register struct entry *    ep;
  964. X    register int        i, j, slot;
  965. X    long            t2;
  966. X    char            buf[20];
  967. X
  968. X    dayhours = (midnight(m + MAXDAYHOURS * HOUR) - m) / HOUR;
  969. X    if (dayhours <= 0 || dayhours > MAXDAYHOURS)
  970. X        for ( ; ; )
  971. X            wildexit("number of hours in a day");
  972. X    dayslots = hourslots * dayhours;
  973. X    headline[0] = '\0';
  974. X    for (i = 0; i < dayhours; ++i) {
  975. X        if (dayhours == 24)
  976. X            j = i;
  977. X        else {
  978. X            t2 = m + i * HOUR;
  979. X            j = localtime(&t2)->tm_hour;
  980. X        }
  981. X        if ((j %= 12) == 0)
  982. X            j = 12;
  983. X        if (j == 12 && lflag)
  984. X            if (i == 0)
  985. X                (void) strcpy(buf, "Mid.-");
  986. X            else    (void) strcpy(buf, "Noon-");
  987. X        else    (void) sprintf(buf, "%d-----", j);
  988. X        buf[hourslots] = '\0';
  989. X        (void) strcat(headline, buf);
  990. X    }
  991. X    if (t == 0)
  992. X        slot = 0;
  993. X    else {
  994. X        slot = (t - m) / (HOUR / hourslots);
  995. X        headline[slot] = headchar;
  996. X    }
  997. X    for (ep = entries; ep < ebeyond; ++ep) {
  998. X        (void) sprintf(ep->l, "%*s", dayslots, "");
  999. X        ep->daysecs = 0;
  1000. X        if (ep->u.ut_name[0] == '\0') {
  1001. X            ep->u.ut_time = 0;
  1002. X            if (t != 0)
  1003. X                ep->l[slot] = headchar;
  1004. X        } else {
  1005. X            ep->u.ut_time = (t == 0) ? m : t;
  1006. X            for (i = 0; i < NAMESIZE; ++i) {
  1007. X                if (ep->u.ut_name[i] == '\0')
  1008. X                    break;
  1009. X                if ((slot + i) >= dayslots)
  1010. X                    break;
  1011. X                ep->l[slot + i] = ep->u.ut_name[i];
  1012. X            }
  1013. X        }
  1014. X    }
  1015. X}
  1016. EOF
  1017. exit
  1018.  
  1019.  
  1020.