UNPKG

5.59 kBJavaScriptView Raw
1(function() {
2 "use strict";
3
4 var fs = require('fs'),
5 StringDecoder = require('string_decoder').StringDecoder;
6
7 function createLineReader(fd, options, cb) {
8 if (options instanceof Function) {
9 cb = options;
10 options = undefined;
11 }
12 if (!options) options = {};
13
14 var filePosition = 0,
15 encoding = options.encoding || 'utf8',
16 separator = options.separator || /\r\n?|\n/,
17 bufferSize = options.bufferSize || 1024,
18 buffer = new Buffer(bufferSize),
19 bufferStr = '',
20 decoder = new StringDecoder(encoding),
21 closed = false,
22 eof = false,
23 separatorIndex = -1,
24 separatorLen;
25
26 var findSeparator;
27
28 if (separator instanceof RegExp) {
29 findSeparator = function() {
30 var result = separator.exec(bufferStr);
31 if (result && (result.index + result[0].length < bufferStr.length || eof)) {
32 separatorIndex = result.index;
33 separatorLen = result[0].length;
34 } else {
35 separatorIndex = -1;
36 }
37 }
38 } else {
39 separatorLen = separator.length;
40 findSeparator = function() {
41 separatorIndex = bufferStr.indexOf(separator);
42 }
43 }
44
45 function _fd() {
46 return fd;
47 }
48
49 function close(cb) {
50 if (!closed) {
51 fs.close(fd, cb);
52 closed = true;
53 }
54 }
55
56 function isOpen() {
57 return !closed;
58 }
59
60 function isClosed() {
61 return closed;
62 }
63
64 function readToSeparator(cb) {
65 function readChunk() {
66 fs.read(fd, buffer, 0, bufferSize, filePosition, function(err, bytesRead) {
67 if (err) {
68 return cb(err);
69 }
70
71 if (bytesRead < bufferSize) {
72 eof = true;
73 }
74
75 filePosition += bytesRead;
76
77 bufferStr += decoder.write(buffer.slice(0, bytesRead));
78
79 findSeparator();
80
81 if (bytesRead && separatorIndex < 0 && !eof) {
82 readChunk();
83 } else {
84 cb();
85 }
86 });
87 }
88
89 readChunk();
90 }
91
92 function hasNextLine() {
93 return bufferStr.length > 0 || !eof;
94 }
95
96 function nextLine(cb) {
97 if (closed) {
98 return cb(new Error('LineReader has been closed'));
99 }
100
101 function getLine(err) {
102 if (err) {
103 return cb(err);
104 }
105
106 if (separatorIndex < 0 && eof) {
107 separatorIndex = bufferStr.length;
108 }
109 var ret = bufferStr.substring(0, separatorIndex);
110
111 bufferStr = bufferStr.substring(separatorIndex + separatorLen);
112 separatorIndex = -1;
113 cb(undefined, ret);
114 }
115
116 findSeparator();
117
118 if (separatorIndex < 0) {
119 if (eof) {
120 if (hasNextLine()) {
121 separatorIndex = bufferStr.length;
122 getLine();
123 } else {
124 return cb(new Error('No more lines to read.'));
125 }
126 } else {
127 readToSeparator(getLine);
128 }
129 } else {
130 getLine();
131 }
132 }
133
134 readToSeparator(function(err) {
135 if (err) {
136 return close(function(err2) {
137 return cb(err || err2);
138 });
139 }
140 return cb(undefined, {
141 hasNextLine: hasNextLine,
142 nextLine: nextLine,
143 close: close,
144 isOpen: isOpen,
145 isClosed: isClosed,
146 fd: _fd,
147 });
148 });
149 }
150
151 function open(filename, options, cb) {
152 if (options instanceof Function) {
153 cb = options;
154 options = undefined;
155 }
156
157 fs.open(filename, 'r', parseInt('666', 8), function(err, fd) {
158 if (err) {
159 return cb(err);
160 }
161
162 createLineReader(fd, options, cb);
163 });
164 }
165
166 function eachLine(filename, options, iteratee, cb) {
167 if (options instanceof Function) {
168 cb = iteratee;
169 iteratee = options;
170 options = undefined;
171 }
172 var finalFn,
173 asyncIteratee = iteratee.length == 3;
174
175 var theReader;
176 var getReaderCb;
177
178 open(filename, options, function(err, reader) {
179 theReader = reader;
180 if (getReaderCb) {
181 getReaderCb(reader);
182 }
183
184 if (err) {
185 if (cb) cb(err);
186 return;
187 }
188
189 function finish(err) {
190 reader.close(function(err2) {
191 if (cb) cb(err || err2);
192 });
193 }
194
195 function newRead() {
196 if (reader.hasNextLine()) {
197 setImmediate(readNext);
198 } else {
199 finish();
200 }
201 }
202
203 function continueCb(continueReading) {
204 if (continueReading !== false) {
205 newRead();
206 } else {
207 finish();
208 }
209 }
210
211 function readNext() {
212 reader.nextLine(function(err, line) {
213 if (err) {
214 finish(err);
215 }
216
217 var last = !reader.hasNextLine();
218
219 if (asyncIteratee) {
220 iteratee(line, last, continueCb);
221 } else {
222 if (iteratee(line, last) !== false) {
223 newRead();
224 } else {
225 finish();
226 }
227 }
228 });
229 }
230
231 newRead();
232 });
233
234 // this hook is only for the sake of testing; if you choose to use it,
235 // please don't file any issues (unless you can also reproduce them without
236 // using this).
237 return {
238 getReader: function(cb) {
239 if (theReader) {
240 cb(theReader);
241 } else {
242 getReaderCb = cb;
243 }
244 }
245 };
246 }
247
248 module.exports.open = open;
249 module.exports.eachLine = eachLine;
250}());