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