+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
#include <vector>
#include <map>
#include <string>
+#include <regex>
#include "datamap.h"
-static std::vector<std::pair<std::string, std::string>> dmap;
+#ifdef WIN32
+#include <malloc.h>
+#else
+#include <alloca.h>
+#endif
+
+static char *clean_line(char *s);
+
+static std::vector<std::pair<std::regex, std::string>> dmap;
static std::map<std::string, std::string> cache;
static std::string root;
-void datmap_reset()
+void datamap_reset()
{
root.clear();
dmap.clear();
cache.clear();
}
-void datmap_set_path(const char *path)
+void datamap_set_path(const char *path)
{
root = std::string(path);
}
-bool datmap_load_map(const char *fname)
+bool datamap_load_map(const char *fname)
{
std::string path = root.empty() ? fname : root + std::string("/") + fname;
- return false; // TODO cont...
+ fname = path.c_str();
+
+ FILE *fp = fopen(fname, "r");
+ if(!fp) {
+ fprintf(stderr, "failed to open data map: %s\n", fname);
+ return false;
+ }
+
+ char buf[256];
+ if(fread(buf, 1, 8, fp) < 8 || memcmp(buf, "DATAMAP0", 8) != 0) {
+ fprintf(stderr, "invalid datamap file: %s\n", fname);
+ fclose(fp);
+ return false;
+ }
+
+ datamap_reset();
+
+ char *line;
+ int nline = 0;
+ while(fgets(buf, sizeof buf, fp)) {
+ ++nline;
+ line = clean_line(buf);
+ if(!line || !*line) continue;
+
+ char *colon = strchr(line, ':');
+ if(!colon) {
+ goto err;
+ }
+ *colon = 0;
+
+ std::pair<std::regex, std::string> pair;
+ pair.first = std::regex(line);
+
+ char *value = clean_line(colon + 1);
+ if(!value || !*value) {
+ goto err;
+ }
+ pair.second = std::string(value);
+ dmap.push_back(pair);
+ }
+ fclose(fp);
+
+ printf("loaded datamap %s: %d mappings\n", fname, (int)dmap.size());
+ return true;
+
+err:
+ fprintf(stderr, "error while parsing %s, invalid line %d: %s\n", fname, nline, line);
+ datamap_reset();
+ fclose(fp);
+ return false;
}
-void datmap_map(const char *re, const char *path)
+void datamap_map(const char *re, const char *path)
{
- std::pair<std::string, std::string> mapping;
- mapping.first = std::string(re);
+ std::pair<std::regex, std::string> mapping;
+ mapping.first = std::regex(re);
mapping.second = std::string(path);
- dmap.push_back(mapping);
+ dmap.push_back(std::move(mapping));
+}
+
+int datamap_lookup(const char *in, char *buf, int bsz)
+{
+ std::string res;
+
+ char *inbuf = (char*)alloca(strlen(in) + 1);
+ strcpy(inbuf, in);
+ in = clean_line(inbuf);
+
+ // first check the cache
+ std::map<std::string, std::string>::iterator it = cache.find(in);
+ if(it != cache.end()) {
+ res = it->second;
+ } else {
+ // try matching with the available mappings
+ int num = dmap.size();
+ for(int i=0; i<num; i++) {
+ if(std::regex_match(in, dmap[i].first)) {
+ res = root.empty() ? dmap[i].second : root + "/" + dmap[i].second;
+ cache[in] = res; // add it to the cache
+ break;
+ }
+ }
+ return 0;
+ }
+
+ // copy result in buf, truncating if necessary and return the size of the
+ // buffer required to hold it
+ if(buf) {
+ int n = std::min(bsz - 1, (int)res.length());
+ memcpy(buf, res.c_str(), n);
+ buf[bsz - 1] = 0; // make sure it's null-terminated even if it got truncated
+ }
+ return res.length() + 1;
}
-int datmap_lookup(const char *in, char *buf, int bsz)
+int datamap_path_size(const char *in)
{
- return -1; // TODO
+ return datamap_lookup(in, 0, 0);
}
-int datmap_path_size(const char *in)
+static char *clean_line(char *s)
{
- return datmap_lookup(in, 0, 0);
+ while(*s && isspace(*s)) ++s;
+ if(!*s) return 0;
+
+ char *end;
+ if(!(end = strchr(s, '#'))) {
+ end = s + strlen(s) - 1;
+ }
+ while(end > s && isspace(*end)) --end;
+ if(s == end) return 0;
+ end[1] = 0;
+
+ // app-specific: convert backslashes
+ char *c = s;
+ while(*c) {
+ if(*c == '\\') *c = '/';
+ ++c;
+ }
+
+ return s;
}