1 /*******************************************************************************
2 
3         copyright:      Copyright (c) 2004 Kris Bell. All rights reserved
4 
5         license:        BSD style: $(LICENSE)
6 
7         version:        Initial release: January 2006
8 
9         author:         Kris
10 
11 *******************************************************************************/
12 
13 module tango.io.stream.Lines;
14 
15 private import tango.io.model.IConduit;
16 private import tango.io.stream.Iterator;
17 
18 /*******************************************************************************
19 
20         Iterate across a set of text patterns.
21 
22         Each pattern is exposed to the client as a slice of the original
23         content, where the slice is transient. If you need to retain the
24         exposed content, then you should .dup it appropriately.
25 
26         The content exposed via an iterator is supposed to be entirely
27         read-only. All current iterators abide by this rule, but it is
28         possible a user could mutate the content through a get() slice.
29         To enforce the desired read-only aspect, the code would have to
30         introduce redundant copying or the compiler would have to support
31         read-only arrays.
32 
33         See Delimiters, Patterns, Quotes.
34 
35 *******************************************************************************/
36 
37 class Lines(T) : Iterator!(T)
38 {
39         /***********************************************************************
40 
41                 Construct an uninitialized iterator. For example:
42                 ---
43                 auto lines = new Lines!(char);
44 
45                 void somefunc (InputStream stream)
46                 {
47                         foreach (line; lines.set(stream))
48                                  Cout (line).newline;
49                 }
50                 ---
51 
52                 Construct a streaming iterator upon a stream:
53                 ---
54                 void somefunc (InputStream stream)
55                 {
56                         foreach (line; new Lines!(char) (stream))
57                                  Cout (line).newline;
58                 }
59                 ---
60 
61                 Construct a streaming iterator upon a conduit:
62                 ---
63                 foreach (line; new Lines!(char) (new File ("myfile")))
64                          Cout (line).newline;
65                 ---
66 
67         ***********************************************************************/
68 
69         this (InputStream stream = null)
70         {
71                 super (stream);
72         }
73 
74         /***********************************************************************
75 
76                 Read a line of text, and return false when there's no
77                 further content available.
78 
79         ***********************************************************************/
80 
81         final bool readln (ref const(T)[] content)
82         {
83                 content = super.next;
84                 return content.ptr !is null;
85         }
86 
87         /***********************************************************************
88 
89                 Scanner implementation for this iterator. Find a '\n',
90                 and eat any immediately preceeding '\r'.
91 
92         ***********************************************************************/
93 
94         protected override size_t scan (const(void)[] data)
95         {
96                 auto content = (cast(const(T)*) data.ptr) [0 .. data.length / T.sizeof];
97 
98                 foreach (i, T c; content)
99                          if (c is '\n')
100                             {
101                             auto slice = i;
102                             if (i && content[i-1] is '\r')
103                                 --slice;
104                             set (content.ptr, 0, slice, i);
105                             return found (i);
106                             }
107 
108                 return notFound();
109         }
110 }
111 
112 
113 
114 /*******************************************************************************
115 
116 *******************************************************************************/
117 
118 debug(UnitTest)
119 {
120         private import tango.io.device.Array;
121 
122         unittest
123         {
124                 auto p = new Lines!(char) (new Array("blah".dup));
125         }
126 }
127 
128 
129 /*******************************************************************************
130 
131 *******************************************************************************/
132 
133 debug (Lines)
134 {
135         import tango.io.Console;
136         import tango.io.device.Array;
137 
138         void main()
139         {
140                 auto lines = new Lines!(char)(new Array("one\ntwo\r\nthree".dup));
141                 foreach (i, line, delim; lines)
142                          Cout (line) (delim);
143         }
144 }