generator.php 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545
  1. <?php
  2. global $options;
  3. global $preamble;
  4. $preamble = "";
  5. global $existing_commands;
  6. global $included_usepackages;
  7. $included_usepackages = [];
  8. global $additional_usepackages;
  9. $additional_usepackages = [];
  10. $steps = 10;
  11. function user_consent($msg,$defaultyes = null,$defaultno = null)
  12. {
  13. global $options;
  14. if (isset($options["-n"]))
  15. return false;
  16. if ($defaultyes !== null && isset($options[$defaultyes]))
  17. return true;
  18. if ($defaultno !== null && isset($options[$defaultno]))
  19. return false;
  20. $response = false;
  21. do
  22. {
  23. echo "$msg (y/n)\n";
  24. $response = rtrim(fgets(STDIN));
  25. if ($response === "n" || $response == "N")
  26. return false;
  27. } while ($response !== "y" && $response !== "Y" && $response !== "");
  28. return true;
  29. }
  30. function user_prompt($msg,$default)
  31. {
  32. global $options;
  33. echo "$msg [Default: $default]\n";
  34. $response = "";
  35. if (!isset($options["-n"]))
  36. $response = rtrim(fgets(STDIN));
  37. if ($response == "")
  38. $response = $default;
  39. echo "You entered: '$response'\n";
  40. return $response;
  41. }
  42. function print_help()
  43. {
  44. $name = basename(__FILE__);
  45. echo<<<END
  46. Usage: php $name /path/folder1/main.tex /path/folder2/article.tex ... /path/folderN/paper.tex\n
  47. Generate a thesis template from the provided paper tex files and folders.
  48. The absolute minimum for a cumulative thesis is currently 3 papers first-authored and 6 in total.
  49. --force-overwrite overwrite files and folders
  50. --no-overwrite do not overwrite files and folders
  51. --no-compile-check do not perform a compile check
  52. --use-backups restore backups before making changes
  53. --not-sloppy disable use of sloppypar around places that struggle without
  54. (mainly the bibliographies)
  55. --cv add a CV
  56. (unusual for PhD theses, required for habilitation theses)
  57. --no-stat-declaration do not add the statutory declaration
  58. (required for PhD thesis, unusual for habilitation theses)
  59. -n skip all prompts (implies --no-overwrite)
  60. --help display this help and exit
  61. END;
  62. exit(0);
  63. }
  64. function error_exit($msg,$usage = false)
  65. {
  66. echo "ERROR: $msg\n";
  67. if ($usage)
  68. echo "Usage: php ".basename(__FILE__)." /path/folder1/main.tex /path/folder2/article.tex ... /path/folderN/paper.tex\n";
  69. exit(-1);
  70. }
  71. function check_software()
  72. {
  73. if (!str_starts_with(shell_exec("latexmk --version"),"Latexmk,"))
  74. error_exit("latexmk not installed");
  75. if (!str_starts_with(shell_exec("pdftotext --help 2>&1"),"pdftotext version"))
  76. error_exit("pdftotext not installed");
  77. }
  78. function rrmdir($dir) {
  79. if (is_dir($dir)) {
  80. $files = scandir($dir);
  81. foreach ($files as $file)
  82. if ($file != "." && $file != "..") rrmdir("$dir/$file");
  83. rmdir($dir);
  84. }
  85. else if (file_exists($dir)) unlink($dir);
  86. }
  87. function rcopy($src, $dst) {
  88. if (file_exists($dst)) rrmdir($dst);
  89. if (is_dir($src)) {
  90. mkdir($dst);
  91. $files = scandir($src);
  92. foreach ($files as $file)
  93. if ($file != "." && $file != "..") rcopy("$src/$file", "$dst/$file");
  94. }
  95. else if (file_exists($src)) copy($src, $dst);
  96. }
  97. function rscandir($dir,$extensions = []) {
  98. $result = [];
  99. if (!str_ends_with($dir,"/"))
  100. $dir .= "/";
  101. if (str_ends_with($dir,"latex.out/"))
  102. return $result;
  103. $array = scandir($dir);
  104. foreach ($array as $item) {
  105. if (!str_starts_with($item,"."))
  106. {
  107. if (!is_dir($dir.$item))
  108. {
  109. $extension_match = true;
  110. if ($extensions != [])
  111. {
  112. $extension_match = false;
  113. foreach ($extensions as $e)
  114. {
  115. if (str_ends_with($item,"$e"))
  116. {
  117. $extension_match = true;
  118. }
  119. }
  120. }
  121. if ($extension_match)
  122. $result[] = $dir.$item;
  123. }
  124. else
  125. $result = array_merge($result, rscandir($dir.$item,$extensions));
  126. }
  127. }
  128. return $result;
  129. }
  130. function unique_targets($targets)
  131. {
  132. $parts_required = 1;
  133. $unique_targets = [];
  134. while (count($unique_targets) != count($targets))
  135. {
  136. foreach ($targets as $t)
  137. {
  138. $tparts = explode("/",$t);
  139. $tpcount = count($tparts);
  140. $tparts = array_splice($tparts,$tpcount-$parts_required,$parts_required);
  141. $t = implode("_",$tparts);
  142. if (isset($unique_targets[$t]))
  143. {
  144. $parts_required++;
  145. $unique_targets = [];
  146. break;
  147. }
  148. $unique_targets[$t] = 1;
  149. }
  150. }
  151. return array_keys($unique_targets);
  152. }
  153. function check_folders($argv)
  154. {
  155. $args = array_slice($argv,1);
  156. $sourcedirs = [];
  157. foreach ($args as $a)
  158. {
  159. if (!str_starts_with($a,"-"))
  160. $sourcedirs[] = $a;
  161. }
  162. if (count($sourcedirs) < 1)
  163. {
  164. error_exit("Too few papers. Need at least one paper to run this.",true);
  165. }
  166. if (count($sourcedirs) < 3)
  167. {
  168. 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.";
  169. }
  170. if (count($sourcedirs) < 5)
  171. {
  172. 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.";
  173. }
  174. $texfiles = [];
  175. for ($i = 0; $i < count($sourcedirs); $i++)
  176. {
  177. $texfiles[$i] = basename($sourcedirs[$i]);
  178. $sourcedirs[$i] = dirname($sourcedirs[$i]);
  179. }
  180. $targets = unique_targets($sourcedirs);
  181. return [$targets,$texfiles,$sourcedirs];
  182. }
  183. function check_and_copy_folders($argv)
  184. {
  185. [$targets,$texfiles,$sourcedirs] = check_folders($argv);
  186. //echo "Source -> Target Directory Mapping:\n";
  187. $mapping = array_combine($sourcedirs,$targets);
  188. //print_r($mapping);
  189. foreach ($mapping as $s => $t)
  190. {
  191. if (!file_exists($s))
  192. error_exit("$s not found");
  193. if (file_exists($t) && !user_consent("$t already exists... overwrite?","--force-overwrite","--no-overwrite"))
  194. {
  195. echo "Skipping ".$s.", ".$t." already exists...\n";
  196. }
  197. else
  198. {
  199. echo "Copying ".$s." to ".$t."...\n";
  200. rcopy($s,$t);
  201. }
  202. }
  203. return array_combine($targets,$texfiles);
  204. }
  205. function file_get_and_backup($f,$bak = ".orig_bak")
  206. {
  207. global $options;
  208. if (isset($options["--use-backups"]))
  209. {
  210. if (file_exists("$f$bak"))
  211. copy("$f$bak", $f);
  212. }
  213. $content = file_get_contents("$f");
  214. if (!file_exists("$f$bak"))
  215. file_put_contents("$f$bak",$content);
  216. return $content;
  217. }
  218. function adjust_references($papers,$extensions = [])
  219. {
  220. $bibresources = [];
  221. foreach ($papers as $d => $f)
  222. {
  223. $files = rscandir($d,$extensions);
  224. foreach ($files as $file)
  225. {
  226. if (str_ends_with($file,".bib"))
  227. {
  228. $bib = file_get_and_backup($file);
  229. $bib = preg_replace("/(^\s*@\s*[a-z]+\s*\{\s*)((?!$d)\S)/i",'${1}'.$d.':${2}',$bib);
  230. $bib = preg_replace("/(\n\s*@\s*[a-z]+\s*\{\s*)((?!$d)\S)/i",'${1}'.$d.':${2}',$bib);
  231. file_put_contents($file,$bib);
  232. $bibresources[$d] = $file;
  233. }
  234. else
  235. {
  236. $tex = file_get_and_backup($file);
  237. // labels of lstlistings etc
  238. $tex = preg_replace("/([\[,]\s*label\s*=\s*)((?!$d)[^\[,]+[\[,])/i",'${1}'.$d.':${2}',$tex);
  239. // normal labels
  240. $tex = preg_replace("/(\\\label\s*\{\s*)((?!$d)[^}]+\})/i",'${1}'.$d.':${2}',$tex);
  241. // cref ref autoref
  242. $refs = [];
  243. preg_match_all("/\\\(cref|ref|autoref)\s*\{\s*[^}]+\}/i",$tex,$refs);
  244. foreach ($refs[0] as $ref)
  245. {
  246. $oldref = $ref;
  247. $ref = preg_replace("/([{,])\s*((?!$d)[^,}]+[},])/i",'${1}'.$d.':${2}',$ref);
  248. $ref = preg_replace("/([{,])\s*((?!$d)[^,}]+[},])/i",'${1}'.$d.':${2}',$ref);
  249. $tex = str_replace($oldref,$ref,$tex);
  250. }
  251. // cite citeA citeauthor etc
  252. $cites = [];
  253. preg_match_all("/\\\([a-z]*cite[a-z]*)\s*\{\s*[^}]+\}/i",$tex,$cites);
  254. foreach ($cites[0] as $cite)
  255. {
  256. $oldcite = $cite;
  257. $cite = preg_replace("/([{,])\s*((?!$d)[^,}]+[},])/i",'${1}'.$d.':${2}',$cite);
  258. $cite = preg_replace("/([{,])\s*((?!$d)[^,}]+[},])/i",'${1}'.$d.':${2}',$cite);
  259. $tex = str_replace($oldcite,$cite,$tex);
  260. }
  261. file_put_contents($file,$tex);
  262. }
  263. }
  264. }
  265. return $bibresources;
  266. }
  267. function stripformatting($tex)
  268. {
  269. return str_ireplace(['\Large ','\bf ','\rm ','\\\\'],[' ',' ',' ',' '],$tex);
  270. }
  271. function getusepackages($tex)
  272. {
  273. $packages = [];
  274. preg_match_all("/\n\s*(\\\\usepackage([^{}]*)(?<R>{((?:[^{}]+|(?&R))*)}))/i",$tex,$packages);
  275. if (isset($packages[1]))
  276. {
  277. return [$packages[1],$packages[4]];
  278. }
  279. return [[],[]];
  280. }
  281. function getmathops($tex)
  282. {
  283. $mathops = [];
  284. preg_match_all("/\n\s*(\\\\DeclareMathOperator([^{}]*)(?<R>{((?:[^{}]+|(?&R))*)})([^{}]*)(?<P>{((?:[^{}]+|(?&P))*)}))/i",$tex,$mathops);
  285. if (isset($mathops[4]))
  286. return [$mathops[1],$mathops[4]];
  287. return [];
  288. }
  289. function getnewcommands($tex)
  290. {
  291. $newcommands = [];
  292. preg_match_all("/\n\s*(\\\\newcommand([^{}]*)(?<R>{((?:[^{}]+|(?&R))*)})([^{}]*)(?<P>{((?:[^{}]+|(?&P))*)}))/i",$tex,$newcommands);
  293. if (isset($newcommands[4]))
  294. return $newcommands[4];
  295. return [];
  296. }
  297. function adjust_papers($papers,$extensions = [])
  298. {
  299. foreach ($papers as $d => $f)
  300. {
  301. $paper_fulltitle = "";
  302. $authors = "";
  303. $files = rscandir($d,$extensions);
  304. foreach ($files as $file)
  305. {
  306. if (!str_ends_with($file,"main.tex"))
  307. continue;
  308. $tex = file_get_and_backup($file,".interm_bak");
  309. if ($authors == "" && preg_match("/\n\s*\\\\author/i",$tex) == 1)
  310. {
  311. $authors = [];
  312. preg_match_all("/\n\s*\\\\author\s*(?<R>{((?:[^{}]+|(?&R))*)})/i",$tex,$authors);
  313. if (isset($authors[2]) && isset($authors[2][0]))
  314. $authors = trim(stripformatting($authors[2][0]));
  315. }
  316. if ($paper_fulltitle == "" && preg_match("/\n\s*\\\\title/i",$tex) == 1)
  317. {
  318. $title = [];
  319. preg_match_all("/\n\s*\\\\title\s*(?<R>{((?:[^{}]+|(?&R))*)})/i",$tex,$title);
  320. if (isset($title[2]) && isset($title[2][0]))
  321. $title = trim(stripformatting($title[2][0]));
  322. else
  323. $title = $d;
  324. $shorttitle = $title;
  325. if (strpos($title,":") !== false)
  326. $shorttitle = trim(explode(":",$title)[0]);
  327. $paper_fulltitle = trim(user_prompt("Please enter the full title for the paper in folder '$d'","$title"));
  328. $paper_shorttitle = trim(user_prompt("Please enter a short title for the paper in folder '$d'","$shorttitle"));
  329. }
  330. $header = "";
  331. if ($paper_fulltitle != "" && $authors != "")
  332. {
  333. $header=<<<END
  334. \chapter[$paper_shorttitle]{$paper_fulltitle}\label{chapter:$d}
  335. \section*{Publication Data}
  336. \\fullcite{TODO}
  337. \section*{Contributions}
  338. TODO
  339. \\newpage
  340. \begin{center}
  341. {\Large \\textbf{
  342. $paper_fulltitle%
  343. }}\\\\\
  344. \\vspace{0.6cm}
  345. {\large $authors%
  346. } % TODO: check author list
  347. \\end{center}
  348. END;
  349. }
  350. $tex = preg_replace("/\\\documentclass.*\\n/i",'',$tex);
  351. $tex = preg_replace("/\\\begin{document}/i",$header,$tex);
  352. $tex = preg_replace("/\\\begin{abstract}/i",'\section*{Abstract}',$tex);
  353. $tex = preg_replace("/\\\end{abstract}/i",'',$tex);
  354. $tex = preg_replace("/\\\end{document}/i",'',$tex);
  355. $tex = preg_replace("/\n\s*\\\\author\s*(?<R>{((?:[^{}]+|(?&R))*)})/i","",$tex);
  356. $tex = preg_replace("/\n\s*\\\\title\s*(?<R>{((?:[^{}]+|(?&R))*)})/i","",$tex);
  357. $tex = preg_replace("/\n\s*\\\\date\s*(?<R>{((?:[^{}]+|(?&R))*)})/i","",$tex);
  358. $tex = preg_replace("/\n\s*\\\\maketitle/i","",$tex);
  359. [$ups,$ps] = getusepackages($tex);
  360. global $included_usepackages;
  361. global $additional_usepackages;
  362. foreach (array_diff($ps,$included_usepackages) as $k => $v) {
  363. if ($v != "usenix,epsfig,endnotes" && $v != "cite" && !($v == 'floatrow' && in_array('float',$included_usepackages)))
  364. {
  365. $additional_usepackages[] = $ups[$k];
  366. $included_usepackages[] = $v;
  367. }
  368. }
  369. $additional_usepackages = array_unique($additional_usepackages);
  370. sort($additional_usepackages);
  371. $tex = preg_replace("/\n\s*\\\\usepackage([^{}]*)(?<R>{((?:[^{}]+|(?&R))*)})/i","",$tex);
  372. $newcommands = getnewcommands($tex);
  373. global $existing_commands;
  374. $rmcommands = array_intersect($newcommands,$existing_commands);
  375. foreach ($rmcommands as $rmc)
  376. {
  377. $tex = preg_replace("/\n\s*\\\\newcommand([^{}]*)({\s*\\".$rmc."\s*}.*\n)/i","\n",$tex);
  378. }
  379. [$mathopsfull,$mathops] = getmathops($tex);
  380. global $existing_mathops;
  381. global $preamble;
  382. $addmathops = array_diff($mathops,$existing_mathops);
  383. foreach ($addmathops as $k => $v)
  384. {
  385. $preamble .= $mathopsfull[$k] . "\n";
  386. $existing_mathops[] = $v;
  387. }
  388. $rmmathops = array_intersect($mathops,$existing_mathops);
  389. foreach ($rmmathops as $rmo)
  390. {
  391. $tex = preg_replace("/\n\s*\\\\DeclareMathOperator([^{}]*)({\s*\\".$rmo."\s*}.*\n)/i","\n",$tex);
  392. }
  393. $matches = [];
  394. $tex = preg_replace("/\n\s*\\\\pgfplotsset\s*(?<R>{((?:[^{}]+|(?&R))*)})/i","",$tex);
  395. $tex = preg_replace("/\n\s*\\\\setlength{\s*\\\marginparwidth.*\n/i","\n",$tex);
  396. file_put_contents($file,$tex);
  397. }
  398. }
  399. return;
  400. }
  401. function check_options($argv)
  402. {
  403. $options = [];
  404. foreach ($argv as $a)
  405. {
  406. if (str_starts_with($a,"-"))
  407. $options[$a] = 1;
  408. if ($a == "-n")
  409. $options["--no-overwrite"] = 1;
  410. }
  411. return $options;
  412. }
  413. function compile_check($papers,$precopy = false)
  414. {
  415. if ($precopy)
  416. $papers = array_combine($papers,$papers);
  417. foreach ($papers as $p => $f)
  418. {
  419. echo "======= Paper $p -> $f START =======\n";
  420. if ($precopy)
  421. {
  422. echo "$p\n";
  423. if (strpos($p,"/") === false || str_starts_with($p,".") || !is_dir(dirname($p)))
  424. continue;
  425. $f = basename($p);
  426. $p = dirname($p);
  427. }
  428. else
  429. {
  430. shell_exec("cd $p; latexmk -c 2>/dev/null; latexmk -C 2>/dev/null; rm -f *.bbl");
  431. }
  432. $str = shell_exec("cd $p; latexmk -latexoption=\"-shell-escape\" -g -pdf $f 2>&1");
  433. $lines = explode("\n",$str);
  434. $skip = true;
  435. $failed = false;
  436. foreach($lines as $l)
  437. {
  438. if (str_starts_with($l,"Latexmk: ====List of undefined refs and citations:"))
  439. $skip = false;
  440. if ($skip)
  441. continue;
  442. if (stripos($l,"fail") !== false)
  443. $failed = true;
  444. echo "$l\n";
  445. }
  446. 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"))
  447. error_exit("The paper did not compile properly, please fix the errors next before continuing.");
  448. echo "======= Paper $p -> $f END =======\n";
  449. }
  450. }
  451. check_software();
  452. $options = check_options($argv);
  453. if (isset($options["--help"]))
  454. print_help();
  455. echo "\n=== Step 1/$steps: Check Folders ===\n\n";
  456. [$targets,$texfiles,$sourcedirs] = check_folders($argv);
  457. echo "\n=== Step 2/$steps: Pre-Copy Compile Check ===\n\n";
  458. $targets_exist = true;
  459. foreach ($targets as $t)
  460. if (!is_dir($t))
  461. $targets_exist = false;
  462. if (isset($options["--no-compile-check"]) || $targets_exist)
  463. echo "Skipping...\n";
  464. else
  465. compile_check($argv,true);
  466. echo "\n=== Step 3/$steps: Copying Files ===\n\n";
  467. $papers = check_and_copy_folders($argv);
  468. echo "\n=== Step 4/$steps: Adjusting References (in *.bib *.tex *.tikz) ===\n\n";
  469. $bibresources = adjust_references($papers,[".bib",".tex",".tikz"]);
  470. echo "\n=== Step 5/$steps: Compile Check ===\n\n";
  471. if (isset($options["--no-compile-check"]))
  472. echo "Skipping...\n";
  473. else
  474. compile_check($papers);
  475. [$_t,$usepackages1] = getusepackages(file_get_contents("main.tex.php"));
  476. [$_t,$usepackages2] = getusepackages(file_get_contents("tugraz_defaults.sty"));
  477. global $included_usepackages;
  478. $included_usepackages = array_unique(array_merge($usepackages1, $usepackages2));
  479. sort($included_usepackages);
  480. $cmds1 = getnewcommands(file_get_contents("main.tex.php"));
  481. $cmds2 = getnewcommands(file_get_contents("tugraz_defaults.sty"));
  482. global $existing_commands;
  483. $existing_commands = array_unique(array_merge($cmds1, $cmds2));
  484. [$del,$mathops1] = getmathops(file_get_contents("main.tex.php"));
  485. [$del,$mathops2] = getmathops(file_get_contents("tugraz_defaults.sty"));
  486. global $existing_mathops;
  487. $existing_mathops = array_unique(array_merge($mathops1, $mathops2));
  488. echo "\n=== Step 5/$steps: Adjusting Papers (checking only *.tex files) ===\n\n";
  489. adjust_papers($papers,[".tex"]);
  490. echo "\n=== Step 6/$steps: Generate main.tex ===\n\n";
  491. $thesis_type = user_prompt("Enter Thesis Type ","PhD Thesis");
  492. $thesis_title = user_prompt("Enter Thesis Title ","Security of TODO");
  493. $thesis_part1_title = user_prompt("Enter Introductory Part Title ","Introduction to the Security of TODO");
  494. $thesis_author = user_prompt("Enter Your Name ","Harry Potter");
  495. $thesis_date = user_prompt("Enter Prospective Defense Month", "July 1998");
  496. $thesis_institute = user_prompt("Enter Your Institute","Institute for Applied Information Processing and Communications");
  497. $thesis_assessors = user_prompt("Enter Names of your Assessors (comma-separated)","Severus Snape, Minerva McGonagall");
  498. $num_publications_in_thesis = count($papers);
  499. $num_publications = user_prompt("How many publications did you co-author during your PhD? (6 is the absolute minimum for a cumulative thesis)","6");
  500. $sloppy_begin = "";
  501. $sloppy_end = "";
  502. if (!isset($options["--no-sloppy"]))
  503. {
  504. $sloppy_begin = '\begin{sloppypar}';
  505. $sloppy_end = '\end{sloppypar}';
  506. }
  507. ob_start();
  508. require "main.tex.php";
  509. $maintex_content = ob_get_contents();
  510. ob_end_clean();
  511. //if (!file_exists("main.tex") || user_consent("main.tex already exists. overwrite?")) // TODO: enable this check
  512. file_put_contents("main.tex",$maintex_content);
  513. if (!file_exists("mypreamble.sty"))
  514. file_put_contents("mypreamble.sty","\ProvidesPackage{mypreamble}\n\n".implode("\n",$additional_usepackages)."\n\n".$preamble);
  515. @mkdir("tikz");
  516. if (!file_exists("abstract.tex"))
  517. file_put_contents("abstract.tex","");
  518. if (!file_exists("intro.tex"))
  519. file_put_contents("intro.tex","");
  520. if (!file_exists("main.bib"))
  521. file_put_contents("main.bib","");
  522. if (isset($options["--cv"]))
  523. {
  524. if (!file_exists("cv.bib"))
  525. file_put_contents("cv.bib","");
  526. if (!file_exists("cv.tex"))
  527. file_put_contents("cv.tex","This is my CV! Thanks for checking it out!");
  528. }
  529. ?>