1 //=============================================================================
3 * @file imore.cpp (imore stands for indirect more.)
5 * This program demonstrates how to redirect stdout of a parent
6 * process to the stdin of its child process using either unnamed pipe
7 * or named pipes to relay data to subprocess which runs "more" to
8 * display data on the screen. Run imore to see how to use this
11 * Unfortunately, on Win32, this program doesn't use any pipe at all because
12 * using pipes confuses MORE.COM on Win32 and it just acts like "cat" on Unix.
14 * @author Nanbor Wang <nanbor@cs.wustl.edu>
16 //=============================================================================
19 #include "ace/OS_NS_stdio.h"
20 #include "ace/OS_NS_errno.h"
21 #include "ace/OS_NS_unistd.h"
22 #include "ace/OS_NS_fcntl.h"
23 #include "ace/FIFO_Recv.h"
24 #include "ace/FIFO_Send.h"
26 #include "ace/Get_Opt.h"
27 #include "ace/Log_Msg.h"
28 #include "ace/Process.h"
29 #include "ace/Signal.h"
32 #if defined (ACE_WIN32)
33 static const ACE_TCHAR
*executable
= ACE_TEXT("MORE.COM");
35 static const char * executable
= "more"; // I like less better.
36 static const ACE_TCHAR
*rendezvous_dir
= ACE_TEXT("/tmp");
37 static const ACE_TCHAR
*rendezvous_pfx
= ACE_TEXT("imore");
38 #endif /* ACE_WIN32 */
40 static ACE_TCHAR
*fname
= 0; // File you want to view.
41 static int use_named_pipe
= 0; // Do we want to use named pipe?
46 ACE_ERROR ((LM_ERROR
, "Usage: imore [-n|-u] <filename>\n"
47 "\t-n Use named pipe.\n"
48 "\t-u Use unnamed pipe.\n"));
52 parse_args (int argc
, ACE_TCHAR
**argv
)
54 ACE_Get_Opt
get_opt (argc
, argv
, ACE_TEXT("un"));
57 while ((c
= get_opt ()) != -1)
61 case 'n': // We want to use named pipe.
62 #if !defined (ACE_WIN32)
65 ACE_ERROR_RETURN ((LM_ERROR
, "Named pipes not supported on Win32\n"), -1);
66 #endif /* !ACE_WIN32 */
68 case 'u': // Use unnamed pipe.
71 default: // What are you talking about?
77 if (get_opt
.opt_ind () >= argc
) // Do you forget to give me a filename to "more?"
83 fname
= argv
[get_opt
.opt_ind ()]; // Alright.
88 #if !defined (ACE_WIN32) && !defined (ACE_DISABLE_TEMPNAM)
90 setup_named_pipes (ACE_Process_Options
&opt
)
92 // Create a unique temporary name for named pipe.
93 ACE_TCHAR
*rendezvous
= ACE_OS::tempnam (rendezvous_dir
,
100 // Alright, this is indeed strange. Named pipes are meant to be
101 // used for unrelated processes. Because of the constraints in
102 // ACE_Process, I have to pre-open the named pipes here.
103 ACE_FIFO_Recv rfifo
; // read end fifo.
104 ACE_FIFO_Send wfifo
; // write end fifo.
106 // Check if the pipes are created successfully.
107 if (rfifo
.open (rendezvous
) == -1 || wfifo
.open (rendezvous
) == -1)
109 ACE_OS::free (rendezvous
);
110 ACE_ERROR_RETURN ((LM_ERROR
, "%p\n", "fifo.open"), -1);
113 // Remove (rm, del) the file after no one uses it any more.
114 ACE_OS::unlink (rendezvous
);
115 ACE_OS::free (rendezvous
);
117 // Setting up pipe between parent and child process. Use the read
118 // end of the named pipe as child process'es ACE_STDIN.
119 // ACE_Process_Options will keep copies (by dup) of fd's that we
120 // pass in. Notice that we have to specify child process to use
121 // ACE_STDOUT for output explicitly because we'll close it down in
122 // the line after. Child process will use whatever we use to dup2
123 // ACE_STDOUT as its stdout.
124 opt
.set_handles (rfifo
.get_handle (), ACE_STDOUT
);
126 // The previous keep a copy of original ACE_STDOUT fd, now we
127 // can replace ACE_STDOUT of parent process to the write end
128 // of the named pipe.
129 ACE_OS::dup2 (wfifo
.get_handle (), ACE_STDOUT
);
131 // Close unused fd's. Notice ACE_FIFO doesn't close the fd
132 // when it goes out of scope.
139 #if !defined (ACE_WIN32)
141 setup_unnamed_pipe (ACE_Process_Options
&opt
)
143 // Create an unnamed pipe instance.
146 // Check if the pipe is created successfully.
147 if (pipe
.open () == -1)
148 ACE_ERROR_RETURN ((LM_ERROR
, "%p\n", "pipe.open"), -1);
150 // Setting up pipe between parent and child process. Use the pipe
151 // as child process'es ACE_STDIN. ACE_Process_Options will keep
152 // copies (by dup) of fd's that we pass in. Notice that we have to
153 // specify child process to use ACE_STDOUT for output explicitly
154 // because we'll close it down in the line after. Child process
155 // will use whatever we use to dup2 ACE_STDOUT as its stdout.
156 opt
.set_handles (pipe
.read_handle (), ACE_STDOUT
);
158 // The previous keep a copy of original ACE_STDOUT fd, now we
159 // can replace ACE_STDOUT of parent process to the pipe.
160 ACE_OS::dup2 (pipe
.write_handle (), ACE_STDOUT
);
162 // Don't forget to close the unused fd.
168 #if !defined (ACE_WIN32)
170 print_file (ACE_HANDLE infd
)
175 while ((len
= ACE_OS::read (infd
, buffer
, BUFSIZ
)) > 0)
177 if ((ACE_OS::write (ACE_STDOUT
, buffer
, len
) != len
))
181 // I tried to "produce" EPIPE warning to test
182 // the program but never seen one. (odd.)
183 // ACE_ERROR ((LM_ERROR, "\n\nEPIPE\n"));
188 ACE_ERROR_RETURN ((LM_ERROR
, "%p\n", "write"), -1);
198 ACE_TMAIN (int argc
, ACE_TCHAR
*argv
[])
200 // Ignore SIGPIPE signal on Unix platforms in case
201 // child process (more) terminates before we finish
202 // writing to stdout.
203 #if !defined (ACE_WIN32)
204 ACE_Sig_Action
sig_act (SIG_IGN
);
205 if (sig_act
.register_action (SIGPIPE
) == -1)
206 ACE_ERROR_RETURN ((LM_ERROR
, "%p\n", "ACE_Sig_Action::register_action"), -1);
207 #endif /* ACE_WIN32 */
209 // Alright, what you want me to do now?
210 if (::parse_args (argc
, argv
) == -1)
213 // Can I find the file you want?
214 ACE_HANDLE infile
= ACE_OS::open (fname
, O_RDONLY
);
215 if (infile
== ACE_INVALID_HANDLE
)
216 ACE_ERROR_RETURN ((LM_DEBUG
, "%p\n", fname
), -1);
218 ACE_Process new_process
;
220 // The ACE_Process_Options does not need to be enclosed in a block
221 // because it does not close the file handles, the ACE_Process closes
222 // them upon destruction.
223 #if !defined (ACE_WIN32)
224 ACE_Process_Options options
;
228 # if defined (ACE_DISABLE_TEMPNAM)
229 ACE_ERROR_RETURN ((LM_ERROR
,
230 "ACE_DISABLE_TEMPNAM set; can't use named pipes\n"),
233 if (::setup_named_pipes (options
) == -1)
234 ACE_ERROR_RETURN ((LM_ERROR
, "Error, bailing out!\n"), -1);
235 # endif /* ACE_DISABLE_TEMPNAM */
239 if (::setup_unnamed_pipe (options
) == -1)
240 ACE_ERROR_RETURN ((LM_ERROR
, "Error, bailing out!\n"), -1);
243 options
.command_line (ACE_TEXT("%") ACE_TEXT_PRIs
, executable
);
244 if (new_process
.spawn (options
) == -1)
246 int const error_number
= ACE_OS::last_error ();
247 ACE_ERROR_RETURN ((LM_ERROR
, "%p errno = %d.\n",
248 "test_more", error_number
), -1);
251 // write file to ACE_STDOUT.
252 if (::print_file (infile
) == -1)
253 ACE_ERROR_RETURN ((LM_ERROR
, "Error, bailing out!\n"), -1);
255 // Close the STDOUT to inform child eof.
256 ACE_OS::close (ACE_STDOUT
);
258 // We can only pass a file handler directly to child process
259 // otherwise "more" doesn't act quite the way we want. Nonetheless,
260 // if your child process don't need to interact with the terminal,
261 // we can use the exact code for Unixes on NT.
262 ACE_Process_Options options
;
263 options
.command_line (ACE_TEXT("%") ACE_TEXT_PRIs
, executable
);
264 options
.set_handles (infile
);
265 if (new_process
.spawn (options
) == -1)
267 int error
= ACE_OS::last_error ();
268 ACE_ERROR_RETURN ((LM_ERROR
, "%p errno = %d.\n",
269 "test_more", error
), -1);
271 #endif /* ! ACE_WIN32 */
273 // Wait till we are done.
275 new_process
.wait (&status
);
276 ACE_DEBUG ((LM_DEBUG
, "Process exit with status %d\n", status
));
278 ACE_OS::close (infile
);