1 # copyright neuroponic 2006
2 # author : Nicholas Niro
3 # by using this piece of software, you accept that it
4 # has no warranty at all, use it at your own risk.
6 # permission is hereby granted to modify and distribute
7 # this script as long as the original authoring is kept.
14 # documentation section, the syntax of the configuration file
15 # and the syntax of the comments in the header files.
17 # configuration file syntax
19 # the configuration file requires 5 lines
20 # the first line is the name of the program/library
21 # the firs line is the section number
22 # here are all the sections with a description but you will
23 # generally use the section 3.
26 # 2 System calls, that is, functions provided by the kernel.
27 # 3 Subroutines, that is, library functions.
28 # 4 Devices, that is, special files in the /dev directory.
29 # 5 File format descriptions, e.g. /etc/passwd.
30 # 6 Games, self-explanatory.
31 # 7 Miscellaneous, e.g. macro packages, conventions.
32 # 8 System administration tools that only root can execute.
34 # n New documentation, that may be moved to a more appropriate section.
35 # l Local documentation referring to this particular system.
36 # the second line is the center footer
37 # the third line is the left footer
38 # and the last line is the center header
40 # here is an example configuration file
48 # the syntax inside the header files
50 # first of all, the script will parse the comments that are
51 # just above a function prototype which starts with the extern keyword
52 # and it will only parse those with an extra star ie /**
53 # so to make this script work good you need to have this minimum:
58 #extern void somefunc(int foo, int bar);
60 # the actual stuff that will be put in the man page will need to be enclosed
61 # between the /** and the */ this is how the script will know the end of the
64 # the script will also fetch data from the extern prototype too, it will
65 # get the function name, return type, variable types and names it will parse
66 # until it goes to the ending ';' and it will ignore any ( and ) characters.
68 # inside the /** */ comments, a special "language" has to be used which uses
69 # commands or variables. commands/variables always start with the character '@'
70 # for example : @description
71 # the script will parse all the current data (the text) into the last "command"
73 # heres the complete list of those commands/variables and what they do :
75 # take note that those commands can be entered in any order.
79 # @name -- the name the man page file name will be, necessary for all except
80 # the functions which automatically defaults to the function name.
81 # If this variable is present for a function prototype then it will
82 # override the function name with this name.
83 # @sdescri -- this is the small description, the summary that should be about 1
85 # @description -- this is the exhaustive description, it can be of any length you want.
86 # @param -- this command is special, you can not just write @param , you also have
87 # to put the param's "direction" (input output or both) in the form
88 # @param[...] (see below) You don't need to specify which function
89 # argument the param goes to, it goes sequentially and remember to put a
90 # description for each @param[...].
91 # @param[in] -- the input type see @param
92 # @param[out] -- the output type see @param
93 # @param[io] -- both input and output see @param
94 # @related -- text after this (which should be functions or such) will be put in the see also
96 # @examples -- text after this is put in the code examples section.
97 # @returnval -- text after this is put in the return value section.
98 # @errors -- text after this is put in the error codes section.
103 # put the version here
106 # -- global variables --
114 echo
"Usage : neuroman \[OPTIONS\] ... \[FILES\]"
115 echo
"A very simple tcl header file parser to the man format."
116 echo
"The documentation on how to format the comments before"
117 echo
"the functions is inside this script."
118 echo
" -c file input the configuration file"
119 echo
" -i,--individual saves each functions from FILES into their own man"
120 echo
" pages instead of outputing to standard output."
121 echo
" -v,--version output the version of neuroman"
122 echo
" -h,--help output this help message"
124 echo
"report bugs to neuroman-bugs@neuroponic.com"
127 proc showVersion
{ } {
129 echo
"neuroman version $version"
132 proc handleArgs
{ } {
145 #we check and handle the arguments
147 if {$elem == "-h" ||
$elem == "--help"} {
150 } elseif
{$elem == "-v" ||
$elem == "--version"} {
153 } elseif
{$elem == "-i" ||
$elem == "--individual"} {
155 } elseif
{$elem == "-c"} {
157 } elseif
{$got_config == 1} {
162 if {[string index
$elem 0] == "-"} {
163 echo
"Invalid argument $elem"
164 echo
"use --help to see the list of valid arguments."
166 set files
[linsert $files 1 "$elem"]
178 # this function parses a C function prototype to remove its leading
179 # and ending brackets.
180 # it will not touch brackets that are not leading and ending.
181 proc arrangeFunction
{function
} {
184 #set total [llength $function]
189 # gets rid of all the commas
190 #while {$t < [string length $function]} {
191 # set t [string first "," $function $t]
197 # set function [string replace $function $t $t " "]
202 # we find the first opening bracket
203 set t
[string first
"(" $function]
205 # if we found one, we remove it
207 set function
[string replace
$function $t $t " "]
210 # we find the last opening bracket
211 set t
[string last
")" $function]
213 # if we found one, we remove it
215 set function
[string replace
$function $t $t " "]
219 # now we will kind of hack a way to assemble ()() function pointers
226 while {$t < [string length
$function]} {
227 set t
[string first
"(" $function $t]
231 } elseif
{$found_begin == 0} {
233 #set function [string replace $function [expr $t - 1] [expr $t - 1] "\{"]
240 set t
[string first
")" $function $t]
244 } elseif
{$found_end == 1} {
246 #set function [string replace $function [expr $t + 1] [expr $t + 1] "\}"]
256 # we get rid of the beginning extern if any
257 if {[lindex $function 0] == "extern"} {
258 set function
[lrange $function 1 end
]
264 proc pushData
{storage toadd extra
} {
265 upvar $storage buffer
268 if {$storage == ""} {
275 set buffer
"$buffer $xtra {$toadd}"
281 proc parseComment
{comment name sdescri ldescri
options returnval example errors related
} {
283 upvar $sdescri small_d
284 upvar $ldescri long_d
286 upvar $returnval retv
295 foreach word
$comment {
299 #set word [string trim $word \"*\"]
301 if {$word == "@name"} {
302 pushData
$ctype $buffer extra
307 } elseif
{$word == "@sdescri"} {
308 pushData
$ctype $buffer extra
313 } elseif
{$word == "@description"} {
314 pushData
$ctype $buffer extra
319 } elseif
{$word == "@param\[in\]"} {
320 pushData
$ctype $buffer extra
326 } elseif
{$word == "@param\[out\]"} {
327 pushData
$ctype $buffer extra
333 } elseif
{$word == "@param\[io\]"} {
334 pushData
$ctype $buffer extra
340 } elseif
{$word == "@related"} {
341 pushData
$ctype $buffer extra
346 } elseif
{$word == "@examples"} {
347 pushData
$ctype $buffer extra
352 } elseif
{$word == "@returnval"} {
353 pushData
$ctype $buffer extra
358 } elseif
{$word == "@errors"} {
359 pushData
$ctype $buffer extra
365 set buffer
"$buffer $word"
369 pushData
$ctype $buffer extra
371 # we clean the small buffers of any * characters
372 # from the comment syntax
383 proc GetStringWord
{ str current
} {
384 #returns the word it fetched
385 #current needs to be the variable itself, not the data
387 set slen
[string length
$str]
393 set c
[string index
$str $a]
396 set outword
"$outword$c"
397 #echo "outword : $outword initial $initial current $a char : $c"
409 proc Clean_String
{str
} {
415 # \\\\175 == '\}' and \c\}
417 regsub -all " \\052( |\n|$)" $tochange {} new_string
420 # we get rid of any * right before a '\}'
421 regsub -all " \\052(\})" $temp "\}" new_string
425 # we get rid of the very annoying space that gets
426 # added at the very beginning for every paragraphs.
427 # we replace by the full string and add a \b character
428 # at the end to get rid of the space.
429 regsub -all "\(\\\\n \)*\\\\n " $temp "&\b" new_string
432 # we also get rid of any trailing spaces
433 regsub -all -line "^ " $temp "" new_string
436 set tochange
$new_string
439 proc genMan_Unique
{comment
} {
443 set msmall_description
""
450 set config_i
[parseConfig
$config]
453 # by default we output to stdout (default screen)
456 # we get rid of the /* character in the string
457 set comment
[string trim
$comment "/*"]
459 # populate our variables
460 parseComment
$comment name msmall_description mdescription moptions mreturnv mexamples merrors mseealso
463 puts "Error : missing a @name element from a comment"
467 if {$individual == 1} {
468 # we get rid of any ending spaces
469 regsub "\( \)*$" $name "" name
470 # we trim the function's name of any leading * in case
471 # it returns a pointer -- example : char *somefunction()
472 set fp
[open "[string trim $name \"*\"].[lindex $config_i 0]" w
]
473 #set fp [open "$name.[lindex $config 0]" w]
476 puts $fp ".TH $name [lindex $config_i 0] \"[lindex $config_i 1]\" \"[lindex $config_i 2]\" \"[lindex $config_i 3]\""
479 if {[llength $msmall_description] > 0} {
481 puts $fp "[string trim $name \"*\"]"
482 puts $fp "\- $msmall_description"
484 puts $fp "[string trim $name \"*\"]"
487 if {[llength $mdescription] > 0} {
488 puts $fp ".SH DESCRIPTION"
489 puts $fp [subst "$mdescription"]
492 if {[llength $mreturnv] > 0 && [lindex $function 0] != ""} {
493 puts $fp ".SH RETURN VALUES"
497 if {[string length
$mexamples] > 0} {
498 puts $fp ".SH EXAMPLE(S)"
500 puts $fp [subst $mexamples]
503 if {[llength $merrors] > 0} {
504 puts $fp ".SH ERRORS"
508 if {[llength $mseealso] > 0} {
509 puts $fp ".SH SEE ALSO"
514 if {$individual == 1} {
521 proc genMan
{comment function
} {
525 set msmall_description
""
532 set config_i
[parseConfig
$config]
534 # we get rid of any { or } characters
535 set function
[eval "concat $function"]
537 # by default we output to stdout (default screen)
540 # arranges the function data so its easy to handle
541 set function
[arrangeFunction
$function]
543 # we get rid of the /* character in the string
544 set comment
[string trim
$comment "/*"]
546 # populate our variables
547 parseComment
$comment name msmall_description mdescription moptions mreturnv mexamples merrors mseealso
550 if {[lindex $function 1] == ""} {
554 set name
[lindex $function 1]
558 if {$individual == 1} {
559 # we trim the function's name of any leading * in case
560 # it returns a pointer -- example : char *somefunction()
561 set fp
[open "[string trimleft $name \"*\"].[lindex $config_i 0]" w
]
564 puts $fp ".TH $name [lindex $config_i 0] \"[lindex $config_i 1]\" \"[lindex $config_i 2]\" \"[lindex $config_i 3]\""
567 if {[llength $msmall_description] > 0} {
569 puts $fp "[string trim $name \"*\"]"
570 puts $fp "\- [subst $msmall_description]"
572 puts $fp "[string trim $name \"*\"]"
577 if {[lindex $function 0] != ""} {
578 puts $fp ".SH SYNOPSIS"
579 puts $fp "[lindex $function 0] $name\([lrange $function 2 [expr [llength $function] - 2]]\)"
582 if {[string length
$mdescription] > 0} {
583 puts $fp ".SH DESCRIPTION"
584 puts $fp [subst $mdescription]
587 # The arguments... beats me why I initially called that
589 if {[llength $moptions] > 0 && [lindex $function 0] != ""} {
590 set ototal
[llength $moptions]
594 puts $fp ".SH ARGUMENTS"
596 while {$i < $ototal} {
597 set ctype
[lindex $moptions [expr "$i - 1"]]
603 } elseif
{$ctype == 1} {
606 set type
"(input and output)"
609 #set nfunc [string trim [lindex $function $cfunc] "*"]
610 set nfunc
[lindex $function $cfunc]
612 # we check to see if the current argument is a pointer to
613 # a function and if yes, we do something special to include
615 if {[string index
$nfunc 0] == "("} {
619 if {$is_funcptr == 1} {
624 while { $go_on == 1 } {
625 set cur
[string last
")" $str]
627 # this is a special case where the function pointer
628 # has no arguments at all.
629 # like : void (*callback)()
631 if {[lindex $function [expr $cur - 1]] == "("} {
633 } elseif
{$found_one == 1} {
641 set str
[lindex $function $cfunc]
643 set nfunc
[linsert $nfunc [llength $nfunc] $str]
644 #echo "SPECIAL $nfunc -> $str"
649 # we remove the last comma if we find one
650 set t
[string last
"," $nfunc]
652 #echo "cur $cfunc last [llength $function]"
653 if {$t > 0 && [expr [llength $function] - 1] > $cfunc} {
654 set nfunc
[string replace
$nfunc $t $t " "]
658 puts $fp ".BI \"$nfunc \" $type"
659 puts $fp "[lindex $moptions $i]\n"
667 if {[llength $mreturnv] > 0 && [lindex $function 0] != ""} {
668 puts $fp ".SH RETURN VALUES"
672 if {[string length
$mexamples] > 0} {
673 puts $fp ".SH EXAMPLE(S)"
674 puts $fp [subst $mexamples]
677 if {[llength $merrors] > 0} {
678 puts $fp ".SH ERRORS"
682 if {[llength $mseealso] > 0} {
683 puts $fp ".SH SEE ALSO"
691 if {$individual == 1} {
698 # "/**" gather -> "**/" stop gather
699 # OR "*/" stop gather and look for anything except "/**"
700 # after a "/**" "*/" combination (next line)
701 # look for "extern" gather2 -> ";" stop gather2 dump in function ...
703 # the part action can include :
704 # none : none yet (put that when nothing is needed anymore)
705 # gather : starts buffering inside the buffer part. And starts matching in the
706 # end_string_list list.
707 # childonly : we got a match in the end_string_list so we process only in it
708 # process_data : tells the algo that the current branch got a complete set of
709 # matching patterns so it can now be processed. If this action
710 # is choosen, the extra part is used to know which function to
711 # call with what arguments.
712 # gather_line : exactly like gather but it doesn't match anything, it stops after
713 # a line change. It uses the extra part for the function to call.
714 # it also uses the current_action element to the current line number.
716 # the end_string part is a list with a list of strings that can be matched.
717 # and the next element is the child elements exactly like the MATCH list.
719 # buffer is the current buffer which is filled only when the begin_string is
720 # matched. Filling it only ends when a match is found inside end_string_list
722 # begin_string_list action buffer current_action extra end_string_list
724 # take note that the general order is important for correct dependencies
725 # the more dependencies a match has, the more it is to the end.
726 proc MATCH_format
{} {
728 set MATCH_Handled_Test
"{\"houba\" gather_line \"\" none \"genMan_Test\" {} }"
730 set MATCH_Handled_Comment_End_Unique
"{\"**/\" process_data \"\" none \"genMan_Unique\" {} }"
732 set MATCH_Function_Prototype_End
"{\";\" process_data \"\" none \"genMan\" {}}"
734 set MATCH_Function_Prototype
"{\"extern\" gather \"\" none \"\" {\
735 $MATCH_Function_Prototype_End} }"
737 set MATCH_Handled_Comment_End
"{\"*/\" childonly \"\" none \"\" {\
738 $MATCH_Function_Prototype} }"
741 set MATCH_Handled_Comment_Start
"{\"/**\" gather \"\" none \"\" {\
742 $MATCH_Handled_Comment_End_Unique \
743 $MATCH_Handled_Comment_End} }"
746 set MATCH_list
"$MATCH_Handled_Comment_Start $MATCH_Handled_Test"
749 #foreach parent $MATCH_list {
750 # MATCH_process "/**" MATCH_list "" $parent $i
757 #MATCH_process "/**" MATCH_list
758 #MATCH_process "hello" MATCH_list
759 #MATCH_process "*/" MATCH_list
760 #MATCH_process "extern" MATCH_list
761 #MATCH_process ";" MATCH_list
763 #puts "---> $MATCH_list"
765 #MATCH_resetProcess MATCH_list 0 0
770 proc MATCH_process
{stringtm thelist
} {
777 #puts "fetched whole \"[Fetch_Cascade_Whole $tlist 5 {0} 0 1]\""
778 #puts "fetched data \"[Fetch_Cascade_Data $tlist 5 0 {0 1} 1]\""
780 #puts "\nChanging data"
781 #set tlist [Set_Cascade_Data $tlist 5 3 "childonly" {0 1 0 0} 3]
782 #puts "fetched modified data \"[Fetch_Cascade_Data $tlist 5 3 {0 1 0 0} 3]\""
784 #puts "STRING -> $stringtm"
786 MATCH_loopProcess
$stringtm tlist
0 0
789 proc MATCH_resetProcess
{thelist num depth
} {
792 #puts "will process num $num depth $depth"
795 set cData
[Fetch_Cascade_Whole
$tlist 5 $num $depth 1]
799 if {$cData == -1 ||
$cData == ""} {
803 foreach elem
$cData {
806 # we reset the buffer elemenet
807 set tlist
[Set_Cascade_Data
$tlist 5 2 "" $num $depth]
809 # we reset the current_action element
810 set tlist
[Set_Cascade_Data
$tlist 5 3 "none" $num $depth]
819 #puts "child num : $child_num -- depth [expr $depth + 1]"
821 MATCH_resetProcess tlist
$child_num [expr $depth + 1]
823 lset num
$depth [expr [lindex $num $depth] + 1]
828 proc MATCH_loopProcess
{stringtm thelist num depth
} {
831 #puts "will process num $num depth $depth string $stringtm"
834 set cData
[Fetch_Cascade_Whole
$tlist 5 $num $depth 1]
838 if {$cData == -1 ||
$cData == ""} {
842 foreach elem
$cData {
845 #puts [Fetch_Cascade_Data $tlist 5 0 $num $depth]
846 set _err
[MATCH_subprocess
$stringtm tlist
$num $depth]
849 # we call a subsequent child
851 #lset num $depth [expr [lindex $num $depth] + 1]
858 #puts "child num : $child_num -- depth [expr $depth + 1]"
860 if {[MATCH_loopProcess
$stringtm tlist
$child_num [expr $depth + 1]] == 1} {
864 #puts "finished child run"
866 # we don't need to run on the next nodes
868 } elseif
{$_err == 2} {
871 lset num
$depth [expr [lindex $num $depth] + 1]
879 # this function calls itself
880 # if the current node needs to call subsequent childs, it will return 0
881 # if not, it will return 1
882 proc MATCH_subprocess
{stringtm thelist num depth
} {
884 set node
[Fetch_Cascade_Whole
$tlist 5 $num $depth]
886 #puts "node [lindex $node 0] string $stringtm"
888 switch [lindex $node 3] {
890 foreach string_match
[lindex $node 0] {
891 #puts -nonewline "trying to see if $stringtm is $string_match "
893 if {[ParseString
$string_match $stringtm] == 1} {
898 switch [lindex $node 1] {
905 set tlist
[Set_Cascade_Data
$tlist 5 3 "gather" $num $depth]
910 set tlist
[Set_Cascade_Data
$tlist 5 3 "childonly" $num $depth]
912 set tlist
[Set_Cascade_Data
$tlist 5 3 "childonly" [lrange $num 0 [expr $depth - 1]] [expr $depth - 1]]
920 # this is the "last" command set for a list of matches, thats why we reset the list
921 # need to call the extra element with the necessary arguments
923 while {$i < $depth} {
925 if {[Fetch_Cascade_Data
$tlist 5 1 [lrange $num 0 $i] $i] == "gather"} {
926 lappend buf
[Fetch_Cascade_Data
$tlist 5 2 [lrange $num 0 $i] $i]
934 eval "[lindex $node 4] $buf"
936 #puts "we reset the process"
938 MATCH_resetProcess tlist
0 0
944 # don't know yet how to handle this one
952 #puts "initial gathering action [lindex $node 0] -> $stringtm"
954 set buf
[lindex $node 2]
956 lappend buf
$stringtm
958 set tlist
[Set_Cascade_Data
$tlist 5 2 $buf $num $depth]
960 # we only need one match
971 #puts "gathering action [lindex $node 0] -> $stringtm"
973 set buf
[lindex $node 2]
975 lappend buf
$stringtm
977 set tlist
[Set_Cascade_Data
$tlist 5 2 $buf $num $depth]
984 #puts "child only state [lindex $node 0]"
990 puts "process data state? this is an error buddy!"
994 puts "gather line state? this is an error buddy!"
1001 # sets a single data in a list/sublist corresponding to depth
1002 # and number pattern
1003 proc Set_Cascade_Data
{alist sublist_num elem data num_in depth
} {
1008 set i
[expr $depth + 1]
1013 set current
[Fetch_Cascade_Whole
$alist $sublist_num $num $i]
1014 set whole
[Fetch_Cascade_Whole
$alist $sublist_num $num $i 1]
1017 lset current
$elem $data
1018 } elseif
{$child != ""} {
1019 #puts "$sublist_num --> $current -- $child"
1020 set current
[lreplace $current $sublist_num $sublist_num $child]
1023 lset whole
[lindex $num_in $i] $current
1026 #puts "$i -- $current"
1031 #set tempo [lreplace [Fetch_Cascade_Whole $alist $sublist_num $num $i 1] [lindex $num 0] [lindex $num 0] $current]
1032 #puts "$whole TEST --_-_->> [lindex $whole [lindex $num_in $i]]"
1034 set num
[lrange $num 0 end-1
]
1035 #puts "DEBUG -- $num"
1040 # in a cascading list/sublist variable, output a full list corresponding
1041 # to a certain depth and a number pattern.
1042 proc Fetch_Cascade_Whole
{alist sublist_num num_in depth
{relative
0}} {
1046 # we first reverse the num variable
1047 set i
[llength $num_in]
1049 foreach item
$num_in {
1050 set num
[linsert $num 0 $item]
1055 #puts "Whole output : number -- [lindex $num $depth]"
1057 foreach item
$alist {
1058 #puts "THERES #[llength $alist] elements total! ?$relative?"
1059 #puts "$i - [lindex $num $depth]"
1061 if {$i == [lindex $num $depth]} {
1063 #puts "PROCESSING -- $item"
1067 if {$relative == 0} {
1073 return [Fetch_Cascade_Whole
[lindex $item $sublist_num] $sublist_num $num_in [expr $depth - 1] $relative]
1083 # in a cascading list/sublist variable, output a single element
1084 # corresponding to a certain depth and a number pattern.
1085 proc Fetch_Cascade_Data
{alist sublist_num elem num_in depth
} {
1088 set output
[Fetch_Cascade_Whole
$alist $sublist_num $num_in $depth]
1090 if {$output != -1} {
1091 if {$elem >= 0 && $elem < [llength $output]} {
1092 return [lindex $output $elem]
1099 proc parseFile
{file config
} {
1100 # gather 1 is used to gather the comment
1103 # gather 2 is used to gather principally
1104 # function prototypes and in the future,
1107 # contains the current line number for use
1108 # by the code to know when a line changed
1112 #puts [subst $MATCH_list]
1114 set matchform
[MATCH_format
]
1117 # we loop line by line, string by string and character by character
1119 # we continue until eof to loop line by line
1120 while {[eof $file] == 0} {
1121 set line
[gets $file]
1124 # count the number of characters in the line
1125 set line_len
[string length
$line]
1127 # loop string by string in the current line
1128 while {$a < $line_len} {
1129 set str
[GetStringWord
$line a
]
1131 MATCH_process
$str matchform
1134 # we increment the current number of lines
1139 # returns 0 if theres no matching strings
1141 proc ParseString
{string_indep string_depen
} {
1142 # current matching characters
1145 set len
[string length
$string_depen]
1147 #puts "Checking String \"$string_indep\" over \"$string_depen\""
1149 # loop character by character in the current string
1151 set char
[string index
$string_depen $i]
1152 set indep
[string index
$string_indep $match]
1154 #puts "depen $char indep $indep"
1156 if {$char == $indep} {
1160 # special case where we check the former character with this one to see
1161 # if it matches... if it matches, it means we might have a redundant
1162 # character which was primarily matched.
1164 # example : our indep string is */ and our input string is **/
1165 # without this code, the first * is matched then it checks the
1166 # second * over with / but it won't match and it restarts the check.
1170 # */ **/ 0 (so the algo restarts)
1172 # */ **/ 0 (see, this is a bit false because it was
1173 # reset and thus the string wasn't matched.)
1175 if {$match > 0 && $char == [string index
$string_indep [expr $match - 1]]} {
1182 if {$match == [string length
$string_indep]} {
1192 proc parseConfig
{file} {
1195 if { $use_config == 0} {
1196 # we output a default stock config
1197 set conf
{{3} {"center footer"} {"left footer"} {"center header"}}
1200 # we parse the configuration from the file
1203 set fp
[open $file r
]
1205 while {[eof $fp] == 0} {
1206 set conf
"$conf {[gets $fp]}"
1215 proc handleFiles
{} {
1221 if {$use_config == 1} {
1222 # we first check if the configuration file exists
1223 set fp
[open $config r
]
1227 # we check to see if the files exist or not
1228 foreach elem
$files {
1229 set fp
[open $elem r
]
1234 # now we need to open each of them and parse their content
1235 foreach elem
$files {
1236 set fp
[open $elem r
]
1238 parseFile
$fp [parseConfig
$config]
1245 set err
[handleArgs
]