source: trunk/tools/database/www/cvs.php3@ 3947

Last change on this file since 3947 was 3947, checked in by bird, 25 years ago

More coding...

File size: 21.0 KB
Line 
1<?php
2
3
4$sCVSROOT = "d:/odin32/cvs/cvsroot";
5$sCVSROOT = ".";
6
7/**
8 * Quick and dirty CVS file parser.
9 */
10class CVSFile
11{
12 var $fOk; /* Status of contructor. */
13 var $sError; /* Last error message. */
14 var $sFullName; /* Full path of the */
15 var $sDir; /* CVSROOT relative directory */
16 var $sName; /* Workfile filename. */
17 var $sExt; /* Workfile extention. */
18 var $aasKeys; /* base keys */
19 var $aasDeltas; /* the text values only */
20 var $aaasRevs; /* all types of revision info (but the text) */
21
22
23 /**
24 * Constructor.
25 * Opens a CVS repository file, reads it into memory and closes it.
26 */
27 function CVSFile($sFilename, $fNoDeltas)
28 {
29 global $sCVSROOT;
30
31 $this->fOk = 0;
32 /*
33 * TODO: Security: Check that the path and filename is valid!
34 * We can't allow relative paths (ie. "..")
35 */
36 if (strlen($sFilename) < 3 || substr($sFilename, strlen($sFilename)-2) != ",v")
37 {
38 $this->sError = "filename is invalid";
39 return 1;
40 }
41
42 /*
43 * Check filesize. Minimum size is 10 bytes!
44 */
45 $this->sFullname = $sCVSROOT."/".$sFilename;
46 $cbFile = filesize($this->sFullname);
47 if ($cbFile <= 10)
48 {
49 $this->sError = "too small file, " . $this->sFullname . ", ". $cbFile ."\n";
50 return 1;
51 }
52 if (!$fNoDeltas && $cbFile >= (2*1024*1024)) //currently max size of 2MB.
53 {
54 $this->sError = "\ntoo large file, ". $this->sFullname .", ". $cbFile ."\n";
55 return 1;
56 }
57
58
59 /*
60 * Seems ok. Let's, init object variables
61 */
62 $this->fOk = 0;
63 $this->sError = "";
64 $i = strrpos($sFilename, "\\");
65 $j = strrpos($sFilename, "/");
66 $i = ($i > $j) ? $i : $j;
67 $this->sName = substr($sFilename, $i > 0 ? $i + 1 : 0, strlen($sFilename)-2);
68 $this->sDir = substr($sFilename, 0, $i);
69 if (($i = strrpos($this->sName, '.')) > 0)
70 $this->sExt = substr($this->sName, $i+1);
71 else
72 $this->sExt = "";
73 $this->aasKeys = array();
74 $this->aasDeltas = array();
75 $this->aaasRevs = array();
76
77
78 /*
79 * Open the file
80 */
81 $hFile = fopen($this->sFullname, "rb");
82 if (!$hFile)
83 {
84 $this->sError = "\nfailed to open the file $this->sFullname\n";
85 fclose($hFile);
86 return 1;
87 }
88
89
90 /*
91 * Parse file.
92 */
93 $fAt = 0;
94 $fNewKey= 1;
95 $sKey = "";
96 $sRev = "";
97 $fDesc = 0;
98
99 $iLine = -1;
100 $sLine = "";
101 $fStop = 0;
102 while (($sLine != "" || !feof($hFile)) && !$fStop)
103 {
104 /*
105 * Left trim.
106 * If empty line, get next and iterate.
107 */
108 $sLine = ltrim($sLine);
109 if (!$sLine || $sLine == "" || $sLine == "\n" || $sLine == "\r")
110 {
111 $iLine++;
112 $sLine = fgets($hFile, 0x1000);
113 continue;
114 }
115
116 /*
117 * Are we looking for a new key word?
118 */
119 if ($fNewKey)
120 {
121 $sKey = CopyWord($sLine);
122 $sLine = ltrim(SkipWord($sLine));
123 if ($sKey[0] >= "0" && $sKey[0] <= "9")
124 /* Revision number: delta or revision info */
125 $sRev = $sKey;
126 else
127 $fNewKey = 0;
128 continue;
129 }
130
131
132 /*
133 * Extract value
134 */
135 $fNoSemicolon = ($sKey == "desc" || $sKey == "log" || $sKey == "desc");
136 if ($fAt = ($sLine[0] == "@")) //check if the value is enclosed in '@'s
137 $sLine = substr($sLine, 1);
138 $asValue = array();
139 $fEnd = 0;
140 while (!$fEnd)
141 {
142 /* get new line? */
143 if (!$sLine || $sLine == "" || $sLine == "\n" || $sLine == "\r")
144 {
145 if (feof($hFile))
146 break;
147 /* Get next line and remove any EOF chars */
148 $iLine++;
149 $sLine = str_replace("\x1a", "", fgets($hFile, 0x1000));
150 continue;
151 }
152
153 //echo "debug line $iLine: $sLine";
154
155 /*
156 * Look for end char (either ; or @) and copy.
157 * If end of value then $sLine <- rest of line.
158 */
159 $fEnd = 0;
160 $cchLine = strlen($sLine);
161 if ($fAt)
162 { /* terminated with @ */
163 $iAt = 0;
164 for ($iAt; $iAt+1 < $cchLine; $iAt++)
165 if ($sLine[$iAt] == '@' && ($fEnd = ($sLine[++$iAt] != '@')))
166 break;
167 if ($fEnd)
168 {
169 $asValue[] = str_replace("@@", "@", substr($sLine, 0, $iAt - 1));
170 /* if semicolon end, skip to it. ASSUMES: same line! */
171 if (!$fNoSemicolon && ($iAt = strpos($sLine, ";", $iAt)) >= 0)
172 $iAt++;
173 $sLine = (strlen($sLine) > $iAt && $iAt >= 0) ? substr($sLine, $iAt) : "";
174 }
175 else
176 {
177 $asValue[] = str_replace("@@", "@", $sLine);
178 $sLine = "";
179 }
180 }
181 else
182 { /* terminated with ';' */
183 $i = strpos($sLine, ';');
184 if ($fEnd = ($i >= 0))
185 {
186 $asValue[] = str_replace("@@", "@", substr($sLine, 0, $i));
187 $sLine = (strlen($sLine) > $i+1) ? substr($sLine, $i+1) : "";
188 }
189 else
190 {
191 $asValue[] = str_replace("@@", "@", $sLine);
192 $sLine = "";
193 }
194 }
195 }
196
197
198 /*
199 * Process the key.
200 */
201 switch ($sKey)
202 {
203 /*
204 * This is normally the keyword separating
205 * revision info from log+text info.
206 */
207 case "desc":
208 $fDesc = 1;
209 $sRev = "";
210 break;
211
212 /*
213 * Stop after the first log entry.
214 */
215 case "log":
216 $fStop = $fNoDeltas;
217 break;
218
219 /*
220 * Don'r read deltas for archives with the expand tag set
221 */
222 case "expand":
223 $fNoDeltas = 1;//= $asValue[0] != "";
224 break;
225 }
226
227 /*
228 * Save key and value in the appopriate place.
229 */
230 if ($sRev == "")
231 { /* Base keys */
232 if (sizeof($this->aaKeys) <= 0 //sanity check! head must come first and have a value!
233 && ($sKey != "head" || sizeof($asValue) <= 0 || $asValue[0] == ""))
234 {
235 $this->sError = "Invalid file format.";
236 fclose($hFile);
237 return 1;
238 }
239 $this->aasKeys[$sKey] = $asValue;
240 }
241 else if ($sKey != "text")
242 { /* Revision information keys */
243 if (!isset($this->aaasRevs[$sRev]))
244 $this->aaasRevs[$sRev] = array($sKey => $asValue);
245 else
246 $this->aaasRevs[$sRev][$sKey] = $asValue;
247 }
248 else
249 { /* Delta (ie. 'text') key */
250 $this->aasDeltas[$sRev] = $asValue;
251 }
252
253 /*
254 * Completed reading of this key, so next one.
255 */
256 $fNewKey = 1;
257
258 /* debug */
259 //echo "debug key: $sKey value(".sizeof($asValue)."):".$asValue[0]."\n";
260 }
261
262 fclose($hFile);
263
264 /*
265 * Return successfully.
266 */
267 $this->fOk = 1;
268 return 1;
269 }
270
271
272 /**
273 * Debug dump function.
274 */
275 function DumpInfo()
276 {
277 echo "\nDump:\n";
278 while (list ($sKey, $asValue) = each ($this->aasKeys))
279 {
280 echo "* key: $sKey *\n";
281 if (sizeof((array)$asValue) > 0)
282 {
283 while (list ($key, $s) = each ($asValue))
284 echo $s;
285 echo "\n";
286 }
287 }
288
289 while (list ($sRev, $aasKeys) = each ($this->aaasRevs))
290 {
291 echo "* Revision: $sRev *\n";
292 if (sizeof((array)$aasKeys) > 0)
293 {
294 while (list ($sKey, $asValue) = each ($aasKeys))
295 {
296 echo "* key: $sKey *\n";
297 if (sizeof((array)$asValue) > 0)
298 {
299 while (list ($key, $s) = each ($asValue))
300 echo $s;
301 echo "\n";
302 }
303 }
304 }
305 }
306
307 while (list ($sKey, $asValue) = each ($this->aasDeltas))
308 {
309 echo "* delta for revision: $sKey *\n";
310 if (sizeof((array)$asValue) > 0)
311 {
312 while (list ($key, $s) = each ($asValue))
313 echo $s;
314 echo "\n";
315 }
316 }
317
318 }
319
320
321 /**
322 * Prints the contents of the file to stdout.
323 *
324 * Color coding is enabled. (TODO)
325 *
326 * Currently only $sRevision == head revision is supported
327 * @returns Success indicator (true / false)
328 * @param $sRevision. Revision number. defaults to head revision.
329 *
330 */
331 function PrintRevision($sRevision)
332 {
333 /* defaults to head revision if empty */
334 if ($sRevision == "") $sRevision = $this->aasKeys["head"][0];
335 if (!isset($this->aasDeltas[$sRevision]))
336 {
337 $this->sError = "CVSFile::PrintRevision is called with an invalid revision number. ($sRevision)";
338 return 0;
339 }
340 /* to-be-removed - TODO - FIXME */
341 if ($sRevision != $this->aasKeys["head"][0])
342 {
343 $this->sError = "CVSFile::PrintRevision is called with an invalid revision number (not head).";
344 return 0;
345 }
346
347 /*
348 * Initiate the color encoder.
349 */
350 switch ($this->sExt)
351 {
352 case 'c':
353 case 'cpp':
354 case 'cxx':
355 case 'h':
356 case 'hpp':
357 C_ColorInit($aVariables);
358 break;
359 }
360
361
362
363 /*
364 * Write it!
365 */
366 echo "<table><tr><td bgcolor=\"#020286\"><pre><font size=-0 face=\"System VIO, System Monospaced\" color=\"#02FEFE\">\n";
367
368 $fComment = 0;
369 $iLine = 0;
370 $cLines = sizeof($this->aasDeltas[$sRevision]);
371 //echo "<!-- debug $this->sExt -->\n";
372 while ($iLine < $cLines)
373 {
374 $sLine = htmlspecialchars($this->aasDeltas[$sRevision][$iLine++]);
375
376 /*
377 * Preprocessing... Color coding
378 */
379 switch ($this->sExt)
380 {
381 case 'c':
382 case 'cpp':
383 case 'cxx':
384 case 'h':
385 case 'hpp':
386 $sLine = C_ColorEncode($sLine, $aVariables);
387 break;
388 }
389
390 /*
391 * Finished processing of the line. So, write it.
392 */
393 echo "<a name=$iLine>$sLine</a>";
394 }
395
396 echo "</pre></td></tr></table>\n";
397
398 return 1;
399 }
400
401
402}
403
404
405
406/**
407 * Copies the first word.
408 * A words is: [a-zA-Z0-9_.]
409 *
410 * tested ok
411 * @returns Returns the word at the start of $s.
412 */
413function CopyWord($s)
414{
415 $cch = strlen($s);
416 for ($i = 0; $i < $cch; $i++)
417 {
418 $c = $s[$i];
419 if (!($c >= 'a' && $c <= 'z')
420 &&
421 !($c >= 'A' && $c <= 'Z')
422 &&
423 !($c >= '0' && $c <= '9')
424 &&
425 !($c == '.' || $c == '_')
426 )
427 break;
428 }
429 return substr($s, 0, $i);
430}
431
432
433/**
434 * Skips the first word.
435 * A words is: [a-zA-Z0-9_.]
436 *
437 * tested ok
438 * @returns $s - first word.
439 */
440function SkipWord($s)
441{
442 $cch = strlen($s);
443 for ($i = 0; $i < $cch; $i++)
444 {
445 $c = $s[$i];
446 if (!($c >= 'a' && $c <= 'z')
447 &&
448 !($c >= 'A' && $c <= 'Z')
449 &&
450 !($c >= '0' && $c <= '9')
451 &&
452 !($c == '.' || $c == '_')
453 )
454 break;
455 }
456 return substr($s, $i);
457}
458
459
460
461
462/*
463 * C color encoding.
464 */
465$aC_Keywords = array(
466 "auto" => 1,
467 "break" => 1,
468 "case" => 1,
469 "char" => 1,
470 "const" => 1,
471 "continue" => 1,
472 "default" => 1,
473 "defined" => 1,
474 "do" => 1,
475 "double" => 1,
476 "else" => 1,
477 "enum" => 1,
478 "extern" => 1,
479 "float" => 1,
480 "for" => 1,
481 "goto" => 1,
482 "if" => 1,
483 "int" => 1,
484 "long" => 1,
485 "register" => 1,
486 "return" => 1,
487 "short" => 1,
488 "sizeof" => 1,
489 "static" => 1,
490 "struct" => 1,
491 "switch" => 1,
492 "typedef" => 1,
493 "union" => 1,
494 "unsigned" => 1,
495 "void" => 1,
496 "while" => 1,
497 "class" => 1,
498 "delete" => 1,
499 "finally" => 1,
500 "friend" => 1,
501 "inline" => 1,
502 "new" => 1,
503 "operator" => 1,
504 "overload" => 1,
505 "private" => 1,
506 "protected" => 1,
507 "public" => 1,
508 "this" => 1,
509 "virtual" => 1,
510 "bool" => 1,
511 "true" => 1,
512 "false" => 1,
513 "explicit" => 1,
514 "mutable" => 1,
515 "typename" => 1,
516 "static_cast" => 1,
517 "const_cast" => 1,
518 "reinterpret_cast" => 1,
519 "dynamic_cast" => 1,
520 "using" => 1,
521 "typeid" => 1,
522 "asm" => 1,
523 "catch" => 1,
524 "signed" => 1,
525 "template" => 1,
526 "throw" => 1,
527 "try" => 1,
528 "volatile" => 1,
529 "namespace" => 1);
530
531$aC_Symbols = array(
532 "{" => 1,
533 "}" => 1,
534// "[" => 1,
535// "]" => 1,
536// "(" => 1,
537// ")" => 1,
538// "." => 1,
539// "," => 1,
540 "!" => 1,
541 "%" => 1,
542 "&" => 1,
543 "&amp;" => 1,
544 "*" => 1,
545 "-" => 1,
546 "=" => 1,
547 "+" => 1,
548 ":" => 1,
549 ";" => 1,
550 "<" => 1,
551 "&lt;" => 1,
552 ">" => 1,
553 "&gt;" => 1,
554 "?" => 1,
555 "/" => 1,
556 "|" => 1,
557 "~" => 1,
558 "^" => 1,
559 "*" => 1);
560
561/**
562 * Initiate the variable array used by the C Color encoder.
563 * @param $aVaraibles Variable array. (output)
564 */
565function C_ColorInit(&$aVariables)
566{
567 $aVariables["fComment"] = 0;
568}
569
570
571/**
572 * Encode a line of C code.
573 * @param $sLine Line string to encode.
574 * @param $aVariables Variable array.
575 * @returns Color encoded line string.
576 */
577function C_ColorEncode($sLine, &$aVariables)
578{
579 global $aC_Keywords;
580 global $aC_Symbols;
581
582 $sRet = "";
583 $cchLine = strlen($sLine);
584
585 /*
586 * If mulitline comment we'll only check if it ends at this line.
587 * if it doesn't we'll do nothing.
588 * if it does we'll skip to then end of it.
589 */
590 if ($aVariables["fComment"])
591 {
592 if (!(($i = strpos($sLine, "*/")) || ($cchLine >= 2 && $sLine[0] == '*' && $sLine[1] == '/')))
593 return $sLine;
594 $i += 2;
595 $sRet = substr($sLine, 0, $i)."</font>";
596 $aVariables["fComment"] = 0;
597 }
598 else
599 $i = 0;
600
601 /*
602 * Loop thru the (remainings) of the line.
603 */
604 $fFirstNonBlank = 1;
605 while ($i < $cchLine)
606 {
607 /* comment check */
608 if ($i+1 < $cchLine && $sLine[$i] == '/')
609 {
610 if ($sLine[$i+1] == '/')
611 { /* one-line comment */
612 return $sRet . "<font color=\"#02FE02\">" . substr($sLine, $i) . "</font>";
613 }
614
615 if ($sLine[$i+1] == '*')
616 { /* Start of multiline comment */
617 if ($j = strpos($sLine, "*/", $i + 2))
618 {
619 $sRet = $sRet . "<font color=\"#02FE02\">" . substr($sLine, $i, $j+2 - $i) . "</font>";
620 $i = $j + 2;
621 }
622 else
623 {
624 $aVariables["fComment"] = 1;
625 return $sRet . "<font color=\"#02FE02\">" . substr($sLine, $i);
626 }
627 continue;
628 }
629 }
630
631
632 /*
633 * Check for string.
634 */
635 if ((($fDbl = ($sLine[$i] == '"' || substr($sLine, $i, 6) == "&quot;")) || $sLine[$i] == "'")
636 && ($i == 0 || $sLine[$i-1] != '\\'))
637 { /* start of a string */
638 $j = $i + 1;
639 if ($fDbl)
640 {
641 if ($sLine[$i] == '"')
642 while ($j < $cchLine && $sLine[$j] != '"')
643 $j += ($sLine[$j] == '\\') ? 2 : 1;
644 else
645 {
646 while ($j < $cchLine && ($sLine[$j] != '&' || substr($sLine, $j, 6) != "&quot;"))
647 $j += ($sLine[$j] == '\\') ? 2 : 1;
648 if ($j < $cchLine)
649 $j += 5;
650 }
651 }
652 else
653 while ($j < $cchLine && $sLine[$j] != "'")
654 $j += ($sLine[$j] == '\\') ? 2 : 1;
655 $j++;
656 $sRet .= "<font color=\"#FEFE02\">".substr($sLine, $i, $j - $i)."</font>";
657 $i = $j;
658 continue;
659 }
660
661
662 /*
663 * Check for preprocessor directive.
664 */
665 if ($fFirstNonBlank && $sLine[$i] == "#")
666 {
667 $j = $i + 1;
668 while ($j < $cchLine && ($sLine[$j] == ' ' || $sLine[$j] == '\t'))
669 $j++;
670 $j += C_WordLen($sLine, $cchLine, $j);
671 $sRet .= "<font color=\"#CECECE\">" . substr($sLine, $i, $j - $i) . "</font>";
672 $i = $j;
673 $fFirstNonBlank = 0;
674 continue;
675 }
676
677 /*
678 * If non-blank, lets check if we're at the start of a word...
679 */
680 $fBlank = ($sLine[$i] == ' ' || $sLine[$i] == '\t'); //TODO more "blanks"?
681 if ($fFirstNonBlank) $fFirstNonBlank = $fBlank;
682 $cchWord = !$fBlank ? C_WordLen($sLine, $cchLine, $i) : 0;
683
684 if ($cchWord > 0)
685 {
686 /*
687 * Check for keyword.
688 */
689 if ($cchWord > 0 && isset($aC_Keywords[substr($sLine, $i, $cchWord)]))
690 $sRet .= "<font color=\"#FF0202\">" . substr($sLine, $i, $cchWord) . "</font>";
691
692 /*
693 * Check for number
694 */
695 else if ($sLine[$i] >= '0' && $sLine[$i] <= '9')
696 $sRet .= "<font color=\"#FE0202\">" . substr($sLine, $i, $cchWord) . "</font>";
697
698 /*
699 * Skip word.
700 */
701 else
702 $sRet .= substr($sLine, $i, $cchWord);
703 $i += $cchWord;
704 continue;
705 }
706
707
708 /*
709 * Prepare for symbol check. (we'll have to check for HTML stuff like &amp;).
710 */
711 $cchWord = 1;
712 if ($sLine[$i] == '&')
713 {
714 while ($cchWord < 8 && $sLine[$i+$cchWord] != ';' &&
715 ( ($sLine[$i+$cchWord] >= 'a' && $sLine[$i+$cchWord] <= 'z')
716 || ($sLine[$i+$cchWord] >= 'A' && $sLine[$i+$cchWord] <= 'Z')
717 )
718 )
719 $cchWord++;
720 if ($sLine[$i + $cchWord++] != ';')
721 $cchWord = 1;
722 }
723
724 /*
725 * Check for Symbol.
726 */
727 if (isset($aC_Symbols[substr($sLine, $i, 1)]))
728 {
729 $sRet .= "<font color=\"#CECECE\">" . substr($sLine, $i, $cchWord) . "</font>";
730 $i += $cchWord;
731 continue;
732 }
733
734
735 /*
736 * Copy char
737 */
738 $sRet = $sRet.$sLine[$i];
739 $i++;
740 }
741
742 return $sRet;
743}
744
745
746
747/**
748 * Calculates the lenght of the word which eventually starts at [$i].
749 * @param $sLine Line.
750 * @param $cchLine Line length.
751 * @param $i Line index.
752 * @returns Word length.
753 */
754function C_WordLen($sLine, $cchLine, $i)
755{
756
757 /*
758 * Check that previous letter wasen't a possible
759 * word part.
760 */
761 if ($i > 0 &&
762 (
763 ($sLine[$i-1] >= 'a' && $sLine[$i-1] <= 'z')
764 || ($sLine[$i-1] >= 'A' && $sLine[$i-1] <= 'Z')
765 || ($sLine[$i-1] >= '0' && $sLine[$i-1] <= '9')
766 || ($sLine[$i-1] == '_')
767 || ($sLine[$i-1] == '$')
768 )
769 )
770 return 0;
771
772 /*
773 * Count letters in the word
774 */
775 $cch = 0;
776 $cchLine = strlen($sLine);
777 while ($i < $cchLine &&
778 (
779 ($sLine[$i] >= 'a' && $sLine[$i] <= 'z')
780 || ($sLine[$i] >= 'A' && $sLine[$i] <= 'Z')
781 || ($sLine[$i] >= '0' && $sLine[$i] <= '9')
782 || ($sLine[$i] == '_')
783 || ($sLine[$i] == '$')
784 )
785 )
786 {
787 $i++;
788 $cch++;
789 }
790 return $cch;
791}
792
793?>
794
Note: See TracBrowser for help on using the repository browser.