1 /*
2 
3  *  This program is free software; you can redistribute it and/or modify
4  *  it under the terms of the GNU General Public License as published by
5  *  the Free Software Foundation; either version 2 of the License, or
6  *  (at your option) any later version.
7  *
8  *  This program is distributed in the hope that it will be useful,
9  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
10  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  *  GNU General Public License for more details.
12  *
13  *  You should have received a copy of the GNU General Public License
14  *  along with this program; if not, write to the Free Software
15  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307, USA
16  *
17  *  Author : Richard GAYRAUD - 04 Nov 2003
18  *           From Hewlett Packard Company.
19  *           Charles P. Wright from IBM Research
20  */
21 
22 #include "sipp.hpp"
23 #include "screen.hpp"
24 #include "stat.hpp"
25 #include "infile.hpp"
26 #include <iostream>
27 #include <assert.h>
28 
29 /* Read MAX_CHAR_BUFFER_SIZE size lines from the "fileName" and populate it in
30  * the fileContents vector. Each line should be terminated with a '\n'
31  */
FileContents(const char * fileName)32 FileContents::FileContents(const char *fileName)
33 {
34     ifstream *inFile    = new ifstream(fileName);
35     char      line[MAX_CHAR_BUFFER_SIZE];
36     int virtualLines = 0;
37 
38     if (!inFile->good()) {
39         ERROR("Unable to open file %s", fileName);
40     }
41 
42     this->fileName = fileName;
43 
44     realLinesInFile = lineCounter = numLinesInFile = 0;
45     /* Initialize printf info. */
46     printfFile = false;
47     printfOffset = 0;
48     printfMultiple = 1;
49 
50 
51     line[0] = '\0';
52     inFile->getline(line, MAX_CHAR_BUFFER_SIZE);
53 
54     if (NULL != strstr(line, "RANDOM")) {
55         usage = InputFileRandomOrder;
56     } else if (NULL != strstr(line, "SEQUENTIAL")) {
57         usage = InputFileSequentialOrder;
58     } else if (NULL != strstr(line, "USER")) {
59         usage = InputFileUser;
60     } else {
61         ERROR("Unknown file type (valid values are RANDOM, SEQUENTIAL, and USER) for %s:%s\n", fileName, line);
62     }
63 
64     char *useprintf;
65     if ((useprintf = strstr(line, "PRINTF"))) {
66         /* We are going to operate in printf mode, which uses the line as a format
67          * string for printf with the line number. */
68         useprintf += strlen("PRINTF");
69         if (*useprintf != '=') {
70             ERROR("Invalid file printf specification (requires =) for %s:%s\n", fileName, line);
71         }
72         useprintf++;
73         char *endptr;
74         virtualLines = strtoul(useprintf, &endptr, 0);
75         if (*endptr && *endptr != '\r' && *endptr != '\n' && *endptr != ',') {
76             ERROR("Invalid file printf specification for (invalid end character '%c') %s:%s\n", *endptr, fileName, line);
77         }
78         if (virtualLines == 0) {
79             ERROR("A printf file must have at least one virtual line %s:%s\n", fileName, line);
80         }
81         printfFile = true;
82     }
83 
84     if ((useprintf = strstr(line, "PRINTFOFFSET"))) {
85         useprintf += strlen("PRINTFOFFSET");
86         if (*useprintf != '=') {
87             ERROR("Invalid file PRINTFOFFSET specification (requires =) for %s:%s\n", fileName, line);
88         }
89         useprintf++;
90         char *endptr;
91         printfOffset = strtoul(useprintf, &endptr, 0);
92         if (*endptr && *endptr != '\n' && *endptr != ',') {
93             ERROR("Invalid PRINTFOFFSET specification for (invalid end character '%c') %s:%s\n", *endptr, fileName, line);
94         }
95     }
96 
97     if ((useprintf = strstr(line, "PRINTFMULTIPLE"))) {
98         useprintf += strlen("PRINTFMULTIPLE");
99         if (*useprintf != '=') {
100             ERROR("Invalid PRINTFMULTIPLE specification (requires =) for %s:%s\n", fileName, line);
101         }
102         useprintf++;
103         char *endptr;
104         printfMultiple = strtoul(useprintf, &endptr, 0);
105         if (*endptr && *endptr != '\n' && *endptr != ',') {
106             ERROR("Invalid PRINTFOFFSET specification for (invalid end character '%c') %s:%s\n", *endptr, fileName, line);
107         }
108     }
109 
110     while (!inFile->eof()) {
111         line[0] = '\0';
112         inFile->getline(line, MAX_CHAR_BUFFER_SIZE);
113         if (line[0]) {
114             if ('#' != line[0]) {
115                 fileLines.push_back(line);
116                 realLinesInFile++; /* this counts number of valid data lines */
117             }
118         } else {
119             break;
120         }
121     }
122 
123     if (realLinesInFile == 0) {
124         ERROR("Input file has zero lines: %s\n", fileName);
125     }
126 
127     if (printfFile) {
128         numLinesInFile = virtualLines;
129     } else {
130         numLinesInFile = realLinesInFile;
131     }
132 
133     delete inFile;
134 
135     indexMap = NULL;
136     indexField = -1;
137 }
138 
getLine(int line,char * dest,int len)139 int FileContents::getLine(int line, char *dest, int len)
140 {
141     if (printfFile) {
142         line %= realLinesInFile;
143     }
144     return snprintf(dest, len, "%s", fileLines[line].c_str());
145 }
146 
getField(int lineNum,int field,char * dest,int len)147 int FileContents::getField(int lineNum, int field, char *dest, int len)
148 {
149     int curfield = field;
150     int curline = lineNum;
151 
152     dest[0] = '\0';
153     if (lineNum >= numLinesInFile) {
154         return 0;
155     }
156 
157     if (printfFile) {
158         curline %= realLinesInFile;
159     }
160     const string & line = fileLines[curline];
161 
162     size_t pos(0), oldpos(0);
163 
164     do {
165         oldpos = pos;
166         size_t localpos = line.find(';', oldpos);
167 
168         if (localpos != string::npos) {
169             pos = localpos + 1;
170         } else {
171             pos = localpos;
172             break;
173         }
174 
175         if (curfield == 0) {
176             break;
177         }
178 
179         curfield --;
180     } while (oldpos != string::npos);
181 
182 
183     if (curfield) {
184         WARNING("Field %d not found in the file %s", field, fileName);
185         return 0;
186     }
187 
188 
189     if (string::npos == oldpos) {
190         return 0;
191     }
192 
193     if (string::npos != pos) {
194         // should not be decremented for fieldN
195         pos -= (oldpos + 1);
196     }
197 
198     string x = line.substr(oldpos, pos);
199     if (x.length()) {
200         if (printfFile) {
201             const char *s = x.c_str();
202             int l = strlen(s);
203             int copied = 0;
204             for (int i = 0; i < l; i++) {
205                 if (s[i] == '%') {
206                     if (s[i + 1] == '%') {
207                         dest[copied++] = s[i];
208                     } else {
209                         const char *format = s + i;
210                         i++;
211                         while (s[i] != 'd') {
212                             if (i == l) {
213                                 ERROR("Invalid printf injection field (ran off end of line): %s", s);
214                             }
215                             if (!(isdigit(s[i]) || s[i] == '.' || s[i] == '-')) {
216                                 ERROR("Invalid printf injection field (only decimal values allowed '%c'): %s", s[i], s);
217                             }
218                             i++;
219                         }
220                         assert(s[i] == 'd');
221                         char *tmp = (char *)malloc(s + i + 2 - format);
222                         if (!tmp) {
223                             ERROR("Out of memory!\n");
224                         }
225                         memcpy(tmp, format, s + i + 1 - format);
226                         tmp[s + i + 1 - format] = '\0';
227                         copied += sprintf(dest + copied, tmp, printfOffset + (lineNum * printfMultiple));
228                         free(tmp);
229                     }
230                 } else {
231                     dest[copied++] = s[i];
232                 }
233             }
234             dest[copied] = '\0';
235             return copied;
236         } else {
237             return snprintf(dest, len, "%s", x.c_str());
238         }
239     } else {
240         return 0;
241     }
242 }
243 
numLines()244 int FileContents::numLines()
245 {
246     return numLinesInFile;
247 }
248 
nextLine(int userId)249 int FileContents::nextLine(int userId)
250 {
251     switch(usage) {
252     case InputFileRandomOrder:
253         return rand() % numLinesInFile;
254     case InputFileSequentialOrder: {
255         int ret = lineCounter;
256         lineCounter = (lineCounter + 1) % numLinesInFile;
257         return ret;
258     }
259     case InputFileUser:
260         if (userId == 0) {
261             return -1;
262         }
263         if ((userId  - 1) >= numLinesInFile) {
264             ERROR("%s has only %d lines, yet user %d was requested.", fileName, numLinesInFile, userId);
265         }
266         return userId - 1;
267     default:
268         ERROR("Internal error: unknown file usage mode!");
269         return -1;
270     }
271 }
272 
dump(void)273 void FileContents::dump(void)
274 {
275     WARNING("Line choosing strategy is [%s]. m_counter [%d] numLinesInFile [%d] realLinesInFile [%d]",
276             usage == InputFileSequentialOrder ? "SEQUENTIAL" :
277             usage == InputFileRandomOrder ? "RANDOM" :
278             usage == InputFileUser ? "USER" : "UNKNOWN",
279             lineCounter, numLinesInFile, realLinesInFile);
280 
281     for (int i = 0; i < realLinesInFile && fileLines[i][0]; i++) {
282         WARNING("%s:%d reads [%s]", fileName, i, fileLines[i].c_str());
283     }
284 }
285 
index(int field)286 void FileContents::index(int field)
287 {
288     this->indexField = field;
289 
290     indexMap = new str_int_map;
291     for (int line = 0; line < numLines(); line++) {
292         reIndex(line);
293     }
294 }
295 
lookup(char * key)296 int FileContents::lookup(char *key)
297 {
298     if (indexField == -1) {
299         ERROR("Invalid Index File: %s", fileName);
300     }
301     if (!indexMap) {
302         ERROR("Invalid Index File: %s", fileName);
303     }
304 
305     str_int_map::iterator index_it = indexMap->find(key);
306     if (index_it == indexMap->end()) {
307         return -1;
308     }
309     return index_it->second;
310 }
311 
312 
insert(char * value)313 void FileContents::insert(char *value)
314 {
315     if (printfFile) {
316         ERROR("Can not insert or replace into a printf file: %s", fileName);
317     }
318     fileLines.push_back(value);
319     realLinesInFile++;
320     numLinesInFile++;
321     if (indexField != -1) {
322         reIndex(realLinesInFile - 1);
323     }
324     char line[1024];
325     getLine(realLinesInFile - 1, line, sizeof(line));
326     char tmp[1024];
327     getField(realLinesInFile - 1, 0, tmp, sizeof(tmp));
328 }
329 
replace(int line,char * value)330 void FileContents::replace(int line, char *value)
331 {
332     if (printfFile) {
333         ERROR("Can not insert or replace into a printf file: %s", fileName);
334     }
335     if (line >= realLinesInFile || line < 0) {
336         ERROR("Invalid line number (%d) for file: %s (%d lines)", line, fileName, realLinesInFile);
337     }
338     deIndex(line);
339     fileLines[line] = value;
340     reIndex(line);
341 }
342 
reIndex(int line)343 void FileContents::reIndex(int line)
344 {
345     if (indexField == -1) {
346         return;
347     }
348     assert(line >= 0);
349     assert(line < realLinesInFile);
350 
351     char tmp[SIPP_MAX_MSG_SIZE];
352     getField(line, indexField, tmp, SIPP_MAX_MSG_SIZE);
353     str_int_map::iterator index_it = indexMap->find(str_int_map::key_type(tmp));
354     if (index_it != indexMap->end()) {
355         indexMap->erase(index_it);
356     }
357     indexMap->insert(pair<str_int_map::key_type,int>(str_int_map::key_type(tmp), line));
358 }
359 
deIndex(int line)360 void FileContents::deIndex(int line)
361 {
362     if (indexField == -1) {
363         return;
364     }
365     assert(line >= 0);
366     assert(line < realLinesInFile);
367 
368     char tmp[SIPP_MAX_MSG_SIZE];
369     getField(line, indexField, tmp, SIPP_MAX_MSG_SIZE);
370     str_int_map::iterator index_it = indexMap->find(str_int_map::key_type(tmp));
371     if (index_it != indexMap->end()) {
372         if (index_it->second == line) {
373             indexMap->erase(index_it);
374         }
375     }
376 }
377