1/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
2/* If you are missing that file, acquire a complete release at teeworlds.com. */
3#include "linereader.h"
4
5#include <base/dbg.h>
6#include <base/io.h>
7#include <base/str.h>
8
9#include <cstdlib>
10
11CLineReader::CLineReader()
12{
13 m_pBuffer = nullptr;
14}
15
16CLineReader::~CLineReader()
17{
18 free(ptr: m_pBuffer);
19}
20
21bool CLineReader::OpenFile(IOHANDLE File)
22{
23 if(!File)
24 {
25 return false;
26 }
27 char *pBuffer = io_read_all_str(io: File);
28 io_close(io: File);
29 if(pBuffer == nullptr)
30 {
31 return false;
32 }
33 OpenBuffer(pBuffer);
34 return true;
35}
36
37void CLineReader::OpenBuffer(char *pBuffer)
38{
39 dbg_assert(pBuffer != nullptr, "Line reader initialized without valid buffer");
40
41 m_pBuffer = pBuffer;
42 m_BufferPos = 0;
43 m_ReadLastLine = false;
44
45 // Skip UTF-8 BOM
46 if(m_pBuffer[0] == '\xEF' && m_pBuffer[1] == '\xBB' && m_pBuffer[2] == '\xBF')
47 {
48 m_BufferPos += 3;
49 }
50}
51
52const char *CLineReader::Get()
53{
54 dbg_assert(m_pBuffer != nullptr, "Line reader not initialized");
55 if(m_ReadLastLine)
56 {
57 return nullptr;
58 }
59
60 unsigned LineStart = m_BufferPos;
61 while(true)
62 {
63 if(m_pBuffer[m_BufferPos] == '\0' || m_pBuffer[m_BufferPos] == '\n' || (m_pBuffer[m_BufferPos] == '\r' && m_pBuffer[m_BufferPos + 1] == '\n'))
64 {
65 if(m_pBuffer[m_BufferPos] == '\0')
66 {
67 m_ReadLastLine = true;
68 }
69 else
70 {
71 if(m_pBuffer[m_BufferPos] == '\r')
72 {
73 m_pBuffer[m_BufferPos] = '\0';
74 ++m_BufferPos;
75 }
76 m_pBuffer[m_BufferPos] = '\0';
77 ++m_BufferPos;
78 }
79
80 if(!str_utf8_check(str: &m_pBuffer[LineStart]))
81 {
82 // Skip lines containing invalid UTF-8
83 if(m_ReadLastLine)
84 {
85 return nullptr;
86 }
87 LineStart = m_BufferPos;
88 continue;
89 }
90 // Skip trailing empty line
91 if(m_ReadLastLine && m_pBuffer[LineStart] == '\0')
92 {
93 return nullptr;
94 }
95 return &m_pBuffer[LineStart];
96 }
97 ++m_BufferPos;
98 }
99}
100