source: trunk/test/commandline.cc @ 2382

Last change on this file since 2382 was 2382, checked in by Peter, 11 years ago

merged patch release 0.6.3 into trunk.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 18.6 KB
Line 
1// $Id: commandline.cc 2382 2010-12-21 13:16:58Z peter $
2
3/*
4  Copyright (C) 2007, 2008, 2009, 2010 Jari Häkkinen, Peter Johansson
5
6  This file is part of the yat library, http://dev.thep.lu.se/yat
7
8  The yat library is free software; you can redistribute it and/or
9  modify it under the terms of the GNU General Public License as
10  published by the Free Software Foundation; either version 3 of the
11  License, or (at your option) any later version.
12
13  The yat library is distributed in the hope that it will be useful,
14  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  General Public License for more details.
17
18  You should have received a copy of the GNU General Public License
19  along with yat. If not, see <http://www.gnu.org/licenses/>.
20*/
21
22#include "Suite.h"
23
24#include "yat/utility/CommandLine.h"
25#include "yat/utility/FileUtil.h"
26#include "yat/utility/OptionArg.h"
27#include "yat/utility/OptionFile.h"
28#include "yat/utility/OptionHelp.h"
29#include "yat/utility/OptionSwitch.h"
30
31#include <cassert>
32#include <cstdlib>
33#include <fstream>
34#include <stdexcept>
35#include <string>
36
37#include <string.h>
38
39using namespace theplu;
40using namespace yat::utility;
41bool cmd_help(yat::test::Suite& error);
42char** stradup(const char* str[], int size);
43void strafree(char** str, int size);
44bool test_switch(yat::test::Suite& error);
45bool test_arg(yat::test::Suite& error);
46void test_exception_msg(yat::test::Suite& error);
47bool test_file(yat::test::Suite& error);
48bool test_file2(yat::test::Suite& error);
49bool test_file3(yat::test::Suite& error);
50bool test_file3_(yat::test::Suite& error, const std::string&);
51bool test_file4(yat::test::Suite& error);
52bool test_file4_(yat::test::Suite& error, const std::string&);
53bool test_failures(yat::test::Suite& error);
54bool test_option_name_clash(yat::test::Suite& suite);
55bool test_free_arg(yat::test::Suite& suite);
56
57int main(int argc, char* argv[])
58{ 
59  yat::test::Suite suite(argc, argv);
60
61  suite.err() << "testing commandline" << std::endl;
62
63  // Peter, creation of this directory should perhaps be in a more central place
64  FileUtil dir("testSubDir");
65  if (!dir.exists())
66    if (mkdir(dir.path().c_str(), 0755)) {
67      suite.err() << "mkdir " << dir.path() << " failed\n";
68      return 1;
69    }
70  FileUtil subdir("testSubDir/commandline_test.dir");
71  if (!subdir.exists()) {
72    if (mkdir(subdir.path().c_str(), 0755)) {
73      suite.err() << "mkdir " << subdir.path() << " failed\n";
74      return 1;
75    }
76  }
77  if (subdir.permissions("w")) {
78    chmod(subdir.path().c_str(), S_IREAD | S_IWRITE | S_IEXEC);
79  }
80
81  try {
82    suite.add(cmd_help(suite));
83    suite.add(test_switch(suite));
84    suite.add(test_arg(suite));
85    suite.add(test_file(suite));
86    suite.add(test_file2(suite));
87    suite.add(test_file3(suite));
88    suite.add(test_file4(suite));
89    suite.add(test_failures(suite));
90    suite.add(test_option_name_clash(suite));
91    suite.add(test_free_arg(suite));
92    test_exception_msg(suite);
93  }
94  catch (std::runtime_error& e) {
95    suite.err() << "Error: unexpected exception thrown\n" << e.what() 
96                << std::endl;
97    suite.add(false);
98  }
99
100  CommandLine cmd;
101  OptionFile file(cmd, "f,file", "description");
102
103  return suite.return_value();
104}
105
106
107bool cmd_help(yat::test::Suite& suite)
108{
109  using namespace theplu::yat::utility;
110  CommandLine cmd;
111  OptionArg<std::string> dir(cmd, "d,dir", "output directory");
112  OptionHelp help(cmd);
113  OptionArg<std::string> target(cmd, "T,target", "treat DEST as a normal file", 
114                                true);
115  target.print_arg("=TARGET");
116  OptionSwitch verbose(cmd, "v,verbose", "explain what is being done");
117  OptionSwitch version(cmd, "version", "output version and exit");
118
119  suite.err() << cmd;
120  return true;
121}
122
123
124char** stradup(const char** str, int size)
125{
126  char** res = new char*[size];
127  for (int i = 0; i<size; ++i) {
128    res[i] = strdup(str[i]);
129    assert(res[i]);
130  }
131  return res;
132}
133
134
135void strafree(char** str, int size)
136{
137  for (int i = 0; i<size; ++i) {
138    free(str[i]);
139  }
140}
141
142
143void test_exception_msg(yat::test::Suite& suite)
144{
145  // test for ticket 508
146  using namespace theplu::yat::utility;
147  CommandLine cmd;
148  OptionHelp help(cmd, "h,help", "");
149  OptionFile indata(cmd, "i,in", "input file", true, true,"r");
150  OptionSwitch cmd_memory(cmd, "m", "transpose in a memory cheap manner");
151  OptionArg<size_t> cmd_n(cmd, "n", "number of rows to print in each iteration");
152  OptionSwitch cmd_numeric(cmd, "numeric", 
153                                         "input is a numeric matrix");
154  OptionFile outdata(cmd, "o,out", "output file",true, false,"w");
155  OptionSwitch verbose(cmd, "v,verbose", "display progress");
156  int ac = 2;
157  const char* cav[] = { "test_prog", "--haha" };
158  char** av = stradup(cav, ac);
159  try {
160    cmd.parse(ac,av);
161  }
162  catch (std::runtime_error& e) {
163    std::string msg(e.what());
164    if (msg.size()<15) {
165      suite.xadd(false);
166      suite.err() << "Error: short exception message\n" 
167                  << "  exception message is: `" << msg
168                  << "' that is " << msg.size() << " characters long.\n" 
169                  << "  expected at least 15 characters\n";
170    }
171  }
172  strafree(av, ac);
173}
174
175
176bool test_switch(yat::test::Suite& suite)
177{
178  bool ok=true;
179  CommandLine cmd;
180  OptionSwitch target(cmd, "T,target", "treat DEST as a normal file", true);
181  OptionSwitch verbose(cmd, "v,verbose", "explain what is being done", true);
182
183  suite.err() << "Testing OptionSwitch -T...";
184  {
185    int ac = 2;
186    const char* cav[] = { "test_prog", "-T" };
187    char** av = stradup(cav, ac);
188    cmd.parse(ac,av);
189    if (target.present() && !verbose.present())
190      suite.err() << "ok\n";
191    else {
192      suite.err() << "failed\n";
193      ok =false;
194    }
195    strafree(av, ac);
196  }
197
198
199  suite.err() << "Testing OptionSwitch --target...";
200  {
201    int ac = 2;
202    const char* cav[] = { "test_prog", "--target" };
203    char** av = stradup(cav, ac);
204    cmd.parse(ac,av);
205    if (target.present() && target.value() && !verbose.present())
206      suite.err() << "ok\n";
207    else {
208      suite.err() << "failed\n";
209      ok =false;
210    }
211    strafree(av, ac);
212  }
213
214  suite.err() << "Testing OptionSwitch -T -v...";
215  {
216    int ac = 3;
217    const char* cav[] = { "test_prog", "-T" , "-v"};
218    char** av = stradup(cav, ac);
219    cmd.parse(ac,av);
220    if (target.present() && verbose.present())
221      suite.err() << "ok\n";
222    else {
223      suite.err() << "failed\n";
224      ok =false;
225    }
226    strafree(av, ac);
227  }
228
229  suite.err() << "Testing OptionSwitch -vT...";
230  {
231    int ac = 2;
232    const char* cav[] = { "test_prog", "-vT"};
233    char** av = stradup(cav, ac);
234    cmd.parse(ac,av);
235    if (target.present() && verbose.present())
236      suite.err() << "ok\n";
237    else {
238      suite.err() << "failed\n";
239      ok =false;
240    }
241    strafree(av, ac);
242  }
243
244  suite.err() << "Testing OptionSwitch --no-target...";
245  {
246    int ac = 2;
247    const char* cav[] = { "test_prog", "--no-target" };
248    char** av = stradup(cav, ac);
249    cmd.parse(ac,av);
250    if (target.present() && !target.value())
251      suite.err() << "ok\n";
252    else {
253      suite.err() << "failed\n";
254      if (!target.present())
255        suite.err() << "target.present() returned false\n";
256      if (target.value())
257        suite.err() << "target.value() returned true\n";
258      ok =false;
259    }
260    strafree(av, ac);
261  }
262  return ok;
263}
264
265
266bool test_arg(yat::test::Suite& suite)
267{
268  bool ok=true;
269  CommandLine cmd;
270  OptionArg<std::string> input(cmd, "i,input", "input file");
271  OptionArg<unsigned int> n(cmd, "n", "number of lines");
272  OptionArg<double> x(cmd, "x,extra", "a float number");
273
274  suite.err() << "Testing OptionArg existence -i file...";
275  {
276    int ac = 3;
277    const char* cav[] = { "test_prog", "-i", "file" };
278    char** av = stradup(cav, ac);
279    cmd.parse(ac,av);
280    if (input.present())
281      suite.err() << "ok\n";
282    else {
283      suite.err() << "failed\n";
284      ok =false;
285    }
286    strafree(av, ac);
287  }
288
289  suite.err() << "Testing OptionArg value -i file...";
290  {
291    int ac = 3;
292    const char* cav[] = { "test_prog", "-i", "file" };
293    char** av = stradup(cav, ac);
294    cmd.parse(ac,av);
295    if (input.value()=="file")
296      suite.err() << "ok\n";
297    else {
298      suite.err() << "failed\n";
299      ok =false;
300    }
301    strafree(av, ac);
302  }
303
304  suite.err() << "Testing OptionArg existence --input file...";
305  {
306    int ac = 3;
307    const char* cav[] = { "test_prog", "--input", "file" };
308    char** av = stradup(cav, ac);
309    cmd.parse(ac,av);
310    if (input.present())
311      suite.err() << "ok\n";
312    else {
313      suite.err() << "failed\n";
314      ok =false;
315    }
316    strafree(av, ac);
317  }
318
319  suite.err() << "Testing OptionArg value --input=file...";
320  {
321    int ac = 2;
322    const char* cav[] = { "test_prog", "--input=file" };
323    char** av = stradup(cav, ac);
324    cmd.parse(ac,av);
325    if (input.value()=="file")
326      suite.err() << "ok\n";
327    else {
328      suite.err() << "failed\n";
329      ok =false;
330    }
331    strafree(av, ac);
332  }
333
334  suite.err() << "Testing OptionArg value --input=\"file called something\"...";
335  {
336    int ac = 2;
337    const char* cav[] = { "test_prog", "--input=\"file called something\"" };
338    char** av = stradup(cav, ac);
339    cmd.parse(ac,av);
340    if (input.value()=="file called something")
341      suite.err() << "ok\n";
342    else {
343      suite.err() << "failed\n";
344      suite.err() << "value is `" << input.value() << "'\n"
345            << "expected `file called something'\n";
346      ok =false;
347    }
348    strafree(av, ac);
349  }
350
351  suite.err() << "Testing OptionArg unsigned int value -n 3...";
352  {
353    int ac = 3;
354    const char* cav[] = { "test_prog", "-n", "3" };
355    char** av = stradup(cav, ac);
356    cmd.parse(ac,av);
357    if (n.value()==3)
358      suite.err() << "ok\n";
359    else {
360      suite.err() << "failed\n";
361      ok =false;
362    }
363    strafree(av, ac);
364  }
365
366
367  suite.err() << "Testing OptionArg 2 value --input file -n 3...";
368  {
369    int ac = 5;
370    const char* cav[] = { "test_prog", "--input", "file", "-n", "3" };
371    char** av = stradup(cav, ac);
372    cmd.parse(ac,av);
373    if (input.value()=="file")
374      suite.err() << "ok\n";
375    else {
376      suite.err() << "failed\n";
377      ok =false;
378    }
379    strafree(av, ac);
380  }
381
382  suite.err() << "Testing OptionArg double value -x -6...";
383  try {
384    int ac = 3;
385    const char* cav[] = { "test_prog", "-x", "-6" };
386    char** av = stradup(cav, ac);
387    cmd.parse(ac,av);
388    if (x.value()==-6)
389      suite.err() << "ok\n";
390    else {
391      suite.err() << "failed\n";
392      ok =false;
393    }
394    strafree(av, ac);
395  }
396  catch (std::runtime_error& e) {
397    ok = false;
398    suite.err() << "failed\nexception thrown with what(): " << e.what() << "\n";
399  }
400
401  suite.err() << "Testing OptionArg double value --extra -6...";
402  try {
403    int ac = 3;
404    const char* cav[] = { "test_prog", "--extra", "-6" };
405    char** av = stradup(cav, ac);
406    cmd.parse(ac,av);
407    if (x.value()==-6)
408      suite.err() << "ok\n";
409    else {
410      suite.err() << "failed\n";
411      ok =false;
412    }
413    strafree(av, ac);
414  }
415  catch (std::runtime_error& e) {
416    ok=false;
417    suite.err() << "failed\nexception thrown with what(): " << e.what() << "\n";
418  }
419
420  suite.err() << "Testing OptionArg::value(T) ... ";
421  try {
422    int ac = 1;
423    const char* cav[] = { "test_prog"};
424    char** av = stradup(cav, ac);
425    cmd.parse(ac,av);
426    n.value(5);
427    if (n.present()==false && n.value()==5)
428      suite.err() << "ok\n";
429    else {
430      suite.err() << "failed\n";
431      ok = false;
432    }
433    strafree(av, ac);
434  }
435  catch (std::runtime_error& e) {
436    suite.err() << "failed\n";
437    ok = false;
438  }
439
440  // test for ticket 645
441  suite.err() << "Testing error message with -n STRING\n";
442  try {
443    int ac = 3;
444    const char* cav[] = { "test_prog", "-n", "STRING"};
445    char** av = stradup(cav, ac);
446    try {
447      cmd.parse(ac,av);
448      strafree(av, ac);
449      ok=false;
450      suite.err() << "error: expected parse to throw\n"; 
451    }
452    catch (std::runtime_error& e) {
453      strafree(av, ac);
454      suite.err() << "expected exception: what(): " << e.what() << "\n";
455    }
456  }
457  catch (std::runtime_error& e) {
458    suite.err() << "failed: " << e.what() << "\n";
459    ok=false;
460  }
461  return ok;
462}
463
464bool test_failures(yat::test::Suite& suite)
465{
466  bool ok=true;
467  CommandLine cmd;
468  OptionSwitch verbose(cmd, "v,verbose", "explain what is being done");
469  OptionArg<std::string> input(cmd, "i,input", "input file");
470  OptionArg<unsigned int> n(cmd, "n", "number of lines");
471
472  suite.err() << "Testing unknown option --peter...";
473  {
474    int ac = 2;
475    const char* cav[] = { "test_prog", "--peter"};
476    char** av = stradup(cav, ac);
477    try{
478      cmd.parse(ac,av);
479      suite.err() << "failed\n";
480      ok =false;
481    }
482    catch (...) {
483      suite.err() << "ok\n";
484    }
485    strafree(av, ac);
486  }
487
488  suite.err() << "Testing unknown option -jvjhsgad...";
489  {
490    int ac = 2;
491    const char* cav[] = { "test_prog", "-vjhsgad"};
492    char** av = stradup(cav, ac);
493    try{
494      cmd.parse(ac,av);
495      suite.err() << "failed\n";
496      ok =false;
497    }
498    catch (...) {
499      suite.err() << "ok\n";
500    }
501    strafree(av, ac);
502  }
503
504
505  suite.err() << "Testing invalid option -nv 3...";
506  {
507    int ac = 3;
508    const char* cav[] = { "test_prog", "-nv", "3"};
509    char** av = stradup(cav, ac);
510    try{
511      cmd.parse(ac,av);
512      suite.err() << "failed\n";
513      ok =false;
514    }
515    catch (...) {
516      suite.err() << "ok\n";
517    }
518    strafree(av, ac);
519  }
520
521
522  suite.err() << "Testing 23.12 is not unsigned int...";
523  {
524    int ac = 3;
525    const char* cav[] = { "test_prog", "-n", "23.12"};
526    char** av = stradup(cav, ac);
527    try{
528      cmd.parse(ac,av);
529      suite.err() << "failed\n";
530      ok =false;
531    }
532    catch (std::runtime_error& e) {
533      suite.err() << "ok\n";
534    }
535    strafree(av, ac);
536  }
537
538  suite.err() << "Testing -1 is not unsigned int...";
539  {
540    int ac = 3;
541    const char* cav[] = { "test_prog", "-n" "-1"};
542    char** av = stradup(cav, ac);
543    try{
544      cmd.parse(ac,av);
545      suite.err() << "failed\n";
546      ok =false;
547    }
548    catch (...) {
549      suite.err() << "ok\n";
550    }
551    strafree(av, ac);
552  }
553
554
555  suite.err() << "Testing invalid: test_prog peter...";
556  {
557    int ac = 2;
558    const char* cav[] = { "test_prog", "peter" };
559    char** av = stradup(cav, ac);
560    try{
561      cmd.parse(ac,av);
562      suite.err() << "failed\n";
563      ok =false;
564    }
565    catch (...) {
566      suite.err() << "ok\n";
567    }
568    strafree(av, ac);
569  }
570
571  suite.err() << "Testing OptionArg required ...";
572  {
573    OptionArg<std::string> required(cmd, "required", "required", true); 
574    int ac = 1;
575    const char* cav[] = { "test_prog" };
576    char** av = stradup(cav, ac);
577    try{
578      cmd.parse(ac,av);
579      suite.err() << "failed\n";
580      ok =false;
581    }
582    catch (...) {
583      suite.err() << "ok\n";
584    }
585    strafree(av, ac);
586  }
587
588
589  return ok;
590}
591
592
593bool test_file(yat::test::Suite& suite)
594{
595  bool ok=true;
596  CommandLine cmd;
597  OptionFile inclones(cmd, "clones", "file containing clones");
598  OptionFile indata(cmd, "data", "data to merge");
599  OptionFile out(cmd, "o,out", "data to merge", true, false, "w");
600  OptionSwitch help(cmd, "h,help", "display this help and exit");
601
602  suite.err() << "Testing OptionFile... ";
603  {
604    int ac = 7;
605    const char* cav[] = { "test_prog", "--clones", "commandline_test.cc", 
606                          "--data", "commandline_test.cc", "-o", 
607                          "commandline_test.cc"};
608    char** av = stradup(cav, ac);
609    cmd.parse(ac,av);
610    suite.err() << "ok\n";
611    strafree(av, ac);
612  }
613  return ok;
614}
615
616
617bool test_file2(yat::test::Suite& suite)
618{
619  suite.err() << "Testing OptionFile in write-protected directory... ";
620
621  if (yat::test::run_as_root()) {
622    suite.err() << "skipped because user is root\n";
623    // skipping means test should still be successful
624    return true;
625  }
626
627  FileUtil subdir("testSubDir/commandline_test.dir/write-protected");
628  if (!subdir.exists()) {
629    if (mkdir(subdir.path().c_str(), 0555)) {
630      suite.err() << "mkdir " << subdir.path() << " failed\n";
631      return false;
632    }
633  }
634  else
635    if (!subdir.permissions("w")) {
636      suite.err() << subdir.path() << " already exists\n";
637      return false;
638    }
639
640  FileUtil file("testSubDir/commandline_test.dir/write-protected/out.txt");
641  if (file.exists()) {
642    suite.err() << "FileUtil::exists returns true unexpectedly\n";
643    return false;
644  }
645  if (file.permissions("w")==0) {
646    suite.err() << "FileUtil::permission(\"w\") returns 0 while "
647                << "-1 was expected\n";
648    return false;
649  }
650
651  bool ok=true;
652  CommandLine cmd;
653  OptionFile out(cmd, "o,out", "output to file", false, false, "w");
654  OptionSwitch help(cmd, "h,help", "display this help and exit");
655
656  {
657    int ac = 3;
658    const char* cav[] = { "test_prog", "--out", 
659                    "testSubDir/commandline_test.dir/write-protected/out.txt"};
660    char** av = stradup(cav, ac);
661    try {
662      cmd.parse(ac,av);
663      ok =false;
664      suite.err() << "failed\n";
665    }
666    catch (cmd_error& e) {
667      suite.err() << "ok\n";
668      suite.err() << "catch expected error: " << e.what() << "\n";
669    }
670    strafree(av, ac);
671  }
672  return ok;
673}
674
675
676bool test_file3(yat::test::Suite& suite)
677{
678  suite.err() << "Testing OptionFile in non-existing tree\n";
679  // testing error message from OptionFile when tree is not existing
680  bool ok=true;
681  ok &= test_file3_(suite, "r");
682  ok &= test_file3_(suite, "w");
683  ok &= test_file3_(suite, "x");
684  ok &= test_file3_(suite, "d");
685  return ok;
686}
687
688
689bool test_file3_(yat::test::Suite& suite, const std::string& perm)
690{
691  CommandLine cmd;
692  OptionFile file(cmd, "file", "", true, false, perm);
693
694  suite.err() << "Testing OptionFile '" << perm << "' ... "; 
695  int ac = 3;
696  const char* cav[] = { "test_prog", "--file", "sjhgaw/jmegb/tmp.tmpx"};
697  char** av = stradup(cav, ac);
698  try {
699    cmd.parse(ac,av);
700    suite.err() << "no\n";
701    strafree(av, ac);
702    return false;
703  }
704  catch (cmd_error& e) {
705    suite.err() << "ok\n";
706    suite.err() << "catch expected error: " << e.what() << "\n";
707  }
708  strafree(av, ac);
709  return true;
710}
711
712
713bool test_file4(yat::test::Suite& suite)
714{
715  FileUtil file("testSubDir/commandline_test.dir/test_file4.txt");
716  if (!file.exists()) {
717    std::ofstream os(file.path().c_str());
718  } 
719  chmod(file.path().c_str(), 0);
720
721  suite.err() << "Testing OptionFile with no permssions\n";
722  bool ok=true;
723  ok &= test_file4_(suite, "r");
724  ok &= test_file4_(suite, "w");
725  ok &= test_file4_(suite, "x");
726  ok &= test_file4_(suite, "d");
727  return ok;
728}
729
730
731bool test_file4_(yat::test::Suite& suite, const std::string& perm)
732{
733  CommandLine cmd;
734  OptionFile file(cmd, "file", "", true, false, perm);
735
736  suite.err() << "Testing OptionFile '" << perm << "' ... "; 
737  try {
738    int ac = 3;
739    const char* cav[] = { "test_prog", "--file", 
740                          "testSubDir/commandline_test.dir/test_file4.txt"};
741    char** av = stradup(cav, ac);
742    cmd.parse(ac,av);
743    suite.err() << "no\n";
744    strafree(av, ac);
745    return false;
746  }
747  catch (cmd_error& e) {
748    suite.err() << "ok\n";
749    suite.err() << "catch expected error: " << e.what() << "\n";
750  }
751  return true;
752}
753
754
755bool test_option_name_clash(yat::test::Suite& suite)
756{
757  bool ok=true;
758  suite.err() << "Testing long option name clash ...";
759  try {
760    CommandLine cmd;
761    OptionSwitch op1(cmd, "opt", "bla bla");
762    OptionSwitch op2(cmd, "o,opt", "other bla");
763    ok=false;
764    suite.err() << "failed\n";
765  }
766  catch (std::runtime_error& e) {
767    suite.err() << "ok\n";
768  }
769  suite.err() << "Testing short option name clash ...";
770  try {
771    CommandLine cmd;
772    OptionSwitch op1(cmd, "o", "bla bla");
773    OptionSwitch op2(cmd, "o,opt", "other bla");
774    ok=false;
775    suite.err() << "failed\n";
776  }
777  catch (std::runtime_error& e) {
778    suite.err() << "ok\n";
779  }
780
781  return ok;
782}
783
784
785bool test_free_arg(yat::test::Suite& suite)
786{
787  bool ok=true;
788  suite.err() << "Testing free arguments ...";
789
790  CommandLine cmd;
791  cmd.allow_free_args(2);
792  OptionHelp help(cmd);
793  int ac = 3;
794  const char* cav[] = { "test_prog", "file", "kl"};
795  char** av = stradup(cav, ac);
796  cmd.parse(ac, av);
797  suite.err() << "ok\n";
798  strafree(av, ac);
799  return ok;
800}
801
802
803
Note: See TracBrowser for help on using the repository browser.