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 first line is the section number
22 # here are all the sections with a description but the most common
23 # is generally 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 text
37 # the third line is the left footer text
38 # and the last line is the center header
40 # here is an example configuration file
48 # the syntax inside the header files
50 # In order for this script to correctly parse comment blocks,
51 # a special comment block has to be used. Any comments starting
52 # by /* are ignored, only those starting by /** are parsed
53 # by this script. Terminating a comment block can be done using
54 # two methods, either with the normal comment block end */
55 # or the double comment block end **/. The normal comment block
56 # is meant to precede a function prototype like this example :
61 #extern void somefunc(int foo, int bar);
63 # The double ending comment block simply parses the comment and nothing
64 # else, here is an example :
69 # using this method, a near infinite amount of man pages can be created
70 # without the need to have a function. The only drawback is that the
71 # name macro (see @name below) _has_ to be set because there's no
72 # function prototype to gather the name of the man page file from.
74 # an ending comment block of type */ actually has a special parser
75 # which also parses the next line's function prototype.
76 # It gets the function name, return type, variable type and variable name.
78 # inside the /** (*)*/ comments, a special "language" has to be used which uses
79 # commands or variables. commands/variables always start with the character '@'
80 # for example : @description
82 # the script parses all the current data (the text) into the last "command"
84 # here are the complete list of those commands/variables and what they do :
86 # take note that those commands can be entered in any order.
90 # @name -- Name of the man page file. (not required for */ ending comments
91 # but mandatory for **/ ending comments)
92 # If this variable is present for a function prototype then it will
93 # override the function name with this value.
94 # @sdescri -- A small description, a summary that should only be about 1 sentence.
95 # @description -- An exhaustive description, it can be of any length.
96 # @param[in] -- Input argument type is used to signify that the particular variable
97 # is meant as read only by the function. See @param for more details.
98 # @param[out] -- Output argument type, same as input but signifies that the variable
99 # is meant to be output of the function. See @param for more details.
100 # @param[io] -- Both way argument type is used as both input and output at once,
101 # see @param for more details.
102 # @param -- Can not be used as is, has to be used in the form shown right above
103 # like @param[in]. This argument has the particularity to be automatically
104 # matched with arguments of the prototype function at hand.
105 # Say theres 5 arguments in the function prototype, well a total amount of
106 # 5 @param[...] will need to be used to describe all the argument's meaning
108 # @related -- text after this (which should be functions or such) will be put in the see also
110 # @examples -- text after this is put in the code examples section.
111 # @returnval -- text after this is put in the return value section.
112 # @errors -- text after this is put in the error codes section.
117 # put the version here
120 # -- global variables --
128 echo
"Usage : neuroman \[OPTIONS\] ... \[FILES\]"
129 echo
"A very simple tcl header file parser to the man format."
130 echo
"The documentation on how to format the comments before"
131 echo
"the functions is inside this script."
132 echo
" -c file input the configuration file"
133 echo
" -i,--individual saves each functions from FILES into their own man"
134 echo
" pages instead of outputing to standard output."
135 echo
" -v,--version output the version of neuroman"
136 echo
" -h,--help output this help message"
138 echo
"report bugs to neuroman-bugs@neuroponic.com"
141 proc showVersion
{ } {
143 echo
"neuroman version $version"
146 proc handleArgs
{ } {
159 #we check and handle the arguments
161 if {$elem == "-h" ||
$elem == "--help"} {
164 } elseif
{$elem == "-v" ||
$elem == "--version"} {
167 } elseif
{$elem == "-i" ||
$elem == "--individual"} {
169 } elseif
{$elem == "-c"} {
171 } elseif
{$got_config == 1} {
176 if {[string index
$elem 0] == "-"} {
177 echo
"Invalid argument $elem"
178 echo
"use --help to see the list of valid arguments."
180 set files
[linsert $files 1 "$elem"]
192 # this function parses a C function prototype to remove its leading
193 # and ending brackets.
194 # it will not touch brackets that are not leading and ending.
195 proc arrangeFunction
{function
} {
198 #set total [llength $function]
203 # gets rid of all the commas
204 #while {$t < [string length $function]} {
205 # set t [string first "," $function $t]
211 # set function [string replace $function $t $t " "]
216 # we find the first opening bracket
217 set t
[string first
"(" $function]
219 # if we found one, we remove it
221 set function
[string replace
$function $t $t " "]
224 # we find the last opening bracket
225 set t
[string last
")" $function]
227 # if we found one, we remove it
229 set function
[string replace
$function $t $t " "]
233 # now we will kind of hack a way to assemble ()() function pointers
240 while {$t < [string length
$function]} {
241 set t
[string first
"(" $function $t]
245 } elseif
{$found_begin == 0} {
247 #set function [string replace $function [expr $t - 1] [expr $t - 1] "\{"]
254 set t
[string first
")" $function $t]
258 } elseif
{$found_end == 1} {
260 #set function [string replace $function [expr $t + 1] [expr $t + 1] "\}"]
270 # we get rid of the beginning extern if any
271 if {[lindex $function 0] == "extern"} {
272 set function
[lrange $function 1 end
]
278 proc pushData
{storage toadd extra
} {
279 upvar $storage buffer
282 if {$storage == ""} {
289 set buffer
"$buffer $xtra {$toadd}"
295 proc parseComment
{comment name sdescri ldescri
options returnval example errors related
} {
297 upvar $sdescri small_d
298 upvar $ldescri long_d
300 upvar $returnval retv
309 foreach word
$comment {
311 puts "Parse all words : $word"
313 #set word [string trim $word \"*\"]
315 if {$word == "@name"} {
316 pushData
$ctype $buffer extra
320 } elseif
{$word == "@sdescri"} {
321 pushData
$ctype $buffer extra
325 } elseif
{$word == "@description"} {
326 pushData
$ctype $buffer extra
330 } elseif
{$word == "@param\[in\]"} {
331 pushData
$ctype $buffer extra
336 } elseif
{$word == "@param\[out\]"} {
337 pushData
$ctype $buffer extra
342 } elseif
{$word == "@param\[io\]"} {
343 pushData
$ctype $buffer extra
348 } elseif
{$word == "@related"} {
349 pushData
$ctype $buffer extra
353 } elseif
{$word == "@examples"} {
354 pushData
$ctype $buffer extra
358 } elseif
{$word == "@returnval"} {
359 pushData
$ctype $buffer extra
363 } elseif
{$word == "@errors"} {
364 pushData
$ctype $buffer extra
369 set buffer
"$buffer $word"
373 pushData
$ctype $buffer extra
375 # we clean the small buffers of any * characters
376 # from the comment syntax
387 proc GetStringWord
{ str current
} {
388 #returns the word it fetched
389 #current needs to be the variable itself, not the data
391 set slen
[string length
$str]
397 set c
[string index
$str $a]
400 set outword
"$outword$c"
401 #echo "outword : $outword initial $initial current $a char : $c"
413 proc Clean_String
{str
} {
418 set new_string
$tochange
421 # \\\\175 == '\}' and \c\}
422 #regsub -all " \\052( |\n|$|)" $tochange {} new_string
423 regsub -all {(\*[[:blank
:]]|
[[:blank
:]]\*)} $tochange {} new_string
424 regsub -all {( | | | |
)\*( | |
)} $tochange {} new_string
426 # this gets rid of any stars at the end of a line without
427 # getting rid of the carriage return
428 #regsub -all "\\052\n" $new_string "\n" new_string
432 # we get rid of any * right before a '\}'
433 regsub -all " \\052(\})" $temp "\}" new_string
435 #set temp $new_string
437 # we get rid of the very annoying space that gets
438 # added at the very beginning for every paragraphs.
439 # we replace by the full string and add a \b character
440 # at the end to get rid of the space.
441 #regsub -all "\(\\\\n \)*\\\\n " $temp "&\b" new_string
442 #regsub -all {$ \n[[:space:]]\n[[:space:]]} $temp "\n\n" new_string
443 #regsub -all -line {^[[:space:]]} $temp "coco" new_string
445 #set temp $new_string
447 # we also get rid of any beginning spaces
448 regsub -all -line "^ " $temp "" new_string
449 #regsub -all -nocase {\n[[:space:]][a,z]} $temp "coco" new_string
450 #regsub -all {} $temp "coco" new_string
452 set tochange
$new_string
455 # parses strictly a comment block
456 proc genMan_Unique
{comment
} {
460 set msmall_description
""
467 set config_i
[parseConfig
$config]
470 # by default we output to stdout (default screen)
473 # we get rid of the /* character in the string
474 set comment
[string trim
$comment "/*"]
476 # populate our variables
477 parseComment
$comment name msmall_description mdescription moptions mreturnv mexamples merrors mseealso
480 puts "Error : missing a @name element from a comment"
484 if {$individual == 1} {
485 # we get rid of any ending spaces
486 regsub "\( \)*$" $name "" name
487 # we trim the function's name of any leading * in case
488 # it returns a pointer -- example : char *somefunction()
489 set fp
[open "[string trim $name \"*\"].[lindex $config_i 0]" w
]
490 #set fp [open "$name.[lindex $config 0]" w]
493 puts $fp ".TH $name [lindex $config_i 0] \"[lindex $config_i 1]\" \"[lindex $config_i 2]\" \"[lindex $config_i 3]\""
496 if {[llength $msmall_description] > 0} {
498 puts $fp "[string trim $name \"*\"]"
499 puts $fp "\- $msmall_description"
501 puts $fp "[string trim $name \"*\"]"
504 if {[llength $mdescription] > 0} {
505 puts $fp ".SH DESCRIPTION"
506 puts $fp [subst "$mdescription"]
509 if {[llength $mreturnv] > 0 && [lindex $function 0] != ""} {
510 puts $fp ".SH RETURN VALUES"
514 if {[string length
$mexamples] > 0} {
515 puts $fp ".SH EXAMPLE(S)"
517 puts $fp [subst $mexamples]
520 if {[llength $merrors] > 0} {
521 puts $fp ".SH ERRORS"
525 if {[llength $mseealso] > 0} {
526 puts $fp ".SH SEE ALSO"
531 if {$individual == 1} {
538 # parses a comment block followed by a function prototype
539 proc genMan
{comment function
} {
543 set msmall_description
""
550 set config_i
[parseConfig
$config]
552 # we get rid of any { or } characters
553 set function
[eval "concat $function"]
555 # by default we output to stdout (default screen)
558 # arranges the function data so its easy to handle
559 set function
[arrangeFunction
$function]
561 # we get rid of the /* character in the string
562 set comment
[string trim
$comment "/*"]
564 # populate our variables
565 parseComment
$comment name msmall_description mdescription moptions mreturnv mexamples merrors mseealso
568 if {[lindex $function 1] == ""} {
572 set name
[lindex $function 1]
576 if {$individual == 1} {
577 # we trim the function's name of any leading * in case
578 # it returns a pointer -- example : char *somefunction()
579 set fp
[open "[string trimleft $name \"*\"].[lindex $config_i 0]" w
]
582 puts $fp ".TH $name [lindex $config_i 0] \"[lindex $config_i 1]\" \"[lindex $config_i 2]\" \"[lindex $config_i 3]\""
585 if {[llength $msmall_description] > 0} {
587 puts $fp "[string trim $name \"*\"]"
588 puts $fp "\- [subst $msmall_description]"
590 puts $fp "[string trim $name \"*\"]"
595 if {[lindex $function 0] != ""} {
596 puts $fp ".SH SYNOPSIS"
597 puts $fp "[lindex $function 0] $name\([lrange $function 2 [expr [llength $function] - 2]]\)"
600 if {[string length
$mdescription] > 0} {
601 puts $fp ".SH DESCRIPTION"
602 puts $fp [subst $mdescription]
605 # The arguments... beats me why I initially called that
607 if {[llength $moptions] > 0 && [lindex $function 0] != ""} {
608 set ototal
[llength $moptions]
612 puts $fp ".SH ARGUMENTS"
614 while {$i < $ototal} {
615 set ctype
[lindex $moptions [expr "$i - 1"]]
621 } elseif
{$ctype == 1} {
624 set type
"(input and output)"
627 #set nfunc [string trim [lindex $function $cfunc] "*"]
628 set nfunc
[lindex $function $cfunc]
630 # we check to see if the current argument is a pointer to
631 # a function and if yes, we do something special to include
633 if {[string index
$nfunc 0] == "("} {
637 if {$is_funcptr == 1} {
642 while { $go_on == 1 } {
643 set cur
[string last
")" $str]
645 # this is a special case where the function pointer
646 # has no arguments at all.
647 # like : void (*callback)()
649 if {[lindex $function [expr $cur - 1]] == "("} {
651 } elseif
{$found_one == 1} {
659 set str
[lindex $function $cfunc]
661 set nfunc
[linsert $nfunc [llength $nfunc] $str]
662 #echo "SPECIAL $nfunc -> $str"
667 # we remove the last comma if we find one
668 set t
[string last
"," $nfunc]
670 #echo "cur $cfunc last [llength $function]"
671 if {$t > 0 && [expr [llength $function] - 1] > $cfunc} {
672 set nfunc
[string replace
$nfunc $t $t " "]
676 puts $fp ".BI \"$nfunc \" $type"
677 puts $fp "[lindex $moptions $i]\n"
685 if {[llength $mreturnv] > 0 && [lindex $function 0] != ""} {
686 puts $fp ".SH RETURN VALUES"
690 if {[string length
$mexamples] > 0} {
691 puts $fp ".SH EXAMPLE(S)"
692 puts $fp [subst $mexamples]
695 if {[llength $merrors] > 0} {
696 puts $fp ".SH ERRORS"
700 if {[llength $mseealso] > 0} {
701 puts $fp ".SH SEE ALSO"
709 if {$individual == 1} {
716 # "/**" gather -> "**/" stop gather
717 # OR "*/" stop gather and look for anything except "/**"
718 # after a "/**" "*/" combination (next line)
719 # look for "extern" gather2 -> ";" stop gather2 dump in function ...
721 # the part action can include :
722 # none : none yet (put that when nothing is needed anymore)
723 # gather : starts buffering inside the buffer part. And starts matching in the
724 # end_string_list list.
725 # childonly : we got a match in the end_string_list so we process only in it
726 # process_data : tells the algo that the current branch got a complete set of
727 # matching patterns so it can now be processed. If this action
728 # is choosen, the extra part is used to know which function to
729 # call with what arguments.
730 # gather_line : exactly like gather but it doesn't match anything, it stops after
731 # a line change. It uses the extra part for the function to call.
732 # it also uses the current_action element to the current line number.
734 # the end_string part is a list with a list of strings that can be matched.
735 # and the next element is the child elements exactly like the MATCH list.
737 # buffer is the current buffer which is filled only when the begin_string is
738 # matched. Filling it only ends when a match is found inside end_string_list
740 # begin_string_list action buffer current_action extra end_string_list
742 # take note that the general order is important for correct dependencies
743 # the more dependencies a match has, the more it is to the end.
744 proc MATCH_format
{} {
746 set MATCH_Handled_Test
"{\"houba\" gather_line \"\" none \"genMan_Test\" {} }"
748 set MATCH_Handled_Comment_End_Unique
"{\"**/\" process_data \"\" none \"genMan_Unique\" {} }"
750 set MATCH_Function_Prototype_End
"{\";\" process_data \"\" none \"genMan\" {}}"
752 set MATCH_Function_Prototype
"{\"extern\" gather \"\" none \"\" {\
753 $MATCH_Function_Prototype_End} }"
755 set MATCH_Handled_Comment_End
"{\"*/\" childonly \"\" none \"\" {\
756 $MATCH_Function_Prototype} }"
759 set MATCH_Handled_Comment_Start
"{\"/**\" gather \"\" none \"\" {\
760 $MATCH_Handled_Comment_End_Unique \
761 $MATCH_Handled_Comment_End} }"
764 set MATCH_list
"$MATCH_Handled_Comment_Start $MATCH_Handled_Test"
767 #foreach parent $MATCH_list {
768 # MATCH_process "/**" MATCH_list "" $parent $i
775 #MATCH_process "/**" MATCH_list
776 #MATCH_process "hello" MATCH_list
777 #MATCH_process "*/" MATCH_list
778 #MATCH_process "extern" MATCH_list
779 #MATCH_process ";" MATCH_list
781 #puts "---> $MATCH_list"
783 #MATCH_resetProcess MATCH_list 0 0
788 proc MATCH_process
{stringtm thelist
} {
795 #puts "fetched whole \"[Fetch_Cascade_Whole $tlist 5 {0} 0 1]\""
796 #puts "fetched data \"[Fetch_Cascade_Data $tlist 5 0 {0 1} 1]\""
798 #puts "\nChanging data"
799 #set tlist [Set_Cascade_Data $tlist 5 3 "childonly" {0 1 0 0} 3]
800 #puts "fetched modified data \"[Fetch_Cascade_Data $tlist 5 3 {0 1 0 0} 3]\""
802 #puts "STRING -> $stringtm"
804 MATCH_loopProcess
$stringtm tlist
0 0
807 proc MATCH_resetProcess
{thelist num depth
} {
810 #puts "will process num $num depth $depth"
813 set cData
[Fetch_Cascade_Whole
$tlist 5 $num $depth 1]
817 if {$cData == -1 ||
$cData == ""} {
821 foreach elem
$cData {
824 # we reset the buffer elemenet
825 set tlist
[Set_Cascade_Data
$tlist 5 2 "" $num $depth]
827 # we reset the current_action element
828 set tlist
[Set_Cascade_Data
$tlist 5 3 "none" $num $depth]
837 #puts "child num : $child_num -- depth [expr $depth + 1]"
839 MATCH_resetProcess tlist
$child_num [expr $depth + 1]
841 lset num
$depth [expr [lindex $num $depth] + 1]
846 proc MATCH_loopProcess
{stringtm thelist num depth
} {
849 #puts "will process num $num depth $depth string $stringtm"
852 set cData
[Fetch_Cascade_Whole
$tlist 5 $num $depth 1]
856 if {$cData == -1 ||
$cData == ""} {
860 foreach elem
$cData {
863 #puts [Fetch_Cascade_Data $tlist 5 0 $num $depth]
864 set _err
[MATCH_subprocess
$stringtm tlist
$num $depth]
867 # we call a subsequent child
869 #lset num $depth [expr [lindex $num $depth] + 1]
876 #puts "child num : $child_num -- depth [expr $depth + 1]"
878 if {[MATCH_loopProcess
$stringtm tlist
$child_num [expr $depth + 1]] == 1} {
882 #puts "finished child run"
884 # we don't need to run on the next nodes
886 } elseif
{$_err == 2} {
889 lset num
$depth [expr [lindex $num $depth] + 1]
897 # this function calls itself
898 # if the current node needs to call subsequent childs, it will return 0
899 # if not, it will return 1
900 proc MATCH_subprocess
{stringtm thelist num depth
} {
902 set node
[Fetch_Cascade_Whole
$tlist 5 $num $depth]
904 #puts "node [lindex $node 0] string $stringtm"
906 switch [lindex $node 3] {
908 foreach string_match
[lindex $node 0] {
909 #puts -nonewline "trying to see if $stringtm is $string_match "
911 if {[ParseString
$string_match $stringtm] == 1} {
916 switch [lindex $node 1] {
923 set tlist
[Set_Cascade_Data
$tlist 5 3 "gather" $num $depth]
928 set tlist
[Set_Cascade_Data
$tlist 5 3 "childonly" $num $depth]
930 set tlist
[Set_Cascade_Data
$tlist 5 3 "childonly" [lrange $num 0 [expr $depth - 1]] [expr $depth - 1]]
938 # this is the "last" command set for a list of matches, thats why we reset the list
939 # need to call the extra element with the necessary arguments
941 while {$i < $depth} {
943 if {[Fetch_Cascade_Data
$tlist 5 1 [lrange $num 0 $i] $i] == "gather"} {
944 lappend buf
[Fetch_Cascade_Data
$tlist 5 2 [lrange $num 0 $i] $i]
952 eval "[lindex $node 4] $buf"
954 #puts "we reset the process"
956 MATCH_resetProcess tlist
0 0
962 # don't know yet how to handle this one
970 #puts "initial gathering action [lindex $node 0] -> $stringtm"
972 set buf
[lindex $node 2]
974 lappend buf
$stringtm
976 set tlist
[Set_Cascade_Data
$tlist 5 2 $buf $num $depth]
978 # we only need one match
989 #puts "gathering action [lindex $node 0] -> $stringtm"
991 set buf
[lindex $node 2]
993 lappend buf
$stringtm
995 set tlist
[Set_Cascade_Data
$tlist 5 2 $buf $num $depth]
1002 #puts "child only state [lindex $node 0]"
1008 puts "process data state? this is an error buddy!"
1012 puts "gather line state? this is an error buddy!"
1019 # sets a single data in a list/sublist corresponding to depth
1020 # and number pattern
1021 proc Set_Cascade_Data
{alist sublist_num elem data num_in depth
} {
1026 set i
[expr $depth + 1]
1031 set current
[Fetch_Cascade_Whole
$alist $sublist_num $num $i]
1032 set whole
[Fetch_Cascade_Whole
$alist $sublist_num $num $i 1]
1035 lset current
$elem $data
1036 } elseif
{$child != ""} {
1037 #puts "$sublist_num --> $current -- $child"
1038 set current
[lreplace $current $sublist_num $sublist_num $child]
1041 lset whole
[lindex $num_in $i] $current
1044 #puts "$i -- $current"
1049 #set tempo [lreplace [Fetch_Cascade_Whole $alist $sublist_num $num $i 1] [lindex $num 0] [lindex $num 0] $current]
1050 #puts "$whole TEST --_-_->> [lindex $whole [lindex $num_in $i]]"
1052 set num
[lrange $num 0 end-1
]
1053 #puts "DEBUG -- $num"
1058 # in a cascading list/sublist variable, output a full list corresponding
1059 # to a certain depth and a number pattern.
1060 proc Fetch_Cascade_Whole
{alist sublist_num num_in depth
{relative
0}} {
1064 # we first reverse the num variable
1065 set i
[llength $num_in]
1067 foreach item
$num_in {
1068 set num
[linsert $num 0 $item]
1073 #puts "Whole output : number -- [lindex $num $depth]"
1075 foreach item
$alist {
1076 #puts "THERES #[llength $alist] elements total! ?$relative?"
1077 #puts "$i - [lindex $num $depth]"
1079 if {$i == [lindex $num $depth]} {
1081 #puts "PROCESSING -- $item"
1085 if {$relative == 0} {
1091 return [Fetch_Cascade_Whole
[lindex $item $sublist_num] $sublist_num $num_in [expr $depth - 1] $relative]
1101 # in a cascading list/sublist variable, output a single element
1102 # corresponding to a certain depth and a number pattern.
1103 proc Fetch_Cascade_Data
{alist sublist_num elem num_in depth
} {
1106 set output
[Fetch_Cascade_Whole
$alist $sublist_num $num_in $depth]
1108 if {$output != -1} {
1109 if {$elem >= 0 && $elem < [llength $output]} {
1110 return [lindex $output $elem]
1117 proc parseFile
{file config
} {
1118 # gather 1 is used to gather the comment
1121 # gather 2 is used to gather principally
1122 # function prototypes and in the future,
1125 # contains the current line number for use
1126 # by the code to know when a line changed
1130 #puts [subst $MATCH_list]
1132 set matchform
[MATCH_format
]
1135 # we loop line by line, string by string and character by character
1137 # we continue until eof to loop line by line
1138 while {[eof $file] == 0} {
1139 set line
[gets $file]
1142 # count the number of characters in the line
1143 set line_len
[string length
$line]
1145 # loop string by string in the current line
1146 while {$a < $line_len} {
1147 set str
[GetStringWord
$line a
]
1149 MATCH_process
$str matchform
1152 # we increment the current number of lines
1157 # returns 0 if theres no matching strings
1159 proc ParseString
{string_indep string_depen
} {
1160 # current matching characters
1163 set len
[string length
$string_depen]
1165 #puts "Checking String \"$string_indep\" over \"$string_depen\""
1167 # loop character by character in the current string
1169 set char
[string index
$string_depen $i]
1170 set indep
[string index
$string_indep $match]
1172 #puts "depen $char indep $indep"
1174 if {$char == $indep} {
1178 # special case where we check the former character with this one to see
1179 # if it matches... if it matches, it means we might have a redundant
1180 # character which was primarily matched.
1182 # example : our indep string is */ and our input string is **/
1183 # without this code, the first * is matched then it checks the
1184 # second * over with / but it won't match and it restarts the check.
1188 # */ **/ 0 (so the algo restarts)
1190 # */ **/ 0 (see, this is a bit false because it was
1191 # reset and thus the string wasn't matched.)
1193 if {$match > 0 && $char == [string index
$string_indep [expr $match - 1]]} {
1200 if {$match == [string length
$string_indep]} {
1210 proc parseConfig
{file} {
1213 if { $use_config == 0} {
1214 # we output a default stock config
1215 set conf
{{3} {"center footer"} {"left footer"} {"center header"}}
1218 # we parse the configuration from the file
1221 set fp
[open $file r
]
1223 while {[eof $fp] == 0} {
1224 set conf
"$conf {[gets $fp]}"
1233 proc handleFiles
{} {
1239 if {$use_config == 1} {
1240 # we first check if the configuration file exists
1241 set fp
[open $config r
]
1245 # we check to see if the files exist or not
1246 foreach elem
$files {
1247 set fp
[open $elem r
]
1252 # now we need to open each of them and parse their content
1253 foreach elem
$files {
1254 set fp
[open $elem r
]
1256 parseFile
$fp [parseConfig
$config]
1263 set err
[handleArgs
]