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 if (($fsection) && ($section != "NO_SECTION")){
77 if (preg_match("/^\*CloseGroup:*\s+\**$section\/*/", $line)){
78 $fsection= false;
79 $ret .=$line;
80 continue;
81 }
84 if (preg_match("/^\*OpenUI:*\s+\**$attribute\/*/", $line)){
85 $fattribute= true;
86 $ret .= $line;
87 continue;
88 }
90 if ($fattribute){
91 if (preg_match("/^\*CloseUI:*\s+\**$attribute\/*/", $line)){
92 $fattribute= false;
93 $ret .= $line;
94 continue;
95 }
97 if (preg_match("/^\*Default$attribute:*\s+/", $line)){
98 $line= "*Default$attribute: $value\n";
99 $done =true;
100 }
101 }
102 }else{
103 if (preg_match("/^\*OpenUI:*\s+\**$attribute\/*/", $line)){
104 $fattribute= true;
105 $ret .= $line;
106 continue;
107 }
109 if ($fattribute){
110 if (preg_match("/^\*CloseUI:*\s+\**$attribute\/*/", $line)){
111 $fattribute= false;
112 $ret .= $line;
113 continue;
114 }
116 if (preg_match("/^\*Default$attribute:*\s+/", $line)){
117 $line= "*Default$attribute: $value\n";
118 $done =true;
119 }
120 }
121 }
122 $ret .=$line;
123 }
125 fwrite($wp,$ret);
127 fclose($wp);
128 fclose($rp);
130 copy("$file.tmp", "$file");
131 unlink("$file.tmp");
132 }
135 function saveProperties($ppdFile, $properties)
136 {
137 if(!is_readable($ppdFile)){
138 print_red(sprintf(_("Specified ppd file '%s' can't be opened for reading."),$ppdFile));
139 }elseif(!is_writeable(preg_replace("#(^.*/).*$#","\\1",$ppdFile.".tmp"))){
140 print_red(sprintf(_("The required tmp file file '%s' can't be opened for writing."),$ppdFile.".tmp"));
141 }elseif(!is_writeable($ppdFile.".tmp")){
142 print_red(sprintf(_("The required tmp file file '%s' can't be opened for writing."),$ppdFile.".tmp"));
143 }else{
144 foreach ($properties as $name => $section){
145 foreach ($section as $attribute => $value){
146 if (is_array($value)){
147 $this->updateAttribute($ppdFile, $name, $attribute, $value['_default']);
148 }
149 }
150 }
151 }
152 }
154 function loadProperties($ppdFile)
155 {
156 $group= "";
157 $option= "";
158 $properties= array();
160 $fh= fopen ($ppdFile, 'r');
161 while (!feof($fh)) {
163 /* Read line */
164 $line= fgets($fh, 256);
165 if (strlen($line) >= 256){
166 trigger_error(_('Parsing PPD file %s failed - line too long. Trailing characters have been ignored'), E_USER_WARNING);
167 }
169 /* Trigger for option groups */
170 if (preg_match('/^\*OpenGroup:/i', $line)){
172 /* Sanity checks */
173 if ($group != ""){
174 trigger_error(_('Nested groups are not supported!'), E_USER_WARNING);
175 continue;
176 }
177 if (in_array($group, $properties)){
178 trigger_error(_('Group name not unique!'), E_USER_WARNING);
179 continue;
180 }
182 // TODO: Symbol values are not supported yet!
183 if (preg_match('/\^/', $line)){
184 trigger_error(_('Symbol values are not supported yet!'), E_USER_WARNING);
185 }
186 $complete= preg_replace('@^\*OpenGroup:\s+(.*)$@i', '\1', $line);
187 $complete= trim($complete, '"');
188 if (preg_match('@/@', $complete)){
189 $group= trim(preg_replace('@^\*OpenGroup:\s+"?([^/]+)/.*$@i', '\1', $line));
190 $name = preg_replace('@^\*OpenGroup:\s+"?[^/]+/([^/]+).*$@i', '\1', $line);
191 } else {
192 $group= $complete;
193 $name = $complete;
194 }
195 $properties[$group]= array('_name' => $name);
196 continue;
197 }
198 if (preg_match("/^\*CloseGroup:\s+\"?$group\"?/i", $line)){
199 $group= "";
200 continue;
201 }
203 /* Trigger for options */
204 if (preg_match('/^\*OpenUI\s+/i', $line)){
206 /* Sanity check */
207 if ($option != ""){
208 trigger_error(_('Nested options are not supported!'), E_USER_WARNING);
209 continue;
210 }
212 // TODO: Symbol values are not supported yet!
213 if (preg_match('/\^/', $line)){
214 trigger_error(_('Symbol values are not supported yet!'), E_USER_WARNING);
215 }
216 $complete= preg_replace('@^\*OpenUI\s+(.*)$@i', '\1', $line);
217 $complete= trim($complete, '"');
218 if (preg_match('@/@', $complete)){
219 $option= trim(preg_replace('@^\*OpenUI\s+([^/]+)/.*$@i', '\1', $line));
220 $name = trim(preg_replace('@^\*OpenUI\s+[^/]+/([^/]+).*$@i', '\1', $line));
221 } else {
222 $option= trim($complete);
223 $name = trim($complete);
224 }
226 /* Extract option type */
227 $type= trim(preg_replace('/^[^:]+:\s+/', '', $line));
228 $name= preg_replace('/:.*$/', '', $name);
229 $option= preg_replace('/:.*$/', '', $option);
231 // TODO: PickMany is not supported yet!
232 if (preg_match('/PickMany/i', $type)){
233 trigger_error(_('PickMany is not supported yet!'), E_USER_WARNING);
234 }
235 if(empty($group)){
236 $properties["NO_SECTION"][$option]= array('_name' => $name, '_type' => $type);
237 }else{
238 $properties[$group][$option]= array('_name' => $name, '_type' => $type);
239 }
240 continue;
241 }
242 /* Show interest for option parsing */
243 if ($option != ""){
245 $eoption= preg_replace('@\*@', '', $option);
247 /* Close section? */
248 if (preg_match("@^\*CloseUI:\s+\*$eoption@i", $line)){
249 $option= "";
250 continue;
251 }
253 /* Default value? */
254 if (preg_match("@^\*Default$eoption:@", $line)){
255 $c= preg_replace("@^\*Default$eoption:\s+@", "", $line);
256 if(empty($group)){
257 $properties["NO_SECTION"][$option]['_default']= trim(trim($c, '"'));
258 }else{
259 $properties[$group][$option]['_default']= trim(trim($c, '"'));
260 }
261 continue;
262 }
264 /* Possible value? */
265 if (preg_match("@^\*$eoption\s+@", $line)){
266 #*PageSize Letter/US Letter: "<>setpagedevice"
267 $c= preg_replace("@^\*$eoption\s+([^/]+).*$@", "$1", $line);
268 $d= preg_replace("@^\*$eoption\s+[^/]+/([^:]+).*$@", "$1", $line);
269 if(empty($group)){
270 $properties["NO_SECTION"][$option][trim($c)]= trim($d);
271 }else{
272 $properties[$group][$option][trim($c)]= trim($d);
273 }
274 continue;
275 }
276 }
277 }
278 fclose ($fh);
279 return ($properties);
280 }
282 function loadDescription($ppdFile)
283 {
284 $model= "";
285 $manufacturer= "";
287 $fh= fopen ($ppdFile, 'r');
288 while ((!feof($fh))&&($fh)) {
290 /* Read line */
291 $line= fgets($fh, 256);
292 if (strlen($line) >= 256){
293 trigger_error(_('Parsing PPD file %s failed - line too long. Trailing characters have been ignored'), E_USER_WARNING);
294 }
296 /* Extract interesting informations */
297 if (preg_match('/^\*Manufacturer:/i', $line)){
298 $manufacturer= preg_replace('/^\*Manufacturer:\s+"?([^"]+)"?.*$/i', '\1', $line);
299 }
300 if (preg_match('/^\*ModelName:/i', $line)){
301 $model= preg_replace('/^\*ModelName:\s+"?([^"]+)"?.*$/i', '\1', $line);
302 }
304 /* Got everything we need? Skip rest for speed reasons... */
305 if ($model != '' && $manufacturer != ''){
306 break;
307 }
308 }
309 fclose ($fh);
311 /* Write out a notice that the PPD file seems to be broken if we can't
312 extract any usefull informations */
313 if ($model == '' || $manufacturer == ''){
314 trigger_error(sprintf(_('Parsing PPD file %s failed - no information found.'), $ppdFile), E_USER_WARNING);
315 }
317 return ($manufacturer.' - '.$model);
318 }
321 function getPrinterList($reload= false)
322 {
323 /* Load list of PPD files */
324 if (count($this->cachedList) == 0 || $reload){
325 $list= $this->findPPD($this->path);
327 /* Load descriptive informations to build final printer list */
328 $this->cachedList= array();
329 foreach ($list as $ppdFile){
330 $this->cachedList[$ppdFile]= $this->loadDescription($ppdFile);
331 }
333 }
335 return ($this->cachedList);
336 }
338 }
340 ?>