home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume3 / nwho / nwho.c < prev    next >
Encoding:
C/C++ Source or Header  |  1986-11-30  |  16.6 KB  |  847 lines

  1. /* vi:set sw=4 ts=4: */
  2. #ifndef    lint
  3. static char       *sccsid = "@(#)nwho.c    3.12 rayssd!gmp 12/22/85";
  4. #endif    lint
  5. /*
  6. **    nwho.c (version 3.12): written by
  7. **        Greg Paris
  8. **        Raytheon Submarine Signal Division
  9. **        Portsmouth, RI  02871
  10. */
  11.  
  12. /*
  13. **    NOTE: correct operation of this program depends
  14. **        on the alias file being sorted by login name.
  15. **
  16. **    NOTE: the "-u utmp" option is undocumented.  This
  17. **        program is quite memory intensive -- great for
  18. **        /etc/utmp use, but a real hog when used on large
  19. **        /usr/adm/wtmp files.
  20. */
  21.  
  22. #include    <sys/types.h>
  23. #include    <utmp.h>
  24. #include    <sys/stat.h>
  25.  
  26. #ifdef    bsd42
  27. #define        hashosts
  28. #include    <sys/time.h>
  29. #else    bsd42
  30. #include    <time.h>
  31. #include    <sys/timeb.h>
  32. #endif    bsd42
  33.  
  34. #ifdef    pyr
  35. /*
  36. **    compile in ucb universe
  37. **    works with ucb init being run
  38. */
  39. char        utmpfl[]    = "/etc/.ucbutmp";        /* for att users */
  40. #else    pyr
  41. char        utmpfl[]    = "/etc/utmp";            /* default utmp file */
  42. #endif    pyr
  43.  
  44. char        aliasfl[]    = ".nwhorc";            /* default alias file */
  45.  
  46. char        *progname;                            /* program name */
  47. int            proglen;                            /* program name length */
  48. char        *requtmpfl;                            /* requested utmp file */
  49. char        *reqaliasfl;                        /* requested alias file */
  50.  
  51. extern int    optind;
  52. extern char    *optarg;
  53. extern int    getopt();
  54. #ifndef    EOF
  55. #define        EOF        -1
  56. #endif    EOF
  57.  
  58. int            logsort(), alsort(), termsort();
  59. char        *strcpy(), *strncpy(), *index(), *timefmt();
  60. char        *getenv(), *malloc(), *getaliases(), *logalias();
  61. long        gmtoffset();
  62.  
  63. /*
  64. **    plus: structure for aliasing login names
  65. */
  66. struct plus {
  67.     struct utmp        *pu;            /* pointer to utmp entry */
  68.     char            *al;            /* pointer to alias */
  69. };
  70.  
  71. struct utmp    dumutmp;                            /* for SZ defines */
  72.  
  73. #ifndef    TTYLEN
  74. #define    TTYLEN        2                            /* assume ttyxx */
  75. #endif    TTYLEN
  76. #define    MINENTLEN    (TTYLEN+7)                    /* space for term and time */
  77. #define    MINSPACES    2                            /* min between columns */
  78. #define    WHITESPACE    case ' ': case '\t'            /* white space in alias file */
  79. #define    DEFLINELEN    79                            /* avoids premature wrapping */
  80.  
  81. #define    SZ_UTMP        sizeof(struct utmp)
  82. #define    SZ_PLUS        sizeof(struct plus)
  83. #define    SZ_UTNAME    sizeof(dumutmp.ut_name)
  84.  
  85. #ifdef    hashosts
  86. #define    SZ_UTHOST    sizeof(dumutmp.ut_host)
  87. #endif    hashosts
  88.  
  89. /*
  90. **    add characters to output buffer
  91. */
  92. #define    bufadd(str)    for(fr = str;*fr;*pb++ = *fr++)
  93. #define    bufaddn(st, nn)    for(fr = st, i = nn;*fr && i--;*pb++ = *fr++)
  94.  
  95. /*
  96. **    add TTYLEN-letter abbreviation for terminal name
  97. **    loses if non-tty devices named t* exist
  98. */
  99. #define    buftty(tty)    if(tty[0] == 't') bufadd(tty+3); \
  100.         else bufaddn(tty, TTYLEN)
  101.  
  102. /*
  103. **    add " hh:mm "
  104. */
  105. #define    buftime(tm)    bufadd(timefmt(tm -= offset))
  106.  
  107. /*
  108. **    add alias or login name
  109. */
  110. #define    bufname(ppp)    \
  111.         if(ppp->al) bufadd(ppp->al); \
  112.         else bufaddn(ppp->pu->ut_name, SZ_UTNAME)
  113.  
  114. #ifdef    hashosts
  115. /*
  116. **    add " (hostname)"
  117. */
  118. #define    bufhost(ppp)    \
  119.         if(hosts && ppp->pu->ut_host[0]) { \
  120.             *pb++ = ' '; *pb++ = '('; \
  121.             bufaddn(ppp->pu->ut_host, SZ_UTHOST); \
  122.             *pb++ = ')'; }
  123. #endif    hashosts
  124.  
  125. #ifdef    NOERRMSGS
  126. #define    errexit(code,msg)    exit(code)
  127. #else    NOERRMSGS
  128. #define    errexit(code,msg)    \
  129.         static char errmsg[] = msg; \
  130.         (void) write(2, progname, proglen); \
  131.         (void) write(2, errmsg, sizeof(errmsg) - 1); \
  132.         exit(code)
  133. #endif    NOERRMSGS
  134.         
  135. main(ac, av)
  136. int        ac;
  137. char    *av[];
  138. {
  139.     register struct plus    *pp, *ppp;
  140.     register int            entries;
  141.     register char            *pb, *fr;
  142.     register int            i;
  143.     struct plus                *ppmax;
  144.     struct utmp                *pu, *puu;
  145.     int                        size;
  146.     int                        linelen, entrylen, spaces, cols;
  147.     int                        termord, quick, colreq;
  148.     char                    *linebuf, *aliasbuf, *entryend;
  149.     long                    offset;
  150.     int                        hosts;
  151.  
  152.     proglen = strlen(progname = av[0]);
  153.     requtmpfl = reqaliasfl = (char *) 0;
  154.     hosts = termord = colreq = linelen = quick = 0;
  155.  
  156.     while((i = getopt(ac, av, "hqta:l:c:u:")) != EOF) {
  157.         switch(i) {
  158.         case 'h':    /* show remote host field */
  159.             hosts = 1;
  160.             break;
  161.         case 'a':    /* alias file request */
  162.             reqaliasfl = optarg;
  163.             break;
  164.         case 'u':    /* utmp file request */
  165.             requtmpfl = optarg;
  166.             break;
  167.         case 'q':    /* don't expand */
  168.             quick = 1;
  169.             break;
  170.         case 'l':    /* line length */
  171.             if(*optarg == 't') {
  172.                 linelen = termlen(optarg);
  173.             } else if((linelen = atoi(optarg)) < MINENTLEN + 2) {
  174.                 errexit(1, ": improper line length\n");
  175.             }
  176.             break;
  177.         case 'c':    /* set columns */
  178.             if((colreq = atoi(optarg)) < 1) {
  179.                 errexit(1, ": improper column count\n");
  180.             }
  181.             break;
  182.         case 't':    /* terminal order output */
  183.             termord = 1;
  184.             break;
  185.         case '?':    /* bad option */
  186.         default:    /* error */
  187.             usage();
  188.         }
  189.     }
  190.  
  191.     offset = gmtoffset();
  192.     linelen = linelen ? linelen : DEFLINELEN;
  193.  
  194.     if((i = open(requtmpfl ? requtmpfl : utmpfl, 0)) < 0) {
  195.         errexit(1, ": can't open utmp file\n");
  196.     }
  197.  
  198.     if((entries = (size = fdsize(i))/SZ_UTMP) == 0) {
  199.         /*
  200.         **    no users
  201.         */
  202.         exit(0);
  203.     }
  204.     if(size != entries * SZ_UTMP) {
  205.         errexit(2, ": bad utmp file\n");
  206.     }
  207.  
  208.     puu = pu = (struct utmp *) malloc((unsigned) size);
  209.     ppp = pp = (struct plus *) malloc((unsigned) (entries * SZ_PLUS));
  210.     if(!pu || !pp) {
  211.         errexit(2, ": no memory for utmp entries\n");
  212.     }
  213.     if(read(i, (char *) pu, size) != size) {
  214.         errexit(2, ": read error on utmp file\n");
  215.     }
  216.     (void) close(i);
  217.  
  218.     /*
  219.     **    initialize plus entries, ignoring null utmp entries, then sort
  220.     */
  221.     puu += entries;                        /* start at end */
  222.     entries = 0;                        /* forget... */
  223.     while(puu-- > pu) {                    /* easier to go backwards */
  224.         if(puu->ut_name[0]) {            /* active entry */
  225.             ppp->al = (char *) 0;        /* no alias yet */
  226.             (ppp++)->pu = puu;            /* set utmp pointer */
  227.             ++entries;                    /* active count */
  228.         }
  229.     }
  230.     ppmax = ppp;                        /* at end */
  231.  
  232.     if(entries == 0) {
  233.         /*
  234.         **    there were inactive utmp entries,
  235.         **    but no active entries (yes, it happens)
  236.         */
  237.         exit(0);
  238.     }
  239.  
  240.     if(!quick || !termord) {
  241.         /*
  242.         **    sort by login necessary for alias expansion
  243.         **    default if terminal order not requested
  244.         */
  245.         qsort((char *) pp, entries, SZ_PLUS, logsort);
  246.     }
  247.  
  248.     /*
  249.     **    if alias file, expand aliases
  250.     */
  251.     if(!quick && (aliasbuf = getaliases())) {
  252.         /*
  253.         **    expand aliases for login names
  254.         **    found in the alias file
  255.         */
  256.         expand(pp, ppmax, aliasbuf);
  257.  
  258.         if(termord) {
  259.             /*
  260.             **    put back in terminal order
  261.             */
  262.             qsort((char *) pp, entries, SZ_PLUS, termsort);
  263.         } else {
  264.             /*
  265.             **    sort by name to be printed
  266.             **    (alias if one, login otherwise)
  267.             */
  268.             qsort((char *) pp, entries, SZ_PLUS, alsort);
  269.         }
  270.     }
  271.  
  272.     /*
  273.     **    take extreme pains to format the output
  274.     **        variable number of columns
  275.     **        ragged right (Raytheon standard)
  276.     **        uses as much of line as possible
  277.     **        minimum character output (no tabs)
  278.     **        at least MINSPACES spaces between columns
  279.     **        sorted alphabetically by user name down columns
  280.     **        single write
  281.     */
  282.     entrylen = 0;
  283.     for(ppp = pp;ppp < ppmax;++ppp) {
  284.         if(ppp->al) {
  285.             /*
  286.             **    check alias length
  287.             */
  288.             i = strlen(ppp->al);
  289.         } else {
  290.             /*
  291.             **    check login length
  292.             */
  293.             pb = ppp->pu->ut_name;
  294.             for(i = 0;*pb++ && i < SZ_UTNAME;++i)
  295.                 ;
  296.         }
  297.  
  298. #ifdef    hashosts
  299.         /*
  300.         **    add room for remote host
  301.         */
  302.         if(hosts && ppp->pu->ut_host[0]) {
  303.             size = i + 3;    /* room for parens and space */
  304.             pb = ppp->pu->ut_host;
  305.             for(i = 0;*pb++ && i < SZ_UTHOST;++i)
  306.                 ;
  307.             i += size;
  308.         }
  309. #endif    hashosts
  310.  
  311.         entrylen = (entrylen < i) ? i : entrylen;
  312.     }
  313.  
  314.     if(colreq == 1 || entries == 1) {
  315.         /*
  316.         **    single-column is always valid
  317.         */
  318.         cols = 1;
  319.     } else {
  320.         /*
  321.         **    here's the basic equation for formatting
  322.         **        linelen = (cols - 1) * (entrylen + spaces) + entrylen
  323.         **    solve for columns using
  324.         **        entrylen = maxnamelen + MINENTLEN
  325.         **        spaces = MINSPACES
  326.         **    then solve for entrylen with
  327.         **        spaces = MINSPACES
  328.         **    then solve for spaces
  329.         */
  330.         spaces = MINSPACES;
  331.         entrylen += MINENTLEN;
  332.  
  333.         if(linelen <= entrylen) {
  334.             cols = 1;
  335.         } else {
  336.             cols = 1 + (linelen - entrylen) / (entrylen + spaces);
  337.         }
  338.  
  339.         /*
  340.         **    if there is a column request
  341.         **    check it's validity
  342.         */
  343.         if(colreq && cols > colreq) {
  344.             /*
  345.             **    use number of columns requested 
  346.             */
  347.             cols = colreq;
  348.         }
  349.     }
  350.  
  351.     if(cols == 1) {
  352.         /*
  353.         **    simpler processing for single-column mode
  354.         */
  355.         if(!(pb = linebuf = malloc((unsigned)(entries * entrylen)))) {
  356.             errexit(2, ": no memory for output buffer\n");
  357.         }
  358.  
  359.         for(ppp = pp;ppp < ppmax;++ppp) {
  360.             entryend = pb + linelen;
  361.  
  362.             buftty(ppp->pu->ut_line);
  363.             buftime(ppp->pu->ut_time);
  364.             bufname(ppp);
  365. #ifdef    hashosts
  366.             bufhost(ppp);
  367. #endif    hashosts
  368.  
  369.             /*
  370.             **    truncate if necessary, add newline
  371.             */
  372.             pb = (pb >= entryend) ? (entryend - 1) : pb;
  373.             *pb++ = '\n';
  374.         }
  375.     } else {
  376.         int        lines, linenum, colnum, lastcol, maxcols;
  377.  
  378.         /*
  379.         **    multi-column mode
  380.         **    (can't have more columns than entries)
  381.         */
  382.         cols = (cols > entries) ? entries : cols;
  383.         entrylen = (linelen - (cols - 1) * spaces) / cols;
  384.         spaces = (linelen - cols * entrylen) / (cols - 1);
  385.         lines = entries / cols;
  386.         if(lastcol = entries % cols) {
  387.             ++lines;
  388.         } else {
  389.             lastcol = cols;
  390.         }
  391.  
  392.         if(!(pb = linebuf = malloc((unsigned) (lines * linelen)))) {
  393.             errexit(2, ": no memory for output buffer\n");
  394.         }
  395.  
  396.         maxcols = cols;
  397.         for(linenum = 0;linenum < lines;) {
  398.             ppp = pp + linenum;
  399.             for(colnum = 0;;) {
  400.                 entryend = pb + spaces + entrylen;
  401.  
  402.                 buftty(ppp->pu->ut_line);
  403.                 buftime(ppp->pu->ut_time);
  404.                 bufname(ppp);
  405. #ifdef    hashosts
  406.                 bufhost(ppp);
  407. #endif    hashosts
  408.  
  409.                 if(++colnum < maxcols) {
  410.                     ppp += (colnum <= lastcol) ? lines : (lines - 1);
  411.                     while(pb < entryend) {
  412.                         *pb++ = ' ';
  413.                     }
  414.                     continue;
  415.                 }
  416.                 break;
  417.             }
  418.  
  419.             *pb++ = '\n';
  420.             if(++linenum == lines - 1) {
  421.                 maxcols = lastcol;
  422.             }
  423.         }
  424.     }
  425.  
  426.     size = (int) (pb - linebuf);
  427.     if(write(1, linebuf, size) != size) {
  428.         errexit(2, ": output write error\n");
  429.     }
  430.  
  431.     exit(0);
  432. }
  433.  
  434. /*
  435. **    logsort: sort plus entries by login first, time second
  436. */
  437. logsort(pp0, pp1)
  438. struct plus       *pp0;
  439. struct plus       *pp1;
  440. {
  441.     int            val;
  442.  
  443.     /*
  444.     **    string compare
  445.     */
  446.     if(val = strncmp(pp0->pu->ut_name, pp1->pu->ut_name, SZ_UTNAME)) {
  447.         return(val);
  448.     }
  449.  
  450.     /*
  451.     **    chronological
  452.     */
  453.     return((int) (pp0->pu->ut_time - pp1->pu->ut_time));
  454. }
  455.  
  456. /*
  457. **    alsort: sort plus entries by alias or ut_name first, time second
  458. */
  459. alsort(pp0, pp1)
  460. struct plus       *pp0;
  461. struct plus       *pp1;
  462. {
  463.     int            val;
  464.  
  465.     /*
  466.     **    string compare
  467.     */
  468.     if(pp0->al && pp1->al) {
  469.         /*
  470.         **    both have aliases -- easy
  471.         */
  472.         if(val = strcmp(pp0->al, pp1->al)) {
  473.             return(val);
  474.         }
  475.     } else {
  476.         /*
  477.         **    one or both missing alias
  478.         */
  479.         if(val = strncmp(
  480.             (pp0->al ? pp0->al : pp0->pu->ut_name),            /* arg0 */
  481.             (pp1->al ? pp1->al : pp1->pu->ut_name),            /* arg1 */
  482.             SZ_UTNAME)
  483.         ) {
  484.             return(val);
  485.         }
  486.  
  487.         /*
  488.         **    different lengths?
  489.         **    assume one with alias is longer
  490.         **    (fast but possibly incorrect?)
  491.         */
  492.         if(val = (int) (pp0->al - pp1->al)) {
  493.             return(val);
  494.         }
  495.     }
  496.  
  497.     /*
  498.     **    chronological
  499.     */
  500.     return((int) (pp0->pu->ut_time - pp1->pu->ut_time));
  501. }
  502.  
  503. /*
  504. **    termsort: sort plus entries by terminal order
  505. */
  506. termsort(pp0, pp1)
  507. struct plus       *pp0;
  508. struct plus       *pp1;
  509. {
  510.     return((int)(pp0->pu - pp1->pu));
  511. }
  512.  
  513. /*
  514. **    getaliases: read alias file into dynamic buffer
  515. */
  516. char *
  517. getaliases()
  518. {
  519.     char       *s;
  520.     char       *b;
  521.     int            fd;
  522.     int            size;
  523.  
  524.     if(reqaliasfl) {
  525.         /*
  526.         **    open requested alias file -- must be readable
  527.         */
  528.         if((fd = open(reqaliasfl, 0)) < 0) {
  529.             errexit(1, ": can't open alias file\n");
  530.         }
  531.     } else {
  532.         /*
  533.         **    try to open default alias file
  534.         */
  535.         if(!(b = getenv("HOME"))) {
  536.             return((char *) 0);
  537.         }
  538.  
  539.         if(!(s = malloc(
  540.             (unsigned) ((size = strlen(b)) + 1 + sizeof(aliasfl))))
  541.             ) {
  542.             errexit(2, ": no memory for path name expansion\n");
  543.         }
  544.  
  545.         (void) strcpy(s, b);
  546.         *(s+size++) = '/';
  547.         (void) strcpy(s+size, aliasfl);
  548.  
  549.         if((fd = open(s, 0)) < 0) {
  550.             /*
  551.             **    can't open for read -- not error
  552.             */
  553.             free(s);
  554.             return((char *) 0);
  555.         }
  556.         free(s);
  557.     }
  558.  
  559.     if((size = fdsize(fd)) < 4) {
  560.         /*
  561.         **    smaller than smallest possible alias -- ignore
  562.         */
  563.         (void) close(fd);
  564.         return((char *) 0);
  565.     }
  566.  
  567.     /*
  568.     **    get null-terminated buffer
  569.     */
  570.     if(!(b = malloc((unsigned) (size + 1)))) {
  571.         errexit(2, ": no memory for alias buffer\n");
  572.     }
  573.     *(b+size) = '\0';
  574.  
  575.     if(read(fd, b, size) != size) {
  576.         errexit(2, ": read error on alias file\n");
  577.     }
  578.     (void) close(fd);
  579.  
  580.     return(b);
  581. }
  582.  
  583. /*
  584. **    fdsize: return size of file corresponding to fd
  585. */
  586. fdsize(fd)
  587. int        fd;
  588. {
  589.     struct stat        st;
  590.  
  591.     if(fstat(fd, &st) < 0) {
  592.         errexit(2, ": fstat failed in fdsize\n");
  593.     }
  594.  
  595.     return((int) st.st_size);
  596. }
  597.  
  598. /*
  599. **    expand: expand alias names
  600. */
  601. expand(ppp, ppmax, buf)
  602. register struct plus    *ppp;
  603. register struct plus    *ppmax;
  604. register char            *buf;
  605. {
  606.     register int    next;
  607.     register int    cmp;
  608.     char            *login;
  609.     char            *alias;
  610.  
  611.     next = 1;
  612.     while(ppp < ppmax) {
  613.         if(next) {
  614.             if(!(buf = logalias(buf, &login, &alias))) {
  615.                 /*
  616.                 **    no more list entries
  617.                 */
  618.                 return;
  619.             }
  620.         }
  621.  
  622.         if((cmp = strncmp(login, ppp->pu->ut_name, SZ_UTNAME)) < 0) {
  623.             /*
  624.             **    no match, keep utmp entry, continue in list
  625.             */
  626.             next = 1;
  627.             continue;
  628.         }
  629.  
  630.         if(cmp > 0) {
  631.             /*
  632.             **    no match, next utmp entry, too far in list
  633.             */
  634.             ++ppp;
  635.             next = 0;
  636.             continue;
  637.         }
  638.  
  639.         /*
  640.         **    matched, next utmp entry, try to match this list entry again
  641.         */
  642.         (ppp++)->al = alias;
  643.         next = 0;
  644.     }
  645. }
  646.  
  647. /*
  648. **    logalias: get pointer to login and alias
  649. **        return NULL if end of buffer, new position otherwise
  650. **        buf must point to first character in line
  651. */
  652. char *
  653. logalias(buf, plogin, palias)
  654. register char    *buf;
  655. char            **plogin;
  656. char            **palias;
  657. {
  658.     register int    inword;
  659.  
  660.     inword = 0;
  661.     while(!inword) {                    /* find beginning of login */
  662.         switch(*buf) {
  663.         case '\r':                        /* empty lines -- OK */
  664.         case '\n':                        /* empty lines -- OK */
  665.         WHITESPACE:                        /* white space at beginning */
  666.             ++buf;
  667.             break;
  668.         case '\0':                        /* end of buffer */
  669.             return((char *) 0);
  670.         default:                        /* found login */
  671.             inword = 1;
  672.             *plogin = buf;                /* save login */
  673.             break;
  674.         }
  675.     }
  676.  
  677.     while(inword) {                        /* find end of login */
  678.         switch(*buf) {
  679.         WHITESPACE:                        /* end of login */
  680.             inword = 0;
  681.             *buf++ = '\0';                /* null terminate */
  682.             break;
  683.         case '\0':                        /* end of buffer */
  684.             return((char *) 0);
  685.         default:                        /* still in login */
  686.             ++buf;
  687.             break;
  688.         }
  689.     }
  690.  
  691.     while(!inword) {                    /* find beginning of alias */
  692.         switch(*buf) {
  693.         WHITESPACE:                        /* still in white space */
  694.             ++buf;
  695.             break;
  696.         case '\0':                        /* end of buffer */
  697.             return((char *) 0);
  698.         default:                        /* beginning of alias */
  699.             inword = 1;
  700.             *palias = buf;                /* save alias */
  701.             break;
  702.         }
  703.     }
  704.  
  705.     while(inword) {                        /* find end of alias */
  706.         switch(*buf) {
  707.         case '\r':                        /* end of line */
  708.         case '\n':                        /* end of line */
  709.             inword = 0;                    /* end of alias */
  710.             *buf++ = '\0';                /* null terminate */
  711.             break;
  712.         case '\0':                        /* end of buffer */
  713.             inword = 0;                    /* end of alias */
  714.             break;                        /* leave buffer here (OK) */
  715.         default:                        /* still in alias */
  716.             if(*buf < ' ' || *buf > '~') {    /* ASCII */
  717.                 /*
  718.                 **    replace control characters
  719.                 **    (they screw up output format)
  720.                 */
  721.                 *buf = ' ';                /* replace with space */
  722.             }
  723.             ++buf;
  724.             break;
  725.         }
  726.     }
  727.  
  728.     return(buf);                        /* return buffer position */
  729. }
  730.  
  731. /*
  732. **    usage: print a usage message and exit
  733. */
  734. usage()
  735. {
  736.     static char    preamble[]        = "usage -- ";
  737.     static char    postamble[]        =
  738.         " [-q] [-t] [-c columns] [-l length] [-a aliasfl]\n";
  739.  
  740. #ifdef    hashosts
  741.     static char    hostamble[]    = " [-h]";
  742. #endif    hashosts
  743.  
  744.     (void) write(2, preamble, sizeof(preamble) - 1);
  745.     (void) write(2, progname, proglen);
  746. #ifdef    hashosts
  747.     (void) write(2, hostamble, sizeof(hostamble) - 1);
  748. #endif    hashosts
  749.     (void) write(2, postamble, sizeof(postamble) - 1);
  750.  
  751.     exit(1);
  752. }
  753.  
  754. /*
  755. **    gmtoffset: get seconds offset from gmt
  756. */
  757. long
  758. gmtoffset()
  759. {
  760. #ifdef    bsd42
  761.  
  762.     struct timeval        tv;
  763.     struct timezone        tz;
  764.     struct tm           *pt;
  765.     struct tm           *localtime();
  766.  
  767.     (void) gettimeofday(&tv, &tz);
  768.     pt = localtime((time_t *) &tv.tv_sec);
  769.  
  770.     return(tz.tz_minuteswest * 60 - (pt->tm_isdst ? 3600 : 0));
  771.  
  772. #else    bsd42
  773.  
  774.     struct timeb        tb;
  775.     struct tm           *pt;
  776.     struct tm           *localtime();
  777.  
  778.     (void) ftime(&tb);
  779.     pt = localtime(&tb.time);
  780.  
  781.     return((long) tb.timezone * 60 - (pt->tm_isdst ? 3600 : 0));
  782.  
  783. #endif    bsd42
  784. }
  785.  
  786. /*
  787. **    timefmt: format the time string
  788. **        this routine is ASCII dependent
  789. */
  790. char *
  791. timefmt(tim)
  792. long    tim;
  793. {
  794.     static char    tbf[8];
  795.     int            t;
  796.     int            tens;
  797.  
  798.     (void) strcpy(tbf, " 00:00 ");            /* initialize */
  799.  
  800.     if((t = (tim / 3600) % 24) > 9) {        /* hours */
  801.         tbf[1] = (tens = t / 10) + '0';
  802.         tbf[2] = t - (tens * 10) + '0';        /* mult faster than mod */
  803.     } else {
  804.         tbf[1] = ' ';                        /* leading space */
  805.         tbf[2] = t + '0';
  806.     }
  807.  
  808.     if((t = (tim / 60) % 60) > 9) {            /* minutes */
  809.         tbf[4] = (tens = t / 10) + '0';
  810.         tbf[5] = t - (tens * 10) + '0';        /* mult faster than mod */
  811.     } else {
  812.         tbf[4] = '0';                        /* leading zero */
  813.         tbf[5] = t + '0';
  814.     }
  815.  
  816.     return(tbf);
  817. }
  818.  
  819. /*
  820. **    termlen: get line length from termcap
  821. **        if arg contains '=' use following as
  822. **        terminal name, else use $TERM
  823. */
  824. termlen(arg)
  825. char   *arg;
  826. {
  827.     char   *name;
  828.     char    tbuf[1024];
  829.     int        len;
  830.  
  831.     if((name = index(arg, '=')) == (char *)0) {
  832.         name = getenv("TERM");
  833.     } else {
  834.         ++name;
  835.     }
  836.  
  837.     if(name != (char *)0) {
  838.         if(tgetent(tbuf, name) == 1) {
  839.             if((len = tgetnum("co")) > 0) {
  840.                 return(tgetflag("am") ? len - 1 : len);        /* avoid wrap */
  841.             }
  842.         }
  843.     }
  844.  
  845.     return(DEFLINELEN);
  846. }
  847.