2 rcs_id('$Id: loadsave.php,v 1.77 2003-02-21 04:12:05 dairiki Exp $');
5 Copyright 1999, 2000, 2001, 2002 $ThePhpWikiProgrammingTeam
7 This file is part of PhpWiki.
9 PhpWiki is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 PhpWiki is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with PhpWiki; if not, write to the Free Software
21 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 require_once("lib/ziplib.php");
26 require_once("lib/Template.php");
28 function StartLoadDump(&$request, $title, $html = '')
30 // FIXME: This is a hack
31 $tmpl = Template('html', array('TITLE' => $title,
33 'CONTENT' => '%BODY%'));
34 echo ereg_replace('%BODY%.*', '', $tmpl->getExpansion($html));
37 function EndLoadDump(&$request)
39 // FIXME: This is a hack
40 $pagelink = WikiLink($request->getPage());
42 PrintXML(HTML::p(HTML::strong(_("Complete."))),
43 HTML::p(fmt("Return to %s", $pagelink)));
44 echo "</body></html>\n";
48 ////////////////////////////////////////////////////////////////
50 // Functions for dumping.
52 ////////////////////////////////////////////////////////////////
56 * http://www.nacs.uci.edu/indiv/ehood/MIME/2045/rfc2045.html
57 * http://www.faqs.org/rfcs/rfc2045.html
58 * (RFC 1521 has been superceeded by RFC 2045 & others).
60 * Also see http://www.faqs.org/rfcs/rfc2822.html
62 function MailifyPage ($page, $nversions = 1)
64 $current = $page->getCurrentRevision();
67 if (STRICT_MAILABLE_PAGEDUMPS) {
68 $from = defined('SERVER_ADMIN') ? SERVER_ADMIN : 'foo@bar';
69 //This is for unix mailbox format: (not RFC (2)822)
70 // $head .= "From $from " . CTime(time()) . "\r\n";
71 $head .= "Subject: " . rawurlencode($page->getName()) . "\r\n";
72 $head .= "From: $from (PhpWiki)\r\n";
73 // RFC 2822 requires only a Date: and originator (From:)
74 // field, however the obsolete standard RFC 822 also
75 // requires a destination field.
76 $head .= "To: $from (PhpWiki)\r\n";
78 $head .= "Date: " . Rfc2822DateTime($current->get('mtime')) . "\r\n";
79 $head .= sprintf("Mime-Version: 1.0 (Produced by PhpWiki %s)\r\n",
82 // This should just be entered by hand (or by script?)
83 // in the actual pgsrc files, since only they should have
85 //$head .= "X-Rcs-Id: \$Id\$\r\n";
87 $iter = $page->getAllRevisions();
89 while ($revision = $iter->next()) {
90 $parts[] = MimeifyPageRevision($revision);
91 if ($nversions > 0 && count($parts) >= $nversions)
94 if (count($parts) > 1)
95 return $head . MimeMultipart($parts);
97 return $head . $parts[0];
101 * Compute filename to used for storing contents of a wiki page.
103 * Basically we do a rawurlencode() which encodes everything except
104 * ASCII alphanumerics and '.', '-', and '_'.
106 * But we also want to encode leading dots to avoid filenames like
107 * '.', and '..'. (Also, there's no point in generating "hidden" file
108 * names, like '.foo'.)
110 * @param $pagename string Pagename.
111 * @return string Filename for page.
113 function FilenameForPage ($pagename)
115 $enc = rawurlencode($pagename);
116 return preg_replace('/^\./', '%2e', $enc);
120 * The main() function which generates a zip archive of a PhpWiki.
122 * If $include_archive is false, only the current version of each page
123 * is included in the zip file; otherwise all archived versions are
126 function MakeWikiZip (&$request)
128 if ($request->getArg('include') == 'all') {
129 $zipname = "wikidb.zip";
130 $include_archive = true;
133 $zipname = "wiki.zip";
134 $include_archive = false;
139 $zip = new ZipWriter("Created by PhpWiki " . PHPWIKI_VERSION, $zipname);
141 $dbi = $request->getDbh();
142 $pages = $dbi->getAllPages();
143 while ($page = $pages->next()) {
144 if (! ini_get('safe_mode'))
145 set_time_limit(30); // Reset watchdog.
147 $current = $page->getCurrentRevision();
148 if ($current->getVersion() == 0)
152 $attrib = array('mtime' => $current->get('mtime'),
154 if ($page->get('locked'))
155 $attrib['write_protected'] = 1;
157 if ($include_archive)
158 $content = MailifyPage($page, 0);
160 $content = MailifyPage($page);
162 $zip->addRegularFile( FilenameForPage($page->getName()),
168 function DumpToDir (&$request)
170 $directory = $request->getArg('directory');
171 if (empty($directory))
172 $request->finish(_("You must specify a directory to dump to"));
174 // see if we can access the directory the user wants us to use
175 if (! file_exists($directory)) {
176 if (! mkdir($directory, 0755))
177 $request->finish(fmt("Cannot create directory '%s'", $directory));
179 $html = HTML::p(fmt("Created directory '%s' for the page dump...",
182 $html = HTML::p(fmt("Using directory '%s'", $directory));
185 StartLoadDump($request, _("Dumping Pages"), $html);
187 $dbi = $request->getDbh();
188 $pages = $dbi->getAllPages();
190 while ($page = $pages->next()) {
191 if (! ini_get('safe_mode'))
192 set_time_limit(30); // Reset watchdog.
194 $filename = FilenameForPage($page->getName());
196 $msg = HTML(HTML::br(), $page->getName(), ' ... ');
198 if($page->getName() != $filename) {
199 $msg->pushContent(HTML::small(fmt("saved as %s", $filename)),
203 if ($request->getArg('include') == 'all')
204 $data = MailifyPage($page, 0);
206 $data = MailifyPage($page);
208 if ( !($fd = fopen("$directory/$filename", "w")) ) {
209 $msg->pushContent(HTML::strong(fmt("couldn't open file '%s' for writing",
210 "$directory/$filename")));
211 $request->finish($msg);
214 $num = fwrite($fd, $data, strlen($data));
215 $msg->pushContent(HTML::small(fmt("%s bytes written", $num)));
219 assert($num == strlen($data));
223 EndLoadDump($request);
227 function DumpHtmlToDir (&$request)
229 $directory = $request->getArg('directory');
230 if (empty($directory))
231 $request->finish(_("You must specify a directory to dump to"));
233 // see if we can access the directory the user wants us to use
234 if (! file_exists($directory)) {
235 if (! mkdir($directory, 0755))
236 $request->finish(fmt("Cannot create directory '%s'", $directory));
238 $html = HTML::p(fmt("Created directory '%s' for the page dump...",
241 $html = HTML::p(fmt("Using directory '%s'", $directory));
244 StartLoadDump($request, _("Dumping Pages"), $html);
246 $dbi = $request->getDbh();
247 $pages = $dbi->getAllPages();
249 global $HTML_DUMP_SUFFIX, $Theme;
250 if ($HTML_DUMP_SUFFIX)
251 $Theme->HTML_DUMP_SUFFIX = $HTML_DUMP_SUFFIX;
253 while ($page = $pages->next()) {
254 if (! ini_get('safe_mode'))
255 set_time_limit(30); // Reset watchdog.
257 $pagename = $page->getName();
258 $filename = FilenameForPage($pagename) . $Theme->HTML_DUMP_SUFFIX;
260 $msg = HTML(HTML::br(), $pagename, ' ... ');
262 if($page->getName() != $filename) {
263 $msg->pushContent(HTML::small(fmt("saved as %s", $filename)),
267 $revision = $page->getCurrentRevision();
269 $transformedContent = $revision->getTransformedContent();
271 $template = new Template('browse', $request,
272 array('revision' => $revision,
273 'CONTENT' => $transformedContent));
275 $data = GeneratePageasXML($template, $pagename);
277 if ( !($fd = fopen("$directory/$filename", "w")) ) {
278 $msg->pushContent(HTML::strong(fmt("couldn't open file '%s' for writing",
279 "$directory/$filename")));
280 $request->finish($msg);
283 $num = fwrite($fd, $data, strlen($data));
284 $msg->pushContent(HTML::small(fmt("%s bytes written", $num) . "\n"));
288 assert($num == strlen($data));
292 //CopyImageFiles() will go here;
293 $Theme->$HTML_DUMP_SUFFIX = '';
295 EndLoadDump($request);
298 /* Known problem: any plugins or other code which echo()s text will
299 * lead to a corrupted html zip file which may produce the following
300 * errors upon unzipping:
302 * warning [wikihtml.zip]: 2401 extra bytes at beginning or within zipfile
303 * file #58: bad zipfile offset (local header sig): 177561
304 * (attempting to re-compensate)
306 * However, the actual wiki page data should be unaffected.
308 function MakeWikiZipHtml (&$request)
310 $zipname = "wikihtml.zip";
311 $zip = new ZipWriter("Created by PhpWiki " . PHPWIKI_VERSION, $zipname);
312 $dbi = $request->getDbh();
313 $pages = $dbi->getAllPages();
315 global $HTML_DUMP_SUFFIX, $Theme;
316 if ($HTML_DUMP_SUFFIX)
317 $Theme->HTML_DUMP_SUFFIX = $HTML_DUMP_SUFFIX;
319 while ($page = $pages->next()) {
320 if (! ini_get('safe_mode'))
321 set_time_limit(30); // Reset watchdog.
323 $current = $page->getCurrentRevision();
324 if ($current->getVersion() == 0)
327 $attrib = array('mtime' => $current->get('mtime'),
329 if ($page->get('locked'))
330 $attrib['write_protected'] = 1;
332 $pagename = $page->getName();
333 $filename = FilenameForPage($pagename) . $Theme->HTML_DUMP_SUFFIX;
334 $revision = $page->getCurrentRevision();
336 $transformedContent = $revision->getTransformedContent();
338 $template = new Template('browse', $request,
339 array('revision' => $revision,
340 'CONTENT' => $transformedContent));
342 $data = GeneratePageasXML($template, $pagename);
344 $zip->addRegularFile( $filename, $data, $attrib);
346 // FIXME: Deal with images here.
348 $Theme->$HTML_DUMP_SUFFIX = '';
352 ////////////////////////////////////////////////////////////////
354 // Functions for restoring.
356 ////////////////////////////////////////////////////////////////
358 function SavePage (&$request, $pageinfo, $source, $filename)
360 $pagedata = $pageinfo['pagedata']; // Page level meta-data.
361 $versiondata = $pageinfo['versiondata']; // Revision level meta-data.
363 if (empty($pageinfo['pagename'])) {
364 PrintXML(HTML::dt(HTML::strong(_("Empty pagename!"))));
368 if (empty($versiondata['author_id']))
369 $versiondata['author_id'] = $versiondata['author'];
371 $pagename = $pageinfo['pagename'];
372 $content = $pageinfo['content'];
374 if ($pagename ==_("InterWikiMap"))
375 $content = _tryinsertInterWikiMap($content);
377 $dbi = $request->getDbh();
378 $page = $dbi->getPage($pagename);
380 $current = $page->getCurrentRevision();
381 // Try to merge if updated pgsrc contents are different. This
382 // whole thing is hackish
384 // TODO: try merge unless:
385 // if (current contents = default contents && pgsrc_version >=
386 // pgsrc_version) then just upgrade this pgsrc
387 $needs_merge = false;
391 if ($request->getArg('merge')) {
394 else if ($request->getArg('overwrite')) {
398 if ( (! $current->hasDefaultContents())
399 && ($current->getPackedContent() != $content)
400 && ($merging == true) ) {
401 require_once('lib/editpage.php');
402 $request->setArg('pagename', $pagename);
403 $r = $current->getVersion();
404 $request->setArg('revision', $current->getVersion());
405 $p = new LoadFileConflictPageEditor($request);
406 $p->_content = $content;
407 $p->_currentVersion = $r - 1;
408 $p->editPage($saveFailed = true);
409 return; //early return
412 foreach ($pagedata as $key => $value) {
414 $page->set($key, $value);
420 $mesg->pushContent(' ', fmt("from %s", $source));
423 $current = $page->getCurrentRevision();
424 if ($current->getVersion() == 0) {
425 $mesg->pushContent(' ', _("new page"));
429 if ( (! $current->hasDefaultContents())
430 && ($current->getPackedContent() != $content) ) {
432 $mesg->pushContent(' ',
433 fmt("has edit conflicts - overwriting anyway"));
435 if (substr_count($source, 'pgsrc')) {
436 $versiondata['author'] = _("The PhpWiki programming team");
437 // but leave authorid as userid who loaded the file
441 $mesg->pushContent(' ', fmt("has edit conflicts - skipped"));
442 $needs_merge = true; // hackish
446 else if ($current->getPackedContent() == $content
447 && $current->get('author') == $versiondata['author']) {
448 $mesg->pushContent(' ',
449 fmt("is identical to current version %d - skipped",
450 $current->getVersion()));
457 $new = $page->save($content, WIKIDB_FORCE_CREATE, $versiondata);
459 $mesg->pushContent(' ', fmt("- saved to database as version %d",
460 $new->getVersion()));
464 // hackish, $source contains needed path+filename
465 $f = str_replace(sprintf(_("MIME file %s"), ''), '', $f);
466 $f = str_replace(sprintf(_("Serialized file %s"), ''), '', $f);
467 $f = str_replace(sprintf(_("plain file %s"), ''), '', $f);
469 $meb = $Theme->makeButton($text = ("Merge Edit"),
470 $url = _("PhpWikiAdministration")
471 . "?action=loadfile&source=$f&merge=1",
472 $class = 'wikiadmin');
473 $owb = $Theme->makeButton($text = _("Restore Anyway"),
474 $url = _("PhpWikiAdministration")
475 . "?action=loadfile&source=$f&overwrite=1",
476 $class = 'wikiunsafe');
477 $mesg->pushContent(' ', $meb, " ", $owb);
481 PrintXML(HTML::dt(HTML::em(WikiLink($pagename))), $mesg);
483 PrintXML(HTML::dt(WikiLink($pagename)), $mesg);
487 function _tryinsertInterWikiMap($content) {
489 if (strpos($content, "<verbatim>")) {
490 //$error_html = " The newly loaded pgsrc already contains a verbatim block.";
493 if (!$goback && !defined('INTERWIKI_MAP_FILE')) {
494 $error_html = sprintf(" "._("%s: not defined"), "INTERWIKI_MAP_FILE");
497 if (!$goback && !file_exists(INTERWIKI_MAP_FILE)) {
498 $error_html = sprintf(" "._("%s: file not found"), INTERWIKI_MAP_FILE);
502 if (!empty($error_html))
503 trigger_error(_("Default InterWiki map file not loaded.")
504 . $error_html, E_USER_NOTICE);
509 $filename = INTERWIKI_MAP_FILE;
510 trigger_error(sprintf(_("Loading InterWikiMap from external file %s."),
511 $filename), E_USER_NOTICE);
513 $fd = fopen ($filename, "rb");
514 $data = fread ($fd, filesize($filename));
516 $content = $content . "\n<verbatim>\n$data</verbatim>\n";
520 function ParseSerializedPage($text, $default_pagename, $user)
522 if (!preg_match('/^a:\d+:{[si]:\d+/', $text))
525 $pagehash = unserialize($text);
527 // Split up pagehash into four parts:
530 // page-level meta-data
531 // revision-level meta-data
533 if (!defined('FLAG_PAGE_LOCKED'))
534 define('FLAG_PAGE_LOCKED', 1);
535 $pageinfo = array('pagedata' => array(),
536 'versiondata' => array());
538 $pagedata = &$pageinfo['pagedata'];
539 $versiondata = &$pageinfo['versiondata'];
542 if (empty($pagehash['pagename']))
543 $pagehash['pagename'] = $default_pagename;
544 if (empty($pagehash['author'])) {
545 $pagehash['author'] = $user->getId();
548 foreach ($pagehash as $key => $value) {
553 $pageinfo[$key] = $value;
556 $pageinfo[$key] = join("\n", $value);
559 if (($value & FLAG_PAGE_LOCKED) != 0)
560 $pagedata['locked'] = 'yes';
563 $pagedata[$key] = $value;
566 $versiondata['mtime'] = $value;
571 $versiondata[$key] = $value;
578 function SortByPageVersion ($a, $b) {
579 return $a['version'] - $b['version'];
582 function LoadFile (&$request, $filename, $text = false, $mtime = false)
584 if (!is_string($text)) {
586 $stat = stat($filename);
588 $text = implode("", file($filename));
591 if (! ini_get('safe_mode'))
592 set_time_limit(30); // Reset watchdog.
594 // FIXME: basename("filewithnoslashes") seems to return garbage sometimes.
595 $basename = basename("/dummy/" . $filename);
598 $mtime = time(); // Last resort.
600 $default_pagename = rawurldecode($basename);
602 if ( ($parts = ParseMimeifiedPages($text)) ) {
603 usort($parts, 'SortByPageVersion');
604 foreach ($parts as $pageinfo)
605 SavePage($request, $pageinfo, sprintf(_("MIME file %s"),
606 $filename), $basename);
608 else if ( ($pageinfo = ParseSerializedPage($text, $default_pagename,
609 $request->getUser())) ) {
610 SavePage($request, $pageinfo, sprintf(_("Serialized file %s"),
611 $filename), $basename);
614 $user = $request->getUser();
616 // Assume plain text file.
617 $pageinfo = array('pagename' => $default_pagename,
618 'pagedata' => array(),
620 => array('author' => $user->getId()),
621 'content' => preg_replace('/[ \t\r]*\n/', "\n",
624 SavePage($request, $pageinfo, sprintf(_("plain file %s"), $filename),
629 function LoadZip (&$request, $zipfile, $files = false, $exclude = false) {
630 $zip = new ZipReader($zipfile);
631 while (list ($fn, $data, $attrib) = $zip->readFile()) {
632 // FIXME: basename("filewithnoslashes") seems to return
633 // garbage sometimes.
634 $fn = basename("/dummy/" . $fn);
635 if ( ($files && !in_array($fn, $files))
636 || ($exclude && in_array($fn, $exclude)) ) {
637 PrintXML(HTML::dt(WikiLink($fn)),
638 HTML::dd(_("Skipping")));
642 LoadFile($request, $fn, $data, $attrib['mtime']);
646 function LoadDir (&$request, $dirname, $files = false, $exclude = false) {
647 $fileset = new LimitedFileSet($dirname, $files, $exclude);
649 if (($skiplist = $fileset->getSkippedFiles())) {
650 PrintXML(HTML::dt(HTML::strong(_("Skipping"))));
652 foreach ($skiplist as $file)
653 $list->pushContent(HTML::li(WikiLink($file)));
654 PrintXML(HTML::dd($list));
657 // Defer HomePage loading until the end. If anything goes wrong
658 // the pages can still be loaded again.
659 $files = $fileset->getFiles();
660 if (in_array(HOME_PAGE, $files)) {
661 $files = array_diff($files, array(HOME_PAGE));
662 $files[] = HOME_PAGE;
664 foreach ($files as $file)
665 LoadFile($request, "$dirname/$file");
668 class LimitedFileSet extends FileSet {
669 function LimitedFileSet($dirname, $_include, $exclude) {
670 $this->_includefiles = $_include;
671 $this->_exclude = $exclude;
672 $this->_skiplist = array();
673 parent::FileSet($dirname);
676 function _filenameSelector($fn) {
677 $incl = &$this->_includefiles;
678 $excl = &$this->_exclude;
680 if ( ($incl && !in_array($fn, $incl))
681 || ($excl && in_array($fn, $excl)) ) {
682 $this->_skiplist[] = $fn;
689 function getSkippedFiles () {
690 return $this->_skiplist;
695 function IsZipFile ($filename_or_fd)
697 // See if it looks like zip file
698 if (is_string($filename_or_fd))
700 $fd = fopen($filename_or_fd, "rb");
701 $magic = fread($fd, 4);
706 $fpos = ftell($filename_or_fd);
707 $magic = fread($filename_or_fd, 4);
708 fseek($filename_or_fd, $fpos);
711 return $magic == ZIP_LOCHEAD_MAGIC || $magic == ZIP_CENTHEAD_MAGIC;
715 function LoadAny (&$request, $file_or_dir, $files = false, $exclude = false)
717 // Try urlencoded filename for accented characters.
718 if (!file_exists($file_or_dir)) {
719 // Make sure there are slashes first to avoid confusing phps
720 // with broken dirname or basename functions.
721 // FIXME: windows uses \ and :
722 if (is_integer(strpos($file_or_dir, "/"))) {
723 $file_or_dir = FindFile($file_or_dir);
725 if (!file_exists($file_or_dir))
726 $file_or_dir = dirname($file_or_dir) . "/"
727 . urlencode(basename($file_or_dir));
729 // This is probably just a file.
730 $file_or_dir = urlencode($file_or_dir);
734 $type = filetype($file_or_dir);
735 if ($type == 'link') {
736 // For symbolic links, use stat() to determine
737 // the type of the underlying file.
738 list(,,$mode) = stat($file_or_dir);
739 $type = ($mode >> 12) & 017;
742 elseif ($type == 004)
747 $request->finish(fmt("Unable to load: %s", $file_or_dir));
749 else if ($type == 'dir') {
750 LoadDir($request, $file_or_dir, $files, $exclude);
752 else if ($type != 'file' && !preg_match('/^(http|ftp):/', $file_or_dir))
754 $request->finish(fmt("Bad file type: %s", $type));
756 else if (IsZipFile($file_or_dir)) {
757 LoadZip($request, $file_or_dir, $files, $exclude);
759 else /* if (!$files || in_array(basename($file_or_dir), $files)) */
761 LoadFile($request, $file_or_dir);
765 function LoadFileOrDir (&$request)
767 $source = $request->getArg('source');
768 StartLoadDump($request, sprintf(_("Loading '%s'"), $source));
770 LoadAny($request, $source);
772 EndLoadDump($request);
775 function SetupWiki (&$request)
777 global $GenericPages, $LANG;
780 //FIXME: This is a hack (err, "interim solution")
781 // This is a bogo-bogo-login: Login without
782 // saving login information in session state.
783 // This avoids logging in the unsuspecting
784 // visitor as "The PhpWiki programming team".
786 // This really needs to be cleaned up...
787 // (I'm working on it.)
788 $real_user = $request->_user;
789 $request->_user = new WikiUser($request, _("The PhpWiki programming team"),
792 StartLoadDump($request, _("Loading up virgin wiki"));
795 $pgsrc = FindLocalizedFile(WIKI_PGSRC);
796 $default_pgsrc = FindFile(DEFAULT_WIKI_PGSRC);
798 if ($default_pgsrc != $pgsrc)
799 LoadAny($request, $default_pgsrc, $GenericPages);
801 LoadAny($request, $pgsrc);
804 EndLoadDump($request);
807 function LoadPostFile (&$request)
809 $upload = $request->getUploadedFile('file');
812 $request->finish(_("No uploaded file to upload?")); // FIXME: more concise message
815 // Dump http headers.
816 StartLoadDump($request, sprintf(_("Uploading %s"), $upload->getName()));
819 $fd = $upload->open();
821 LoadZip($request, $fd, false, array(_("RecentChanges")));
823 LoadFile($request, $upload->getName(), $upload->getContents());
826 EndLoadDump($request);
830 $Log: not supported by cvs2svn $
831 Revision 1.76 2003/02/16 19:47:17 dairiki
832 Update WikiDB timestamp when editing or deleting pages.
834 Revision 1.75 2003/02/15 03:04:30 dairiki
835 Fix for WikiUser constructor API change.
837 Revision 1.74 2003/02/15 02:18:04 dairiki
838 When default language was English (at least), pgsrc was being
841 LimitedFileSet: Fix typo/bug. ($include was being ignored.)
843 SetupWiki(): Fix bugs in loading of $GenericPages.
845 Revision 1.73 2003/01/28 21:09:17 zorloc
846 The get_cfg_var() function should only be used when one is
847 interested in the value from php.ini or similar. Use ini_get()
848 instead to get the effective value of a configuration variable.
851 Revision 1.72 2003/01/03 22:25:53 carstenklapp
852 Cosmetic fix to "Merge Edit" & "Overwrite" buttons. Added "The PhpWiki
853 programming team" as author when loading from pgsrc. Source
856 Revision 1.71 2003/01/03 02:48:05 carstenklapp
857 function SavePage: Added loadfile options for overwriting or merge &
858 compare a loaded pgsrc file with an existing page.
860 function LoadAny: Added a general error message when unable to load a
861 file instead of defaulting to "Bad file type".
870 // c-hanging-comment-ender-p: nil
871 // indent-tabs-mode: nil