]> git.llucax.com Git - mecon/scripts.git/blob - code2xmi/code2xmi.php
0fb2ea73bab2405aa94a8fcf203c0f2fe4f397ba
[mecon/scripts.git] / code2xmi / code2xmi.php
1 #! /usr/bin/php4 -qC
2 <?php 
3 /* vim: set binary expandtab tabstop=4 shiftwidth=4 foldmethod=marker:
4 -------------------------------------------------------------------------------
5 Created: jue jul 31 14:01:57 ART 2003
6 Author : Martin Marrese <m_marrese@argentina.com> <mmarre@mecon.gov.ar>
7 -------------------------------------------------------------------------------
8 $Id$
9 -----------------------------------------------------------------------------*/
10
11 //TAG's XML {{{
12 $xmi_start = <<<EOT
13 <?xml version="1.0" encoding="UTF-8"?>
14 <XMI xmlns:UML="org.omg/standards/UML" verified="false" timestamp="" xmi.version="1.2" >
15   <XMI.content>
16     <umlobjects>
17 EOT;
18 $xmi_half = <<<EOT2
19     </umlobjects>
20     <listview>
21       <listitem open="1" type="800" id="-1" label="Views" >
22         <listitem open="1" type="801" id="-1" label="Logical View" >
23 EOT2;
24 $xmi_end = <<<EOT3
25         </listitem>
26         <listitem open="1" type="802" id="-1" label="Use Case View" />
27         <listitem open="1" type="821" id="-1" label="Component View" />
28         <listitem open="1" type="827" id="-1" label="Deployment View" />
29         <listitem open="0" type="823" id="-1" label="Diagrams" />
30       </listitem>
31     </listview>                                                                     
32   </XMI.content>
33 </XMI>
34 EOT3;
35 //}}}
36 //TEMPLATES XMI {{{
37 $umlclass       = '<UML:Class stereotype="##STEREOTYPE##" package="##PACKAGE##" xmi.id="##ID##" abstract="##ABSTRACT##" documentation="##DOCUMENTATION##" name="##NAME##" static="##STATIC##" scope="##SCOPE##" >';
38 $umlclass_c     = '</UML:Class>';
39
40 $umloperation   = '<UML:Operation stereotype="##STEREOTYPE##" package="##PACKAGE##" xmi.id="##ID##" type="##TYPE##" abstract="##ABSTRACT##" documentation="##DOCUMENTATION##" name="##NAME##" static="##STATIC##" scope="##SCOPE##">';
41 $umloperation_c = '</UML:Operation>';
42
43 $umlparameter   = '<UML:Parameter stereotype="##STEREOTYPE##" package="##PACKAGE##" xmi.id="##ID##" value="##VALUE##" type="##TYPE##" abstract="##ABSTRACT##" documentation="##DOCUMENTATION##" name="##NAME##" static="##STATIC##" scope="##SCOPE##" />';
44
45 $umlattribute   = '<UML:Attribute stereotype="##STEREOTYPE##" package="##PACKAGE##" xmi.id="##ID##" value="##VALUE##" type="##TYPE##" abstract="##ABSTRACT##" documentation="##DOCUMENTATION##" name="##NAME##" static="##STATIC##" scope="##SCOPE##" />';
46
47 $umllistitem   = '<listitem open="0" type="##TYPE##" id="##ID##" label="##LABEL##" >';
48 $umllistitem_c = '</listitem>';
49 //}}}
50 //GLOBALS {{{
51 $ID      = 0;       //Global ID
52 $IDPARAM = 0;       //Functions parameter ID
53 $ARRAY   = array(); //Array with information obtained from the code file
54 //}}}
55
56 //GETTING DATA FROM FILES {{{
57 for ($j = 1; $j < count($argv); $j++) {
58     $file = $argv[$j];
59     if (is_dir($file)) {
60         $dh = opendir($file);
61         while (($f = readdir($dh)) !== false) {
62             if (is_readable("$file/$f") and substr($f, -4) == '.php') {
63                 $argv[] = "$file/$f";
64             }
65         }
66         closedir($dh);
67         continue;
68     } 
69     //PARSING INFORMATION {{{
70     elseif (is_readable($file) and substr($file, -4) == '.php') {
71         $cont = file ($file);
72         $DOCUMENTING = false;
73         $options = array ();
74         $cont_param = -1;
75         foreach ($cont as $line) {
76             $line = trim ($line);
77             $tmp = preg_split ('/[^\w_\/\*\@\$\&\.\']+/', $line);
78             //DOCUMENTING? {{{
79             if ($tmp['0'] == '/**') { //Starts documentation
80                 $DOCUMENTING = true;
81                 $doc_param = 0;       //If 1 the line belongs to a parameter documentation
82             }
83             if ($tmp['0'] == '*/') {  //Ends documentation
84                 $DOCUMENTING = false;
85                 $doc_param = 0;
86             }
87             //}}}
88             //Parse documentation {{{
89             if ($DOCUMENTING) {
90                 $tmp2   = ltrim ($line,'* /**'); //Removes * or /** from the beginning of the line
91                 $action = substr($tmp2, 0, strpos($tmp2,' ')); //Gets the action (evrerything before the @)
92                 $action = ($action === '') ? $tmp2 : $action;
93                 $value  = trim(strstr($tmp2, ' '));            //Action value
94                 //SWITCH ACTION {{{
95                 switch ($action) {
96                     case '@access'  : switch ($value) {
97                                           case 'private'  : $options['access'] = 201;
98                                                             break;
99                                           case 'protected': $options['access'] = 202;
100                                                             break;
101                                           default         : $options['access'] = 200;
102                                       }
103                                       $doc_param = 0;
104                                       break;
105                     case '@package' : $options['package'] = $value;
106                                       $doc_param = 0;
107                                       break;
108                     case '@abstract': $options['abstract'] = 1;
109                                       $doc_param = 0;
110                                       break;
111                     case '@static'  : $options['static'] = 1;
112                                       $doc_param = 0;
113                                       break;
114                     case '@var'     : $options['type'] = substr($value, 0, strpos($value,' '));
115                                       $doc_param = 0;
116                                       break;
117                     case '@return'  : $type = substr($value, 0, strpos($value,' '));
118                                       $rest = (strpos($value,' ')) ? substr($value, strpos($value,' ')) : '';  
119                                       if ($type === '') {
120                                           $type = $value;
121                                       }
122                                       if (strtolower($type) == 'object') {
123                                           $rest = trim($rest);
124                                           $type = substr($rest, 0, strpos($rest,' '));
125                                           $rest = (strpos($rest,' ')) ? substr($rest, strpos($rest,' ')) : '';  
126                                       }
127                                       $options['type'] = $type;
128                                       //If there is more documentation, I add it to the main documentation
129                                       if (trim($rest)) {
130                                           $options['documentation'].= "Returns: ".trim($rest);  
131                                       }
132                                       $doc_param = 0;
133                                       $rest = '';
134                                       break;
135                     case '@param'   : $cont_param++;
136                                       $type = substr($value, 0, strpos($value,' '));
137                                       $rest = substr($value, strpos($value,' ') + 1);
138                                       if (strtolower($type) == 'object') {
139                                           $rest = trim($rest);
140                                           $type = substr($rest, 0, strpos($rest,' '));
141                                           $rest = substr($rest, strpos($rest,' ') + 1);
142                                       }
143                                       $options['param'][$cont_param]['type'] = $type;
144                                       $options['param'][$cont_param]['documentation'] = trim(substr(trim($rest), strpos(trim($rest), ' ')));
145                                       $doc_param = 1;
146                                       break;
147                     default:
148                         $tmp2 = str_replace('"', '&quot;', $tmp2);
149                         $tmp2 = str_replace('<', '&lt;'  , $tmp2);
150                         $tmp2 = str_replace('>', '&gt;'  , $tmp2);
151                         $tmp2 = str_replace('&', '&amp;' , $tmp2);
152                         if (@$doc_param) {
153                             $options['param'][$cont_param]['documentation'].= "\n".$tmp2;
154                         }
155                         else {
156                             @$options['documentation'].= $tmp2."\n";
157                         }
158                 }
159                 //}}}
160             }
161             //}}}
162             //CLASS {{{
163             if (!$DOCUMENTING && $tmp['0'] == 'class') {
164                 $ID++;
165                 $IDCLASS = $ID;
166                 $ARRAY[$ID]['id']            = $ID;
167                 $ARRAY[$ID]['name']          = $tmp['1'];
168                 $ARRAY[$ID]['stereotype']    = (@$options['stereotype'])    ? $options['stereotype']    : '';
169                 $ARRAY[$ID]['package']       = (@$options['package'])       ? $options['package']       : '';
170                 $ARRAY[$ID]['abstract']      = (@$options['abstract'])      ? $options['abstract']      : 0;
171                 $ARRAY[$ID]['documentation'] = (@$options['documentation']) ? $options['documentation'] : '';
172                 $ARRAY[$ID]['static']        = (@$options['static'])        ? $options['static']        : 0;
173                 $ARRAY[$ID]['scope']         = (@$options['access'])        ? $options['access']        : 200;
174                 $ARRAY[$ID]['operations']    = array();
175                 $ARRAY[$ID]['attributes']    = array();
176                 $options = array();
177             }
178             //}}}
179             //FUNCTION {{{
180             if (!$DOCUMENTING && $tmp['0'] == 'function') {
181                 $ID++;
182
183                 if ($tmp['1']{0} == '&') {
184                     $tmp['1'] = substr($tmp['1'], 1); //Removes the &
185                     $options['type'] = (@$options['type']) ? '&amp;'.$options['type'] : '';
186  
187                 }
188                 if ($tmp['1']{0} == '_') {
189                     $tmp['1'] = substr($tmp['1'], 1); //Removes the _
190                 }
191                 $ARRAY[$IDCLASS]['operations'][$ID]['id']            = $ID;
192                 $ARRAY[$IDCLASS]['operations'][$ID]['name']          = $tmp['1'];
193                 $ARRAY[$IDCLASS]['operations'][$ID]['stereotype']    = (@$options['stereotype'])    ? $options['stereotype']    : '';  
194                 $ARRAY[$IDCLASS]['operations'][$ID]['package']       = (@$options['package'])       ? $options['package']       : '';  
195                 $ARRAY[$IDCLASS]['operations'][$ID]['abstract']      = (@$options['abstract'])      ? $options['abstract']      : 0;   
196                 $ARRAY[$IDCLASS]['operations'][$ID]['documentation'] = (@$options['documentation']) ? $options['documentation'] : '';  
197                 $ARRAY[$IDCLASS]['operations'][$ID]['static']        = (@$options['static'])        ? $options['static']        : 0;   
198                 $ARRAY[$IDCLASS]['operations'][$ID]['scope']         = (@$options['access'])        ? $options['access']        : 200;
199                 $ARRAY[$IDCLASS]['operations'][$ID]['type']          = (@$options['type'])          ? $options['type']          : '';
200                 $ARRAY[$IDCLASS]['operations'][$ID]['param']         = array();
201
202                 //PARAMETERS {{{
203                 $param_line = trim(substr(strstr($line,'('),0),'{ '); //Removes the last {
204                 $param = parseParenthesis($param_line);
205                 $i = 0;
206                 foreach ($param as $par) {
207                     if ($par['name']{0} == '$'|| $par['name']{0} == '&') { //If starts with $ or &
208                         switch ($par['name']{0}) {
209                             case '$': $par['name'] = substr($par['name'], 1); //Removes the $
210                                       break;
211                             case '&': $par['name'] = substr($par['name'], 2); //Removes the & and the $
212                                       $options['param'][$i]['type'] = 
213                                           (@$options['param'][$i]['type']) ? '&amp;'.$options['param'][$i]['type'] : '';
214                                       break;
215                         }                        
216                         $ARRAY[$IDCLASS]['operations'][$ID]['param'][$i]['id'] = $i;
217                         $ARRAY[$IDCLASS]['operations'][$ID]['param'][$i]['name'] = $par['name'];
218                         $ARRAY[$IDCLASS]['operations'][$ID]['param'][$i]['stereotype'] = 
219                             (@$options['param'][$i]['stereotype']) ? $options['param'][$i]['stereotype'] : '';
220                         
221                         $ARRAY[$IDCLASS]['operations'][$ID]['param'][$i]['package'] = 
222                             (@$options['param'][$i]['package']) ? $options['param'][$i]['package'] : '';
223                         
224                         $ARRAY[$IDCLASS]['operations'][$ID]['param'][$i]['abstract'] = 
225                             (@$options['param'][$i]['abstract']) ? $options['param'][$i]['abstract'] : 0;
226                         
227                         $ARRAY[$IDCLASS]['operations'][$ID]['param'][$i]['documentation'] = 
228                             (@$options['param'][$i]['documentation']) ? $options['param'][$i]['documentation'] : '';
229                         
230                         $ARRAY[$IDCLASS]['operations'][$ID]['param'][$i]['static'] = 
231                             (@$options['param'][$i]['static']) ? $options['param'][$i]['static'] : 0;
232                         
233                         $ARRAY[$IDCLASS]['operations'][$ID]['param'][$i]['scope'] = 
234                             (@$options['param'][$i]['access']) ? $options['param'][$i]['access'] : '';
235                         
236                         $ARRAY[$IDCLASS]['operations'][$ID]['param'][$i]['type'] = 
237                             (@$options['param'][$i]['type']) ? $options['param'][$i]['type'] : '';
238                         
239                         $ARRAY[$IDCLASS]['operations'][$ID]['param'][$i]['value'] = 
240                             (@$par['value']) ? $par['value'] : '';
241                     }
242                     $i++;
243                 }
244                 //}}}
245                 $options = array();
246                 $cont_param = -1;
247                 $i = 0;
248            }
249             //}}}
250             //ATRIBUTTES {{{
251             if (!$DOCUMENTING && $tmp['0'] == 'var') {
252                 $ID++;
253                 $tmp['1'] = substr($tmp['1'], 1); //Removes the $
254                 if ($tmp['1']{0} == '_') {
255                     $tmp['1'] = substr($tmp['1'],1); //Removes the _
256                     if (!(@$options['access'])) {
257                         $options['access'] = 201;
258                     }
259                 }
260                 //Check for default values
261                 if (array_key_exists('2',$tmp)) {
262                     $options['value'] = $tmp['2'];
263                 }
264                 
265                 $ARRAY[$IDCLASS]['attributes'][$ID]['id']            = $ID;
266                 $ARRAY[$IDCLASS]['attributes'][$ID]['name']          = $tmp['1'];
267                 $ARRAY[$IDCLASS]['attributes'][$ID]['stereotype']    = (@$options['stereotype'])    ? $options['stereotype']    : '';  
268                 $ARRAY[$IDCLASS]['attributes'][$ID]['package']       = (@$options['package'])       ? $options['package']       : '';  
269                 $ARRAY[$IDCLASS]['attributes'][$ID]['abstract']      = (@$options['abstract'])      ? $options['abstract']      : 0;   
270                 $ARRAY[$IDCLASS]['attributes'][$ID]['documentation'] = (@$options['documentation']) ? $options['documentation'] : '';  
271                 $ARRAY[$IDCLASS]['attributes'][$ID]['static']        = (@$options['static'])        ? $options['static']        : 0;   
272                 $ARRAY[$IDCLASS]['attributes'][$ID]['scope']         = (@$options['access'])        ? $options['access']        : 200;  
273                 $ARRAY[$IDCLASS]['attributes'][$ID]['type']          = (@$options['type'])          ? $options['type']          : '';
274                 $ARRAY[$IDCLASS]['attributes'][$ID]['value']         = (@$options['value'])         ? $options['value']         : '';
275                 $options = array();
276             }
277         //}}}
278         }
279     } 
280     //}}}
281 }
282 //}}}
283
284 //WRITES XMI FILE {{{
285 $m =fopen ('./umlOut.xmi', 'w');
286 fwrite($m, $xmi_start);
287 $LISTITEM = '';
288 //CLASS
289 foreach ($ARRAY as $ar) {
290     //Prepare class line
291     $tmp2 = $umlclass;
292     fwrite($m, preg_replace (array ('/##STEREOTYPE##/','/##PACKAGE##/','/##ID##/','/##ABSTRACT##/','/##DOCUMENTATION##/','/##NAME##/',
293                                     '/##STATIC##/','/##SCOPE##/'), 
294                              array ($ar['stereotype'],$ar['package'],$ar['id'],$ar['abstract'],trim($ar['documentation']),$ar['name'],
295                                     $ar['static'],$ar['scope']), 
296                              $tmp2)."\n");
297     //Prepare class listitem line 
298     $tmp2 = $umllistitem;
299     $LISTITEM.= preg_replace (array('/##TYPE##/','/##LABEL##/','/##ID##/'),array(813, $ar['name'], $ar['id']),$tmp2)."\n";  
300
301     //FUNCTIONS
302     foreach ($ar['operations'] as $op) {
303         $tmp2 = $umloperation;
304         fwrite($m, preg_replace (array('/##STEREOTYPE##/','/##PACKAGE##/','/##ID##/','/##TYPE##/','/##ABSTRACT##/','/##DOCUMENTATION##/',
305                                        '/##NAME##/','/##STATIC##/','/##SCOPE##/'), 
306                                  array($op['stereotype'],$op['package'],$op['id'],$op['type'],$op['abstract'],trim($op['documentation']),
307                                        $op['name'],$op['static'],$op['scope']) , 
308                                  $tmp2)."\n");
309         //PARAMETERS
310         //Parameters aren't listed in listview
311         foreach ($op['param'] as $pa) {
312             $tmp2 = $umlparameter;
313             fwrite ($m, preg_replace(array('/##STEREOTYPE##/','/##PACKAGE##/','/##ID##/','/##TYPE##/','/##ABSTRACT##/','/##DOCUMENTATION##/',
314                                            '/##NAME##/','/##STATIC##/','/##SCOPE##/','/##VALUE##/'),
315                                      array($pa['stereotype'],$pa['package'],$pa['id'],$pa['type'],$pa['abstract'],trim($pa['documentation']),
316                                            $pa['name'],$pa['static'],$pa['scope'],$pa['value']),
317                                      $tmp2)."\n");
318         } 
319         fwrite($m,$umloperation_c."\n");
320         //Pepare function lisitem line
321         $tmp2 = $umllistitem;
322         $LISTITEM.= preg_replace (array('/##TYPE##/','/##LABEL##/','/##ID##/'),array(815, $op['name'], $op['id']),$tmp2)."\n";  
323         $LISTITEM.=$umllistitem_c."\n";
324     }
325     //ATTRIBUTES
326     foreach ($ar['attributes'] as $op) {
327         $tmp2 = $umlattribute;
328         fwrite($m, preg_replace(array('/##STEREOTYPE##/','/##PACKAGE##/','/##ID##/','/##TYPE##/','/##ABSTRACT##/','/##DOCUMENTATION##/',
329                                       '/##NAME##/','/##STATIC##/','/##SCOPE##/','/##VALUE##/'),
330                                 array($op['stereotype'],$op['package'],$op['id'],$op['type'],$op['abstract'],trim($op['documentation']),$op['name'],
331                                       $op['static'],$op['scope'],$op['value']),
332                                 $tmp2)."\n");
333         //Prepare attributes listitem line
334         $tmp2 = $umllistitem;
335         $LISTITEM.= preg_replace (array('/##TYPE##/','/##LABEL##/','/##ID##/'),array(814, $op['name'], $op['id']),$tmp2)."\n";  
336         $LISTITEM.=$umllistitem_c."\n";
337     }
338     fwrite($m,$umlclass_c);
339     $LISTITEM.=$umllistitem_c."\n";
340 }
341 fwrite($m,  $xmi_half. $LISTITEM."\n".$xmi_end);
342 exit;
343 //}}}
344
345 //parsePARENTHESIS {{{
346 //This function receives the content between the parenthesis ( with the ( and the ) )
347 //It's a recursive function
348 //Returns an array of arrays
349 function parseParenthesis($value) {
350     
351     $pos     = 0;
352     $content = true;
353     $char    = true;
354     $value   = substr(substr(trim($value), 1), 0, strrpos(substr(trim($value), 1),')')); //Removes the ( and the )
355
356     while ($char) {
357         //I look for the following separation ( , or = ) {{{
358         //If its an = then I check if the next character is an >
359         if (!strpos($value, ',') && !strpos($value, '=')) {
360             $char = false;
361         }
362         elseif (!strpos($value, ',') && strpos($value, '=')) {
363             if ($value{strpos($value, '=') + 1} == '>') {
364                 $char = '=>';
365             }
366             else {
367                 $char = '=';
368             }
369         }
370         elseif (strpos($value, ',') && !strpos($value, '=')) {
371             $char = ',';
372         }
373         elseif (strpos($value, ',') && strpos($value, '=') && strpos($value, ',') > strpos($value, '=')) {
374             if ($value{strpos($value, '=') + 1} == '>') {
375                 $char = '=>';
376             }
377             else {
378                 $char = '=';
379             }
380         }
381         elseif (strpos($value, ',') && strpos($value, '=') && strpos($value, ',') < strpos($value, '=')) {
382             $char = ',';
383         }
384         //}}}
385
386         //Get's the option name {{{
387         $result[$pos]['name'] = (strpos($value, $char)) ? substr($value, 0, strpos($value, $char)) : $value;
388         $pos_del = strpos($value, $char);
389         //}}}
390
391         //If $char is an = or an =>, gets the option value {{{
392         if ($char == '=') { 
393             $op_value = (strpos($value, $char)) ? trim(substr($value, strpos($value, $char) + 1)) : false; 
394         }
395         elseif ($char == '=>') {
396             $op_value = (strpos($value, $char)) ? trim(substr($value, strpos($value, $char) + 2)) : false; 
397         }
398         //}}}
399
400         //Parse the rest of the line {{{       
401         if ($op_value) {
402             if ($op_value{0} == "'" || $op_value{0} == '"') {
403                 $pos_del = parseString($op_value);
404                 if ($pos_del == 0) {
405                     $result[$pos]['value'] = "''";
406                 }
407                 else {
408                     $op_value = substr($op_value, 1);
409                     $result[$pos]['value'] = substr($op_value, 0, $pos_del);
410                 }
411             }
412             elseif (trim(strtolower(substr($op_value, 0 ,5))) == 'array') { //Recursive part {{{
413                 $op_value = trim(substr($op_value, 5));
414                 $op_value = trim(substr($op_value, 1)); //Removes the (
415
416                 if ($op_value{0} == ')') {
417                     $result[$pos]['value'] = 'array()';
418                 }
419                 else {
420                     //I look for the colsing ) {{{
421                     $subpos = 0;
422                     $continue = true;
423                     $temp = $op_value;
424                     while ($continue) {
425                         if (($temp{$subpos} == '"' || $temp{$subpos} == "'") && $temp{$subpos - 1} != '\\') {
426                             $pos_del = parseString(substr($temp,$subpos)) + $subpos;
427                             $subpos = $pos_del + 2;
428
429                             if (trim($temp{$subpos}) == ')') {
430                                 $continue = false;
431                             }
432                         }
433                         else {
434                             if ($subpos > strlen($temp)) {
435                                 $continue = false;
436                             }
437                             $subpos++;
438                         }
439                     }
440                     //}}}
441                     $par = parseParenthesis('('.substr($op_value, 0 , $subpos).')');
442                     $result[$pos]['value'] = 'array(';
443                     foreach ($par as $p) {
444                         $result[$pos]['value'] .= $p['name'];
445                         if (@$p['value']) {
446                             $result[$pos]['value'] .= '=>\''.$p['value'].'\',';
447                         }
448                         else {
449                             $result[$pos]['value'] .= ',';
450                         }
451                     }
452                     $result[$pos]['value'] = rtrim($result[$pos]['value'],',');
453                     $result[$pos]['value'] .= ')';
454                     $pos_del = $subpos;
455                 }
456             } //}}}
457             else {
458                 //Look for the next parameter. If its the last one
459                 //I assign it as it came.
460                 if ($pos_del = strpos($op_value, ',')) {
461                     $result[$pos]['value'] = ($pos_del) ? trim(substr($op_value, 0, $pos_del)): $op_value;
462                 }
463             }
464             $value = $op_value;
465         }
466         //}}}
467         
468         //Removes from $value the part that's alredy parsed {{{
469         if ($pos_del) {
470             $value = trim(substr(trim($value), $pos_del + 2));
471         }
472         if (!$value) {
473             $char = false;
474         }
475         //}}}
476        $pos++;
477        $op_value='';
478     }
479     return $result;
480 }
481 //}}}
482 //parseString {{{
483 //Funtion that parse the content between "" or ''
484 //Returns the position of the final ' or "
485 function parseString($value) {
486     $result = -1;
487     $ii     = 1;
488     $cont   = true;
489     switch ($value{0}) { //{{{
490         case "'":
491             $char = "'";
492             break;
493         case '"':
494             $char = '"';
495             break;
496     }//}}}
497     $value = substr($value, 1); //Removes the ' or the " from the beginning of the string
498     if ($value{0} == $char) {
499         $result = 0;
500     }
501     else {
502         while ($cont) { //{{{
503             $ii++;
504             if ($value{$ii} == $char) {
505                 if ($value{$ii - 1} != '\\') {
506                     $cont = false;
507                     $result = $ii;
508                 }
509             }
510         }//}}}
511     }
512     return $result;
513 }
514 //}}}
515
516 ?>