Commit 3fb87f63 3fb87f63f3dc9dbbcf3cecc2a9ef2c85bd007204 by Sergey Poznyakoff

Document null streams

* doc/texinfo/nullstream.texi: New file.

* mh/mh_init.c (mh_real_install): Use mu_stream_t instead of
FILE.
* mh/mh_whatnow.c (_whatnow): Likewise.
1 parent c0220170
1 @c This is part of the GNU Mailutils manual.
2 @c Copyright (C) 2010 Free Software Foundation, Inc.
3 @c See file mailutils.texi for copying conditions.
4 @c *******************************************************************
5
6 @section Null stream
7 @cindex null stream
8 @cindex stream, null
9
10 A @dfn{null stream} is similar to the system @file{/dev/null} device.
11 It is not connected to any particular physical storage. Any data
12 written to such stream are irrevocably lost. Reading from such a
13 stream returns fixed data, depending on the mode of the stream.
14
15 An instance of the null stream is created using the following function:
16
17 @deffn {stream function} int mu_nullstream_create (mu_stream_t *pstr, int mode)
18 Create an instance of the null stream and return it in the memory
19 location pointed to by @var{pstr}. The @var{mode} argument specifies
20 the access mode for this stream. It can be a binary @samp{or} of the
21 following values:
22
23 @table @code
24 @item MU_STREAM_READ
25 Stream is opened for reading.
26 @item MU_STREAM_WRITE
27 Stream is opened for writing.
28 @end table
29
30 The returned stream is always seekable, so the @code{MU_STREAM_SEEK}
31 mode is implied.
32
33 Any other bits set in the @var{mode} argument are silently ignored.
34 @end deffn
35
36 Writing to an instance of null stream and seeking in such streams
37 always succeeds. If the stream was created with the
38 @code{MU_STREAM_READ} on, the reads from it normally behave as if
39 it were connected to an endless source of zero bytes, i.e. each call
40 to:
41
42 @smallexample
43 mu_stream_read (str, buf, size, &n);
44 @end smallexample
45
46 @noindent
47 results in filling @var{buf} with @var{size} zeroes. This is similar
48 to reading from the system @file{/dev/zero} device@footnote{Note that
49 the internal implementation of @dfn{null streams} has nothing to do
50 with @file{/dev/null}, or @file{/dev/zero}. We refer to these devices
51 only to illustrate the behavior of @dfn{null streams}.}.
52
53 This is the default behavior when reading. It can be altered using
54 the following @dfn{ioctls}.
55
56 @defvr {ioctl} MU_IOCTL_NULLSTREAM_SET_PATTERN
57 Set the @dfn{pattern} for reads. The argument is a pointer to
58 @dfn{struct mu_nullstream_pattern}, defined as:
59
60 @smallexample
61 @group
62 struct mu_nullstream_pattern
63 @{
64 char *pattern; /* Return pattern */
65 size_t size; /* Number of bytes in pattern */
66 @};
67 @end group
68 @end smallexample
69
70 The @samp{pattern} member points to @samp{size} bytes of data which
71 are returned cyclically at each read. For example, suppose that
72 @var{str} is a null stream instance, and consider the following code:
73
74 @smallexample
75 struct mu_nullstream_pattern pat;
76 char buf[16];
77 size_t n;
78
79 pat.pattern = "01234567";
80 pat.size = 8;
81 mu_stream_ioctl (str, MU_IOCTL_NULLSTREAM_SET_PATTERN, &pat);
82
83 mu_stream_read (str, buf, sizeof (buf), &n);
84 @end smallexample
85
86 Then, after the call to @code{mu_stream_read}, we will have:
87
88 @smallexample
89 @group
90 n @result{} 16
91 buf @result{} "0123456701234567"
92 @end group
93 @end smallexample
94
95 Similarly, the following code:
96
97 @smallexample
98 mu_stream_seek (str, 3, MU_SEEK_SET, NULL);
99 mu_stream_read (str, buf, sizeof (buf), &n);
100 @end smallexample
101
102 @noindent
103 will yield:
104
105 @smallexample
106 @group
107 n @result{} 16
108 buf @result{} "3456701234567012"
109 @end group
110 @end smallexample
111
112 The default behavior corresponds to the following initialization:
113
114 @smallexample
115 @group
116 pat.pattern = "";
117 pat.size = 1;
118 mu_stream_ioctl (str, MU_IOCTL_NULLSTREAM_SET_PATTERN, &pat);
119 @end group
120 @end smallexample
121
122 Calling the @samp{MU_IOCTL_NULLSTREAM_SET_PATTERN} with a @samp{NULL}
123 argument causes all subsequent reads from that stream to return @samp{EOF}:
124
125 @smallexample
126 @group
127 mu_stream_ioctl (str, MU_IOCTL_NULLSTREAM_SET_PATTERN, NULL);
128 @dots{}
129 rc = mu_stream_read (str, buf, sizeof (buf), &n);
130
131 rc @result{} 0
132 n @result{} 0
133 @end group
134 @end smallexample
135 @end defvr
136
137 @defvr {ioctl} MU_IOCTL_NULLSTREAM_SET_PATCLASS
138 Set read pattern in terms of @dfn{C character classes}
139 (@FIXME-pxref{C character classes}). Argument is a pointer
140 to an integer containing a bitwise @samp{OR} of the desired
141 character classes from @file{mailutils/cctype.h}. For example,
142 the following code:
143
144 @smallexample
145 int class = MU_CTYPE_DIGIT|MU_CTYPE_XLETR;
146 mu_stream_ioctl (str, MU_IOCTL_NULLSTREAM_SET_PATCLASS, &class);
147 @end smallexample
148
149 @noindent
150 initializes the read pattern to the following string:
151
152 @smallexample
153 0123456789ABCDEFabcdef
154 @end smallexample
155 @end defvr
156
157 Two ioctls are provided to control the @dfn{size} of a null stream
158 available to seek and read operations.
159
160 @defvr {ioctl} MU_IOCTL_NULLSTREAM_SETSIZE
161 Limit the addressable size of a null stream. Argument is a pointer
162 to @samp{mu_off_t} object specifying the new size. The example below
163 limits the stream size to 32 bytes:
164
165 @smallexample
166 @group
167 mu_off_t limit = 32;
168 mu_stream_ioctl (str, MU_IOCTL_NULLSTREAM_SETSIZE, &limit);
169 @end group
170 @end smallexample
171 @end defvr
172
173 Another way to set the null stream size is via the
174 @samp{mu_stream_truncate} function:
175
176 @smallexample
177 mu_stream_truncate (str, 32);
178 @end smallexample
179
180 Setting the stream size to @samp{0} causes all subsequent reads from
181 that stream to return @samp{EOF}. The similar effect has the
182 @samp{MU_IOCTL_NULLSTREAM_SET_PATTERN} ioctl with the @samp{NULL} argument.
183
184 @defvr {ioctl} MU_IOCTL_NULLSTREAM_CLRSIZE
185 Cancel the size limitation imposed by a previous
186 @samp{MU_IOCTL_NULLSTREAM_SETSIZE} ioctl or a call to
187 @samp{mu_stream_truncate}. Argument must be @samp{NULL}.
188 @end defvr
189
190 @menu
191 * null stream usage::
192 @end menu
193
194 @node null stream usage
195 @subsection null stream usage
196
197 Due to their nature, null streams are not among the most used stream
198 flavors. They are mainly useful for testing or measuring purposes.
199
200 The @command{mhn} utility from GNU Mailutils MH suite gives a nice
201 example of using a null stream instance to compute the actual
202 (decoded) size of a part of a MIME message. In the example below
203 we present a simplified version of this code. It defines the
204 function @samp{decoded_size}:
205
206 @deffn {example function} mu_off_t decoded_size (mu_stream_t mime_str, @
207 const char *encoding)
208 Return the size of the decoded input strem. Arguments are:
209
210 @table @var
211 @item mime_str
212 A stream obtained from the MIME part.
213
214 @item encoding
215 Encoding type, as obtained from the @samp{Content-Transfer-Encoding}
216 MIME header.
217 @end table
218
219 @example
220 mu_off_t
221 decoded_size (mu_stream_t mime_str, const char *encoding)
222 @{
223 int rc; /* Result code */
224 mu_stream_t fstr /* Filter stream */
225 mu_stream_stat_buffer stat; /* Statistics buffer */
226 mu_stream_t null; /* Null stream */
227 @end example
228
229 First, create the filter stream for decoding the message part:
230 @example
231 rc = mu_filter_create (&fstr, mime_str, encoding,
232 MU_FILTER_DECODE, MU_STREAM_READ);
233 if (rc)
234 abort ();
235 @end example
236
237 Then, create an instance of the null stream which will act
238 as a receiver:
239
240 @example
241 mu_nullstream_create (&null, MU_STREAM_WRITE);
242 @end example
243
244 The data will be read from @var{fstr} and written to @var{null}.
245 Number of bytes written will give the decoded size of the message.
246 To get this number, attach the statistics buffer to the null
247 stream:
248
249 @example
250 mu_stream_set_stat (null, MU_STREAM_STAT_MASK (MU_STREAM_STAT_OUT),
251 stat);
252 @end example
253
254 The second argument instructs the stream to keep track of the
255 bytes output (i.e. written) to the stream.
256
257 Now, copy the entire contents of @var{fstr} to @var{null}:
258
259 @example
260 rc = mu_stream_copy (null, fstr, 0, NULL);
261 if (rc)
262 abort ();
263 @end example
264
265 When done, destroy both streams (they are not needed any more), and
266 return the value of @samp{MU_STREAM_STAT_OUT} element from @var{stat}:
267
268 @example
269 mu_stream_destroy (&null);
270 mu_stream_destroy (&fstr);
271 return stat[MU_STREAM_STAT_OUT];
272 @}
273 @end example
274
275
276
277
278
279
...@@ -707,14 +707,21 @@ mh_real_install (char *name, int automode) ...@@ -707,14 +707,21 @@ mh_real_install (char *name, int automode)
707 char *home = mu_get_homedir (); 707 char *home = mu_get_homedir ();
708 char *mhdir; 708 char *mhdir;
709 char *ctx; 709 char *ctx;
710 mu_stream_t in;
711 int rc;
710 FILE *fp; 712 FILE *fp;
711 713
714 rc = mu_stdio_stream_create (&in, MU_STDIN_FD, 0);
715 if (rc)
716 {
717 mu_error (_("cannot create input stream: %s"), mu_strerror (rc));
718 exit (1);
719 }
720
712 mhdir = mh_safe_make_file_name (home, "Mail"); 721 mhdir = mh_safe_make_file_name (home, "Mail");
713 722
714 if (!automode) 723 if (!automode)
715 { 724 {
716 size_t n = 0;
717
718 /* TRANSLATORS: This is a question and will be followed 725 /* TRANSLATORS: This is a question and will be followed
719 by question mark on output. */ 726 by question mark on output. */
720 if (mh_getyn_interactive (_("Do you need help"))) 727 if (mh_getyn_interactive (_("Do you need help")))
...@@ -725,7 +732,8 @@ mh_real_install (char *name, int automode) ...@@ -725,7 +732,8 @@ mh_real_install (char *name, int automode)
725 if (!mh_getyn_interactive (_("Do you want the standard MH path \"%s\""), mhdir)) 732 if (!mh_getyn_interactive (_("Do you want the standard MH path \"%s\""), mhdir))
726 { 733 {
727 int local; 734 int local;
728 char *p; 735 char *p, *buf = NULL;
736 size_t size = 0;
729 737
730 /* TRANSLATORS: This is a question and will be followed 738 /* TRANSLATORS: This is a question and will be followed
731 by question mark on output. */ 739 by question mark on output. */
...@@ -734,16 +742,12 @@ mh_real_install (char *name, int automode) ...@@ -734,16 +742,12 @@ mh_real_install (char *name, int automode)
734 printf (_("What is the path? ")); 742 printf (_("What is the path? "));
735 else 743 else
736 printf (_("What is the full path? ")); 744 printf (_("What is the full path? "));
737 if (getline (&p, &n, stdin) <= 0) 745 if (mu_stream_getline (in, &buf, &size, NULL))
738 exit (1);
739
740 n = strlen (p);
741 if (n == 0)
742 exit (1); 746 exit (1);
743 747 p = mu_str_stripws (buf);
744 if (p[n-1] == '\n') 748 if (p > buf)
745 p[n-1] = 0; 749 memmove (buf, p, strlen (p) + 1);
746 750
747 free (mhdir); 751 free (mhdir);
748 if (local) 752 if (local)
749 { 753 {
......
...@@ -323,37 +323,51 @@ static int ...@@ -323,37 +323,51 @@ static int
323 _whatnow (struct mh_whatnow_env *wh, struct action_tab *tab) 323 _whatnow (struct mh_whatnow_env *wh, struct action_tab *tab)
324 { 324 {
325 int rc, status = 0; 325 int rc, status = 0;
326 mu_stream_t in;
327 char *line = NULL;
328 size_t size = 0;
329 struct mu_wordsplit ws;
330 int wsflags = MU_WRDSF_DEFFLAGS|MU_WRDSF_COMMENT;
331
332 rc = mu_stdio_stream_create (&in, MU_STDIN_FD, 0);
333 if (rc)
334 {
335 mu_error (_("cannot create input stream: %s"), mu_strerror (rc));
336 exit (1);
337 }
326 338
327 do 339 do
328 { 340 {
329 char *line = NULL;
330 size_t size = 0;
331 struct mu_wordsplit ws;
332 handler_fp fun; 341 handler_fp fun;
333 342
334 printf ("%s ", wh->prompt); 343 printf ("%s ", wh->prompt);
335 getline (&line, &size, stdin); 344 fflush (stdout);
336 if (!line) 345 status = mu_stream_getline (in, &line, &size, NULL);
337 continue; 346 if (rc)
347 {
348 mu_error (_("cannot read input stream: %s"), mu_strerror (rc));
349 break;
350 }
338 351
339 ws.ws_comment = "#"; 352 ws.ws_comment = "#";
340 rc = mu_wordsplit (line, &ws, MU_WRDSF_DEFFLAGS|MU_WRDSF_COMMENT); 353 rc = mu_wordsplit (line, &ws, wsflags);
341 free (line);
342 if (rc) 354 if (rc)
343 { 355 {
344 mu_error (_("cannot split line `%s': %s"), line, 356 mu_error (_("cannot split line `%s': %s"), line,
345 mu_wordsplit_strerror (&ws)); 357 mu_wordsplit_strerror (&ws));
346 break; 358 break;
347 } 359 }
348 360 wsflags |= MU_WRDSF_REUSE;
349 fun = func (tab, ws.ws_wordv[0]); 361 fun = func (tab, ws.ws_wordv[0]);
350 if (fun) 362 if (fun)
351 rc = fun (wh, ws.ws_wordc, ws.ws_wordv, &status); 363 rc = fun (wh, ws.ws_wordc, ws.ws_wordv, &status);
352 else 364 else
353 rc = 0; 365 rc = 0;
354 mu_wordsplit_free (&ws);
355 } 366 }
356 while (rc == 0); 367 while (rc == 0);
368 if (wsflags & MU_WRDSF_REUSE)
369 mu_wordsplit_free (&ws);
370 free (line);
357 return status; 371 return status;
358 } 372 }
359 373
......