don't crash when calling DataMap::lookup with an empty string
[laserbrain_demo] / src / datamap.cc
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include "datamap.h"
5
6 #ifdef WIN32
7 #include <malloc.h>
8 #else
9 #include <alloca.h>
10 #endif
11
12 static char *clean_line(char *s);
13
14 void DataMap::clear()
15 {
16         dmap.clear();
17         cache.clear();
18 }
19
20 void DataMap::set_path(const char *path)
21 {
22         root = std::string(path);
23 }
24
25 bool DataMap::load_map(const char *fname)
26 {
27         std::string path = root.empty() ? fname : root + std::string("/") + fname;
28         fname = path.c_str();
29
30         FILE *fp = fopen(fname, "r");
31         if(!fp) {
32                 fprintf(stderr, "failed to open data map: %s\n", fname);
33                 return false;
34         }
35
36         char buf[256];
37         if(fread(buf, 1, 8, fp) < 8 || memcmp(buf, "DATAMAP0", 8) != 0) {
38                 fprintf(stderr, "invalid datamap file: %s\n", fname);
39                 fclose(fp);
40                 return false;
41         }
42
43         clear();
44
45         char *line;
46         int nline = 0;
47         while(fgets(buf, sizeof buf, fp)) {
48                 ++nline;
49                 line = clean_line(buf);
50                 if(!line || !*line) continue;
51
52                 char *colon = strchr(line, ':');
53                 if(!colon) {
54                         goto err;
55                 }
56                 *colon = 0;
57
58                 //std::pair<std::regex, std::string> pair;
59                 //pair.first = std::regex(line);
60                 std::pair<std::string, std::string> pair;
61                 pair.first = std::string(line);
62
63                 char *value = clean_line(colon + 1);
64                 if(!value || !*value) {
65                         goto err;
66                 }
67                 pair.second = std::string(value);
68                 dmap.push_back(pair);
69         }
70         fclose(fp);
71
72         printf("loaded datamap %s: %d mappings\n", fname, (int)dmap.size());
73         return true;
74
75 err:
76         fprintf(stderr, "error while parsing %s, invalid line %d: %s\n", fname, nline, line);
77         clear();
78         fclose(fp);
79         return false;
80 }
81
82 void DataMap::map(const char *match, const char *path)
83 {
84         std::pair<std::string, std::string> mapping;
85         mapping.first = std::string(match);
86         mapping.second = std::string(path);
87         dmap.push_back(std::move(mapping));
88 }
89
90 int DataMap::lookup(const char *in, char *buf, int bsz) const
91 {
92         std::string res;
93
94         char *inbuf = (char*)alloca(strlen(in) + 1);
95         strcpy(inbuf, in);
96         if(!(in = clean_line(inbuf))) {
97                 if(buf && bsz > 0) {
98                         buf[0] = 0;
99                 }
100                 return 0;
101         }
102
103         // first check the cache
104         std::map<std::string, std::string>::iterator it = cache.find(in);
105         if(it != cache.end()) {
106                 res = it->second;
107         } else {
108                 // try matching with the available mappings
109                 res = root.empty() ? std::string(in) : root + "/" + std::string(in);
110
111                 int num = dmap.size();
112                 for(int i=0; i<num; i++) {
113                         //if(std::regex_search(in, dmap[i].first)) {
114                         if(strstr(in, dmap[i].first.c_str())) {
115                                 res = root.empty() ? dmap[i].second : root + "/" + dmap[i].second;
116                                 cache[in] = res;        // add it to the cache
117                                 break;
118                         }
119                 }
120         }
121
122         // copy result in buf, truncating if necessary and return the size of the
123         // buffer required to hold it
124         if(buf) {
125                 int n = std::min(bsz - 1, (int)res.length());
126                 memcpy(buf, res.c_str(), n);
127                 buf[n] = 0; // make sure it's null-terminated even if it got truncated
128         }
129         return res.length() + 1;
130 }
131
132 int DataMap::path_size(const char *in) const
133 {
134         return lookup(in, 0, 0);
135 }
136
137 static char *clean_line(char *s)
138 {
139         while(*s && isspace(*s)) ++s;
140         if(!*s) return 0;
141
142         char *end;
143         if(!(end = strchr(s, '#'))) {
144                 end = s + strlen(s) - 1;
145         }
146         while(end > s && isspace(*end)) --end;
147         if(s == end) return 0;
148         end[1] = 0;
149
150         // app-specific: convert backslashes
151         char *c = s;
152         while(*c) {
153                 if(*c == '\\') *c = '/';
154                 ++c;
155         }
156
157         return s;
158 }