*! 2.0.0 Aug 97 Jeroen Weesie/ICS STB-40 ip22 program define parsoptp version 5.0 if "`*'" == "" { global S_1 global S_2 global S_3 global S_4 global S_5 exit } local optname "`1'" /* name of option */ mac shift * replaces spaces by a char (space), to put spaces back in later if "$S_PCHAR" == "" { local space "@" } else local space "$S_PCHAR" while "`1'" != "" { local input "`input'`1'`space'" mac shift } local H 0 /* level of nesting (#opened parenthesis/brackets) */ local Mode0 "None" /* None, p(arentheis), b(racket) */ local OptFnd 0 /* set to 1 if optname found */ local ProcOpt 0 /* set to 1 during processing options */ local ProcArg 0 /* set to 1 during processing args of optname */ local NonOpt /* set to input that does not belong to options */ local Arg /* set to argument of optname */ local RestOpt /* set to options other than optname */ parse "`input'", p("`space'()[],") while "`1'" != "" { if "`1'" == "," & `H' == 0 { * toggle Options <--> NonOptions local ProcOpt = 1-`ProcOpt' } else if `ProcOpt'==1 & "`1'" == "`optname'" & `H' == 0 { if `OptFnd' > 0 { di in re "option `optname' occurs more than once" exit 198 } local OptFnd 1 /* option found */ if "`2'" == "(" { mac shift /* skip option name & "(" */ *akward but necessary if option text exceeds 80 chars *so we can't simply take off enclosing () later on local H = `H'+1 /* push "parenthesis" */ local Mode`H' "p" local ProcArg 1 /* options has argument */ } } else { if "`1'" == "(" { local H = `H'+1 /* push "parenthesis" */ local Mode`H' "p" } else if "`1'" == "[" { local H = `H'+1 /* push "bracket" */ local Mode`H' "b" } else if "`1'" == ")" { if "`Mode`H''" ~= "p" { ErrNest } local H = `H'-1 /* pop previous mode */ } else if "`1'" == "]" { if "`Mode`H''" ~= "b" { ErrNest } local H = `H'-1 /* pop previous mode */ } else if "`1'" == "`space'" { local 1 " " /* restore space */ } * store text in -Arg-, -RestOpt-, or -NonOpt- if `ProcArg' == 1 { if `H' > 0 { local Arg "`Arg'`1'" } } else if `ProcOpt' == 1 { local RestOpt "`RestOpt'`1'" } else local NonOpt "`NonOpt'`1'" * end-of-arg reached if `H' == 0 { local ProcArg 0 } } mac shift } if `H' ~= 0 { di in re "too few ')' or ']'" exit 132 } * save results if `OptFnd' > 0 { global S_1 "`optname'" } else global S_1 global S_2 "`Arg'" if "`RestOpt'" ~= "" { local c ", " } global S_3 "`NonOpt'`c'`RestOpt'" global S_4 "`NonOpt'" global S_5 "`RestOpt'" * di "$S_1/$S_2/$S_3" end program define ErrNest di in re "too many or mismatching ')' or ']'" exit 132 end exit output S_1 "optname" if optname was found, otherwise empty S_2 value of arguments between matching parenthesis S_3 the remainder of input: non-options, options S_4 Input that does not belong to -options- S_5 Input that does belong to -options- history 2.0.0 Aug 97 jw/ics -- fairly substantial rewrite of the logic of the code to overcome Stata's akward 80chars limitations in its expression parser -- support for optional arguments to options (cmp -xlabel- in -graph-) -- solved "bug" that optname embedded in other option X is dealt as argument of X -- made parsoptp obey Stata's high-level syntax, i.e., sensitive to comma's: comma's outside expressions toggle options-mode. -- match on [] and () -- the name of option is supplied rather than fixed 1.0.0 modelled after -exec-, written by J. Hardin (StataCorp) bugs and limitations -- input should not contain the character @.