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