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 }elseif(is_link($path) && (is_dir(readlink($path)))) {
14 $this->path= $path;
15 } else {
16 msg_dialog::display(_("PPD manager error"), sprintf(_("The specified path '%s' does not exist."),$path), ERROR_DIALOG);
17 return (false);
18 }
19 }
22 function findPPD($path)
23 {
24 $list= array();
25 $currentDir= getcwd();
27 $dh = opendir($path);
28 while(false !== ($file = readdir($dh))){
30 /* Skip well known files */
31 if( $file == '.' || $file == '..'){
32 continue;
33 }
35 /* Recurse through all "common" directories */
36 $check_path= $path.'/'.$file;
37 if(is_dir($check_path) || (is_link($check_path) && (is_dir(readlink($check_path))))){
38 $list= array_merge($list, $this->findPPD($check_path));
39 continue;
40 }
42 /* Check for PPD extension */
43 if (preg_match('/\.ppd(.gz)?$/i', $file)){
44 $list[]= $path.'/'.$file;
45 }
46 }
48 closedir($dh);
49 chdir ($currentDir);
50 return ($list);
51 }
54 function updateAttribute($file, $section, $attribute, $value)
55 {
56 $fsection= false;
57 $fattribute= false;
58 $section= preg_replace('/^\*/', '', $section);
59 $attribute= preg_replace('/^\*/', '', $attribute);
61 $rp= @gzopen($file, "r");
62 $wp= @gzopen("$file.tmp", "w");
66 while (!gzeof($rp)){
67 $lines[]= gzgets($rp, 1024);
68 }
70 $ret = "";
71 $done =false;
72 foreach($lines as $nr => $line){
74 if (preg_match("/\*OpenGroup:*\s+\**$section\/*/", $line)){
75 $fsection= true;
76 $ret .=$line;
77 continue;
78 }
80 /* Change model name .. */
81 if ((preg_match("/^\*".$attribute.":*\s+/",$line)) && ($attribute == "ModelName")){
82 $line= "*$attribute: \"$value\"\n";
83 $done =true;
84 }
86 if (($fsection) && ($section != "NO_SECTION")){
87 if (preg_match("/^\*CloseGroup:*\s+\**$section\/*/", $line)){
88 $fsection= false;
89 $ret .=$line;
90 continue;
91 }
94 if (preg_match("/^\*OpenUI:*\s+\**$attribute\/*/", $line)){
95 $fattribute= true;
96 $ret .= $line;
97 continue;
98 }
100 if ($fattribute){
101 if (preg_match("/^\*CloseUI:*\s+\**$attribute\/*/", $line)){
102 $fattribute= false;
103 $ret .= $line;
104 continue;
105 }
107 if (preg_match("/^\*Default$attribute:*\s+/", $line)){
108 $line= "*Default$attribute: $value\n";
109 $done =true;
110 }
111 }
112 }else{
113 if (preg_match("/^\*OpenUI:*\s+\**$attribute\/*/", $line)){
114 $fattribute= true;
115 $ret .= $line;
116 continue;
117 }
119 if ($fattribute){
120 if (preg_match("/^\*CloseUI:*\s+\**$attribute\/*/", $line)){
121 $fattribute= false;
122 $ret .= $line;
123 continue;
124 }
126 if (preg_match("/^\*Default$attribute:*\s+/", $line)){
127 $line= "*Default$attribute: $value\n";
128 $done =true;
129 }
130 }
131 }
132 $ret .=$line;
133 }
135 gzwrite($wp,$ret);
137 gzclose($wp);
138 gzclose($rp);
140 copy("$file.tmp", "$file");
141 unlink("$file.tmp");
142 }
145 function saveProperties($ppdFile, $properties)
146 {
147 if(!is_readable($ppdFile)){
148 msg_dialog::display(_("PPD manager error"), sprintf(_("Specified PPD file '%s' cannot be opened for reading."),$ppdFile), ERROR_DIALOG);
149 }elseif(!is_writeable(preg_replace("#(^.*/).*$#","\\1",$ppdFile.".tmp"))){
150 msg_dialog::display(_("PPD manager error"), sprintf(_("The temporary file '%s' cannot be opened for writing."),$ppdFile.".tmp"), ERROR_DIALOG);
151 }else{
152 if(is_array($properties)){
153 foreach ($properties as $name => $section){
154 foreach ($section as $attribute => $value){
155 if (is_array($value)){
156 $this->updateAttribute($ppdFile, $name, $attribute, $value['_default']);
157 }
158 }
159 }
160 }
161 }
162 }
164 function loadProperties($ppdFile)
165 {
166 if(!(filesize($ppdFile))) {
167 trigger_error(_('Parsing PPD file failed - File has 0 bytes'));
168 return;
169 }
171 $group= "";
172 $option= "";
173 $properties= array();
175 $fh= gzopen ($ppdFile, 'r');
176 while (!gzeof($fh)) {
178 /* Read line */
179 $line= gzgets($fh, 256);
180 if (strlen($line) >= 256){
181 trigger_error(_('Parsing PPD file %s failed - line too long. Trailing characters have been ignored'), E_USER_WARNING);
182 }
184 /* Trigger for option groups */
185 if (preg_match('/^\*OpenGroup:/i', $line)){
187 /* Sanity checks */
188 if ($group != ""){
189 trigger_error(_('Nested groups are not supported!'), E_USER_WARNING);
190 continue;
191 }
192 if (in_array($group, $properties)){
193 trigger_error(_('Group name not unique!'), E_USER_WARNING);
194 continue;
195 }
197 // TODO: Symbol values are not supported yet!
198 if (preg_match('/\^/', $line)){
199 trigger_error(_('Symbol values are not supported yet!'), E_USER_WARNING);
200 }
201 $complete= preg_replace('@^\*OpenGroup:\s+(.*)$@i', '\1', $line);
202 $complete= trim($complete, '"');
203 if (preg_match('@/@', $complete)){
204 $group= trim(preg_replace('@^\*OpenGroup:\s+"?([^/]+)/.*$@i', '\1', $line));
205 $name = preg_replace('@^\*OpenGroup:\s+"?[^/]+/([^/]+).*$@i', '\1', $line);
206 } else {
207 $group= $complete;
208 $name = $complete;
209 }
210 $properties[$group]= array('_name' => $name);
211 continue;
212 }
213 if (preg_match("/^\*CloseGroup:\s+\"?$group\"?/i", $line)){
214 $group= "";
215 continue;
216 }
218 /* Trigger for options */
219 if (preg_match('/^\*OpenUI\s+/i', $line)){
221 /* Sanity check */
222 if ($option != ""){
223 trigger_error(_('Nested options are not supported!'), E_USER_WARNING);
224 continue;
225 }
227 // TODO: Symbol values are not supported yet!
228 if (preg_match('/\^/', $line)){
229 trigger_error(_('Symbol values are not supported yet!'), E_USER_WARNING);
230 }
231 $complete= preg_replace('@^\*OpenUI\s+(.*)$@i', '\1', $line);
232 $complete= trim($complete, '"');
233 if (preg_match('@/@', $complete)){
234 $option= trim(preg_replace('@^\*OpenUI\s+([^/]+)/.*$@i', '\1', $line));
235 $name = trim(preg_replace('@^\*OpenUI\s+[^/]+/([^/]+).*$@i', '\1', $line));
236 } else {
237 $option= trim($complete);
238 $name = trim($complete);
239 }
241 /* Extract option type */
242 $type= trim(preg_replace('/^[^:]+:\s+/', '', $line));
243 $name= preg_replace('/:.*$/', '', $name);
244 $option= preg_replace('/:.*$/', '', $option);
246 // TODO: PickMany is not supported yet!
247 if (preg_match('/PickMany/i', $type)){
248 trigger_error(_('PickMany is not supported yet!'), E_USER_WARNING);
249 }
250 if(empty($group)){
251 $properties["NO_SECTION"][$option]= array('_name' => $name, '_type' => $type);
252 }else{
253 $properties[$group][$option]= array('_name' => $name, '_type' => $type);
254 }
255 continue;
256 }
257 /* Show interest for option parsing */
258 if ($option != ""){
260 $eoption= preg_replace('@\*@', '', $option);
262 /* Close section? */
263 if (preg_match("@^\*CloseUI:\s+\*$eoption@i", $line)){
264 $option= "";
265 continue;
266 }
268 /* Default value? */
269 if (preg_match("@^\*Default$eoption:@", $line)){
270 $c= preg_replace("@^\*Default$eoption:\s+@", "", $line);
271 if(empty($group)){
272 $properties["NO_SECTION"][$option]['_default']= trim(trim($c, '"'));
273 }else{
274 $properties[$group][$option]['_default']= trim(trim($c, '"'));
275 }
276 continue;
277 }
279 /* Possible value? */
280 if (preg_match("@^\*$eoption\s+@", $line)){
281 #*PageSize Letter/US Letter: "<>setpagedevice"
282 /* Options can have one of 2 forms:
283 *OptionName OptionValue/TranslationOfValue: ...
284 *OptionName OptionValue: ...
285 i.e. there may be a translation or not.
286 */
287 $c= preg_replace("@^\*$eoption\s+([^/:]+).*$@", "$1", $line);
288 $d= preg_replace("@^\*$eoption\s+[^/]+/([^:]+).*$@", "$1", $line);
289 if ($d == $line) { /* if there is no translation, use untranslated value*/
290 $d = "$c";
291 }
292 if(empty($group)){
293 $properties["NO_SECTION"][$option][trim($c)]= trim($d);
294 }else{
295 $properties[$group][$option][trim($c)]= trim($d);
296 }
297 continue;
298 }
299 }
300 }
301 gzclose ($fh);
302 return ($properties);
303 }
305 function loadDescription($ppdFile)
306 {
307 if(!(filesize($ppdFile))) {
308 trigger_error(_('Parsing PPD file failed - File has 0 bytes'));
309 return;
310 }
312 $ppdDesc = array();
314 /* Only parse complete PPD file again, if it was changed */
315 $modified = filemtime($ppdFile);
316 if(isset($this->cachedList[$ppdFile]) && isset($this->timestamps[$ppdFile]) && $modified == $this->timestamps[$ppdFile]){
317 return($this->cachedList[$ppdFile]);
318 }
320 /* Remember modified timestamp, to speed up next request */
321 $this->timestamps[$ppdFile] = filemtime($ppdFile);
323 $fh= gzopen ($ppdFile, 'r');
324 while ((!gzeof($fh))&&($fh)) {
326 /* Read line */
327 $line= gzgets($fh, 256);
328 if (strlen($line) >= 256){
329 trigger_error(_('Parsing PPD file %s failed - line too long. Trailing characters have been ignored'), E_USER_WARNING);
330 }
332 /* Extract interesting informations */
333 if (preg_match('/^\*Manufacturer:/i', $line)){
334 $ppdDesc['manufacturer'] = trim(preg_replace('/^\*Manufacturer:\s+"?([^"]+)"?.*$/i', '\1', $line));
335 }
336 if (preg_match('/^\*ModelName:/i', $line)){
337 $ppdDesc['model'] = trim(preg_replace('/^\*ModelName:\s+"?([^"]+)"?.*$/i', '\1', $line));
338 }
340 /* Got everything we need? Skip rest for speed reasons... */
341 if (isset($ppdDesc['manufacturer']) && isset($ppdDesc['model'])){
342 break;
343 }
344 }
345 gzclose ($fh);
347 /* If model contains manufacturer strip it */
348 $ppdDesc['model'] = str_replace($ppdDesc['manufacturer']." ", '', $ppdDesc['model']);
350 /* Write out a notice that the PPD file seems to be broken if we can't
351 extract any usefull informations */
352 if ($ppdDesc['manufacturer'] == '' || $ppdDesc['model'] == ''){
353 trigger_error(sprintf(_('Parsing PPD file %s failed - no information found.'), $ppdFile), E_USER_WARNING);
354 }
356 $ppdDesc['name'] = $ppdDesc['manufacturer'] . " - " . $ppdDesc['model'];
358 return $ppdDesc;
359 }
362 function getPrinterList($reload= false)
363 {
364 /* Load list of PPD files */
365 if (count($this->cachedList) == 0 || $reload){
366 $list= $this->findPPD($this->path);
368 /* Load descriptive informations to build final printer list */
369 $new = array();
370 foreach ($list as $ppdFile){
371 $new[$ppdFile] = $this->loadDescription($ppdFile);
372 }
373 $this->cachedList= $new ;
375 }
377 return ($this->cachedList);
378 }
380 }
381 // vim:tabstop=2:expandtab:shiftwidth=2:filetype=php:syntax:ruler:
382 ?>