1 <?php
3 class ppdManager
4 {
5 var $path= "";
6 var $cachedList= array();
8 function ppdManager($path)
9 {
10 if(is_dir($path)){
11 $this->path= $path;
12 }else{
13 print_red(sprintf(_("PPD manager : The specified path '%s' doesn't exists."),$path));
14 return (false);
15 }
16 }
19 function findPPD($path)
20 {
21 $list= array();
22 $currentDir= getcwd();
24 $dh = opendir($path);
25 while(false !== ($file = readdir($dh))){
27 /* Skip well known files */
28 if( $file == '.' || $file == '..'){
29 continue;
30 }
32 /* Recurse through all "common" directories */
33 if(is_dir($path.'/'.$file)){
34 $list= array_merge($list, $this->findPPD($path.'/'.$file));
35 continue;
36 }
38 /* Check for PPD extension */
39 if (preg_match('/\.ppd$/i', $file)){
40 $list[]= $path.'/'.$file;
41 }
42 }
44 closedir($dh);
45 chdir ($currentDir);
46 return ($list);
47 }
50 function updateAttribute($file, $section, $attribute, $value)
51 {
52 $fsection= false;
53 $fattribute= false;
54 $section= preg_replace('/^\*/', '', $section);
55 $attribute= preg_replace('/^\*/', '', $attribute);
57 $rp= @fopen($file, "r");
58 $wp= @fopen("$file.tmp", "w");
62 while (!feof($rp)){
63 $lines[]= fgets($rp, 1024);
64 }
66 $ret = "";
67 $done =false;
68 foreach($lines as $nr => $line){
70 if (preg_match("/\*OpenGroup:*\s+\**$section\/*/", $line)){
71 $fsection= true;
72 $ret .=$line;
73 continue;
74 }
76 /* Change model name .. */
77 if ((preg_match("/^\*".$attribute.":*\s+/",$line)) && ($attribute == "ModelName")){
78 $line= "*$attribute: \"$value\"\n";
79 $done =true;
80 }
82 if (($fsection) && ($section != "NO_SECTION")){
83 if (preg_match("/^\*CloseGroup:*\s+\**$section\/*/", $line)){
84 $fsection= false;
85 $ret .=$line;
86 continue;
87 }
90 if (preg_match("/^\*OpenUI:*\s+\**$attribute\/*/", $line)){
91 $fattribute= true;
92 $ret .= $line;
93 continue;
94 }
96 if ($fattribute){
97 if (preg_match("/^\*CloseUI:*\s+\**$attribute\/*/", $line)){
98 $fattribute= false;
99 $ret .= $line;
100 continue;
101 }
103 if (preg_match("/^\*Default$attribute:*\s+/", $line)){
104 $line= "*Default$attribute: $value\n";
105 $done =true;
106 }
107 }
108 }else{
109 if (preg_match("/^\*OpenUI:*\s+\**$attribute\/*/", $line)){
110 $fattribute= true;
111 $ret .= $line;
112 continue;
113 }
115 if ($fattribute){
116 if (preg_match("/^\*CloseUI:*\s+\**$attribute\/*/", $line)){
117 $fattribute= false;
118 $ret .= $line;
119 continue;
120 }
122 if (preg_match("/^\*Default$attribute:*\s+/", $line)){
123 $line= "*Default$attribute: $value\n";
124 $done =true;
125 }
126 }
127 }
128 $ret .=$line;
129 }
131 fwrite($wp,$ret);
133 fclose($wp);
134 fclose($rp);
136 copy("$file.tmp", "$file");
137 unlink("$file.tmp");
138 }
141 function saveProperties($ppdFile, $properties)
142 {
143 if(!is_readable($ppdFile)){
144 print_red(sprintf(_("Specified ppd file '%s' can't be opened for reading."),$ppdFile));
145 }elseif(!is_writeable(preg_replace("#(^.*/).*$#","\\1",$ppdFile.".tmp"))){
146 print_red(sprintf(_("The required tmp file file '%s' can't be opened for writing."),$ppdFile.".tmp"));
147 }else{
148 foreach ($properties as $name => $section){
149 foreach ($section as $attribute => $value){
150 if (is_array($value)){
151 $this->updateAttribute($ppdFile, $name, $attribute, $value['_default']);
152 }
153 }
154 }
155 }
156 }
158 function loadProperties($ppdFile)
159 {
160 $group= "";
161 $option= "";
162 $properties= array();
164 $fh= fopen ($ppdFile, 'r');
165 while (!feof($fh)) {
167 /* Read line */
168 $line= fgets($fh, 256);
169 if (strlen($line) >= 256){
170 trigger_error(_('Parsing PPD file %s failed - line too long. Trailing characters have been ignored'), E_USER_WARNING);
171 }
173 /* Trigger for option groups */
174 if (preg_match('/^\*OpenGroup:/i', $line)){
176 /* Sanity checks */
177 if ($group != ""){
178 trigger_error(_('Nested groups are not supported!'), E_USER_WARNING);
179 continue;
180 }
181 if (in_array($group, $properties)){
182 trigger_error(_('Group name not unique!'), E_USER_WARNING);
183 continue;
184 }
186 // TODO: Symbol values are not supported yet!
187 if (preg_match('/\^/', $line)){
188 trigger_error(_('Symbol values are not supported yet!'), E_USER_WARNING);
189 }
190 $complete= preg_replace('@^\*OpenGroup:\s+(.*)$@i', '\1', $line);
191 $complete= trim($complete, '"');
192 if (preg_match('@/@', $complete)){
193 $group= trim(preg_replace('@^\*OpenGroup:\s+"?([^/]+)/.*$@i', '\1', $line));
194 $name = preg_replace('@^\*OpenGroup:\s+"?[^/]+/([^/]+).*$@i', '\1', $line);
195 } else {
196 $group= $complete;
197 $name = $complete;
198 }
199 $properties[$group]= array('_name' => $name);
200 continue;
201 }
202 if (preg_match("/^\*CloseGroup:\s+\"?$group\"?/i", $line)){
203 $group= "";
204 continue;
205 }
207 /* Trigger for options */
208 if (preg_match('/^\*OpenUI\s+/i', $line)){
210 /* Sanity check */
211 if ($option != ""){
212 trigger_error(_('Nested options are not supported!'), E_USER_WARNING);
213 continue;
214 }
216 // TODO: Symbol values are not supported yet!
217 if (preg_match('/\^/', $line)){
218 trigger_error(_('Symbol values are not supported yet!'), E_USER_WARNING);
219 }
220 $complete= preg_replace('@^\*OpenUI\s+(.*)$@i', '\1', $line);
221 $complete= trim($complete, '"');
222 if (preg_match('@/@', $complete)){
223 $option= trim(preg_replace('@^\*OpenUI\s+([^/]+)/.*$@i', '\1', $line));
224 $name = trim(preg_replace('@^\*OpenUI\s+[^/]+/([^/]+).*$@i', '\1', $line));
225 } else {
226 $option= trim($complete);
227 $name = trim($complete);
228 }
230 /* Extract option type */
231 $type= trim(preg_replace('/^[^:]+:\s+/', '', $line));
232 $name= preg_replace('/:.*$/', '', $name);
233 $option= preg_replace('/:.*$/', '', $option);
235 // TODO: PickMany is not supported yet!
236 if (preg_match('/PickMany/i', $type)){
237 trigger_error(_('PickMany is not supported yet!'), E_USER_WARNING);
238 }
239 if(empty($group)){
240 $properties["NO_SECTION"][$option]= array('_name' => $name, '_type' => $type);
241 }else{
242 $properties[$group][$option]= array('_name' => $name, '_type' => $type);
243 }
244 continue;
245 }
246 /* Show interest for option parsing */
247 if ($option != ""){
249 $eoption= preg_replace('@\*@', '', $option);
251 /* Close section? */
252 if (preg_match("@^\*CloseUI:\s+\*$eoption@i", $line)){
253 $option= "";
254 continue;
255 }
257 /* Default value? */
258 if (preg_match("@^\*Default$eoption:@", $line)){
259 $c= preg_replace("@^\*Default$eoption:\s+@", "", $line);
260 if(empty($group)){
261 $properties["NO_SECTION"][$option]['_default']= trim(trim($c, '"'));
262 }else{
263 $properties[$group][$option]['_default']= trim(trim($c, '"'));
264 }
265 continue;
266 }
268 /* Possible value? */
269 if (preg_match("@^\*$eoption\s+@", $line)){
270 #*PageSize Letter/US Letter: "<>setpagedevice"
271 $c= preg_replace("@^\*$eoption\s+([^/]+).*$@", "$1", $line);
272 $d= preg_replace("@^\*$eoption\s+[^/]+/([^:]+).*$@", "$1", $line);
273 if(empty($group)){
274 $properties["NO_SECTION"][$option][trim($c)]= trim($d);
275 }else{
276 $properties[$group][$option][trim($c)]= trim($d);
277 }
278 continue;
279 }
280 }
281 }
282 fclose ($fh);
283 return ($properties);
284 }
286 function loadDescription($ppdFile)
287 {
288 $model= "";
289 $manufacturer= "";
291 $fh= fopen ($ppdFile, 'r');
292 while ((!feof($fh))&&($fh)) {
294 /* Read line */
295 $line= fgets($fh, 256);
296 if (strlen($line) >= 256){
297 trigger_error(_('Parsing PPD file %s failed - line too long. Trailing characters have been ignored'), E_USER_WARNING);
298 }
300 /* Extract interesting informations */
301 if (preg_match('/^\*Manufacturer:/i', $line)){
302 $manufacturer= preg_replace('/^\*Manufacturer:\s+"?([^"]+)"?.*$/i', '\1', $line);
303 }
304 if (preg_match('/^\*ModelName:/i', $line)){
305 $model= preg_replace('/^\*ModelName:\s+"?([^"]+)"?.*$/i', '\1', $line);
306 }
308 /* Got everything we need? Skip rest for speed reasons... */
309 if ($model != '' && $manufacturer != ''){
310 break;
311 }
312 }
313 fclose ($fh);
315 /* Write out a notice that the PPD file seems to be broken if we can't
316 extract any usefull informations */
317 if ($model == '' || $manufacturer == ''){
318 trigger_error(sprintf(_('Parsing PPD file %s failed - no information found.'), $ppdFile), E_USER_WARNING);
319 }
321 return ($manufacturer.' - '.$model);
322 }
325 function getPrinterList($reload= false)
326 {
327 /* Load list of PPD files */
328 if (count($this->cachedList) == 0 || $reload){
329 $list= $this->findPPD($this->path);
331 /* Load descriptive informations to build final printer list */
332 $this->cachedList= array();
333 foreach ($list as $ppdFile){
334 $this->cachedList[$ppdFile]= $this->loadDescription($ppdFile);
335 }
337 }
339 return ($this->cachedList);
340 }
342 }
344 ?>