generator.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404
  1. <?php
  2. global $options;
  3. $steps = 10;
  4. function user_consent($msg,$defaultyes = null,$defaultno = null)
  5. {
  6. global $options;
  7. if (isset($options["-n"]))
  8. return false;
  9. if ($defaultyes !== null && isset($options[$defaultyes]))
  10. return true;
  11. if ($defaultno !== null && isset($options[$defaultno]))
  12. return false;
  13. $response = false;
  14. do
  15. {
  16. echo "$msg (y/n)\n";
  17. $response = rtrim(fgets(STDIN));
  18. if ($response === "n" || $response == "N")
  19. return false;
  20. } while ($response !== "y" && $response !== "Y" && $response !== "");
  21. return true;
  22. }
  23. function user_prompt($msg,$default)
  24. {
  25. global $options;
  26. echo "$msg [Default: $default]\n";
  27. $response = "";
  28. if (!isset($options["-n"]))
  29. $response = rtrim(fgets(STDIN));
  30. if ($response == "")
  31. $response = $default;
  32. echo "You entered: '$response'\n";
  33. return $response;
  34. }
  35. function print_help()
  36. {
  37. $name = basename(__FILE__);
  38. echo<<<END
  39. Usage: php $name /path/folder1/main.tex /path/folder2/article.tex ... /path/folderN/paper.tex\n
  40. Generate a thesis template from the provided paper tex files and folders.
  41. The absolute minimum for a cumulative thesis is currently 3 papers first-authored and 6 in total.
  42. --force-overwrite overwrite files and folders
  43. --no-overwrite do not overwrite files and folders
  44. --no-compile-check do not perform a compile check
  45. --use-backups restore backups before making changes
  46. --cv add a CV
  47. (unusual for PhD theses, required for habilitation theses)
  48. --no-stat-declaration do not add the statutory declaration
  49. (required for PhD thesis, unusual for habilitation theses)
  50. -n skip all prompts (implies --no-overwrite)
  51. --help display this help and exit
  52. END;
  53. exit(0);
  54. }
  55. function error_exit($msg,$usage = false)
  56. {
  57. echo "ERROR: $msg\n";
  58. if ($usage)
  59. echo "Usage: php ".basename(__FILE__)." /path/folder1/main.tex /path/folder2/article.tex ... /path/folderN/paper.tex\n";
  60. exit(-1);
  61. }
  62. function check_software()
  63. {
  64. if (!str_starts_with(shell_exec("latexmk --version"),"Latexmk,"))
  65. error_exit("latexmk not installed");
  66. if (!str_starts_with(shell_exec("pdftotext --help 2>&1"),"pdftotext version"))
  67. error_exit("pdftotext not installed");
  68. }
  69. function rrmdir($dir) {
  70. if (is_dir($dir)) {
  71. $files = scandir($dir);
  72. foreach ($files as $file)
  73. if ($file != "." && $file != "..") rrmdir("$dir/$file");
  74. rmdir($dir);
  75. }
  76. else if (file_exists($dir)) unlink($dir);
  77. }
  78. function rcopy($src, $dst) {
  79. if (file_exists($dst)) rrmdir($dst);
  80. if (is_dir($src)) {
  81. mkdir($dst);
  82. $files = scandir($src);
  83. foreach ($files as $file)
  84. if ($file != "." && $file != "..") rcopy("$src/$file", "$dst/$file");
  85. }
  86. else if (file_exists($src)) copy($src, $dst);
  87. }
  88. function rscandir($dir,$extensions = []) {
  89. $result = [];
  90. if (!str_ends_with($dir,"/"))
  91. $dir .= "/";
  92. if (str_ends_with($dir,"latex.out/"))
  93. return $result;
  94. $array = scandir($dir);
  95. foreach ($array as $item) {
  96. if (!str_starts_with($item,"."))
  97. {
  98. if (!is_dir($dir.$item))
  99. {
  100. $extension_match = true;
  101. if ($extensions != [])
  102. {
  103. $extension_match = false;
  104. foreach ($extensions as $e)
  105. {
  106. if (str_ends_with($item,"$e"))
  107. {
  108. $extension_match = true;
  109. }
  110. }
  111. }
  112. if ($extension_match)
  113. $result[] = $dir.$item;
  114. }
  115. else
  116. $result = array_merge($result, rscandir($dir.$item,$extensions));
  117. }
  118. }
  119. return $result;
  120. }
  121. function unique_targets($targets)
  122. {
  123. $parts_required = 1;
  124. $unique_targets = [];
  125. while (count($unique_targets) != count($targets))
  126. {
  127. foreach ($targets as $t)
  128. {
  129. $tparts = explode("/",$t);
  130. $tpcount = count($tparts);
  131. $tparts = array_splice($tparts,$tpcount-$parts_required,$parts_required);
  132. $t = implode("_",$tparts);
  133. if (isset($unique_targets[$t]))
  134. {
  135. $parts_required++;
  136. $unique_targets = [];
  137. break;
  138. }
  139. $unique_targets[$t] = 1;
  140. }
  141. }
  142. return array_keys($unique_targets);
  143. }
  144. function check_folders($argv)
  145. {
  146. $args = array_slice($argv,1);
  147. $sourcedirs = [];
  148. foreach ($args as $a)
  149. {
  150. if (!str_starts_with($a,"-"))
  151. $sourcedirs[] = $a;
  152. }
  153. if (count($sourcedirs) < 1)
  154. {
  155. error_exit("Too few papers. Need at least one paper to run this.",true);
  156. }
  157. if (count($sourcedirs) < 3)
  158. {
  159. echo "Warning: Too few papers. The statutes require as an absolute minimum for a cumulative thesis at least 3 first-author papers and at least 6 in total.";
  160. }
  161. if (count($sourcedirs) < 5)
  162. {
  163. echo "Warning: Few papers. Check your institute guidelines to see how many papers are recommended. Also the statutes require as an absolute minimum for a cumulative thesis at least 6 papers in total.";
  164. }
  165. $texfiles = [];
  166. for ($i = 0; $i < count($sourcedirs); $i++)
  167. {
  168. $texfiles[$i] = basename($sourcedirs[$i]);
  169. $sourcedirs[$i] = dirname($sourcedirs[$i]);
  170. }
  171. $targets = unique_targets($sourcedirs);
  172. return [$targets,$texfiles,$sourcedirs];
  173. }
  174. function check_and_copy_folders($argv)
  175. {
  176. [$targets,$texfiles,$sourcedirs] = check_folders($argv);
  177. //echo "Source -> Target Directory Mapping:\n";
  178. $mapping = array_combine($sourcedirs,$targets);
  179. //print_r($mapping);
  180. foreach ($mapping as $s => $t)
  181. {
  182. if (!file_exists($s))
  183. error_exit("$s not found");
  184. if (file_exists($t) && !user_consent("$t already exists... overwrite?","--force-overwrite","--no-overwrite"))
  185. {
  186. echo "Skipping ".$s.", ".$t." already exists...\n";
  187. }
  188. else
  189. {
  190. echo "Copying ".$s." to ".$t."...\n";
  191. rcopy($s,$t);
  192. }
  193. }
  194. return array_combine($targets,$texfiles);
  195. }
  196. function file_get_and_backup($f,$bak = ".orig_bak")
  197. {
  198. global $options;
  199. if (isset($options["--use-backups"]))
  200. {
  201. if (file_exists("$f$bak"))
  202. copy("$f$bak", $f);
  203. }
  204. $content = file_get_contents("$f");
  205. if (!file_exists("$f$bak"))
  206. file_put_contents("$f$bak",$content);
  207. return $content;
  208. }
  209. function adjust_references($papers,$extensions = [])
  210. {
  211. $bibresources = [];
  212. foreach ($papers as $d => $f)
  213. {
  214. $files = rscandir($d,$extensions);
  215. foreach ($files as $file)
  216. {
  217. if (str_ends_with($file,".bib"))
  218. {
  219. $bib = file_get_and_backup($file);
  220. $bib = preg_replace("/(^\s*@\s*[a-z]+\s*\{\s*)((?!$d)\S)/i",'${1}'.$d.':${2}',$bib);
  221. $bib = preg_replace("/(\n\s*@\s*[a-z]+\s*\{\s*)((?!$d)\S)/i",'${1}'.$d.':${2}',$bib);
  222. file_put_contents($file,$bib);
  223. $bibresources[$d] = $file;
  224. }
  225. else
  226. {
  227. $tex = file_get_and_backup($file);
  228. // labels of lstlistings etc
  229. $tex = preg_replace("/([\[,]\s*label\s*=\s*)((?!$d)[^\[,]+[\[,])/i",'${1}'.$d.':${2}',$tex);
  230. // normal labels
  231. $tex = preg_replace("/(\\\label\s*\{\s*)((?!$d)[^}]+\})/i",'${1}'.$d.':${2}',$tex);
  232. // cref ref autoref
  233. $refs = [];
  234. preg_match_all("/\\\(cref|ref|autoref)\s*\{\s*[^}]+\}/i",$tex,$refs);
  235. foreach ($refs[0] as $ref)
  236. {
  237. $oldref = $ref;
  238. $ref = preg_replace("/([{,])\s*((?!$d)[^,}]+[},])/i",'${1}'.$d.':${2}',$ref);
  239. $ref = preg_replace("/([{,])\s*((?!$d)[^,}]+[},])/i",'${1}'.$d.':${2}',$ref);
  240. $tex = str_replace($oldref,$ref,$tex);
  241. }
  242. // cite citeA citeauthor etc
  243. $cites = [];
  244. preg_match_all("/\\\([a-z]*cite[a-z]*)\s*\{\s*[^}]+\}/i",$tex,$cites);
  245. foreach ($cites[0] as $cite)
  246. {
  247. $oldcite = $cite;
  248. $cite = preg_replace("/([{,])\s*((?!$d)[^,}]+[},])/i",'${1}'.$d.':${2}',$cite);
  249. $cite = preg_replace("/([{,])\s*((?!$d)[^,}]+[},])/i",'${1}'.$d.':${2}',$cite);
  250. $tex = str_replace($oldcite,$cite,$tex);
  251. }
  252. file_put_contents($file,$tex);
  253. }
  254. }
  255. }
  256. return $bibresources;
  257. }
  258. function adjust_papers($papers,$extensions = [])
  259. {
  260. foreach ($papers as $d => $f)
  261. {
  262. $files = rscandir($d,$extensions);
  263. foreach ($files as $file)
  264. {
  265. $tex = file_get_and_backup($file,".interm_bak");
  266. // labels of lstlistings etc
  267. $tex = preg_replace("/([\[,]\s*label\s*=\s*)((?!$d)[^\[,]+[\[,])/i",'${1}'.$d.':${2}',$tex);
  268. // normal labels
  269. $tex = preg_replace("/(\\\label\s*\{\s*)((?!$d)[^}]+\})/i",'${1}'.$d.':${2}',$tex);
  270. // cref ref autoref
  271. $refs = [];
  272. preg_match_all("/\\\(cref|ref|autoref)\s*\{\s*[^}]+\}/i",$tex,$refs);
  273. foreach ($refs[0] as $ref)
  274. {
  275. $oldref = $ref;
  276. $ref = preg_replace("/([{,])\s*((?!$d)[^,}]+[},])/i",'${1}'.$d.':${2}',$ref);
  277. $ref = preg_replace("/([{,])\s*((?!$d)[^,}]+[},])/i",'${1}'.$d.':${2}',$ref);
  278. $tex = str_replace($oldref,$ref,$tex);
  279. }
  280. // cite citeA citeauthor etc
  281. $cites = [];
  282. preg_match_all("/\\\([a-z]*cite[a-z]*)\s*\{\s*[^}]+\}/i",$tex,$cites);
  283. foreach ($cites[0] as $cite)
  284. {
  285. $oldcite = $cite;
  286. $cite = preg_replace("/([{,])\s*((?!$d)[^,}]+[},])/i",'${1}'.$d.':${2}',$cite);
  287. $cite = preg_replace("/([{,])\s*((?!$d)[^,}]+[},])/i",'${1}'.$d.':${2}',$cite);
  288. $tex = str_replace($oldcite,$cite,$tex);
  289. }
  290. file_put_contents($file,$tex);
  291. }
  292. }
  293. return $bibresources;
  294. }
  295. function check_options($argv)
  296. {
  297. $options = [];
  298. foreach ($argv as $a)
  299. {
  300. if (str_starts_with($a,"-"))
  301. $options[$a] = 1;
  302. if ($a == "-n")
  303. $options["--no-overwrite"] = 1;
  304. }
  305. return $options;
  306. }
  307. function compile_check($papers,$precopy = false)
  308. {
  309. if ($precopy)
  310. $papers = array_combine($papers,$papers);
  311. foreach ($papers as $p => $f)
  312. {
  313. echo "======= Paper $p -> $f START =======\n";
  314. if ($precopy)
  315. {
  316. echo "$p\n";
  317. if (strpos($p,"/") === false || str_starts_with($p,".") || !is_dir(dirname($p)))
  318. continue;
  319. $f = basename($p);
  320. $p = dirname($p);
  321. }
  322. else
  323. {
  324. shell_exec("cd $p; latexmk -c 2>/dev/null; latexmk -C 2>/dev/null; rm -f *.bbl");
  325. }
  326. $str = shell_exec("cd $p; latexmk -latexoption=\"-shell-escape\" -g -pdf $f 2>&1");
  327. $lines = explode("\n",$str);
  328. $skip = true;
  329. $failed = false;
  330. foreach($lines as $l)
  331. {
  332. if (str_starts_with($l,"Latexmk: ====List of undefined refs and citations:"))
  333. $skip = false;
  334. if ($skip)
  335. continue;
  336. if (stripos($l,"fail") !== false)
  337. $failed = true;
  338. echo "$l\n";
  339. }
  340. if ($failed || user_consent("Please check the PDF file. Are there any problems with invalid references to figures, tables or with the bibliography?","--","--no-overwrite"))
  341. error_exit("The paper did not compile properly, please fix the errors next before continuing.");
  342. echo "======= Paper $p -> $f END =======\n";
  343. }
  344. }
  345. check_software();
  346. $options = check_options($argv);
  347. if (isset($options["--help"]))
  348. print_help();
  349. echo "\n=== Step 1/$steps: Check Folders ===\n\n";
  350. [$targets,$texfiles,$sourcedirs] = check_folders($argv);
  351. echo "\n=== Step 2/$steps: Pre-Copy Compile Check ===\n\n";
  352. $targets_exist = true;
  353. foreach ($targets as $t)
  354. if (!is_dir($t))
  355. $targets_exist = false;
  356. if (isset($options["--no-compile-check"]) || $targets_exist)
  357. echo "Skipping...\n";
  358. else
  359. compile_check($argv,true);
  360. echo "\n=== Step 3/$steps: Copying Files ===\n\n";
  361. $papers = check_and_copy_folders($argv);
  362. echo "\n=== Step 4/$steps: Adjusting References (in *.bib *.tex *.tikz) ===\n\n";
  363. $bibresources = adjust_references($papers,[".bib",".tex",".tikz"]);
  364. echo "\n=== Step 5/$steps: Compile Check ===\n\n";
  365. if (isset($options["--no-compile-check"]))
  366. echo "Skipping...\n";
  367. else
  368. compile_check($papers);
  369. echo "\n=== Step 5/$steps: Adjusting Papers (checking only *.tex files) ===\n\n";
  370. adjust_papers($papers,[".tex"]);
  371. echo "\n=== Step 6/$steps: Generate main.tex ===\n\n";
  372. $thesis_type = user_prompt("Enter Thesis Type ","PhD Thesis");
  373. $thesis_title = user_prompt("Enter Thesis Title ","Security of TODO");
  374. $thesis_part1_title = user_prompt("Enter Introductory Part Title ","Introduction to the Security of TODO");
  375. $thesis_author = user_prompt("Enter Your Name ","Harry Potter");
  376. $thesis_date = user_prompt("Enter Prospective Defense Month", "July 1998");
  377. $thesis_institute = user_prompt("Enter Your Institute","Institute for Applied Information Processing and Communications");
  378. $thesis_assessors = user_prompt("Enter Names of your Assessors (comma-separated)","Severus Snape, Minerva McGonagall");
  379. $num_publications_in_thesis = count($papers);
  380. $num_publications = user_prompt("How many publications did you co-author during your PhD? (6 is the absolute minimum for a cumulative thesis)","6");
  381. ob_start();
  382. require "main.tex.php";
  383. $maintex_content = ob_get_contents();
  384. ob_end_clean();
  385. //if (!file_exists("main.tex") || user_consent("main.tex already exists. overwrite?")) // TODO: enable this check
  386. file_put_contents("main.tex",$maintex_content);
  387. if (!file_exists("abstract.tex"))
  388. file_put_contents("abstract.tex","");
  389. if (!file_exists("intro.tex"))
  390. file_put_contents("intro.tex","");
  391. if (!file_exists("main.bib"))
  392. file_put_contents("main.bib","");
  393. if (isset($options["--cv"]))
  394. {
  395. if (!file_exists("cv.bib"))
  396. file_put_contents("cv.bib","");
  397. if (!file_exists("cv.tex"))
  398. file_put_contents("cv.tex","This is my CV! Thanks for checking it out!");
  399. }
  400. ?>