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