hqlres.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532
  1. /*##############################################################################
  2. HPCC SYSTEMS software Copyright (C) 2012 HPCC Systems®.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. ############################################################################## */
  13. #include "jliball.hpp"
  14. #include "hql.hpp"
  15. #include "jlib.hpp"
  16. #include "hqlres.hpp"
  17. #include "jmisc.hpp"
  18. #include "jexcept.hpp"
  19. #include "hqlcerrors.hpp"
  20. #include "thorplugin.hpp"
  21. #define BIGSTRING_BASE 101
  22. #define MANIFEST_BASE 1000
  23. class ResourceItem : public CInterface
  24. {
  25. public:
  26. ResourceItem(const char * _type, unsigned _id, size32_t _len, const void * _ptr)
  27. : data(_len, _ptr), type(_type), id(_id) {}
  28. public:
  29. MemoryAttr data;
  30. StringAttr type;
  31. unsigned id;
  32. };
  33. ResourceManager::ResourceManager()
  34. {
  35. nextmfid = MANIFEST_BASE + 1;
  36. nextbsid = BIGSTRING_BASE;
  37. totalbytes = 0;
  38. finalized=false;
  39. }
  40. unsigned ResourceManager::count()
  41. {
  42. return resources.ordinality();
  43. }
  44. unsigned ResourceManager::addString(unsigned len, const char *data)
  45. {
  46. unsigned id = nextbsid++;
  47. resources.append(*new ResourceItem("BIGSTRING", id, len, data));
  48. return id;
  49. }
  50. void ResourceManager::addNamed(const char * type, unsigned len, const void * data, IPropertyTree *manifestEntry, unsigned id, bool addToManifest, bool compressed)
  51. {
  52. if (id==(unsigned)-1)
  53. id = nextmfid++;
  54. if (addToManifest)
  55. {
  56. if (finalized)
  57. throwError1(HQLERR_ResourceAddAfterFinalManifest, type);
  58. Owned<IPropertyTree> entry=createPTree("Resource");
  59. entry->setProp("@type", type);
  60. entry->setPropInt("@id", id);
  61. entry->setPropBool("@compressed", compressed);
  62. entry->setPropBool("@header", true);
  63. if (manifestEntry)
  64. mergePTree(entry, manifestEntry);
  65. ensureManifestInfo()->addPropTree("Resource", entry.getClear());
  66. }
  67. MemoryBuffer mb;
  68. appendResource(mb, len, data, compressed);
  69. resources.append(*new ResourceItem(type, id, mb.length(), mb.toByteArray()));
  70. }
  71. bool ResourceManager::addCompress(const char * type, unsigned len, const void * data, IPropertyTree *manifestEntry, unsigned id, bool addToManifest)
  72. {
  73. addNamed(type, len, data, manifestEntry, id, addToManifest, true);
  74. return true;
  75. }
  76. static void loadResource(const char *filepath, MemoryBuffer &content)
  77. {
  78. Owned <IFile> f = createIFile(filepath);
  79. Owned <IFileIO> fio = f->open(IFOread);
  80. read(fio, 0, (size32_t) f->size(), content);
  81. }
  82. bool ResourceManager::getDuplicateResourceId(const char *srctype, const char *respath, const char *filepath, int &id)
  83. {
  84. StringBuffer xpath;
  85. if (respath && *respath)
  86. xpath.appendf("Resource[@resourcePath='%s']", respath);
  87. else
  88. xpath.appendf("Resource[@originalFilename='%s']", filepath);
  89. Owned<IPropertyTreeIterator> iter = manifest->getElements(xpath.str());
  90. ForEach (*iter)
  91. {
  92. IPropertyTree &item = iter->query();
  93. if (item.hasProp("@id"))
  94. {
  95. const char *type = item.queryProp("@type");
  96. if (type && strieq(type, srctype))
  97. {
  98. id=item.getPropInt("@id");
  99. return true;
  100. }
  101. }
  102. }
  103. return false;
  104. }
  105. void updateManifestResourcePaths(IPropertyTree &resource, const char *dir)
  106. {
  107. StringBuffer filepath;
  108. makeAbsolutePath(resource.queryProp("@filename"), dir, filepath);
  109. resource.setProp("@originalFilename", filepath.str());
  110. StringBuffer respath;
  111. makePathUniversal(filepath.str(), respath);
  112. resource.setProp("@resourcePath", respath.str());
  113. }
  114. void expandManifestDirectory(IPropertyTree *manifestSrc, IPropertyTree &res, StringBuffer &dir, IDirectoryIterator *it, const char*mask, bool recursive)
  115. {
  116. if (!it)
  117. return;
  118. ForEach(*it)
  119. {
  120. if (it->isDir())
  121. {
  122. if (recursive)
  123. expandManifestDirectory(manifestSrc, res, dir, it->query().directoryFiles(mask, false, true), mask, recursive);
  124. continue;
  125. }
  126. StringBuffer reldir;
  127. Owned<IPropertyTree> newRes = createPTreeFromIPT(&res);
  128. reldir.append(splitRelativePath(it->query().queryFilename(), dir, reldir));
  129. VStringBuffer xpath("Resource[@filename='%s']", reldir.str());
  130. if (manifestSrc->hasProp(xpath))
  131. continue;
  132. newRes->setProp("@filename", reldir.str());
  133. updateManifestResourcePaths(*newRes, dir.str());
  134. if (manifestSrc->hasProp(xpath.setf("resource[@resourcePath='%s']", newRes->queryProp("@resourcePath"))))
  135. continue;
  136. manifestSrc->addPropTree("Resource", newRes.getClear());
  137. }
  138. }
  139. void expandManifestDirectory(IPropertyTree *manifestSrc, IPropertyTree &res, StringBuffer &dir, const char *path, const char*mask, bool recursive)
  140. {
  141. Owned<IDirectoryIterator> it = createDirectoryIterator(path, mask);
  142. expandManifestDirectory(manifestSrc, res, dir, it, mask, recursive);
  143. }
  144. void ResourceManager::addManifestFile(const char *filename)
  145. {
  146. Owned<IPropertyTree> manifestSrc = createPTreeFromXMLFile(filename);
  147. StringBuffer dir;
  148. splitDirTail(filename, dir);
  149. ensureManifestInfo();
  150. Owned<IAttributeIterator> aiter = manifestSrc->getAttributes();
  151. ForEach (*aiter)
  152. manifest->setProp(aiter->queryName(), aiter->queryValue());
  153. Owned<IPropertyTreeIterator> iter = manifestSrc->getElements("*");
  154. ForEach(*iter)
  155. {
  156. IPropertyTree &item = iter->query();
  157. if (streq(item.queryName(), "Include") && item.hasProp("@filename"))
  158. addManifestInclude(item, dir.str());
  159. else if (streq(item.queryName(), "Resource") && item.hasProp("@filename"))
  160. {
  161. StringBuffer filepath;
  162. StringBuffer respath;
  163. makeAbsolutePath(item.queryProp("@filename"), dir.str(), filepath);
  164. makePathUniversal(filepath.str(), respath);
  165. item.setProp("@originalFilename", filepath.str());
  166. item.setProp("@resourcePath", respath.str());
  167. if (containsFileWildcard(filepath))
  168. {
  169. StringBuffer wildpath;
  170. const char *tail = splitDirTail(filepath, wildpath);
  171. expandManifestDirectory(manifestSrc, item, dir, wildpath, tail, item.getPropBool("@recursive"));
  172. manifestSrc->removeTree(&item);
  173. }
  174. }
  175. else
  176. manifest->addPropTree(item.queryName(), LINK(&item));
  177. }
  178. Owned<IPropertyTreeIterator> resources = manifestSrc->getElements("Resource[@filename]");
  179. ForEach(*resources)
  180. {
  181. IPropertyTree &item = resources->query();
  182. if (!item.hasProp("@type"))
  183. item.setProp("@type", "UNKNOWN");
  184. int id;
  185. if (getDuplicateResourceId(item.queryProp("@type"), item.queryProp("@resourcePath"), NULL, id))
  186. {
  187. item.setPropInt("@id", id);
  188. manifest->addPropTree("Resource", LINK(&item));
  189. }
  190. else
  191. {
  192. MemoryBuffer content;
  193. loadResource(item.queryProp("@originalFilename"), content);
  194. addCompress(item.queryProp("@type"), content.length(), content.toByteArray(), &item);
  195. }
  196. }
  197. }
  198. void ResourceManager::addManifest(const char *filename)
  199. {
  200. StringBuffer path;
  201. Owned<IPropertyTree> t = createPTree();
  202. t->setProp("@originalFilename", makeAbsolutePath(filename, path).str());
  203. ensureManifestInfo()->addPropTree("Include", t.getClear());
  204. addManifestFile(filename);
  205. }
  206. void ResourceManager::addManifestInclude(IPropertyTree &include, const char *dir)
  207. {
  208. StringBuffer includePath;
  209. makeAbsolutePath(include.queryProp("@filename"), dir, includePath);
  210. VStringBuffer xpath("Include[@originalFilename='%s']", includePath.str());
  211. if (manifest->hasProp(xpath.str()))
  212. return;
  213. include.setProp("@originalFilename", includePath.str());
  214. manifest->addPropTree("Include", LINK(&include));
  215. addManifestFile(includePath.str());
  216. }
  217. void ResourceManager::addManifestFromArchive(IPropertyTree *archive)
  218. {
  219. if (!archive)
  220. return;
  221. if (finalized)
  222. throwError1(HQLERR_ResourceAddAfterFinalManifest, "MANIFEST");
  223. ensureManifestInfo();
  224. Owned<IPropertyTreeIterator> manifests = archive->getElements("AdditionalFiles/Manifest");
  225. ForEach(*manifests)
  226. {
  227. const char *xml = manifests->query().queryProp(NULL);
  228. Owned<IPropertyTree> manifestSrc = createPTreeFromXMLString(xml);
  229. Owned<IAttributeIterator> aiter = manifestSrc->getAttributes();
  230. ForEach (*aiter)
  231. manifest->setProp(aiter->queryName(), aiter->queryValue());
  232. StringBuffer manifestDir;
  233. if (manifestSrc->hasProp("@originalFilename"))
  234. splitDirTail(manifestSrc->queryProp("@originalFilename"), manifestDir);
  235. Owned<IPropertyTreeIterator> iter = manifestSrc->getElements("*");
  236. ForEach(*iter)
  237. {
  238. IPropertyTree &item = iter->query();
  239. if (streq(item.queryName(), "Resource") && item.hasProp("@filename"))
  240. {
  241. if (!item.hasProp("@type"))
  242. item.setProp("@type", "UNKNOWN");
  243. const char *filename;
  244. if (item.hasProp("@originalFilename"))
  245. filename = item.queryProp("@originalFilename");
  246. else
  247. filename = item.queryProp("@filename");
  248. int id;
  249. if (getDuplicateResourceId(item.queryProp("@type"), NULL, filename, id))
  250. {
  251. item.setPropInt("@id", (int)id);
  252. manifest->addPropTree("Resource", LINK(&item));
  253. }
  254. else
  255. {
  256. VStringBuffer xpath("AdditionalFiles/Resource[@originalFilename=\"%s\"]", filename);
  257. MemoryBuffer content;
  258. archive->getPropBin(xpath.str(), content);
  259. addCompress(item.queryProp("@type"), content.length(), content.toByteArray(), &item);
  260. }
  261. }
  262. else
  263. manifest->addPropTree(item.queryName(), LINK(&item));
  264. }
  265. }
  266. }
  267. void ResourceManager::addWebServiceInfo(IPropertyTree *wsinfo)
  268. {
  269. //convert legacy web service info to the new resource format
  270. if (wsinfo)
  271. {
  272. if (wsinfo->hasProp("SOAP"))
  273. ensureManifestInfo()->addProp("WS-PARAMS", wsinfo->queryProp("SOAP"));
  274. if (wsinfo->hasProp("HELP"))
  275. {
  276. const char *content = wsinfo->queryProp("HELP");
  277. addCompress("HELP", strlen(content)+1, content);
  278. }
  279. if (wsinfo->hasProp("INFO"))
  280. {
  281. const char *content = wsinfo->queryProp("INFO");
  282. addCompress("INFO", strlen(content)+1, content);
  283. }
  284. if (wsinfo->hasProp("OTX"))
  285. {
  286. const char *content = wsinfo->queryProp("OTX");
  287. addCompress("HYPER-LINK", strlen(content)+1, content);
  288. }
  289. if (wsinfo->hasProp("HTML"))
  290. {
  291. const char *content = wsinfo->queryProp("HTML");
  292. Owned<IPropertyTree> manifestEntry = createPTree("Resource");
  293. manifestEntry->setProp("@name", "Custom Form");
  294. addCompress("XSLT", strlen(content)+1, content, manifestEntry);
  295. IPropertyTree *view = ensurePTree(ensureManifestInfo(), "Views/XSLT/FORM");
  296. view->setProp("@resource", "Custom Form");
  297. }
  298. if (wsinfo->hasProp("HTMLD"))
  299. {
  300. const char *content = wsinfo->queryProp("HTMLD");
  301. Owned<IPropertyTree> manifestEntry = createPTree("Resource");
  302. manifestEntry->setProp("@name", "Custom HTML");
  303. addCompress("HTML", strlen(content)+1, content, manifestEntry);
  304. IPropertyTree *view = ensurePTree(ensureManifestInfo(), "Views/HTML/FORM");
  305. view->setProp("@resource", "Custom HTML");
  306. }
  307. if (wsinfo->hasProp("RESULT"))
  308. {
  309. const char *content = wsinfo->queryProp("RESULT");
  310. Owned<IPropertyTree> manifestEntry = createPTree("Resource");
  311. manifestEntry->setProp("@name", "Results");
  312. addCompress("XSLT", strlen(content)+1, content, manifestEntry);
  313. IPropertyTree *view = ensurePTree(ensureManifestInfo(), "Views/XSLT/RESULTS");
  314. view->setProp("@resource", "Results");
  315. }
  316. if (wsinfo->hasProp("ERROR"))
  317. {
  318. const char *content = wsinfo->queryProp("ERROR");
  319. Owned<IPropertyTree> manifestEntry = createPTree("Resource");
  320. manifestEntry->setProp("@name", "Error");
  321. addCompress("XSLT", strlen(content)+1, content, manifestEntry);
  322. IPropertyTree *view = ensurePTree(ensureManifestInfo(), "Views/XSLT/ERROR");
  323. view->setProp("@resource", "Error");
  324. }
  325. }
  326. }
  327. void ResourceManager::finalize()
  328. {
  329. if (!finalized)
  330. {
  331. if (manifest)
  332. {
  333. StringBuffer content;
  334. toXML(manifest, content);
  335. addCompress("MANIFEST", content.length()+1, content.str(), NULL, MANIFEST_BASE, false);
  336. }
  337. finalized=true;
  338. }
  339. }
  340. void ResourceManager::putbytes(int h, const void *b, unsigned len)
  341. {
  342. int written = _write(h, b, len);
  343. assertex(written == len);
  344. totalbytes += len;
  345. }
  346. void ResourceManager::flushAsText(const char *filename)
  347. {
  348. finalize();
  349. StringBuffer name;
  350. int len = strlen(filename);
  351. name.append(filename,0,len-4).append(".txt");
  352. FILE* f = fopen(name.str(), "wb");
  353. if (f==NULL)
  354. {
  355. PrintLog("Create resource text file %s failed", name.str());
  356. return; // error is ignorable.
  357. }
  358. ForEachItemIn(idx, resources)
  359. {
  360. ResourceItem&s = (ResourceItem&)resources.item(idx);
  361. fwrite(s.data.get(),1,s.data.length(),f);
  362. }
  363. fclose(f);
  364. }
  365. bool ResourceManager::flush(StringBuffer &filename, const char *basename, bool flushText, bool target64bit)
  366. {
  367. finalize();
  368. // Use "resources" for strings that are a bit large to generate in the c++ (some compilers had limits at 64k)
  369. // or that we want to access without having to run the dll/so
  370. // In linux there is no .res concept but we can achieve the same effect by generating an object file with a specially-named section
  371. // bintils tools can be used to extract the data externally (internally we just have a named symbol for it)
  372. // Alternatively we can generate an assembler file to create the equivalent object file, if binutils is not available
  373. bool isObjectFile = true;
  374. #ifdef _WIN32
  375. filename.append(basename).append(".res");
  376. int h = _open(filename, _O_WRONLY|_O_CREAT|_O_TRUNC|_O_BINARY|_O_SEQUENTIAL, _S_IREAD | _S_IWRITE | _S_IEXEC);
  377. //assertex(h != HFILE_ERROR);
  378. if (h == HFILE_ERROR) // error can not be ignored!
  379. throwError1(HQLERR_ResourceCreateFailed, filename.str());
  380. totalbytes = 0;
  381. putbytes(h, "\x00\x00\x00\x00\x20\x00\x00\x00\xff\xff\x00\x00\xff\xff\x00\x00"
  382. "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 0x20);
  383. MemoryBuffer temp;
  384. ForEachItemIn(idx, resources)
  385. {
  386. ResourceItem&s = static_cast<ResourceItem&>(resources.item(idx));
  387. __int32 len = s.data.length();
  388. unsigned lenType = strlen(s.type);
  389. unsigned sizeType = (lenType+1)*2;
  390. unsigned sizeTypeName = (sizeType + 4);
  391. unsigned packedSizeTypeName = ((sizeTypeName + 2) & ~3);
  392. __int32 lenHeader = 4 + 4 + packedSizeTypeName + 4 + 2 + 2 + 4 + 4;
  393. unsigned short id = s.id;
  394. temp.clear();
  395. temp.append(sizeof(len), &len);
  396. temp.append(sizeof(lenHeader), &lenHeader);
  397. for (unsigned i=0; i < lenType; i++)
  398. temp.append((byte)s.type[i]).append((byte)0);
  399. temp.append((byte)0).append((byte)0);
  400. temp.append((byte)0xff).append((byte)0xff);
  401. temp.append(sizeof(id), &id);
  402. if (temp.length() & 2)
  403. temp.append((byte)0).append((byte)0);
  404. temp.append(4, "\x00\x00\x00\x00"); // version
  405. temp.append(12, "\x30\x10\x09\x04\x00\x00\x00\x00\x00\x00\x00\x00"); // 0x1030 memory 0x0409 language
  406. assertex(lenHeader == temp.length());
  407. putbytes(h, temp.bufferBase(), lenHeader);
  408. putbytes(h, s.data.get(), len);
  409. if (totalbytes & 3)
  410. putbytes(h, "\x00\x00\x00",4-(totalbytes & 3));
  411. }
  412. _close(h);
  413. #else
  414. isObjectFile = false;
  415. filename.append(basename).append(".res.s");
  416. FILE *f = fopen(filename, "wt");
  417. if (!f)
  418. throwError1(HQLERR_ResourceCreateFailed, filename.str());
  419. //MORE: This should really use targetCompiler instead
  420. #if defined(__APPLE__)
  421. const bool generateClang = true;
  422. #else
  423. const bool generateClang = false;
  424. #endif
  425. ForEachItemIn(idx, resources)
  426. {
  427. ResourceItem &s = (ResourceItem &) resources.item(idx);
  428. const char *type = s.type.str();
  429. unsigned id = s.id;
  430. VStringBuffer binfile("%s_%s_%u.bin", filename.str(), type, id);
  431. VStringBuffer label("%s_%u_txt_start", type, id);
  432. if (generateClang)
  433. {
  434. fprintf(f, " .section __TEXT,%s_%u\n", type, id);
  435. fprintf(f, " .global _%s\n", label.str()); // For some reason apple needs a leading underbar and linux does not
  436. fprintf(f, "_%s:\n", label.str());
  437. }
  438. else
  439. {
  440. fprintf(f, " .section .note.GNU-stack,\"\",@progbits\n"); // Prevent the stack from being marked as executable
  441. fprintf(f, " .section %s_%u,\"a\"\n", type, id);
  442. fprintf(f, " .global %s\n", label.str());
  443. fprintf(f, " .type %s,STT_OBJECT\n", label.str());
  444. fprintf(f, "%s:\n", label.str());
  445. }
  446. fprintf(f, " .incbin \"%s\"\n", binfile.str());
  447. FILE *bin = fopen(binfile, "wb");
  448. if (!bin)
  449. {
  450. fclose(f);
  451. throwError1(HQLERR_ResourceCreateFailed, binfile.str());
  452. }
  453. fwrite(s.data.get(), 1, s.data.length(), bin);
  454. fclose(bin);
  455. }
  456. fclose(f);
  457. #endif
  458. if (flushText)
  459. flushAsText(filename);
  460. return isObjectFile;
  461. }
  462. bool ResourceManager::queryWriteText(StringBuffer & resTextName, const char * filename)
  463. {
  464. int len = strlen(filename);
  465. resTextName.append(filename,0,len-4).append(".txt");
  466. return true;
  467. }
  468. #if 0
  469. int test()
  470. {
  471. ResourceManager r;
  472. r.add("Hello there!2");
  473. r.add("Hello again");
  474. r.flush("c:\\t2.res");
  475. return 6;
  476. }
  477. static int dummy = test();
  478. #endif