1 /*
2  * Copyright (C) 2020 Linux Studio Plugins Project <https://lsp-plug.in/>
3  *           (C) 2020 Vladimir Sadovnikov <sadko4u@gmail.com>
4  *
5  * This file is part of lsp-plugins
6  * Created on: 16 июн. 2018 г.
7  *
8  * lsp-plugins is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU Lesser General Public License as published by
10  * the Free Software Foundation, either version 3 of the License, or
11  * any later version.
12  *
13  * lsp-plugins is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with lsp-plugins. If not, see <https://www.gnu.org/licenses/>.
20  */
21 
22 #include <errno.h>
23 #include <core/io/charset.h>
24 #include <core/io/StdioFile.h>
25 #include <core/io/NativeFile.h>
26 #include <core/io/InFileStream.h>
27 #include <core/io/InSequence.h>
28 
29 #define CBUF_SIZE        0x1000
30 #define BBUF_SIZE        0x4000
31 
32 namespace lsp
33 {
34     namespace io
35     {
InSequence()36         InSequence::InSequence()
37         {
38             pIS         = NULL;
39             nWrapFlags  = 0;
40         }
41 
~InSequence()42         InSequence::~InSequence()
43         {
44             // Close file descriptor
45             if (pIS != NULL)
46             {
47                 if (nWrapFlags & WRAP_CLOSE)
48                     pIS->close();
49                 if (nWrapFlags & WRAP_DELETE)
50                     delete pIS;
51                 pIS         = NULL;
52             }
53             nWrapFlags  = 0;
54 
55             // Close decoder
56             sDecoder.close();
57         }
58 
close()59         status_t InSequence::close()
60         {
61             status_t res = STATUS_OK;
62 
63             // Close file descriptor
64             if (pIS != NULL)
65             {
66                 if (nWrapFlags & WRAP_CLOSE)
67                     res = pIS->close();
68                 if (nWrapFlags & WRAP_DELETE)
69                     delete pIS;
70                 pIS         = NULL;
71             }
72             nWrapFlags  = 0;
73 
74             // Close decoder
75             sDecoder.close();
76 
77             // Return result
78             return set_error(res);
79         }
80 
wrap(FILE * fd,bool close,const char * charset)81         status_t InSequence::wrap(FILE *fd, bool close, const char *charset)
82         {
83             if (pIS != NULL)
84                 return set_error(STATUS_BAD_STATE);
85             else if (fd == NULL)
86                 return set_error(STATUS_BAD_ARGUMENTS);
87 
88             // Create input file stream
89             InFileStream *is = new InFileStream();
90             status_t res = is->wrap(fd, close);
91             if (res != STATUS_OK)
92             {
93                 is->close();
94                 delete is;
95                 return set_error(res);
96             }
97 
98             // Wrap input file stream
99             res = wrap(is, WRAP_CLOSE | WRAP_DELETE, charset);
100             if (res != STATUS_OK)
101             {
102                 is->close();
103                 delete is;
104                 return set_error(res);
105             }
106 
107             return set_error(res);
108         }
109 
wrap_native(lsp_fhandle_t fd,bool close,const char * charset)110         status_t InSequence::wrap_native(lsp_fhandle_t fd, bool close, const char *charset)
111         {
112             if (pIS != NULL)
113                 return set_error(STATUS_BAD_STATE);
114 
115             // Create input file stream
116             InFileStream *is = new InFileStream();
117             status_t res = is->wrap_native(fd, close);
118             if (res != STATUS_OK)
119             {
120                 is->close();
121                 delete is;
122                 return set_error(res);
123             }
124 
125             // Wrap input file stream
126             res = wrap(is, WRAP_CLOSE | WRAP_DELETE, charset);
127             if (res != STATUS_OK)
128             {
129                 is->close();
130                 delete is;
131                 return set_error(res);
132             }
133 
134             return set_error(res);
135         }
136 
wrap(File * fd,size_t flags,const char * charset)137         status_t InSequence::wrap(File *fd, size_t flags, const char *charset)
138         {
139             if (pIS != NULL)
140                 return set_error(STATUS_BAD_STATE);
141             else if (fd == NULL)
142                 return set_error(STATUS_BAD_ARGUMENTS);
143 
144             // Create input file stream
145             InFileStream *is = new InFileStream();
146             status_t res = is->wrap(fd, flags);
147             if (res != STATUS_OK)
148             {
149                 is->close();
150                 delete is;
151                 return set_error(res);
152             }
153 
154             // Wrap input file stream
155             res = wrap(is, WRAP_CLOSE | WRAP_DELETE, charset);
156             if (res != STATUS_OK)
157             {
158                 is->close();
159                 delete is;
160                 return set_error(res);
161             }
162 
163             return set_error(res);
164         }
165 
wrap(IInStream * is,size_t flags,const char * charset)166         status_t InSequence::wrap(IInStream *is, size_t flags, const char *charset)
167         {
168             if (pIS != NULL)
169                 return set_error(STATUS_BAD_STATE);
170             else if (is == NULL)
171                 return set_error(STATUS_BAD_ARGUMENTS);
172 
173             // Initialize decoder
174             status_t res = sDecoder.init(charset);
175             if (res != STATUS_OK)
176             {
177                 sDecoder.close();
178                 return set_error(res);
179             }
180 
181             // Store pointers
182             pIS         = is;
183             nWrapFlags  = flags;
184 
185             return set_error(STATUS_OK);
186         }
187 
open(const char * path,const char * charset)188         status_t InSequence::open(const char *path, const char *charset)
189         {
190             if (pIS != NULL)
191                 return set_error(STATUS_BAD_STATE);
192             else if (path == NULL)
193                 return set_error(STATUS_BAD_ARGUMENTS);
194 
195             LSPString tmp;
196             if (!tmp.set_utf8(path))
197                 return set_error(STATUS_NO_MEM);
198             return open(&tmp, charset);
199         }
200 
open(const LSPString * path,const char * charset)201         status_t InSequence::open(const LSPString *path, const char *charset)
202         {
203             if (pIS != NULL)
204                 return set_error(STATUS_BAD_STATE);
205             else if (path == NULL)
206                 return set_error(STATUS_BAD_ARGUMENTS);
207 
208             // Create input file stream
209             InFileStream *is = new InFileStream();
210             status_t res = is->open(path);
211             if (res != STATUS_OK)
212             {
213                 is->close();
214                 delete is;
215                 return set_error(res);
216             }
217 
218             // Wrap input file stream
219             res = wrap(is, WRAP_CLOSE | WRAP_DELETE, charset);
220             if (res != STATUS_OK)
221             {
222                 is->close();
223                 delete is;
224                 return set_error(res);
225             }
226 
227             return set_error(res);
228         }
229 
open(const Path * path,const char * charset)230         status_t InSequence::open(const Path *path, const char *charset)
231         {
232             return open(path->as_string(), charset);
233         }
234 
read(lsp_wchar_t * dst,size_t count)235         ssize_t InSequence::read(lsp_wchar_t *dst, size_t count)
236         {
237             if (pIS == NULL)
238                 return -set_error(STATUS_CLOSED);
239 
240             // Clear line buffer
241             sLine.clear();
242 
243             size_t n_read = 0;
244             while (n_read < count)
245             {
246                 // Try to fetch data
247                 ssize_t fetched = sDecoder.fetch(dst, count - n_read);
248                 if (fetched > 0)
249                 {
250                     n_read     += fetched;
251                     dst        += fetched;
252                     continue;
253                 }
254 
255                 // No data to fetch? Try to fill buffer
256                 ssize_t filled  = sDecoder.fill(pIS);
257                 if (filled > 0)
258                     continue;
259 
260                 // Nothing to do more? Skip any errors if there was data processed
261                 if (n_read > 0)
262                     break;
263 
264                 // Analyze errors
265                 if (fetched < 0)
266                     return -set_error(-fetched);
267                 else if (filled < 0)
268                     return -set_error(-filled);
269 
270                 set_error(STATUS_OK);
271                 break;
272             }
273 
274             return n_read;
275         }
276 
read_internal()277         lsp_swchar_t InSequence::read_internal()
278         {
279             // Try to fetch character
280             lsp_swchar_t ch = sDecoder.fetch();
281             if (ch < 0)
282             {
283                 // Analyze error
284                 if (ch != -STATUS_EOF)
285                     return -set_error(-ch);
286 
287                 // No data to fetch? Try to fill buffer
288                 ssize_t filled  = sDecoder.fill(pIS);
289                 if (filled < 0)
290                     return -set_error(-filled);
291                 else if (filled == 0)
292                     return -set_error(STATUS_EOF);
293 
294                 // Try to fetch character again
295                 ch  = sDecoder.fetch();
296                 if (ch < 0)
297                     return -set_error(-ch);
298             }
299             return ch;
300         }
301 
read()302         lsp_swchar_t InSequence::read()
303         {
304             if (pIS == NULL)
305                 return -set_error(STATUS_CLOSED);
306 
307             // Clear line buffer
308             sLine.clear();
309             return read_internal();
310         }
311 
read_line(LSPString * s,bool force)312         status_t InSequence::read_line(LSPString *s, bool force)
313         {
314             if (pIS == NULL)
315                 return set_error(STATUS_CLOSED);
316 
317             while (true)
318             {
319                 // Try to fetch character
320                 lsp_swchar_t ch = read_internal();
321                 if (ch < 0)
322                 {
323                     if (ch == -STATUS_EOF)
324                         break;
325                     return set_error(-ch);
326                 }
327 
328                 // End of line?
329                 if (ch == '\n')
330                 {
331                     if (sLine.last() == '\r')
332                         sLine.set_length(sLine.length() - 1);
333                     s->take(&sLine);
334                     return set_error(STATUS_OK);
335                 }
336 
337                 // Append character
338                 if (!sLine.append(lsp_wchar_t(ch)))
339                     return set_error(STATUS_NO_MEM);
340             }
341 
342             // Check force flag
343             if ((force) && (sLine.length() > 0))
344             {
345                 s->take(&sLine);
346                 return set_error(STATUS_OK);
347             }
348 
349             return set_error(STATUS_EOF);
350         }
351 
skip(size_t count)352         ssize_t InSequence::skip(size_t count)
353         {
354             sLine.clear();
355             return IInSequence::skip(count);
356         }
357 
358     }
359 } /* namespace lsp */
360