1<?php
2if(!defined("LCE_FUNCTIONS"))
3{
4  define("LCE_FUNCTIONS", 1);
5  include("base.php");
6  include("lce_config.php");
7
8  // Unknown line class
9  define("LCE_UNKNOWN", 0);
10  // pure whitespace
11  define("LCE_WS", 1);
12  // a unqualified comment
13  define("LCE_COMMENT", 2);
14  // a user/translator comment
15  define("LCE_COMMENT_USER", 3);
16  // a tool-generated comment
17  define("LCE_COMMENT_TOOL", 4);
18  // A line containing a MSGID
19  define("LCE_MSGID", 5);
20  // A line containing a MSGSTR
21  define("LCE_MSGSTR", 6);
22  // A quoted text string
23  define("LCE_TEXT", 7);
24
25  define("STATE_ABORT", 0);
26  define("STATE_OK", 1);
27  define("STATE_LOOP", 2);
28
29  class POEntryAD extends AD
30    {
31      function validate($value)
32     {
33       //	  print '"<pre>' . $value . '"<br></pre>';
34       $result =  AD::validate(trim($value));
35       //return $result;
36       if($result[0])
37	 {
38	   $lines = explode("\n", ereg_replace("\r", "", $result[1]));
39	   //$lines = explode("\n", $result[1]);
40	   /*	      print "<pre>";
41	   print_r($lines);
42	   print "</pre>";*/
43	   $res = array();
44	   for($i = 0; $i < count($lines); $i++)
45	     {
46	       if(trim($lines[$i]) != "")
47		 $res[] = $lines[$i];
48	     }
49	   $result[1] = join("\n", $res);
50	   /*	      print "<pre>";
51	   print_r($result[1]);
52	   print "</pre>";*/
53
54	   $result[0] = $this->checkQuotation($result[1]);
55	 }
56       return $result;
57     }
58
59      function checkQuotation($str)
60     {
61       $rex = "\\\\n|\\\\t|\\\\r|\\\\\"";
62       $str = ereg_replace($rex, "", $str);
63       $str = ereg_replace("\\\\\\\\", "", $str);
64       return !(strstr($str, "\"")
65		|| strstr($str, "\\"));
66     }
67    }
68
69
70  class CommentAD extends AD
71    {
72      var $prefix;
73      function CommentAD(
74		      $name,			// the name of the variable
75		      $not_null = 0,
76		      $type = "",	// as returned by gettype
77		      $prefix = "# ")
78     {
79       $this->prefix = $prefix;
80       AD::AD($name, $not_null, $type);
81     }
82
83      function validate($value)
84     {
85       $res = AD::validate($value);
86       return $res;
87       if($res[0] && $res[1] != "")
88	 {
89	   $mod_lines = array();
90	   $lines = explode("\n", $res[1]);
91
92	   for($i = 0; $i < count($lines); $i++)
93	     {
94	       $line = $lines[$i];
95	       if(substr($line, 0, 1) != "#")
96		   $line = $this->prefix . $line;
97	       $mod_lines[] = $line;
98	     }
99	   $res[1] = join("\n", $mod_lines);
100	 }
101       return $res;
102     }
103    }
104
105  class POEntry extends HtmlValidator
106    {
107      var $msgid;
108      var $msgstr;
109      var $user_comment;
110      var $sys_comment;
111      var $unk_comment;
112
113      var $msgid_lc = 0;
114      var $msgstr_lc = 0;
115      var $user_comment_lc = 0;
116      var $sys_comment_lc = 0;
117      var $unk_comment_lc = 0;
118
119      function POEntry()
120     {
121       $this->atts = array(
122			   new AD("msgid"),
123			   new POEntryAD("msgstr", REQUIRED_ATTRIBUTE),
124			   new CommentAD("user_comment"),
125			   new POEntryAD("sys_comment"),
126			   new POEntryAD("unk_comment"),
127			   new AD("msgid_lc", NOT_REQUIRED_ATTRIBUTE, 0),
128			   new AD("msgstr_lc", NOT_REQUIRED_ATTRIBUTE, 0),
129			   new AD("user_comment_lc", NOT_REQUIRED_ATTRIBUTE, 0),
130			   new AD("sys_comment_lc", NOT_REQUIRED_ATTRIBUTE, 0),
131			   new AD("unk_comment_lc", NOT_REQUIRED_ATTRIBUTE, 0)
132			   );
133     }
134
135      function lineCount($entry)
136     {
137       $lc = count(explode("\n", $entry));
138       return $lc;
139     }
140
141      function serializeToVars($prefix)
142     {
143       $this->user_comment_lc = $this->lineCount($this->user_comment);
144       $this->unk_comment_lc = $this->lineCount($this->sys_comment);
145       $this->sys_comment_lc = $this->lineCount($this->unk_comment);
146       $this->msgid_lc = $this->lineCount($this->msgid);
147       $this->msgstr_lc = $this->lineCount($this->msgstr);
148       return HtmlValidator::serializeToVars($prefix);
149     }
150
151      function write()
152     {
153       $content = "";
154       $content .= $this->user_comment . "\n";
155       $content .= $this->unk_comment . "\n";
156       $content .= $this->sys_comment . "\n";
157       $content .= "msgid \"" . $this->msgid . "\"\n";
158       $content .= 'msgstr "' . join("\"\n\"", explode("\n", $this->msgstr)) . "\"" . "\n\n";
159       return $content;
160     }
161    }
162
163  class POReader extends HTMLValidator
164    {
165      var $msgid;
166      var $msgstr;
167      var $user_comment;
168      var $sys_comment;
169      var $unk_comment;
170      var $state;
171      var $ignore_ws;
172      var $po_entries;
173      var $poe_num;
174      var $filename;
175      var $domain;
176
177      function gettext($msgid)
178     {
179       if(isset($this->po_entries[$msgid]))
180	 {
181	   $po = $this->po_entries[$msgid];
182	   return StripCSlashes(join("", explode("\n", $po->msgstr)));
183	   //return $po->msgstr;
184	 }
185       return $msgid;
186     }
187
188
189      function parseFromVars($prefix)
190     {
191       $res = HtmlValidator::parseFromVars($prefix);
192       if($res[0])
193	 {
194	   $poe_res = true;
195	   $this->po_entries = array();
196	   for($i = 0; $i < $this->poe_num; $i++)
197	     {
198	       $poe = new POEntry;
199	       $res = $poe->parseFromVars($prefix . "_POE$i");
200	       if($res[0])
201		 {
202		   $msgid = $prefix . "_POE" . $i . "_MSGID";
203		   $msgid = $$msgid;
204		   $this->po_entries[$prefix . "_POE" . $i . "_MSGID"] = $res[1];
205		 }
206	       else
207		 $poe_res = false;
208	     }
209	 }
210       if(!$poe_res)
211	 $GLOBALS[$prefix . "_ERR"] = 1;
212       return array($poe_res, $this);
213     }
214
215      function serializeToVars($prefix)
216     {
217       HtmlValidator::serializeToVars($prefix);
218       reset($this->po_entries);
219       $i = 0;
220       while($poe = each($this->po_entries))
221	 {
222	   $poe = $poe[1];
223	   $poe->serializeToVars($prefix . "_POE$i");
224	   $i++;
225	 }
226     }
227
228
229      function POReader($domain, $filename)
230     {
231       $this->domain = $domain;
232       $this->filename = $filename;
233       $this->ignore_ws = true;
234       $this->po_entries = array();
235       $this->atts = array(
236			   new AD("domain", REQUIRED_ATTRIBUTE),
237			   new AD("filename", REQUIRED_ATTRIBUTE),
238			   new AD("poe_num", REQUIRED_ATTRIBUTE, 0)
239			   );
240     }
241
242
243      function read()
244     {
245       if($fh = fopen($this->filename, "r"))
246	 {
247	   $this->lines = array();
248	   while (!feof ($fh))
249	     {
250	       $line = fgets($fh, 4096);
251	       $this->lines[] = $line;
252	     }
253	   fclose($fh);
254	 }
255       $this->createPOEntries();
256       $this->poe_num = count($this->po_entries);
257     }
258
259      function write($save="yes")
260     {
261       reset($this->po_entries);
262       $content = "";
263       while($poe = each($this->po_entries))
264	 {
265	   $poe = $poe[1];
266	   $content .= $poe->write();
267	 }
268
269       if(($fh = fopen($this->filename, "w"))
270	  && $save == "yes")
271	 {
272	   fwrite($fh, $content);
273	   }
274       return $content;
275     }
276
277      function isComment($class)
278     {
279       if($class == LCE_COMMENT || $class == LCE_COMMENT_USER || $class == LCE_COMMENT_TOOL)
280	 return true;
281       return false;
282     }
283
284      function comment($line, $class)
285     {
286       if($this->isComment($class))
287	 {
288	   if($class == LCE_COMMENT_USER)
289	     $this->user_comment .= $line;
290	   else if($class == LCE_COMMENT_TOOL)
291	     $this->sys_comment .= $line;
292	   else
293	     $this->unk_comment .= $line;
294	   return STATE_OK;
295	 }
296       if($class == LCE_MSGID)
297	 {
298	   $this->state = "msgid";
299	   return STATE_LOOP;
300	 }
301       return STATE_ABORT;
302     }
303
304      function msgid($line, $class)
305     {
306       if($class == LCE_MSGID || $class == LCE_TEXT)
307	 {
308	   $line = $this->stripLine($line, LCE_MSGID);
309	   $this->msgid .= $line;
310	   return STATE_OK;
311	 }
312       if($class == LCE_MSGSTR)
313	 {
314	   $this->state = "msgstr";
315	   return STATE_LOOP;
316	 }
317       return STATE_ABORT;
318     }
319
320      function msgstr($line, $class)
321     {
322       if($class == LCE_MSGSTR || $class == LCE_TEXT)
323	 {
324	   $line = $this->stripLine($line, $class);
325	   $this->msgstr .= $line;
326	   return STATE_OK;
327	 }
328       // We have a different state, so we have to create a POEntry
329       $poe = new POEntry;
330       $poe->user_comment = trim($this->user_comment);
331       $poe->sys_comment = trim($this->sys_comment);
332       $poe->unk_comment = trim($this->unk_comment);
333       $poe->msgid = trim($this->msgid);
334       $poe->msgstr = trim($this->msgstr);
335       $this->po_entries[trim($this->msgid)] = $poe;
336       $this->state = "start";
337       return STATE_LOOP;
338     }
339
340      function start($line, $class)
341     {
342       $this->user_comment = "";
343       $this->sys_comment = "";
344       $this->unk_comment = "";
345       $this->msgid = "";
346       $this->msgstr = "";
347       if($this->isComment($class))
348	 {
349	   $this->state = "comment";
350	   return STATE_LOOP;
351	 }
352       if($class == LCE_MSGID)
353	 {
354	   $this->state = "msgid";
355	   return STATE_LOOP;
356	 }
357       return STATE_OK;
358     }
359
360      function createPOEntries()
361     {
362       $this->msgid = "";
363       $this->msgstr = "";
364       $this->user_comment = "";
365       $this->sys_comment = "";
366       $this->state = "start";
367
368       reset($this->lines);
369       for($i = 0; $i < count($this->lines); $i++)
370	 {
371	   $line = $this->lines[$i];
372	   $class = $this->classifyLine($line);
373	   if($class != LCE_WS || !$this->ignore_ws)
374	     {
375	       $state_ret = STATE_LOOP;
376	       while($state_ret == STATE_LOOP)
377		 {
378		   $state = $this->state;
379		   //print "$this->state $class:$line <br>";
380		   $state_ret = $this->$state($line, $class);
381		 }
382	       //print "state_ret = $state_ret <br>";
383	     }
384	   if($state_ret == STATE_ABORT)
385	     break;
386	 }
387       // Get the last entry
388       if($state_ret != STATE_ABORT)
389	 {
390	   $this->msgstr("", LCE_UNKNOWN);
391	 }
392     }
393
394      function stripLine($line, $class)
395     {
396       switch($class)
397	 {
398	 case LCE_TEXT:
399	   ereg('^"(.*)"', $line, $regs);
400	   $line = $regs[1] . "\n";
401	   break;
402	 case LCE_MSGID:
403	   if(substr($line, strlen("msgid")) == "msgid")
404	     {
405	       $line = substr($line, strlen("msgid") + 1);
406	     }
407	   ereg('"(.*)"', $line, $regs);
408	   $line = $regs[1];
409	   break;
410	 case LCE_MSGSTR:
411	   // TODO: Check if ^ can be removed
412	   $line = substr($line, strlen("msgstr") + 1);
413	   ereg('^"(.*)"', $line, $regs);
414	   $line = $regs[1] . "\n";
415	   break;
416
417	 }
418       return $line;
419     }
420
421      function printClassification()
422     {
423       reset($this->lines);
424       for($i = 0; $i < count($this->lines); $i++)
425	 {
426	   $line = $this->lines[$i];
427	   $class = $this->classifyLine($line);
428	   print "#$i: $class $line<br>";
429	 }
430     }
431
432      function classifyLine($line)
433     {
434       if(ereg("^[ \n\r\t]*$", $line))
435	 return LCE_WS;
436       if(ereg("^#.*\$", $line))
437	 {
438	   if(ereg("^[,:-~].*", substr($line, 1)))
439	     {
440	       return LCE_COMMENT_TOOL;
441	     }
442	   if(ereg("^[ \n\r\t].*", substr($line, 1)))
443	     {
444	       return LCE_COMMENT_USER;
445	     }
446	   return LCE_COMMENT;
447	 }
448       if(ereg("^msgid (.*)\$", $line, $regs))
449	 {
450	   $line = $regs[1];
451	   if($this->classifyLine($line) == LCE_TEXT)
452	     return LCE_MSGID;
453	 }
454       if(ereg("^msgstr (.*)\$", $line, $regs))
455	 {
456	   $line = $regs[1];
457	   if($this->classifyLine($line) == LCE_TEXT)
458	     return LCE_MSGSTR;
459	 }
460       if(ereg('^".*"', $line))
461	 {
462			     // TODO: Check correct escapes
463	   return LCE_TEXT;
464	 }
465
466       return LCE_UNKNOWN;
467     }
468    }
469
470
471  function getTextDomains($lines)
472    {
473      $default_domain = "";
474      $domains = array();
475      while($gl = each($GLOBALS))
476     {
477       $gname = $gl[0];
478       global $$gname;
479     }
480      for($i = 0; $i < count($lines); $i++)
481     {
482       if(ereg("bindtextdomain\(([^,]+),([^\)]+)\)", $lines[$i], $regs))
483	 {
484			     //print "Line:" .  $lines[$i] . " <br>";
485	   $name = $regs[1];
486	   $ev = "\$directory = ". $regs[2] . ";";
487	   print $ev;
488	   eval($ev);
489	   $domains[] = array($name, $directory);
490	 }
491       if(ereg("textdomain\(([^\)]+)\)", $lines[$i], $regs))
492	 $default_domain = $regs[1];
493     }
494      return array($default_domain, $domains);
495    }
496
497
498  class PORManager extends HtmlValidator
499    {
500      var	$por_a;
501
502      function PORManager()
503     {
504       $this->por_a = array();
505     }
506
507      function addPOReader($d_name, &$por)
508     {
509       $this->por_a[$d_name] = &$por;
510     }
511
512      function &getPOReader($domain)
513     {
514       return $this->por_a[$domain];
515     }
516
517      function getDomainNames()
518     {
519       return array_keys($this->por_a);
520     }
521    }
522
523  function &loadPORManager()
524    {
525      global $LCE_PORMAN;
526      if(!isset($LCE_PORMAN))
527     {
528       $LCE_PORMAN = new PORManager();
529     }
530      return $LCE_PORMAN;
531    }
532
533
534  // More or less intelligent filename joining
535  // As available in PYTHONs os.path
536  function fileJoin()
537    {
538      $numargs = func_num_args();
539      $args = func_get_args();
540      for($i = 0; $i < $numargs - 1; $i++)
541     {
542       if(substr($args[$i], -1) != "/")
543	 $args[$i] = $args[$i] . "/";
544       if($i > 0)
545	 {
546	   if(substr($args[$i],0 , 1) == "/")
547	     $args[$i] = substr($args[$i], 1);
548	 }
549
550     }
551      return join("", $args);
552    }
553
554  if(defined("LCE_TESTSERVER"))
555    {
556
557      function lce_bindtextdomain($d_name, $d_path)
558     {
559       global $LANG, $LC_MESSAGES, $LC_ALL, $LCE_LANG;
560       global $LCE_ERR;
561       global $LCE_PO_SUFFIX;
562       global $LCE_MANAGER;
563
564       $path_orig = $d_path;
565       // This is not complete and reflects
566       // my not very far going understanding of the
567       // different $LC_x thingies.
568       if(isset($LC_MESSAGES))
569	 {
570			     //print "LC_MESSAGES<br>";
571	   $lang_suffix = $LC_MESSAGES;
572	 }
573       else if(isset($LC_ALL))
574	 {
575			     //print "LC_ALL<br>";
576	   $lang_suffix = $LC_ALL;
577	 }
578       else if(isset($LANG))
579	 {
580			     //print "LANG<br>";
581	   $lang_suffix = $LANG;
582	 }
583       else
584	 {
585			     //print "LCE_LANG<br>";
586	   $lang_suffix = $LCE_LANG;
587	 }
588
589       //print "LangSuffix: $lang_suffix \n";
590       //print "D_Path: " . fileJoin($d_path, $lang_suffix, "LC_MESSAGES", $d_name . $LCE_PO_SUFFIX) . "<br>";
591       // First try: the whole lang_suffix
592
593       if(file_exists(fileJoin($d_path, $lang_suffix, "LC_MESSAGES", $d_name . $LCE_PO_SUFFIX)))
594	 $d_path = fileJoin($d_path, $lang_suffix, "LC_MESSAGES", $d_name . $LCE_PO_SUFFIX);
595       else
596	 {
597	   $lang_suffix = substr($lang_suffix, 0, 2);
598	   if(file_exists(fileJoin($d_path, $lang_suffix, "LC_MESSAGES", $d_name. $LCE_PO_SUFFIX)))
599	     $d_path = fileJoin(fileJoin($d_path, $lang_suffix, "LC_MESSAGES", $d_name . $LCE_PO_SUFFIX));
600	   else
601	     {
602	       $LCE_ERR = "No PO-file found";
603	       return false;
604	     }
605	 }
606       //print "D_Path: $d_path \n";
607       $por = new POReader($d_name, $d_path, $path_orig);
608       $por->read();
609       $porman =& loadPORManager();
610       $porman->addPOReader($d_name, $por);
611       return true;
612     }
613
614      function lce_textdomain($domain)
615     {
616       global $LCE_DOMAIN;
617       $LCE_DOMAIN = $domain;
618     }
619
620      function lce_gettext($msgid)
621     {
622       global $LCE_DOMAIN;
623       return lce_dgettext($LCE_DOMAIN, $msgid);
624     }
625
626      function lce_dgettext($domain, $msgid)
627     {
628       $porman =& loadPORManager();
629       if($por = &$porman->getPOReader($domain))
630	 return $por->gettext($msgid);
631       return $msgid;
632     }
633
634      function lce()
635     {
636       global $LCE_LCEDITLOC;
637       $porman =& loadPORManager();
638       $domains = $porman->getDomainNames();
639       for($i = 0; $i < count($domains); $i++)
640	 {
641	   $por =& $porman->getPOReader($domains[$i]);
642	   $domain = "domain=" . urlencode($por->domain);
643	   $filename = "filename=" . urlencode($por->filename);
644	   $url = $LCE_LCEDITLOC . "?" . $domain . "&" . $filename;
645	   print "<a target=\"_blank\" href=\"" . $url . "\">Domain: $por->domain</a><br>";
646	 }
647     }
648    }
649  else
650    {
651      function lce_bindtextdomain($domain, $path)
652     {
653       bindtextdomain($domain, $path);
654     }
655
656      function lce_textdomain($domain)
657     {
658       textdomain($domain);
659     }
660
661      function lce_gettext($msgid)
662     {
663       return gettext($msgid);
664     }
665
666      function lce_dgettext($domain, $msgid)
667     {
668       return dgettext($domain, $msgid);
669     }
670      function lce()
671     {
672     }
673    }
674
675
676  function lce_geteditcode($type, $name, $text, $rows=2)
677    {
678      global $LCE_EDIT_LEVEL;
679      $level_map = array("msgid" => 4,
680		      "sys_comment" => 3,
681		      "user_comment" => 2,
682		      "msgstr" => 1
683		      );
684      if($level_map[$type] > $LCE_EDIT_LEVEL)
685     {
686       return "<input type=\"hidden\" name=\"" . $name . "\" value=\"" . $text . "\"><pre>\n" . $text . "\n</pre>";
687     }
688      else
689     {
690       return "<textarea name=\"" . $name . "\" rows=\"" . $rows . "\" cols=\"60\">" . $text . "</textarea>";
691     }
692    }
693}
694/*
695  ;;; Local Variables: ***
696  ;;; mode:C ***
697  ;;; End: ***
698*/
699?>
700