3 * wmh.c -- window front-end to nmh
7 * This code is Copyright (c) 2002, by the authors of nmh. See the
8 * COPYRIGHT file in the root directory of the nmh distribution for
9 * complete copyright information.
14 * Pass signals to client during execution
16 * Figure out a way for the user to say how big the Scan/Display
17 * windows should be, and where all the windows should be.
21 #include <h/signals.h>
32 #define ALARM ((unsigned int) 10)
33 #define PAUSE ((unsigned int) 2)
35 #define SZ(a) (sizeof a / sizeof a[0])
37 static struct swit switches[] = {
39 { "prompt string", 6 },
41 { "vmhproc program", 7 },
51 static int PEERpid = NOTOK;
53 static jmp_buf PEERctx;
57 static int dfd = NOTOK;
58 static int twd = NOTOK;
59 static char *myprompt = "(%s) ";
88 struct line *w_bottom;
90 char w_buffer[BUFSIZ];
96 static WINDOW *Status;
97 static WINDOW *Display;
98 static WINDOW *Command;
102 WINDOW *windows[NWIN + 1];
107 #ifdef HAVE_TERMIOS_H
108 static struct termios tio;
109 # define ERASE tio.c_cc[VERASE]
110 # define KILL tio.c_cc[VKILL]
111 # define INTR tio.c_cc[VINTR]
113 # ifdef HAVE_TERMIO_H
114 static struct termio tio;
115 # define ERASE tio.c_cc[VERASE]
116 # define KILL tio.c_cc[VKILL]
117 # define INTR tio.c_cc[VINTR]
119 static struct sgttyb tio;
120 static struct tchars tc;
121 # define ERASE tio.sg_erase
122 # define KILL tio.sg_kill
123 # define INTR tc.t_intrc
124 # define EOFC tc.t_eofc
128 #define WERASC ltc.t_werasc
129 static struct ltchars ltc;
133 int ALRMser (), PIPEser (), SIGser ();
134 int ADJser (), REFser ();
139 static void adorn(char *, char *, ...);
143 main (int argc, char **argv)
145 int vecp = 1, nprog = 0;
146 char *cp, buffer[BUFSIZ], **argp;
147 char **arguments, *vec[MAXARGS];
150 setlocale(LC_ALL, "");
152 invo_name = r1bindex (argv[0], '/');
154 /* read user profile/context */
157 arguments = getarguments (invo_name, argc,argv, 1);
160 while ((cp = *argp++))
162 switch (smatch (++cp, switches)) {
164 ambigsw (cp, switches);
171 snprintf (buffer, sizeof(buffer), "%s [switches for vmhproc]",
173 print_help (buffer, switches, 1);
176 print_version(invo_name);
180 if (!(myprompt = *argp++) || *myprompt == '-')
181 adios (NULL, "missing argument to %s", argp[-2]);
185 if (!(vmhproc = *argp++) || *vmhproc == '-')
186 adios (NULL, "missing argument to %s", argp[-2]);
196 if (WINinit (nprog) == NOTOK) {
199 vec[0] = r1bindex (vmhproc, '/');
200 execvp (vmhproc, vec);
201 adios (vmhproc, "unable to exec");
203 PEERinit (vecp, vec);
214 char buffer[BUFSIZ], prompt[BUFSIZ];
217 pLOOP (RC_QRY, NULL);
219 snprintf (prompt, sizeof(prompt), myprompt, invo_name);
221 switch (WINgetstr (Command, prompt, buffer)) {
226 done (0); /* NOTREACHED */
230 pLOOP (RC_CMD, buffer);
239 PEERinit (int vecp, char *vec[])
241 int pfd0[2], pfd1[2];
242 char buf1[BUFSIZ], buf2[BUFSIZ];
245 SIGNAL (SIGPIPE, PIPEser);
247 if (pipe (pfd0) == NOTOK || pipe (pfd1) == NOTOK)
248 adios ("pipe", "unable to");
249 switch (PEERpid = vfork ()) {
251 adios ("vfork", "unable to");/* NOTREACHED */
254 for (w = windows; *w; w++)
255 if ((*w)->w_fd != NOTOK)
260 vec[vecp++] = "-vmhread";
261 snprintf (buf1, sizeof(buf1), "%d", pfd1[0]);
263 vec[vecp++] = "-vmhwrite";
264 snprintf (buf2, sizeof(buf2), "%d", pfd0[1]);
268 SIGNAL (SIGINT, SIG_DFL);
269 SIGNAL (SIGQUIT, SIG_DFL);
270 SIGNAL (SIGTERM, SIG_DFL);
272 vec[0] = r1bindex (vmhproc, '/');
273 execvp (vmhproc, vec);
275 _exit (-1); /* NOTREACHED */
281 rcinit (pfd0[0], pfd1[1]);
291 char *bp, buffer[BUFSIZ];
292 struct record rcs, *rc;
298 /* Get buffer ready to go */
300 buflen = sizeof(buffer);
302 snprintf (bp, buflen, "%d %d", RC_VRSN, numwins);
307 for (w = windows; *w; w++) {
308 snprintf (bp, buflen, " %d", (*w)->w_height);
314 switch (str2rc (RC_INI, buffer, rc)) {
320 adios (NULL, "%s", rc->rc_data);
322 adios (NULL, "pINI peer error");
325 adios (NULL, "%s", rc->rc_data);
328 adios (NULL, "pINI protocol screw-up");
335 pLOOP (char code, char *str)
338 struct record rcs, *rc;
344 str2peer (code, str);
346 switch (peer2rc (rc)) {
348 if (pTTY () == NOTOK)
353 if (sscanf (rc->rc_data, "%d", &i) != 1
356 fmt2peer (RC_ERR, "no such window \"%s\"", rc->rc_data);
359 if ((w = windows[i - 1])->w_flags & W_CMND) {
360 fmt2peer (RC_ERR, "not a display window \"%s\"", rc->rc_data);
363 if (pWIN (w) == NOTOK)
372 adorn (NULL, "%s", rc->rc_data);
374 adorn (NULL, "pLOOP(%s) peer error",
375 code == RC_QRY ? "QRY" : "CMD");
380 adorn (NULL, "%s", rc->rc_data);
382 i = pidwait (PEERpid, OK);
387 adios (NULL, "%s", rc->rc_data);
390 adios (NULL, "pLOOP(%s) protocol screw-up",
391 code == RC_QRY ? "QRY" : "CMD");
399 SIGNAL_HANDLER hstat, istat, qstat, tstat;
400 struct record rcs, *rc;
405 if (ChangeWindowDepth (dfd, twd, 0) == NOTOK)
406 adios ("failed", "ChangeWindowDepth");
408 /* should block here instead of ignore */
409 hstat = SIGNAL (SIGHUP, SIG_IGN);
410 istat = SIGNAL (SIGINT, SIG_IGN);
411 qstat = SIGNAL (SIGQUIT, SIG_IGN);
412 tstat = SIGNAL (SIGTERM, SIG_IGN);
414 rc2rc (RC_ACK, 0, NULL, rc);
416 SIGNAL (SIGHUP, hstat);
417 SIGNAL (SIGINT, istat);
418 SIGNAL (SIGQUIT, qstat);
419 SIGNAL (SIGTERM, tstat);
421 switch (rc->rc_type) {
423 rc2peer (RC_ACK, 0, NULL);
428 adorn (NULL, "%s", rc->rc_data);
430 adorn (NULL, "pTTY peer error");
434 adios (NULL, "%s", rc->rc_data);
437 adios (NULL, "pTTY protocol screw-up");
448 if ((i = pWINaux (w)) == OK)
460 register struct line *lp, *mp;
461 struct record rcs, *rc;
466 for (lp = w->w_head; lp; lp = mp) {
471 w->w_head = w->w_top = w->w_bottom = w->w_tail = NULL;
475 switch (rc2rc (RC_ACK, 0, NULL, rc)) {
477 for (bp = rc->rc_data, n = rc->rc_len; n-- > 0; )
482 rc2peer (RC_ACK, 0, NULL);
489 adorn (NULL, "%s", rc->rc_data);
491 adorn (NULL, "pWIN peer error");
495 adios (NULL, "%s", rc->rc_data);
498 adios (NULL, "pWIN protocol screw-up");
512 rc2peer (RC_FIN, 0, NULL);
515 switch (setjmp (PEERctx)) {
517 SIGNAL (SIGALRM, ALRMser);
520 status = pidwait (PEERpid, OK);
526 kill (PEERpid, SIGKILL);
537 /* should dynamically determine all this stuff from gconfig... */
539 #define MyX 20 /* anchored hpos */
540 #define MyY 40 /* .. vpos */
541 #define MyW 800 /* .. width */
542 #define MyH 500 /* .. height */
543 #define MyS 30 /* .. height for Status, about one line */
546 #define MySlop 45 /* slop */
548 #define EWIDTH 25 /* Width of vertical EBAR */
549 #define ESLOP 5 /* .. slop */
552 static intWINinit (nprog) {
553 short wx, wy, wh, sy;
556 if (GetGraphicsConfig (fileno (stderr), &gc) == NOTOK)
560 adios (NULL, "not a window");
562 if ((dfd = open ("/dev/ttyw0", O_RDWR)) == NOTOK)
563 adios ("/dev/ttyw0", "unable to open");
565 if ((twd = GetTopWindow (dfd)) == NOTOK)
566 adios ("failed", "GetTopWindow");
568 BlockRefreshAdjust (1);
572 wx = gc.w - (MyX + MyW + EWIDTH + ESLOP);
573 Scan = WINnew (wx, wy = MyY, MyW, wh = MyH * 2 / 3, "Scan", W_EBAR);
576 Status = WINnew (wx, sy = wy, MyW, wh = MyS, "Status", W_FAKE);
579 Display = WINnew (wx, wy, MyW, MyH, "Display", W_EBAR);
581 Command = WINnew (wx, sy, MyW, MyS, invo_name, W_CMND);
583 windows[numwins] = NULL;
590 WINnew (short wx, short wy, short ww, short wh, char *name, int flags)
594 if ((w = (WINDOW *) calloc (1, sizeof *w)) == NULL)
595 adios (NULL, "unable to allocate window");
597 if ((w->w_flags = flags) & W_FAKE) {
604 if (w->w_flags & W_EBAR)
605 ww += EWIDTH + ESLOP;
607 wx += EWIDTH + ESLOP;
609 if ((w->w_fd = OpenWindow (wx, wy, ww, wh, name)) == NOTOK)
610 adios ("failed", "OpenWindow");
611 if ((w->w_wd = GetTopWindow (dfd)) == NOTOK)
612 adios ("failed", "GetTopWindow");
613 if (GetWindowState (w->w_fd, &w->w_ws) == NOTOK)
614 adios ("failed", "GetWindowState");
615 if (SetLineDisc (w->w_fd, TWSDISC) == NOTOK)
616 adios ("failed", "SetLineDisc");
618 SetBuf (w->w_fd, 1024);
619 SetAdjust (w->w_fd, numwins, ADJser);
620 SetRefresh (w->w_fd, numwins, REFser);
622 SetAddressing (w->w_fd, VT_ABSOLUTE);
624 if (w->w_flags & W_EBAR) {
625 w->w_eb = CreateElevatorBar (w->w_fd, 0, 0, EWIDTH,
626 w->w_ws.height, VT_Gray50, 1, EB_VERTICAL,
627 EB_ARROWS, w->w_ebloc = 0, w->w_ebsize = EB_MAX,
630 adios (NULL, "CreateElevatorBar failed");
631 RefreshElevatorBar (w->w_eb);
634 if ((w->w_cbase = CharacterBaseline (w->w_ws.font)) <= 0)
637 if ((w->w_cheight = CharacterHeight (w->w_ws.font)) <= 0)
639 w->w_height = w->w_ws.height / w->w_cheight;
644 if ((w->w_cwidth = CharacterWidth (w->w_ws.font, 'm')) <= 0)
646 w->w_width = (w->w_ws.width - (w->w_eb ? (EWIDTH + ESLOP) : 0))
652 windows[numwins++] = w;
659 WINgetstr (WINDOW *w, char *prompt, char *buffer)
662 register char *bp, *ip;
665 register struct vtseq *vt = &vts;
668 adios (NULL, "internal error--elevator bar found");
670 if (w->w_head == NULL
671 && (w->w_head = (struct line *) calloc (1, sizeof *w->w_head))
673 adios (NULL, "unable to allocate line storage");
674 w->w_head->l_buf = image;
675 w->w_top = w->w_bottom = w->w_tail = w->w_head;
677 if (ChangeWindowDepth (dfd, w->w_wd, 0) == NOTOK)
678 adios ("failed", "ChangeWindowDepth");
680 strncpy (image, prompt, sizeof(image));
681 bp = ip = image + strlen (image);
686 switch (getvtseq (w->w_fd, vt)) {
688 DisplayStatus (w->w_fd, "no hardkeys, please");
692 switch (c = toascii (vt->u.ascii)) {
693 case '\f': /* refresh? */
703 adorn (NULL, "Interrupt");
732 } while (isspace (*bp) && bp > ip);
736 } while (!isspace (*bp) && bp > buffer);
743 if (c < ' ' || c >= '\177')
753 switch (vt->u.mouse.buttons
754 & (VT_MOUSE_LEFT | VT_MOUSE_MIDDLE | VT_MOUSE_RIGHT)) {
756 DisplayStatus (w->w_fd, "use middle or right button");
759 #define WPOP "WMH\0Advance\0Burst\0Exit\0EOF\0"
760 case VT_MOUSE_MIDDLE:
761 SetPosition (w->w_fd, vt->u.mouse.x,
763 switch (DisplayPopUp (w->w_fd, WPOP)) {
764 case 1: /* Advance */
766 strcpy (buffer, "advance");
770 strcpy (buffer, "burst");
774 strcpy (buffer, "exit");
780 default: /* failed or none taken */
792 adios (NULL, "end-of-file on window");/* NOTREACHED */
795 DisplayStatus (w->w_fd, "unknown VT sequence");
802 WINputc (WINDOW *w, char c)
806 register struct line *lp;
811 if (WINputc (w, 'M') == NOTOK || WINputc (w, '-') == NOTOK)
816 if (c < ' ' || c == '\177') {
817 if (WINputc (w, '^') == NOTOK)
824 for (i = 8 - (w->w_bufpos & 0x07); i > 0; i--)
825 if (WINputc (w, ' ') == NOTOK)
839 w->w_buffer[w->w_bufpos++] = c;
843 w->w_buffer[w->w_bufpos] = NULL;
846 if ((lp = (struct line *) calloc (1, sizeof *lp)) == NULL)
847 adios (NULL, "unable to allocate line storage");
849 lp->l_no = (w->w_tail ? w->w_tail->l_no : 0) + 1;
850 lp->l_buf = getcpy (w->w_buffer);
851 for (cp = lp->l_buf + strlen (lp->l_buf) - 1; cp >= lp->l_buf; cp--)
857 if (w->w_head == NULL)
859 if (w->w_top == NULL)
861 if (w->w_bottom == NULL)
864 w->w_tail->l_next = lp;
865 lp->l_prev = w->w_tail;
874 static char mylineno[5];
876 static bool cancel[] = { 1 };
877 static struct choice mychoices[] = { LABEL, "cancel", VT_White };
879 static struct question myquestions[] = {
880 STRING, "Line", SZ (mylineno), (struct choice *) 0,
882 TOGGLE, "", SZ (mychoices), mychoices
885 static struct menu mymenu = { "Goto", SZ (myquestions), myquestions };
887 static int *myanswers[] = { (int *) mylineno, (int *) cancel };
893 int clear, pos, forw, refresh;
895 register struct vtseq *vt = &vts;
897 if (w->w_fd == NOTOK) {
899 DisplayStatus (dfd, w->w_top->l_buf);
906 if (ChangeWindowDepth (dfd, w->w_wd, 0) == NOTOK)
907 adios ("failed", "ChangeWindowDepth");
911 if (w->w_bottom == w->w_tail)
915 adios (NULL, "internal error--no elevator bar");
917 for (clear = refresh = 0, forw = 1;;) {
919 RemoveStatus (w->w_fd);
927 switch (getvtseq (w->w_fd, vt)) {
930 DisplayStatus (w->w_fd, "use the mouse");
935 switch (vt->u.mouse.buttons
936 & (VT_MOUSE_LEFT | VT_MOUSE_MIDDLE | VT_MOUSE_RIGHT)) {
938 if ((pos = vt->u.mouse.x) < EWIDTH) {
939 pos = w->w_ebloc = DoElevatorBar (w->w_eb, pos,
941 refresh = WINgoto (w, ((pos * (w->w_tail->l_no
943 / EB_MAX) + w->w_head->l_no);
947 #define WPOP "Paging\0Next\0Prev\0Left\0Right\0First\0Last\0Goto ...\0Exit\0"
948 case VT_MOUSE_MIDDLE:
949 SetPosition (w->w_fd, vt->u.mouse.x,
951 switch (DisplayPopUp (w->w_fd, WPOP)) {
954 if (w->w_bottom == w->w_tail)
956 refresh = WINgoto (w, w->w_bottom->l_no + 1 - PSLOP);
961 if (w->w_top == w->w_head)
963 refresh = WINgoto (w, w->w_top->l_no
964 - w->w_height + PSLOP);
969 DisplayStatus (w->w_fd, "not yet");
975 refresh = WINgoto (w, w->w_head->l_no);
980 refresh = WINgoto (w, w->w_tail->l_no
984 case 7: /* Goto ... */
985 snprintf (mylineno, sizeof(mylineno),
986 "%d", w->w_top->l_no);
988 if (PresentMenu (&mymenu, myanswers)
991 if (sscanf (mylineno, "%d", &pos) != 1) {
992 DisplayStatus (w->w_fd, "bad format");
996 if (pos < w->w_head->l_no
997 || pos > w->w_tail->l_no) {
998 DisplayStatus (w->w_fd, "no such line");
1002 refresh = WINgoto (w, pos);
1008 default: /* failed or none taken */
1014 case VT_MOUSE_RIGHT:
1016 if (w->w_bottom == w->w_tail)
1027 adios (NULL, "end-of-file on window");/* NOTREACHED */
1030 DisplayStatus (w->w_fd, "unknown VT sequence");
1039 WINgoto (WINDOW *w, int n)
1042 register struct line *lp;
1044 if (n > (i = w->w_tail->l_no - w->w_height + 1))
1046 if (n < w->w_head->l_no)
1047 n = w->w_head->l_no;
1049 if ((i = n - (lp = w->w_head)->l_no)
1050 > (j = abs (n - w->w_top->l_no)))
1051 i = j, lp = w->w_top;
1053 if (i > (j = abs (w->w_tail->l_no - n)))
1054 i = j, lp = w->w_tail;
1056 if (n >= lp->l_no) {
1057 for (; lp; lp = lp->l_next)
1062 for (; lp; lp = lp->l_prev)
1079 ADJser (int id, short ww, short wh)
1083 if (id < 0 || id >= numwins)
1084 adios (NULL, "ADJser on bogus window (%d)", id);
1086 if (w->w_fd == NOTOK)
1087 adios (NULL, "ADJser on closed window (%d)", id);
1089 w->w_ws.width = w->w_ws.tw = ww;
1090 w->w_ws.height = w->w_ws.th = wh;
1093 DeleteElevatorBar (w->w_eb);
1094 w->w_eb = CreateElevatorBar (w->w_fd, 0, 0, EWIDTH,
1095 w->w_ws.height, VT_Gray50, 1, EB_VERTICAL,
1096 EB_ARROWS, w->w_ebloc = 0, w->w_ebsize = EB_MAX,
1098 if (w->w_eb == NULL)
1099 adios (NULL, "CreateElevatorBar failed");
1107 REFser (int id, short wx, short wy, short ww, short wh)
1109 short cx, cy, cw, ch;
1112 if (id < 0 || id >= numwins)
1113 adios (NULL, "REFser on bogus window (%d)", id);
1115 if (w->w_fd == NOTOK)
1116 adios (NULL, "REFser on closed window (%d)", id);
1119 if (GetWindowState (w->w_fd, &w->w_ws) == NOTOK)
1120 adios ("failed", "GetWindowState");
1122 GetPermanentClipping (w->w_fd, &cx, &cy, &cw, &ch);
1123 SetPermanentClipping (w->w_fd, wx, wy, ww, wh);
1125 SetPermanentClipping (w->w_fd, cx, cy, cw, ch);
1130 Redisplay (WINDOW *w, int doeb)
1134 register struct line *lp;
1136 if (w->w_fd == NOTOK)
1139 sx = w->w_eb ? (EWIDTH + ESLOP) : 0;
1140 w->w_height = w->w_ws.height / w->w_cheight;
1141 if (w->w_height < 1)
1144 w->w_width = (w->w_ws.width - (w->w_eb ? (EWIDTH + ESLOP) : 0))
1149 SetPosition (w->w_fd, sx, 0);
1150 SetColor (w->w_fd, VT_White);
1151 PaintRectangleInterior (w->w_fd, w->w_ws.width, w->w_ws.height);
1154 SetColor (w->w_fd, VT_Black);
1155 for (lp = w->w_top, y = 0;
1156 lp && y < w->w_height;
1157 w->w_bottom = lp, lp = lp->l_next, y++) {
1158 SetPosition (w->w_fd, sx, y * w->w_cheight + w->w_cbase);
1159 PaintString (w->w_fd, VT_STREND, lp->l_buf);
1164 if ((y = EB_LOC (w)) != w->w_ebloc)
1165 MoveElevator (w->w_eb, w->w_ebloc = y);
1166 if ((y = EB_SIZE (w)) != w->w_ebsize)
1167 SizeElevator (w->w_eb, w->w_ebsize = y);
1169 RefreshElevatorBar (w->w_eb);
1181 if (w->w_head == NULL)
1184 if ((i = w->w_tail->l_no - w->w_head->l_no) <= 0)
1187 return (((w->w_bottom->l_no - w->w_top->l_no) * EB_MAX) / i);
1196 if (w->w_head == NULL)
1199 if ((i = w->w_tail->l_no - w->w_head->l_no) <= 0)
1202 return (((w->w_top->l_no - w->w_head->l_no) * EB_MAX) / i);
1211 if (ioctl (fileno (stdin), TIOCGETP, (char *) &tio) == NOTOK)
1212 adios ("failed", "ioctl TIOCGETP");
1213 if (ioctl (fileno (stdin), TIOCGETC, (char *) &tc) == NOTOK)
1214 adios ("failed", "ioctl TIOCGETC");
1215 if (ioctl (fileno (stdin), TIOCGLTC, (char *) <c) == NOTOK)
1216 adios ("failed", "ioctl TIOCGLTC");
1219 SIGNAL (SIGHUP, SIGser);
1220 SIGNAL (SIGINT, SIGser);
1221 SIGNAL (SIGQUIT, SIGser);
1230 SIGNAL_HANDLER tstat;
1232 if ((pgrp = getpgrp (0)) == NOTOK)
1233 adios ("process group", "unable to determine");
1235 if (ioctl (fileno (stdin), TIOCGPGRP, (char *) &tpgrp) == NOTOK)
1236 adios ("tty's process group", "unable to determine");
1240 tstat = SIGNAL (SIGTTIN, SIG_DFL);
1242 SIGNAL (SIGTTIN, tstat);
1245 SIGNAL (SIGTTIN, SIG_IGN);
1246 SIGNAL (SIGTTOU, SIG_IGN);
1247 SIGNAL (SIGTSTP, SIG_IGN);
1256 SIGNAL (SIGTTIN, SIG_DFL);
1257 SIGNAL (SIGTTOU, SIG_DFL);
1258 SIGNAL (SIGTSTP, SIG_DFL);
1266 longjmp (PEERctx, DONE);
1273 #ifndef RELIABLE_SIGNALS
1274 SIGNAL (sig, SIG_IGN);
1277 adios (NULL, "lost peer");
1284 #ifndef RELIABLE_SIGNALS
1285 SIGNAL (sig, SIG_IGN);
1301 return 1; /* dead code to satisfy the compiler */
1306 adorn (char *what, char *fmt, ...)
1315 advertise (what, NULL, fmt, ap);
1323 advertise (char *what, char *tail, va_list ap)
1326 char buffer[BUFSIZ], err[BUFSIZ];
1327 struct iovec iob[20];
1328 register struct iovec *iov = iob;
1334 iov->iov_len = strlen (iov->iov_base = invo_name);
1336 iov->iov_len = strlen (iov->iov_base = ": ");
1340 vsnprintf (buffer, sizeof(buffer), fmt, ap);
1341 iov->iov_len = strlen (iov->iov_base = buffer);
1345 iov->iov_len = strlen (iov->iov_base = " ");
1347 iov->iov_len = strlen (iov->iov_base = what);
1349 iov->iov_len = strlen (iov->iov_base = ": ");
1352 if (!(iov->iov_base = strerror (eindex))) {
1353 snprintf (err, sizeof(err), "unknown error %d", eindex);
1354 iov->iov_base = err;
1356 iov->iov_len = strlen (iov->iov_base);
1359 if (tail && *tail) {
1360 iov->iov_len = strlen (iov->iov_base = ", ");
1362 iov->iov_len = strlen (iov->iov_base = tail);
1365 iov->iov_len = strlen (iov->iov_base = "\n");
1369 DisplayVector (iob, iov - iob);
1371 writev (fileno (stderr), iob, iov - iob);
1376 DisplayVector (struct iovec *iov, int n)
1380 char buffer[BUFSIZ];
1382 for (i = 0, cp = NULL; i < n; i++, iov++) {
1383 snprintf (buffer, sizeof(buffer), "%*.*s", iov->iov_len,
1384 iov->iov_len, iov->iov_base);
1385 cp = add (buffer, cp);
1388 DisplayStatus (dfd, cp);