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

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

Initial coding.. (not quite finished)

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