use IsisDB module instead of OpenIsis -- this will fix various problems in
[webpac] / openisis / php / Isis / File.php
1 <?php
2 /*
3         OpenIsis - an open implementation of the CDS/ISIS database
4         Version 0.8.x (patchlevel see file Version)
5         Copyright (C) 2001-2003 by Erik Grziwotz, erik@openisis.org
6
7         This library is free software; you can redistribute it and/or
8         modify it under the terms of the GNU Lesser General Public
9         License as published by the Free Software Foundation; either
10         version 2.1 of the License, or (at your option) any later version.
11
12         This library is distributed in the hope that it will be useful,
13         but WITHOUT ANY WARRANTY; without even the implied warranty of
14         MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15         Lesser General Public License for more details.
16
17         You should have received a copy of the GNU Lesser General Public
18         License along with this library; if not, write to the Free Software
19         Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20
21         see README for more information
22 EOH */
23
24 // $Id: File.php,v 1.4 2003/06/13 19:35:24 kripke Exp $
25
26
27 /**
28         the magic for a 4/4/0 little endian isis xref file.
29         http://OpenIsis.org/openisis/doc/Serialized
30 */
31 define( 'ISIS_FILE_MAGIC', "ISIXD\0:)" );
32 /**
33         The flat file database.
34         It does not know anything about isis records,
35         it's just dealing with the string representation.
36
37         58971 records (unesb), 29442129 bytes flat (34565120 as .mst)
38         mkxrf: 8.1 sec (7280 recs / sec)
39         read all (just the string): 3.48 sec (~17.000 recs / sec)
40         read 10.000, creating recs: 3.95 sec
41         read 1.000, creating recs, printing: ~2 sec
42
43         @version $Revision: 1.4 $
44         @license LGPL
45         @package Isis
46 */
47 class Isis_File {
48         var $name;
49         var $mst;
50         var $xrf = 0;
51         var $w;
52         /** number of records == number of last record
53         */
54         var $len = 0;
55
56         function Isis_File ( $name, $write = 0 )
57         {
58                 $this->name = $name;
59                 $this->w = $write;
60                 $this->mst = fopen($name.'.txt', $write ? 'a+' : 'r');
61                 if ( ! $this->mst )
62                         return;
63                 flock($this->mst, $write ? LOCK_EX : LOCK_SH);
64                 $s = fstat($this->mst);
65                 $mtime = $s['mtime'];
66                 if ( file_exists($name.'.ptr') ) // avoid annoying warning
67                         $this->xrf = fopen($name.'.ptr', $write ? 'r+' : 'r');
68                 if (!$this->xrf)
69                         $remake = 1;
70                 else {
71                         $s = fstat($this->xrf);
72                         $magic = fread($this->xrf, 8);
73                         $remake = $s['mtime'] < $mtime || ISIS_FILE_MAGIC != $magic;
74                         if ( !$remake )
75                                 $this->len = $s['size'] / 8 - 1;
76                         if ( $remake && !$write ) {
77                                 fclose($this->xrf);
78                                 $this->xrf = 0;
79                         }
80                 }
81                 if ( !$this->xrf && !($this->xrf = fopen($name.'.ptr','w+')) ) {
82                         fclose($this->mst);
83                         return;
84                 }
85                 if ( $remake )
86                         $this->mkxref();
87         }
88
89
90         function mkxref ()
91         {
92                 fseek($this->mst, 0);
93                 ftruncate($this->xrf, 0);
94                 fseek($this->xrf, 0);
95                 fwrite( $this->xrf, ISIS_FILE_MAGIC, 8 );
96                 $mfn = 0;
97                 $lines = 0;
98                 for (;;) {
99                         $line = fgets($this->mst);
100                         $end = !is_string($line) || '' == $line;
101                         if ( $end || "\n" == $line{0} ) {
102                                 $now = ftell($this->mst);
103                                 $len = $now - $pos - 1;
104                                 if ( $len && !$end )
105                                         $len--;
106                                 if ($mfn) {
107                                         $xrf = pack('VV', $pos, $len);
108                                         // echo "mfn $mfn pos $pos len $len\n";
109                                         if ( 8 != ($foo = fwrite($this->xrf, $xrf)) )
110                                                 echo "FOO! $foo\n";
111                                         if ($this->len < $mfn)
112                                                 $this->len = $mfn;
113                                 }
114                                 if ($end)
115                                         break;
116                                 $mfn++;
117                                 $pos = $now;
118                                 $lines = 0;
119                                 continue;
120                         }
121                         if ( ! $lines
122                                 && ! (int)$line
123                                 && 2 == sscanf($line, "%c %d", &$ctrl, &$num)
124                                 && 'W' == $ctrl
125                         ) {
126                                 echo "W $num\n";
127                                 $pos = ftell($this->mst);
128                                 $mfn = $num;
129                                 fseek($this->xrf, 8*$mfn);
130                                 continue;
131                         }
132                         $lines++;
133                 }
134                 fflush( $this->xrf );
135         }       // mkxref
136
137
138         /**
139                 @return string number $mfn or null
140         */
141         function read ( $mfn )
142         {
143                 fseek($this->xrf, 8*$mfn);
144                 $xrf = fread( $this->xrf, 8 );
145                 if ( 8 != strlen($xrf) ) {
146                         // echo "no xrf for $mfn\n";
147                         return null;
148                 }
149                 $xrf = unpack('Vpos/Vlen', $xrf);
150                 // echo "mfn $mfn pos ${xrf['pos']} len ${xrf['len']}\n";
151                 if ( !$xrf['pos'] || !$xrf['len'] )
152                         return null;
153                 fseek($this->mst, $xrf['pos']);
154                 $rec = fread($this->mst, $xrf['len']);
155                 return $xrf['len'] == strlen($rec) ? $rec : null;
156         }
157
158
159         /**
160                 write $data as $mfn or ++$this->len
161                 @return mfn
162         */
163         function write ( $data, $mfn = 0 )
164         {
165                 if ( !$this->w )
166                         trigger_error( $this->name.' not writable', E_USER_ERROR );
167                 if ( !$mfn )
168                         $mfn = ++$this->len;
169                 elseif ($this->len < $mfn)
170                         $this->len = $mfn;
171                 if ( !is_string($data) )
172                         $len = 0;
173                 elseif ( $len = strlen($data) ) {
174                         if ("\n" == $data{$len-1})
175                                 $len--;
176                         else
177                                 $data .= "\n";
178                 }
179                 fseek($this->mst, 0, SEEK_END); // prbly not needed by mode a+
180                 fwrite($this->mst, "\nW\t$mfn\n");
181                 fflush($this->mst);
182                 $pos = ftell($this->mst);
183                 if ( $len )
184                         fwrite($this->mst, $data);
185                 fseek($this->xrf, 8*$mfn);
186                 // echo "write $mfn len $len\n";
187                 fwrite($this->xrf, pack('VV', $pos, $len));
188                 fflush($this->xrf);
189                 return $mfn;
190         }
191
192
193         function compact ()
194         {
195                 if ( !$this->w )
196                         trigger_error( $this->name.' not writable', E_USER_ERROR );
197                 $c = fopen($this->name.'.new', 'w+');
198                 flock($c, LOCK_EX); // so we have exclusive locks on both
199                 for ( $n=1; $n<=$this->len; $n++ )
200                         fwrite($c, is_null($data = $this->read($n))
201                                 ? "\n" : "\n".$data."\n"
202                         );
203                 fflush($c);
204                 if ( file_exists($this->name.'.old') )
205                         unlink($this->name.'.old');
206                 rename($this->name.'.txt', $this->name.'.old');
207                 rename($this->name.'.new', $this->name.'.txt');
208                 fclose($this->mst);
209                 $this->mst = $c;
210                 $this->mkxref(); // frob the xref in place
211         }
212
213 }       // class Isis_File
214 ?>