626c47f88d71375a8effc8d3afe727b0cba3dfd7
[laserbrain_demo] / src / datamap.cc
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <vector>
5 #include <map>
6 #include <string>
7 #include <regex>
8 #include "datamap.h"
9
10 #ifdef WIN32
11 #include <malloc.h>
12 #else
13 #include <alloca.h>
14 #endif
15
16 static char *clean_line(char *s);
17
18 static std::vector<std::pair<std::regex, std::string>> dmap;
19 static std::map<std::string, std::string> cache;
20 static std::string root;
21
22 void datamap_reset()
23 {
24         root.clear();
25         dmap.clear();
26         cache.clear();
27 }
28
29 void datamap_set_path(const char *path)
30 {
31         root = std::string(path);
32 }
33
34 bool datamap_load_map(const char *fname)
35 {
36         std::string path = root.empty() ? fname : root + std::string("/") + fname;
37         fname = path.c_str();
38
39         FILE *fp = fopen(fname, "r");
40         if(!fp) {
41                 fprintf(stderr, "failed to open data map: %s\n", fname);
42                 return false;
43         }
44
45         char buf[256];
46         if(fread(buf, 1, 8, fp) < 8 || memcmp(buf, "DATAMAP0", 8) != 0) {
47                 fprintf(stderr, "invalid datamap file: %s\n", fname);
48                 fclose(fp);
49                 return false;
50         }
51
52         datamap_reset();
53
54         char *line;
55         int nline = 0;
56         while(fgets(buf, sizeof buf, fp)) {
57                 ++nline;
58                 line = clean_line(buf);
59                 if(!line || !*line) continue;
60
61                 char *colon = strchr(line, ':');
62                 if(!colon) {
63                         goto err;
64                 }
65                 *colon = 0;
66
67                 std::pair<std::regex, std::string> pair;
68                 pair.first = std::regex(line);
69
70                 char *value = clean_line(colon + 1);
71                 if(!value || !*value) {
72                         goto err;
73                 }
74                 pair.second = std::string(value);
75                 dmap.push_back(pair);
76         }
77         fclose(fp);
78
79         printf("loaded datamap %s: %d mappings\n", fname, (int)dmap.size());
80         return true;
81
82 err:
83         fprintf(stderr, "error while parsing %s, invalid line %d: %s\n", fname, nline, line);
84         datamap_reset();
85         fclose(fp);
86         return false;
87 }
88
89 void datamap_map(const char *re, const char *path)
90 {
91         std::pair<std::regex, std::string> mapping;
92         mapping.first = std::regex(re);
93         mapping.second = std::string(path);
94         dmap.push_back(std::move(mapping));
95 }
96
97 int datamap_lookup(const char *in, char *buf, int bsz)
98 {
99         std::string res;
100
101         char *inbuf = (char*)alloca(strlen(in) + 1);
102         strcpy(inbuf, in);
103         in = clean_line(inbuf);
104
105         // first check the cache
106         std::map<std::string, std::string>::iterator it = cache.find(in);
107         if(it != cache.end()) {
108                 res = it->second;
109         } else {
110                 // try matching with the available mappings
111                 int num = dmap.size();
112                 for(int i=0; i<num; i++) {
113                         if(std::regex_match(in, dmap[i].first)) {
114                                 res = root.empty() ? dmap[i].second : root + "/" + dmap[i].second;
115                                 cache[in] = res;        // add it to the cache
116                                 break;
117                         }
118                 }
119                 return 0;
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[bsz - 1] = 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)
133 {
134         return datamap_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 }