Path: utzoo!censor!geac!jtsv16!uunet!cs.utexas.edu!tut.cis.ohio-state.edu!bloom-beacon!SUN.COM!argv From: argv@SUN.COM (Dan Heller) Newsgroups: comp.windows.x Subject: Re: redirecting stderr; passing arg lists Message-ID: <8911081827.AA12003@turnpike.sun.com> Date: 8 Nov 89 18:27:10 GMT Organization: The Internet Lines: 72 > On Nov 8, 10:01am, Bill Wohler wrote: > i want to redirect all of the output going to stderr and have it go > to a X toolkit widget instead of the controlling window. i figured > a > XtAddInput(fileno(stderr), XtInputReadMask|XtInputWriteMask, > output_procedure, output_widget); If you've been reading comp.windows.x, you'll see a very short-lived discussion about how XtAddInput is broken --I recommend *strongly* that you avoid using it till it's fixed. This includes R4; I checked the source. The problem is that your routine gets called continuously rather than, as the spec says, "when there is data to be read." (details below.) You can redirect stderr of a process to a widget quite simply :-) int stderr_pipe[2]; ... pipe(stderr_pipe); /* set up a pipe */ dup2(stderr_pipe[1], fileno(stderr)); /* redirect stderr to pipe */ Now your stderr has been closed and any writing to stderr will now go thru the pipe. You have to read the data from the other end of the pipe. If you want to continue to use FILE *'s to read stderr, then you can do the following: FILE *fp = fdopen(stderr_pipe[0], "r"); /* read the other end of pipe */ Now you can do things like' fgets(buf, sizeof buf, fp); Note, this is buffered IO now, so you might want to setbuf(fp, NULL). Now that you have stderr redirected to a place you can read from, you need to determine when there is data to be read. As you noted, the best way to do it would be using XtAddInput() almost as you had it above. Just remove the "WriteMask" becuase you're not writing to the filedesc. XtAddInput(stderr_pipe[0], XtInputReadMask, widget_output_procedure, output_widget); The problem here is that widget_output_procedure() is going to be called continuously. So, instead, I recommend that you set up a timer to time out evewry 5 seconds or so using XtAddTimeOut(...). (I'm not going to go into the details about how to use this function.) When your callback function from the timer is called, simply check if there is stuff to read: ioctl(stderr_pipe[0], FIONREAD, &stuff_to_read); If (stuff_to_read == 0), then return. NOTE: you should do that in the callback routine specified in XtAddInput anyway! This is what Xt should be doing before it decides to call your callback routine (which is why it's "broken"). ----------- > as a workaround, i then replaced all my fprintf(stderrs) with a > function call to write directly to the widget. > output_procedure("%s: cannot open %s.", "mycommand", filename); void output_procedure(va_alist) va_dcl { char *fmt, buf[BUFSIZ]; va_list args; va_start(args); fmt = va_arg(args, char *); (void) vsprintf(buf, fmt, args); va_end(args); WidgetSet(output_widget, XtNstring, buf, NULL); } > a six-pack of weitzen to the person who solves problem 2 and a case > each of weitzen and pils to the person who solves problem 1. thanks!!! I'll be in your neighborhood around xmas time -- I'm looking forward to those Pils! --dan