Line data Source code
1 : #include "wibmod/WIB1/AddressTable.hh"
2 : #include "wibmod/WIB1/AddressTableException.hh"
3 : #include <boost/algorithm/string/case_conv.hpp> //to_upper
4 : #include <boost/regex.hpp> //regex
5 : #include <boost/tokenizer.hpp> //tokenizer
6 : #include <fstream>
7 : #include <stdlib.h> //strtoul & getenv
8 :
9 : #define MAX_FILE_LEVEL 10
10 :
11 : void
12 0 : AddressTable::LoadFile(std::string const& fileName, std::string const& prefix, uint16_t offset)
13 : {
14 :
15 0 : std::ifstream inFile(fileName.c_str());
16 0 : if (!inFile.is_open()) {
17 0 : std::string envBasedFileName = fileName;
18 : // Try to use address table path if it exists
19 0 : if (getenv("WIBMOD_SHARE") != NULL) {
20 0 : envBasedFileName = getenv("WIBMOD_SHARE");
21 0 : envBasedFileName += "/config/WIB1/tables/";
22 0 : envBasedFileName += fileName;
23 0 : inFile.open(envBasedFileName.c_str());
24 : }
25 0 : if (!inFile.is_open()) {
26 0 : BUException::BAD_FILE e;
27 0 : e.Append("File not found: ");
28 0 : e.Append(envBasedFileName.c_str());
29 0 : throw e;
30 0 : }
31 0 : }
32 0 : const size_t bufferSize = 1000;
33 0 : char buffer[bufferSize + 1];
34 0 : memset(buffer, 0, bufferSize + 1);
35 0 : uint32_t lineNumber = 1;
36 0 : while (!inFile.eof()) {
37 0 : inFile.getline(buffer, bufferSize);
38 0 : ProcessLine(std::string(buffer), lineNumber, prefix, offset);
39 0 : lineNumber++;
40 : }
41 0 : }
42 :
43 : void
44 0 : AddressTable::ProcessLine(std::string const& line, size_t lineNumber, std::string const& prefix, uint16_t offset)
45 : {
46 : // First, ignore commments
47 0 : std::string activeLine = line.substr(0, line.find('#'));
48 :
49 : // Tokenize the activeLine
50 0 : boost::char_separator<char> sep(" ");
51 0 : boost::tokenizer<boost::char_separator<char>> tokens(activeLine, sep);
52 0 : boost::tokenizer<boost::char_separator<char>>::iterator itToken = tokens.begin();
53 :
54 : // crate a new Item;
55 0 : Item* item = new Item;
56 0 : size_t iToken = 0;
57 0 : for (; itToken != tokens.end(); itToken++) {
58 0 : switch (iToken) {
59 0 : case 0: { // keep name out of everyone else's scope
60 : // Assign name
61 :
62 0 : std::string name(*itToken);
63 0 : if (!prefix.empty()) {
64 : // we have a prefix to add on to the name
65 0 : name = prefix + std::string(".") + name;
66 : }
67 :
68 0 : while ((name.size() > 0) && ('.' == name[name.size() - 1])) {
69 : // If the trailing entry is a dot (level marker) drop it as this entry just means the prefix
70 0 : name.erase(name.size() - 1);
71 : }
72 :
73 0 : if (name.size() == 0) {
74 : // We have an emtpy name, this is bad and we should throw
75 0 : BUException::INVALID_NAME e;
76 0 : e.Append("Empty name");
77 0 : throw e;
78 0 : }
79 :
80 0 : boost::to_upper(name);
81 :
82 : // Assign the name to this item
83 0 : item->name.assign(name);
84 0 : iToken++;
85 0 : } break;
86 0 : case 1:
87 : // Assign address and apply any offset
88 : // itToken means we don't have to check for size of string
89 0 : item->address = strtoul(itToken->c_str(), NULL, 0) + offset;
90 0 : iToken++;
91 0 : break;
92 0 : case 2:
93 :
94 : // Check if this is an include line
95 0 : if (!isdigit((itToken->c_str()[0]))) {
96 : // we have an include file, append all following tokens together to use as the filename.
97 0 : std::string filename(*itToken);
98 0 : itToken++;
99 0 : while (itToken != tokens.end()) {
100 0 : filename += " ";
101 0 : filename += *itToken;
102 0 : itToken++;
103 : }
104 0 : fileLevel++;
105 0 : if (fileLevel <= MAX_FILE_LEVEL) {
106 0 : LoadFile(filename, item->name, item->address);
107 : } else {
108 0 : BUException::MAX_INCLUDE_FILE_DEPTH e;
109 0 : e.Append("File: ");
110 0 : e.Append(filename);
111 0 : e.Append(" at prefix ");
112 0 : e.Append(item->name);
113 0 : e.Append(" is too deep\n");
114 0 : throw e;
115 0 : }
116 0 : fileLevel--;
117 : // Delete fake partial item
118 0 : delete item;
119 : // return to move on to the next line of the main file
120 0 : return;
121 0 : }
122 :
123 : // Assign mask
124 0 : item->mask = strtoul(itToken->c_str(), NULL, 0);
125 : // stolen from https://graphics.stanford.edu/~seander/bithacks.html#ZerosOnRightParallel
126 : // Find the bitshift offset
127 0 : {
128 0 : unsigned int v = item->mask; // 32-bit word input to count zero bits on right
129 0 : unsigned int c = 32; // c will be the number of zero bits on the right
130 0 : v &= -signed(v);
131 0 : if (v) {
132 0 : c--;
133 : }
134 0 : if (v & 0x0000FFFF) {
135 0 : c -= 16;
136 : }
137 0 : if (v & 0x00FF00FF) {
138 0 : c -= 8;
139 : }
140 0 : if (v & 0x0F0F0F0F) {
141 0 : c -= 4;
142 : }
143 0 : if (v & 0x33333333) {
144 0 : c -= 2;
145 : }
146 0 : if (v & 0x55555555) {
147 0 : c -= 1;
148 : }
149 0 : item->offset = c;
150 : }
151 :
152 0 : iToken++;
153 0 : break;
154 0 : case 3:
155 : // Assign mode
156 0 : item->mode = 0;
157 :
158 : // Read
159 0 : if (itToken->find('r') != std::string::npos) {
160 0 : item->mode |= Item::READ;
161 0 : } else if (itToken->find('R') != std::string::npos) {
162 0 : item->mode |= Item::READ;
163 : }
164 :
165 : // Write
166 0 : if (itToken->find('w') != std::string::npos) {
167 0 : item->mode |= Item::WRITE;
168 0 : } else if (itToken->find('W') != std::string::npos) {
169 0 : item->mode |= Item::WRITE;
170 : }
171 :
172 : // Action
173 0 : if (itToken->find('a') != std::string::npos) {
174 0 : item->mode |= Item::ACTION;
175 0 : } else if (itToken->find('A') != std::string::npos) {
176 0 : item->mode |= Item::ACTION;
177 : }
178 :
179 : iToken++;
180 : break;
181 0 : default:
182 : // parse user arguments
183 : // repeated arguments will be over-written
184 :
185 : // Find if this is an argument or a flag, flags have no equal signs
186 0 : if (itToken->find('=') == std::string::npos) {
187 : // Create an entry for this with no data
188 0 : item->user[*itToken];
189 0 : iToken++;
190 : } else {
191 : // Get the name of the user value
192 0 : size_t equalSignPos = itToken->find('=');
193 : // make sure there isn't a """ before the =
194 0 : if (itToken->find('"') != std::string::npos) {
195 0 : if (itToken->find('"') < equalSignPos) {
196 0 : BUException::BAD_TOKEN e;
197 0 : e.Append("Malformed token : ");
198 0 : e.Append(itToken->c_str());
199 0 : e.Append(" on line ");
200 0 : char numberBuffer[14] = "\0\0\0\0\0\0\0\0\0\0\0\0";
201 0 : snprintf(numberBuffer, 12, "%zu\n", lineNumber);
202 0 : e.Append(numberBuffer);
203 0 : e.Append("Bad line: ");
204 0 : e.Append(activeLine);
205 0 : e.Append("\n");
206 0 : throw e;
207 0 : }
208 : }
209 : // cache the name of the user field
210 0 : std::string name = itToken->substr(0, equalSignPos);
211 : // Parse the rest of the user value if there is more size
212 0 : if (itToken->size() - 1 == equalSignPos) {
213 0 : BUException::BAD_TOKEN e;
214 0 : e.Append("Malformed token : ");
215 0 : e.Append(itToken->c_str());
216 0 : e.Append(" on line ");
217 0 : char numberBuffer[14] = "\0\0\0\0\0\0\0\0\0\0\0\0";
218 0 : snprintf(numberBuffer, 12, "%zu\n", lineNumber);
219 0 : e.Append(numberBuffer);
220 0 : throw e;
221 0 : }
222 : // star this user field's data (data after the '=' char)
223 0 : std::string val = itToken->substr(equalSignPos + 1);
224 0 : if (val[0] != '"') {
225 : // We have a simple entry that has no quotes
226 0 : item->user[name] = val;
227 0 : iToken++;
228 : } else {
229 : // We have a quoted value
230 0 : val.erase(0, 1); // remove the quote
231 :
232 : // Check if this is still a simple entry, but with quotes
233 0 : if (val.find('"') != std::string::npos) {
234 : // We have another quote, remove it and everything after it
235 0 : val = val.substr(0, val.find('"'));
236 : } else {
237 : // We have a complicated value
238 0 : itToken++;
239 0 : while (itToken != tokens.end()) {
240 0 : val.append(" ");
241 0 : val.append(*itToken);
242 0 : if ((*itToken)[itToken->size() - 1] == '"') {
243 : // stop now that we've reached the closing quote
244 : // remove it from the string
245 0 : val.erase(val.size() - 1);
246 : break;
247 : }
248 0 : iToken++;
249 0 : itToken++;
250 : }
251 : }
252 : // convert "\n" to '\n'
253 0 : while (val.find("\\n") != std::string::npos) {
254 0 : val.replace(val.find("\\n"), 2, std::string("\n"));
255 : }
256 0 : item->user[name] = val;
257 : }
258 0 : }
259 : break;
260 : }
261 0 : if (itToken == tokens.end()) {
262 : break;
263 : }
264 : }
265 :
266 0 : if (iToken >= 4) {
267 0 : AddEntry(item);
268 : } else {
269 0 : delete item;
270 : }
271 0 : }
|