1 <?php
3 class ppdManager
4 {
5 var $path= "";
6 var $cachedList= array();
7 var $timestamps = array();
9 function ppdManager($path)
10 {
11 if(is_dir($path)){
12 $this->path= $path;
13 }else{
14 msg_dialog::display(_("PPD manager error"), sprintf(_("The specified path '%s' does not exist."),$path), ERROR_DIALOG);
15 return (false);
16 }
17 }
20 function findPPD($path)
21 {
22 $list= array();
23 $currentDir= getcwd();
25 $dh = opendir($path);
26 while(false !== ($file = readdir($dh))){
28 /* Skip well known files */
29 if( $file == '.' || $file == '..'){
30 continue;
31 }
33 /* Recurse through all "common" directories */
34 if(is_dir($path.'/'.$file)){
35 $list= array_merge($list, $this->findPPD($path.'/'.$file));
36 continue;
37 }
39 /* Check for PPD extension */
40 if (preg_match('/\.ppd$/i', $file)){
41 $list[]= $path.'/'.$file;
42 }
43 }
45 closedir($dh);
46 chdir ($currentDir);
47 return ($list);
48 }
51 function updateAttribute($file, $section, $attribute, $value)
52 {
53 $fsection= false;
54 $fattribute= false;
55 $section= preg_replace('/^\*/', '', $section);
56 $attribute= preg_replace('/^\*/', '', $attribute);
58 $rp= @fopen($file, "r");
59 $wp= @fopen("$file.tmp", "w");
63 while (!feof($rp)){
64 $lines[]= fgets($rp, 1024);
65 }
67 $ret = "";
68 $done =false;
69 foreach($lines as $nr => $line){
71 if (preg_match("/\*OpenGroup:*\s+\**$section\/*/", $line)){
72 $fsection= true;
73 $ret .=$line;
74 continue;
75 }
77 /* Change model name .. */
78 if ((preg_match("/^\*".$attribute.":*\s+/",$line)) && ($attribute == "ModelName")){
79 $line= "*$attribute: \"$value\"\n";
80 $done =true;
81 }
83 if (($fsection) && ($section != "NO_SECTION")){
84 if (preg_match("/^\*CloseGroup:*\s+\**$section\/*/", $line)){
85 $fsection= false;
86 $ret .=$line;
87 continue;
88 }
91 if (preg_match("/^\*OpenUI:*\s+\**$attribute\/*/", $line)){
92 $fattribute= true;
93 $ret .= $line;
94 continue;
95 }
97 if ($fattribute){
98 if (preg_match("/^\*CloseUI:*\s+\**$attribute\/*/", $line)){
99 $fattribute= false;
100 $ret .= $line;
101 continue;
102 }
104 if (preg_match("/^\*Default$attribute:*\s+/", $line)){
105 $line= "*Default$attribute: $value\n";
106 $done =true;
107 }
108 }
109 }else{
110 if (preg_match("/^\*OpenUI:*\s+\**$attribute\/*/", $line)){
111 $fattribute= true;
112 $ret .= $line;
113 continue;
114 }
116 if ($fattribute){
117 if (preg_match("/^\*CloseUI:*\s+\**$attribute\/*/", $line)){
118 $fattribute= false;
119 $ret .= $line;
120 continue;
121 }
123 if (preg_match("/^\*Default$attribute:*\s+/", $line)){
124 $line= "*Default$attribute: $value\n";
125 $done =true;
126 }
127 }
128 }
129 $ret .=$line;
130 }
132 fwrite($wp,$ret);
134 fclose($wp);
135 fclose($rp);
137 copy("$file.tmp", "$file");
138 unlink("$file.tmp");
139 }
142 function saveProperties($ppdFile, $properties)
143 {
144 if(!is_readable($ppdFile)){
145 msg_dialog::display(_("PPD manager error"), sprintf(_("Specified PPD file '%s' cannot be opened for reading."),$ppdFile), ERROR_DIALOG);
146 }elseif(!is_writeable(preg_replace("#(^.*/).*$#","\\1",$ppdFile.".tmp"))){
147 msg_dialog::display(_("PPD manager error"), sprintf(_("The temporary file '%s' cannot be opened for writing."),$ppdFile.".tmp"), ERROR_DIALOG);
148 }else{
149 if(is_array($properties)){
150 foreach ($properties as $name => $section){
151 foreach ($section as $attribute => $value){
152 if (is_array($value)){
153 $this->updateAttribute($ppdFile, $name, $attribute, $value['_default']);
154 }
155 }
156 }
157 }
158 }
159 }
161 function loadProperties($ppdFile)
162 {
163 $group= "";
164 $option= "";
165 $properties= array();
167 $fh= fopen ($ppdFile, 'r');
168 while (!feof($fh)) {
170 /* Read line */
171 $line= fgets($fh, 256);
172 if (strlen($line) >= 256){
173 trigger_error(_('Parsing PPD file %s failed - line too long. Trailing characters have been ignored'), E_USER_WARNING);
174 }
176 /* Trigger for option groups */
177 if (preg_match('/^\*OpenGroup:/i', $line)){
179 /* Sanity checks */
180 if ($group != ""){
181 trigger_error(_('Nested groups are not supported!'), E_USER_WARNING);
182 continue;
183 }
184 if (in_array($group, $properties)){
185 trigger_error(_('Group name not unique!'), E_USER_WARNING);
186 continue;
187 }
189 // TODO: Symbol values are not supported yet!
190 if (preg_match('/\^/', $line)){
191 trigger_error(_('Symbol values are not supported yet!'), E_USER_WARNING);
192 }
193 $complete= preg_replace('@^\*OpenGroup:\s+(.*)$@i', '\1', $line);
194 $complete= trim($complete, '"');
195 if (preg_match('@/@', $complete)){
196 $group= trim(preg_replace('@^\*OpenGroup:\s+"?([^/]+)/.*$@i', '\1', $line));
197 $name = preg_replace('@^\*OpenGroup:\s+"?[^/]+/([^/]+).*$@i', '\1', $line);
198 } else {
199 $group= $complete;
200 $name = $complete;
201 }
202 $properties[$group]= array('_name' => $name);
203 continue;
204 }
205 if (preg_match("/^\*CloseGroup:\s+\"?$group\"?/i", $line)){
206 $group= "";
207 continue;
208 }
210 /* Trigger for options */
211 if (preg_match('/^\*OpenUI\s+/i', $line)){
213 /* Sanity check */
214 if ($option != ""){
215 trigger_error(_('Nested options are not supported!'), E_USER_WARNING);
216 continue;
217 }
219 // TODO: Symbol values are not supported yet!
220 if (preg_match('/\^/', $line)){
221 trigger_error(_('Symbol values are not supported yet!'), E_USER_WARNING);
222 }
223 $complete= preg_replace('@^\*OpenUI\s+(.*)$@i', '\1', $line);
224 $complete= trim($complete, '"');
225 if (preg_match('@/@', $complete)){
226 $option= trim(preg_replace('@^\*OpenUI\s+([^/]+)/.*$@i', '\1', $line));
227 $name = trim(preg_replace('@^\*OpenUI\s+[^/]+/([^/]+).*$@i', '\1', $line));
228 } else {
229 $option= trim($complete);
230 $name = trim($complete);
231 }
233 /* Extract option type */
234 $type= trim(preg_replace('/^[^:]+:\s+/', '', $line));
235 $name= preg_replace('/:.*$/', '', $name);
236 $option= preg_replace('/:.*$/', '', $option);
238 // TODO: PickMany is not supported yet!
239 if (preg_match('/PickMany/i', $type)){
240 trigger_error(_('PickMany is not supported yet!'), E_USER_WARNING);
241 }
242 if(empty($group)){
243 $properties["NO_SECTION"][$option]= array('_name' => $name, '_type' => $type);
244 }else{
245 $properties[$group][$option]= array('_name' => $name, '_type' => $type);
246 }
247 continue;
248 }
249 /* Show interest for option parsing */
250 if ($option != ""){
252 $eoption= preg_replace('@\*@', '', $option);
254 /* Close section? */
255 if (preg_match("@^\*CloseUI:\s+\*$eoption@i", $line)){
256 $option= "";
257 continue;
258 }
260 /* Default value? */
261 if (preg_match("@^\*Default$eoption:@", $line)){
262 $c= preg_replace("@^\*Default$eoption:\s+@", "", $line);
263 if(empty($group)){
264 $properties["NO_SECTION"][$option]['_default']= trim(trim($c, '"'));
265 }else{
266 $properties[$group][$option]['_default']= trim(trim($c, '"'));
267 }
268 continue;
269 }
271 /* Possible value? */
272 if (preg_match("@^\*$eoption\s+@", $line)){
273 #*PageSize Letter/US Letter: "<>setpagedevice"
274 $c= preg_replace("@^\*$eoption\s+([^/]+).*$@", "$1", $line);
275 $d= preg_replace("@^\*$eoption\s+[^/]+/([^:]+).*$@", "$1", $line);
276 if(empty($group)){
277 $properties["NO_SECTION"][$option][trim($c)]= trim($d);
278 }else{
279 $properties[$group][$option][trim($c)]= trim($d);
280 }
281 continue;
282 }
283 }
284 }
285 fclose ($fh);
286 return ($properties);
287 }
289 function loadDescription($ppdFile)
290 {
291 $model= "";
292 $manufacturer= "";
295 /* Only parse complete PPD file again, if it was changed */
296 $modified = filemtime($ppdFile);
297 if(isset($this->cachedList[$ppdFile]) && isset($this->timestamps[$ppdFile]) && $modified == $this->timestamps[$ppdFile]){
298 return($this->cachedList[$ppdFile]);
299 }
301 /* Remember modified timestamp, to speed up next request */
302 $this->timestamps[$ppdFile] = filemtime($ppdFile);
304 $fh= fopen ($ppdFile, 'r');
305 while ((!feof($fh))&&($fh)) {
307 /* Read line */
308 $line= fgets($fh, 256);
309 if (strlen($line) >= 256){
310 trigger_error(_('Parsing PPD file %s failed - line too long. Trailing characters have been ignored'), E_USER_WARNING);
311 }
313 /* Extract interesting informations */
314 if (preg_match('/^\*Manufacturer:/i', $line)){
315 $manufacturer= preg_replace('/^\*Manufacturer:\s+"?([^"]+)"?.*$/i', '\1', $line);
316 }
317 if (preg_match('/^\*ModelName:/i', $line)){
318 $model= preg_replace('/^\*ModelName:\s+"?([^"]+)"?.*$/i', '\1', $line);
319 }
321 /* Got everything we need? Skip rest for speed reasons... */
322 if ($model != '' && $manufacturer != ''){
323 break;
324 }
325 }
326 fclose ($fh);
328 /* Write out a notice that the PPD file seems to be broken if we can't
329 extract any usefull informations */
330 if ($model == '' || $manufacturer == ''){
331 trigger_error(sprintf(_('Parsing PPD file %s failed - no information found.'), $ppdFile), E_USER_WARNING);
332 }
334 return ($manufacturer.' - '.$model);
335 }
338 function getPrinterList($reload= false)
339 {
340 /* Load list of PPD files */
341 if (count($this->cachedList) == 0 || $reload){
342 $list= $this->findPPD($this->path);
344 /* Load descriptive informations to build final printer list */
345 $new = array();
346 foreach ($list as $ppdFile){
347 $new[$ppdFile] = $this->loadDescription($ppdFile);
348 }
349 $this->cachedList= $new ;
351 }
353 return ($this->cachedList);
354 }
356 }
357 // vim:tabstop=2:expandtab:shiftwidth=2:filetype=php:syntax:ruler:
358 ?>