1*406cdd95SThomas Cort /* $NetBSD: lprint.c,v 1.23 2013/01/18 22:10:31 christos Exp $ */
2*406cdd95SThomas Cort
3*406cdd95SThomas Cort /*
4*406cdd95SThomas Cort * Copyright (c) 1989, 1993
5*406cdd95SThomas Cort * The Regents of the University of California. All rights reserved.
6*406cdd95SThomas Cort *
7*406cdd95SThomas Cort * This code is derived from software contributed to Berkeley by
8*406cdd95SThomas Cort * Tony Nardo of the Johns Hopkins University/Applied Physics Lab.
9*406cdd95SThomas Cort *
10*406cdd95SThomas Cort * Redistribution and use in source and binary forms, with or without
11*406cdd95SThomas Cort * modification, are permitted provided that the following conditions
12*406cdd95SThomas Cort * are met:
13*406cdd95SThomas Cort * 1. Redistributions of source code must retain the above copyright
14*406cdd95SThomas Cort * notice, this list of conditions and the following disclaimer.
15*406cdd95SThomas Cort * 2. Redistributions in binary form must reproduce the above copyright
16*406cdd95SThomas Cort * notice, this list of conditions and the following disclaimer in the
17*406cdd95SThomas Cort * documentation and/or other materials provided with the distribution.
18*406cdd95SThomas Cort * 3. Neither the name of the University nor the names of its contributors
19*406cdd95SThomas Cort * may be used to endorse or promote products derived from this software
20*406cdd95SThomas Cort * without specific prior written permission.
21*406cdd95SThomas Cort *
22*406cdd95SThomas Cort * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23*406cdd95SThomas Cort * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24*406cdd95SThomas Cort * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25*406cdd95SThomas Cort * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26*406cdd95SThomas Cort * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27*406cdd95SThomas Cort * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28*406cdd95SThomas Cort * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29*406cdd95SThomas Cort * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30*406cdd95SThomas Cort * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31*406cdd95SThomas Cort * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32*406cdd95SThomas Cort * SUCH DAMAGE.
33*406cdd95SThomas Cort */
34*406cdd95SThomas Cort
35*406cdd95SThomas Cort #include <sys/cdefs.h>
36*406cdd95SThomas Cort #ifndef lint
37*406cdd95SThomas Cort #if 0
38*406cdd95SThomas Cort static char sccsid[] = "@(#)lprint.c 8.3 (Berkeley) 4/28/95";
39*406cdd95SThomas Cort #else
40*406cdd95SThomas Cort __RCSID( "$NetBSD: lprint.c,v 1.23 2013/01/18 22:10:31 christos Exp $");
41*406cdd95SThomas Cort #endif
42*406cdd95SThomas Cort #endif /* not lint */
43*406cdd95SThomas Cort
44*406cdd95SThomas Cort #include <sys/types.h>
45*406cdd95SThomas Cort #include <sys/stat.h>
46*406cdd95SThomas Cort #include <sys/time.h>
47*406cdd95SThomas Cort #include <fcntl.h>
48*406cdd95SThomas Cort #include <tzfile.h>
49*406cdd95SThomas Cort #include <db.h>
50*406cdd95SThomas Cort #include <err.h>
51*406cdd95SThomas Cort #include <pwd.h>
52*406cdd95SThomas Cort #include <errno.h>
53*406cdd95SThomas Cort #include <unistd.h>
54*406cdd95SThomas Cort #include <stdio.h>
55*406cdd95SThomas Cort #include <string.h>
56*406cdd95SThomas Cort #include <time.h>
57*406cdd95SThomas Cort #include <ctype.h>
58*406cdd95SThomas Cort #include <paths.h>
59*406cdd95SThomas Cort #include <vis.h>
60*406cdd95SThomas Cort
61*406cdd95SThomas Cort #include "utmpentry.h"
62*406cdd95SThomas Cort #include "finger.h"
63*406cdd95SThomas Cort #include "extern.h"
64*406cdd95SThomas Cort
65*406cdd95SThomas Cort #define LINE_LEN 80
66*406cdd95SThomas Cort #define TAB_LEN 8 /* 8 spaces between tabs */
67*406cdd95SThomas Cort #define _PATH_FORWARD ".forward"
68*406cdd95SThomas Cort #define _PATH_PLAN ".plan"
69*406cdd95SThomas Cort #define _PATH_PROJECT ".project"
70*406cdd95SThomas Cort
71*406cdd95SThomas Cort static int demi_print(const char *, int);
72*406cdd95SThomas Cort static void lprint(PERSON *);
73*406cdd95SThomas Cort static int show_text(const char *, const char *, const char *);
74*406cdd95SThomas Cort static void vputc(int);
75*406cdd95SThomas Cort
76*406cdd95SThomas Cort #ifdef __SVR4
77*406cdd95SThomas Cort #define TIMEZONE(a) tzname[0]
78*406cdd95SThomas Cort #else
79*406cdd95SThomas Cort #define TIMEZONE(a) (a)->tm_zone
80*406cdd95SThomas Cort #endif
81*406cdd95SThomas Cort
82*406cdd95SThomas Cort void
lflag_print(void)83*406cdd95SThomas Cort lflag_print(void)
84*406cdd95SThomas Cort {
85*406cdd95SThomas Cort PERSON *pn;
86*406cdd95SThomas Cort int sflag, r;
87*406cdd95SThomas Cort PERSON *tmp;
88*406cdd95SThomas Cort DBT data, key;
89*406cdd95SThomas Cort
90*406cdd95SThomas Cort if (db == NULL)
91*406cdd95SThomas Cort return;
92*406cdd95SThomas Cort
93*406cdd95SThomas Cort for (sflag = R_FIRST;; sflag = R_NEXT) {
94*406cdd95SThomas Cort r = (*db->seq)(db, &key, &data, sflag);
95*406cdd95SThomas Cort if (r == -1)
96*406cdd95SThomas Cort err(1, "db seq");
97*406cdd95SThomas Cort if (r == 1)
98*406cdd95SThomas Cort break;
99*406cdd95SThomas Cort memmove(&tmp, data.data, sizeof tmp);
100*406cdd95SThomas Cort pn = tmp;
101*406cdd95SThomas Cort if (sflag != R_FIRST)
102*406cdd95SThomas Cort putchar('\n');
103*406cdd95SThomas Cort lprint(pn);
104*406cdd95SThomas Cort if (!pplan) {
105*406cdd95SThomas Cort (void)show_text(pn->dir,
106*406cdd95SThomas Cort _PATH_FORWARD, "Mail forwarded to");
107*406cdd95SThomas Cort (void)show_text(pn->dir, _PATH_PROJECT, "Project");
108*406cdd95SThomas Cort if (!show_text(pn->dir, _PATH_PLAN, "Plan"))
109*406cdd95SThomas Cort (void)printf("No Plan.\n");
110*406cdd95SThomas Cort }
111*406cdd95SThomas Cort }
112*406cdd95SThomas Cort }
113*406cdd95SThomas Cort
114*406cdd95SThomas Cort static size_t
visify(char * buf,size_t blen,const char * str)115*406cdd95SThomas Cort visify(char *buf, size_t blen, const char *str)
116*406cdd95SThomas Cort {
117*406cdd95SThomas Cort int len = strnvisx(buf, blen, str, strlen(str), VIS_WHITE|VIS_CSTYLE);
118*406cdd95SThomas Cort if (len == -1) {
119*406cdd95SThomas Cort buf[0] = '\0';
120*406cdd95SThomas Cort return 0;
121*406cdd95SThomas Cort }
122*406cdd95SThomas Cort return len;
123*406cdd95SThomas Cort }
124*406cdd95SThomas Cort
125*406cdd95SThomas Cort static void
fmt_time(char * buf,size_t blen,time_t ti,time_t n)126*406cdd95SThomas Cort fmt_time(char *buf, size_t blen, time_t ti, time_t n)
127*406cdd95SThomas Cort {
128*406cdd95SThomas Cort struct tm *tp = localtime(&ti);
129*406cdd95SThomas Cort if (tp != NULL) {
130*406cdd95SThomas Cort char *t = asctime(tp);
131*406cdd95SThomas Cort char *tzn = TIMEZONE(tp);
132*406cdd95SThomas Cort if (n == (time_t)-1 ||
133*406cdd95SThomas Cort n - ti > SECSPERDAY * DAYSPERNYEAR / 2)
134*406cdd95SThomas Cort snprintf(buf, blen, "%.16s %.4s (%s)", t, t + 20, tzn);
135*406cdd95SThomas Cort else
136*406cdd95SThomas Cort snprintf(buf, blen, "%.16s (%s)", t, tzn);
137*406cdd95SThomas Cort } else
138*406cdd95SThomas Cort snprintf(buf, blen, "[*bad time 0x%llx*]", (long long)ti);
139*406cdd95SThomas Cort }
140*406cdd95SThomas Cort
141*406cdd95SThomas Cort /*
142*406cdd95SThomas Cort * idle time is tough; if have one, print a comma,
143*406cdd95SThomas Cort * then spaces to pad out the device name, then the
144*406cdd95SThomas Cort * idle time. Follow with a comma if a remote login.
145*406cdd95SThomas Cort */
146*406cdd95SThomas Cort static int
print_idle(time_t t,int maxlen,size_t hostlen,size_t ttylen)147*406cdd95SThomas Cort print_idle(time_t t, int maxlen, size_t hostlen, size_t ttylen) {
148*406cdd95SThomas Cort
149*406cdd95SThomas Cort int cpr;
150*406cdd95SThomas Cort struct tm *delta = gmtime(&t);
151*406cdd95SThomas Cort
152*406cdd95SThomas Cort if (delta == NULL)
153*406cdd95SThomas Cort return printf("Bad idle 0x%llx", (long long)t);
154*406cdd95SThomas Cort
155*406cdd95SThomas Cort if (delta->tm_yday == 0 && delta->tm_hour == 0 && delta->tm_min == 0)
156*406cdd95SThomas Cort return 0;
157*406cdd95SThomas Cort
158*406cdd95SThomas Cort cpr = printf("%-*s idle ", (int)(maxlen - ttylen + 1), ",");
159*406cdd95SThomas Cort if (delta->tm_yday > 0) {
160*406cdd95SThomas Cort cpr += printf("%d day%s ", delta->tm_yday,
161*406cdd95SThomas Cort delta->tm_yday == 1 ? "" : "s");
162*406cdd95SThomas Cort }
163*406cdd95SThomas Cort cpr += printf("%d:%02d", delta->tm_hour, delta->tm_min);
164*406cdd95SThomas Cort if (hostlen) {
165*406cdd95SThomas Cort putchar(',');
166*406cdd95SThomas Cort ++cpr;
167*406cdd95SThomas Cort }
168*406cdd95SThomas Cort return cpr;
169*406cdd95SThomas Cort }
170*406cdd95SThomas Cort
171*406cdd95SThomas Cort static void
lprint(PERSON * pn)172*406cdd95SThomas Cort lprint(PERSON *pn)
173*406cdd95SThomas Cort {
174*406cdd95SThomas Cort WHERE *w;
175*406cdd95SThomas Cort int cpr, len, maxlen;
176*406cdd95SThomas Cort int oddfield;
177*406cdd95SThomas Cort char timebuf[128], ttybuf[64], hostbuf[512];
178*406cdd95SThomas Cort size_t ttylen, hostlen;
179*406cdd95SThomas Cort
180*406cdd95SThomas Cort cpr = 0;
181*406cdd95SThomas Cort /*
182*406cdd95SThomas Cort * long format --
183*406cdd95SThomas Cort * login name
184*406cdd95SThomas Cort * real name
185*406cdd95SThomas Cort * home directory
186*406cdd95SThomas Cort * shell
187*406cdd95SThomas Cort * office, office phone, home phone if available
188*406cdd95SThomas Cort * mail status
189*406cdd95SThomas Cort */
190*406cdd95SThomas Cort (void)printf("Login: %-15s\t\t\tName: %s\nDirectory: %-25s",
191*406cdd95SThomas Cort pn->name, pn->realname, pn->dir);
192*406cdd95SThomas Cort (void)printf("\tShell: %-s\n", *pn->shell ? pn->shell : _PATH_BSHELL);
193*406cdd95SThomas Cort
194*406cdd95SThomas Cort if (gflag)
195*406cdd95SThomas Cort goto no_gecos;
196*406cdd95SThomas Cort /*
197*406cdd95SThomas Cort * try and print office, office phone, and home phone on one line;
198*406cdd95SThomas Cort * if that fails, do line filling so it looks nice.
199*406cdd95SThomas Cort */
200*406cdd95SThomas Cort #define OFFICE_TAG "Office"
201*406cdd95SThomas Cort #define OFFICE_PHONE_TAG "Office Phone"
202*406cdd95SThomas Cort oddfield = 0;
203*406cdd95SThomas Cort if (pn->office && pn->officephone &&
204*406cdd95SThomas Cort strlen(pn->office) + strlen(pn->officephone) +
205*406cdd95SThomas Cort sizeof(OFFICE_TAG) + 2 <= 5 * TAB_LEN) {
206*406cdd95SThomas Cort (void)snprintf(timebuf, sizeof(timebuf), "%s: %s, %s",
207*406cdd95SThomas Cort OFFICE_TAG, pn->office, prphone(pn->officephone));
208*406cdd95SThomas Cort oddfield = demi_print(timebuf, oddfield);
209*406cdd95SThomas Cort } else {
210*406cdd95SThomas Cort if (pn->office) {
211*406cdd95SThomas Cort (void)snprintf(timebuf, sizeof(timebuf), "%s: %s",
212*406cdd95SThomas Cort OFFICE_TAG, pn->office);
213*406cdd95SThomas Cort oddfield = demi_print(timebuf, oddfield);
214*406cdd95SThomas Cort }
215*406cdd95SThomas Cort if (pn->officephone) {
216*406cdd95SThomas Cort (void)snprintf(timebuf, sizeof(timebuf), "%s: %s",
217*406cdd95SThomas Cort OFFICE_PHONE_TAG, prphone(pn->officephone));
218*406cdd95SThomas Cort oddfield = demi_print(timebuf, oddfield);
219*406cdd95SThomas Cort }
220*406cdd95SThomas Cort }
221*406cdd95SThomas Cort if (pn->homephone) {
222*406cdd95SThomas Cort (void)snprintf(timebuf, sizeof(timebuf), "%s: %s", "Home Phone",
223*406cdd95SThomas Cort prphone(pn->homephone));
224*406cdd95SThomas Cort oddfield = demi_print(timebuf, oddfield);
225*406cdd95SThomas Cort }
226*406cdd95SThomas Cort if (oddfield)
227*406cdd95SThomas Cort putchar('\n');
228*406cdd95SThomas Cort
229*406cdd95SThomas Cort no_gecos:
230*406cdd95SThomas Cort /*
231*406cdd95SThomas Cort * long format con't:
232*406cdd95SThomas Cort * if logged in
233*406cdd95SThomas Cort * terminal
234*406cdd95SThomas Cort * idle time
235*406cdd95SThomas Cort * if messages allowed
236*406cdd95SThomas Cort * where logged in from
237*406cdd95SThomas Cort * if not logged in
238*406cdd95SThomas Cort * when last logged in
239*406cdd95SThomas Cort */
240*406cdd95SThomas Cort /* find out longest device name for this user for formatting */
241*406cdd95SThomas Cort for (w = pn->whead, maxlen = -1; w != NULL; w = w->next) {
242*406cdd95SThomas Cort visify(ttybuf, sizeof(ttybuf), w->tty);
243*406cdd95SThomas Cort if ((len = strlen(ttybuf)) > maxlen)
244*406cdd95SThomas Cort maxlen = len;
245*406cdd95SThomas Cort }
246*406cdd95SThomas Cort /* find rest of entries for user */
247*406cdd95SThomas Cort for (w = pn->whead; w != NULL; w = w->next) {
248*406cdd95SThomas Cort ttylen = visify(ttybuf, sizeof(ttybuf), w->tty);
249*406cdd95SThomas Cort hostlen = visify(hostbuf, sizeof(hostbuf), w->host);
250*406cdd95SThomas Cort switch (w->info) {
251*406cdd95SThomas Cort case LOGGEDIN:
252*406cdd95SThomas Cort fmt_time(timebuf, sizeof(timebuf), w->loginat, -1);
253*406cdd95SThomas Cort cpr = printf("On since %s on %s", timebuf, ttybuf);
254*406cdd95SThomas Cort
255*406cdd95SThomas Cort cpr += print_idle(w->idletime, maxlen, hostlen,
256*406cdd95SThomas Cort ttylen);
257*406cdd95SThomas Cort
258*406cdd95SThomas Cort if (!w->writable)
259*406cdd95SThomas Cort cpr += printf(" (messages off)");
260*406cdd95SThomas Cort break;
261*406cdd95SThomas Cort case LASTLOG:
262*406cdd95SThomas Cort if (w->loginat == 0) {
263*406cdd95SThomas Cort (void)printf("Never logged in.");
264*406cdd95SThomas Cort break;
265*406cdd95SThomas Cort }
266*406cdd95SThomas Cort fmt_time(timebuf, sizeof(timebuf), w->loginat, now);
267*406cdd95SThomas Cort cpr = printf("Last login %s on %s", timebuf, ttybuf);
268*406cdd95SThomas Cort break;
269*406cdd95SThomas Cort }
270*406cdd95SThomas Cort if (hostlen) {
271*406cdd95SThomas Cort if (LINE_LEN < (cpr + 6 + hostlen))
272*406cdd95SThomas Cort (void)printf("\n ");
273*406cdd95SThomas Cort (void)printf(" from %s", hostbuf);
274*406cdd95SThomas Cort }
275*406cdd95SThomas Cort putchar('\n');
276*406cdd95SThomas Cort }
277*406cdd95SThomas Cort if (pn->mailrecv == -1)
278*406cdd95SThomas Cort printf("No Mail.\n");
279*406cdd95SThomas Cort else if (pn->mailrecv > pn->mailread) {
280*406cdd95SThomas Cort fmt_time(timebuf, sizeof(timebuf), pn->mailrecv, -1);
281*406cdd95SThomas Cort printf("New mail received %s\n", timebuf);
282*406cdd95SThomas Cort fmt_time(timebuf, sizeof(timebuf), pn->mailread, -1);
283*406cdd95SThomas Cort printf(" Unread since %s\n", timebuf);
284*406cdd95SThomas Cort } else {
285*406cdd95SThomas Cort fmt_time(timebuf, sizeof(timebuf), pn->mailread, -1);
286*406cdd95SThomas Cort printf("Mail last read %s\n", timebuf);
287*406cdd95SThomas Cort }
288*406cdd95SThomas Cort }
289*406cdd95SThomas Cort
290*406cdd95SThomas Cort static int
demi_print(const char * str,int oddfield)291*406cdd95SThomas Cort demi_print(const char *str, int oddfield)
292*406cdd95SThomas Cort {
293*406cdd95SThomas Cort static int lenlast;
294*406cdd95SThomas Cort int lenthis, maxlen;
295*406cdd95SThomas Cort
296*406cdd95SThomas Cort lenthis = strlen(str);
297*406cdd95SThomas Cort if (oddfield) {
298*406cdd95SThomas Cort /*
299*406cdd95SThomas Cort * We left off on an odd number of fields. If we haven't
300*406cdd95SThomas Cort * crossed the midpoint of the screen, and we have room for
301*406cdd95SThomas Cort * the next field, print it on the same line; otherwise,
302*406cdd95SThomas Cort * print it on a new line.
303*406cdd95SThomas Cort *
304*406cdd95SThomas Cort * Note: we insist on having the right hand fields start
305*406cdd95SThomas Cort * no less than 5 tabs out.
306*406cdd95SThomas Cort */
307*406cdd95SThomas Cort maxlen = 5 * TAB_LEN;
308*406cdd95SThomas Cort if (maxlen < lenlast)
309*406cdd95SThomas Cort maxlen = lenlast;
310*406cdd95SThomas Cort if (((((maxlen / TAB_LEN) + 1) * TAB_LEN) +
311*406cdd95SThomas Cort lenthis) <= LINE_LEN) {
312*406cdd95SThomas Cort while(lenlast < (4 * TAB_LEN)) {
313*406cdd95SThomas Cort putchar('\t');
314*406cdd95SThomas Cort lenlast += TAB_LEN;
315*406cdd95SThomas Cort }
316*406cdd95SThomas Cort (void)printf("\t%s\n", str); /* force one tab */
317*406cdd95SThomas Cort } else {
318*406cdd95SThomas Cort (void)printf("\n%s", str); /* go to next line */
319*406cdd95SThomas Cort oddfield = !oddfield; /* this'll be undone below */
320*406cdd95SThomas Cort }
321*406cdd95SThomas Cort } else
322*406cdd95SThomas Cort (void)printf("%s", str);
323*406cdd95SThomas Cort oddfield = !oddfield; /* toggle odd/even marker */
324*406cdd95SThomas Cort lenlast = lenthis;
325*406cdd95SThomas Cort return(oddfield);
326*406cdd95SThomas Cort }
327*406cdd95SThomas Cort
328*406cdd95SThomas Cort static int
show_text(const char * directory,const char * file_name,const char * header)329*406cdd95SThomas Cort show_text(const char *directory, const char *file_name, const char *header)
330*406cdd95SThomas Cort {
331*406cdd95SThomas Cort struct stat sb;
332*406cdd95SThomas Cort FILE *fp;
333*406cdd95SThomas Cort int ch, cnt, lastc;
334*406cdd95SThomas Cort char *p;
335*406cdd95SThomas Cort int fd, nr;
336*406cdd95SThomas Cort
337*406cdd95SThomas Cort lastc = 0;
338*406cdd95SThomas Cort (void)snprintf(tbuf, sizeof(tbuf), "%s/%s", directory, file_name);
339*406cdd95SThomas Cort if ((fd = open(tbuf, O_RDONLY)) < 0 || fstat(fd, &sb) ||
340*406cdd95SThomas Cort sb.st_size == 0)
341*406cdd95SThomas Cort return(0);
342*406cdd95SThomas Cort
343*406cdd95SThomas Cort /* If short enough, and no newlines, show it on a single line.*/
344*406cdd95SThomas Cort if (sb.st_size <= (off_t)(LINE_LEN - strlen(header) - 5)) {
345*406cdd95SThomas Cort nr = read(fd, tbuf, sizeof(tbuf));
346*406cdd95SThomas Cort if (nr <= 0) {
347*406cdd95SThomas Cort (void)close(fd);
348*406cdd95SThomas Cort return(0);
349*406cdd95SThomas Cort }
350*406cdd95SThomas Cort for (p = tbuf, cnt = nr; cnt--; ++p)
351*406cdd95SThomas Cort if (*p == '\n')
352*406cdd95SThomas Cort break;
353*406cdd95SThomas Cort if (cnt <= 1) {
354*406cdd95SThomas Cort (void)printf("%s: ", header);
355*406cdd95SThomas Cort for (p = tbuf, cnt = nr; cnt--; ++p)
356*406cdd95SThomas Cort vputc(lastc = (unsigned char)*p);
357*406cdd95SThomas Cort if (lastc != '\n')
358*406cdd95SThomas Cort (void)putchar('\n');
359*406cdd95SThomas Cort (void)close(fd);
360*406cdd95SThomas Cort return(1);
361*406cdd95SThomas Cort }
362*406cdd95SThomas Cort else
363*406cdd95SThomas Cort (void)lseek(fd, 0L, SEEK_SET);
364*406cdd95SThomas Cort }
365*406cdd95SThomas Cort if ((fp = fdopen(fd, "r")) == NULL)
366*406cdd95SThomas Cort return(0);
367*406cdd95SThomas Cort (void)printf("%s:\n", header);
368*406cdd95SThomas Cort while ((ch = getc(fp)) != EOF)
369*406cdd95SThomas Cort vputc(lastc = ch);
370*406cdd95SThomas Cort if (lastc != '\n')
371*406cdd95SThomas Cort (void)putchar('\n');
372*406cdd95SThomas Cort (void)fclose(fp);
373*406cdd95SThomas Cort return(1);
374*406cdd95SThomas Cort }
375*406cdd95SThomas Cort
376*406cdd95SThomas Cort static void
vputc(int ch)377*406cdd95SThomas Cort vputc(int ch)
378*406cdd95SThomas Cort {
379*406cdd95SThomas Cort char visout[5], *s2;
380*406cdd95SThomas Cort
381*406cdd95SThomas Cort if (eightflag || isprint(ch) || isspace(ch)) {
382*406cdd95SThomas Cort (void)putchar(ch);
383*406cdd95SThomas Cort return;
384*406cdd95SThomas Cort }
385*406cdd95SThomas Cort ch = toascii(ch);
386*406cdd95SThomas Cort vis(visout, ch, VIS_SAFE|VIS_NOSLASH, 0);
387*406cdd95SThomas Cort for (s2 = visout; *s2; s2++)
388*406cdd95SThomas Cort (void)putchar(*s2);
389*406cdd95SThomas Cort }
390