Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1289 lines
41 KiB

  1. @rem = '
  2. @goto endofperl
  3. ';
  4. use Time::Local;
  5. use frsobjsup;
  6. package main;
  7. my (%CMD_VARS, %CMD_PARS);
  8. my $DEBUG = 0; # set to one (or use -verbose=all to see emitted comments
  9. my $DEBUG_EXPAND = 0;
  10. my $DEBUG_CHECK = 0; # emit check code to dump FRS_SUB args.
  11. my $DEBUG_PARSE = 0;
  12. my $DEBUG_CODE = 0;
  13. my $InFile;
  14. my $infilelist;
  15. my $inlinenumber;
  16. my $FrsObjectNames = 'FRS_MEMBER|FRS_CONNECTION|FRS_REPLICASET|FRS_SCHEDULE|FRS_SERVER|FRS_SETTINGS';
  17. my $FrsFunctionNames = 'FRS_SET|FRS_COUNT_SET|FRS_ARRAY|FRS_STAGGER|FRS_STAGGER_BY|FRS_SHOW';
  18. my %FRS_CALLS = (
  19. FRS_MEMBER => {
  20. CALL => 1, NSETS => 1,
  21. INTRO => 'FRS_MEMBER->New(', CLOSE => ');',
  22. ARGS => ['UNDER', 'COMPUTER', 'ONAME', 'MAKE_PRIMARY_MEMBER'],
  23. },
  24. FRS_CONNECTION => {
  25. CALL => 1, NSETS => 0,
  26. INTRO => 'FRS_CONNECTION->New(', CLOSE => ');',
  27. ARGS => ['UNDER', 'FROM', 'ONAME', 'SCHED', 'OPTIONS', 'FLAGS', 'ENABLED'],
  28. },
  29. FRS_REPLICASET => {
  30. CALL => 1, NSETS => 1,
  31. INTRO => 'FRS_REPLICASET->New(', CLOSE => ');',
  32. ARGS => ['UNDER', 'SCHED', 'ONAME', 'FLAGS', 'TYPE', 'FILE_FILTER', 'DIR_FILTER'],
  33. },
  34. FRS_SCHEDULE => {
  35. CALL => 1, NSETS => 1,
  36. INTRO => 'FRS_SCHEDULE->New(', CLOSE => ');',
  37. ARGS => ['REPL_INTERVAL', 'REPL_DURATION', 'TIME_ZONE', 'REPL_OFFSET', 'METHOD',
  38. 'STAGGER', 'OVERRIDE', 'DISABLE', 'TYPE', 'NAME'],
  39. },
  40. FRS_SERVER => {
  41. CALL => 1, NSETS => 9,
  42. INTRO => 'FRS_SERVER->New(', CLOSE => ');',
  43. ARGS => ['RP', 'SP', 'COMPUTER', 'NAME', 'WORKPATH', 'MAKE_PRIMARY_MEMBER'],
  44. },
  45. FRS_SETTINGS => {
  46. CALL => 1, NSETS => 1,
  47. INTRO => 'FRS_SETTINGS->New(', CLOSE => ');',
  48. ARGS => ['DN', 'ONAME'],
  49. },
  50. FRS_COUNT_SET => {
  51. CALL => 2, NSETS => 0, #inline func
  52. INTRO => 'scalar @{ ', CLOSE => '}',
  53. ARGS => ['SET'],
  54. },
  55. FRS_ARRAY => {
  56. CALL => 2, NSETS => 0, #inline func
  57. INTRO => '@{ ', CLOSE => '}',
  58. ARGS => ['SET'],
  59. },
  60. FRS_STAGGER => {
  61. CALL => 1, NSETS => 0,
  62. INTRO => 'FRS_SCHEDULE::FRS_STAGGER(', CLOSE => ');',
  63. ARGS => ['SCHED'],
  64. },
  65. FRS_STAGGER_BY => {
  66. CALL => 1, NSETS => 0,
  67. INTRO => 'FRS_SCHEDULE::FRS_STAGGER_BY(', CLOSE => ');',
  68. ARGS => ['SCHED', 'ADJUST'],
  69. },
  70. FRS_SET => {
  71. CALL => 0, NSETS => 0,
  72. INTRO => '@{ FRSSUP::FRS_SET(', CLOSE => ') }',
  73. ARGS => ['SET'],
  74. },
  75. FRS_SHOW => {
  76. CALL => 0, NSETS => 0,
  77. INTRO => 'FRSSUP::FRS_SHOW(', CLOSE => ');',
  78. ARGS => ['SET'],
  79. },
  80. );
  81. my %FRS_ARGS = (
  82. ADJUST => { TYPE => 'VALUE_INT' },
  83. COMPUTER => { TYPE => 'VALUE_STR' },
  84. DIR_FILTER => { TYPE => 'VALUE_STR' },
  85. DISABLE => { TYPE => 'VALUE_TIME_LIST' },
  86. DN => { TYPE => 'VALUE_STR' },
  87. ENABLED => { TYPE => 'VALUE_INT' },
  88. FILE_FILTER => { TYPE => 'VALUE_STR' },
  89. FLAGS => { TYPE => 'VALUE_INT' },
  90. FROM => { TYPE => 'SET_ELEMENT' },
  91. METHOD => { TYPE => 'VALUE_CHOICE_SINGLE',
  92. CHOICES => ['REPEAT', 'CUSTOM'] },
  93. NAME => { TYPE => 'VALUE_STR' },
  94. ONAME => { TYPE => 'VALUE_STR' },
  95. OPTIONS => { TYPE => 'VALUE_CHOICE_LIST', CHOICES => [] },
  96. OVERRIDE => { TYPE => 'VALUE_TIME_LIST' },
  97. REPL_DURATION => { TYPE => 'VALUE_TIME_SINGLE' },
  98. REPL_INTERVAL => { TYPE => 'VALUE_TIME_SINGLE' },
  99. REPL_OFFSET => { TYPE => 'VALUE_TIME_SINGLE' },
  100. RP => { TYPE => 'VALUE_STR' },
  101. WORKPATH => { TYPE => 'VALUE_STR' },
  102. SCHED => { TYPE => 'SCHEDULE' },
  103. SET => { TYPE => 'SET_REF_SET' },
  104. SP => { TYPE => 'VALUE_STR' },
  105. SERVER => { TYPE => 'SET_ELEMENT' },
  106. STAGGER => { TYPE => 'VALUE_TIME_SINGLE' },
  107. TIME_ZONE => { TYPE => 'VALUE_SIGN_TIME' },
  108. TYPE => { TYPE => 'VALUE_CHOICE_SINGLE',
  109. CHOICES => ['', 'SYSVOL', 'DFS', 'OTHER'] },
  110. UNDER => { TYPE => 'SET_ELEMENT' },
  111. TO => { TYPE => 'SET_ELEMENT' },
  112. PRIMARY_MEMBER => { TYPE => 'SET_ELEMENT' },
  113. MAKE_PRIMARY_MEMBER => { TYPE => 'VALUE_BOOL' },
  114. );
  115. my %FRS_ARG_TYPES = (
  116. SET_REF_SINGLE => { INTRO => '&FRSSUP::SelectSet("', CLOSE => '")->[0]' },
  117. #SET_REF_SET => { INTRO => '&FRSSUP::SelectSet("', CLOSE => '")' },
  118. SET_REF_SET => { INTRO => '', CLOSE => '' },
  119. #SET_ELEMENT => { INTRO => '&FRSSUP::SelectSet("', CLOSE => '")->' },
  120. SET_ELEMENT => { INTRO => '', CLOSE => '' },
  121. #SCHEDULE => { INTRO => '&FRSSUP::SelectSet("', CLOSE => '")->' },
  122. SCHEDULE => { INTRO => '', CLOSE => '' },
  123. ARG_REF => { INTRO => 'XXX', CLOSE => 'TBD' },
  124. VALUE_INT => { INTRO => 'XXX', CLOSE => '' },
  125. VALUE_CHOICE_SINGLE => { INTRO => 'XXX', CLOSE => '' },
  126. VALUE_CHOICE_LIST => { INTRO => 'XXX', CLOSE => '' },
  127. VALUE_STR => { INTRO => '', CLOSE => '' },
  128. VALUE_BOOL => { INTRO => '', CLOSE => '' },
  129. VALUE_SIGN_TIME => { INTRO => '', CLOSE => '' },
  130. VALUE_TIME_SINGLE => { INTRO => '', CLOSE => '' },
  131. VALUE_TIME_LIST => { INTRO => '', CLOSE => '' },
  132. VARCON => { INTRO => '', CLOSE => '' },
  133. );
  134. #
  135. #
  136. my @SubDefCleanup;
  137. $SubDefActive = 0;
  138. $SubList = '';
  139. sub Trim {
  140. my @str = @_;
  141. for (@str) {s/^\s+//; s/\s+$//;}
  142. return wantarray ? @str : $str[0];
  143. }
  144. sub ParseArgList {
  145. my $record = shift;
  146. my @result = ();
  147. #
  148. # return an array of parsed parameters of the form lhs=rhs seperated by "/"
  149. # backslash can be used as an escape character and quoted strings are skipped
  150. # (i.e. they are not matched for a "/" separator)
  151. #
  152. # The examples below show the input string (with leading "/" removed)
  153. # followed by a string with ":" separating each returned result.
  154. #
  155. # RP=foo /R\P="D:\RSB" /SP="D:\staging" /COMPUTER="bchb/hubsite/servers/" /NAME="bchb.hubsite.ajax.com" /XXX
  156. # RP=foo :R\P="D:\RSB" :SP="D:\staging" :COMPUTER="bchb/hubsite/servers/" :NAME="bchb.hubsite.ajax.com" :XXX
  157. #
  158. # COMPUTER = "bchb/hubsite/servers/" /NAME="bchb.hubsite.ajax.com" /XXX
  159. # COMPUTER = "bchb/hubsite/servers/" :NAME="bchb.hubsite.ajax.com" :XXX
  160. #
  161. # YYY /"$ff.CO\MPUTER" = "bchb/hub"xx"site/ser""vers/" /NAME="bchb.hubsite.ajax.com" /XXX
  162. # YYY :"$ff.CO\MPUTER" = "bchb/hub"xx"site/ser""vers/" :NAME="bchb.hubsite.ajax.com" :XXX
  163. #
  164. # CO\MPUTER = bchb\/hub"xx"site\/servers\//NAME="bchb.hubsite.ajax.com" /XXX
  165. # CO\MPUTER = bchb\/hub"xx"site\/servers\/:NAME="bchb.hubsite.ajax.com" :XXX
  166. #
  167. # CO\MPUTER = bchb\/hub"xx"site\/servers\/////NAME="bchb.hubsite.ajax.com /XXX
  168. # CO\MPUTER = bchb\/hub"xx"site\/servers\/::::NAME="bchb.hubsite.ajax.com /XXX
  169. #
  170. $record =~ s:^(\s*/)*::; # remove leading white space and leading "/"
  171. $record =~ s:(/\s*)*$::; # remove trailing slashes followed by white space
  172. while ( $record =~ m{
  173. \s* # skip leading whitespace
  174. ( # Start of captured result
  175. (?:[^\"\\/]* # swallow chars up to " or \ or /
  176. (?: # followed by 3 alternatives
  177. (?=/|$) # 1. positive lookahead assertion for / or eos ends match
  178. |(?:\\.) # 2. if backslash, swallow it + next char
  179. |(?:\" # 3. if leading quote then find end of quoted
  180. # string but respect backslash escape char.
  181. (?:[^\"\\]* # swallow up to next " or \ if any
  182. (?:\\. # if prev ended on \ then swallow it + next char
  183. [^\"\\]* # continue to next quote or \, if any
  184. )* # loop if we hit \
  185. )
  186. \"? # consume trailing quote, if any. could be eos
  187. )
  188. ) # end of 3 alternatives
  189. )+ # continue after quoted string or \
  190. ) /? # end match with next / (if any) ends captured result
  191. | ([^/]+) /? # no quotes up to next /, if any, or eos
  192. | / # eat extra slash
  193. }gx ) {
  194. print "## pre:'$`' match:'$+' post:'$'' lastparen:'$+' \n" if $DEBUG_PARSE;
  195. # print " match:'$+' \t\t lastparen:'$+' \n";
  196. push (@result, Trim($+)) if (Trim($+) ne '');
  197. }
  198. # push (@result, undef) if substr($record,-1,1) eq '/';
  199. return @result;
  200. }
  201. sub CleanInput {
  202. my $record = shift;
  203. #
  204. # look for a comment symbol '#' that is not part of a quoted string and not
  205. # escaped with a backslash.
  206. # return the string up to the comment.
  207. #
  208. if (!($record =~ m/\#/)) {
  209. return $record;
  210. }
  211. while ( $record =~ m{
  212. ( # Start of captured result
  213. (?:[^\"\\\=]* # swallow chars up to " or \ or #
  214. (?: # followed by 3 alternatives
  215. (?=\#) # 1. positive lookahead assertion for # ends match
  216. |(?:\\.) # 2. if backslash, swallow it + next char
  217. |(?:\" # 3. if leading quote then find end of quoted
  218. # string but respect backslash escape char.
  219. (?:[^\"\\]* # swallow up to next " or \ if any
  220. (?:\\. # if prev ended on \ then swallow it + next char
  221. [^\"\\]* # continue to next quote or \, if any
  222. )* # loop if we hit \
  223. )
  224. \"? # consume trailing quote, if any. could be eos
  225. )
  226. ) # end of 3 alternatives
  227. )+ # continue after quoted string or \
  228. ) \#? # end match with next # (if any) ends captured result
  229. | ([^\#]+) /? # no quotes up to next #, if any, or eos
  230. | \# # eat extra #
  231. }x ) { # no g since we only want first non-quoted #
  232. print "## pre:'$`' match:'$+' post:'$'' lastparen:'$+' \n" if $DEBUG_PARSE;
  233. # print "Switch match:'$+' \t\t lastparen:'$+' \n";
  234. return $+;
  235. }
  236. return $record;
  237. }
  238. sub FindBalParens {
  239. my $record = shift;
  240. my ($pre, $match, $result);
  241. my $count = 0;
  242. my $post = '';
  243. my $trailbs = '';
  244. my $leftparfound = 0;
  245. #
  246. # Look for the first expression with balanced parens.
  247. # Parens that are part of a quoted string or escaped with a backslash are skipped.
  248. #
  249. # The return value is a 3 element array:
  250. # [0] n=-1 means extra right parens found.
  251. # n=0 means a balanced expr found or no parens at all.
  252. # n>0 means n extra Left Parens found.
  253. # [1] contains the consumed part of the input string if [0] <= 0 above.
  254. # otherwise it contains the entire input string
  255. # [2] contains the remaining part of the string if [0] <= 0 above
  256. # otherwise it is the null string except for the no parens found case.
  257. #
  258. # In the case of no parens found outside of quoted string or escaped chars
  259. # [0] is zero, [1] is the input string, [2] = "FRS-NO-PARENS".
  260. #
  261. #print $record, "\n";
  262. if (!($record =~ m/[\(\)]/)) {
  263. print "## 0, Found: $record Rest: FRS-NO-PARENS\n" if $DEBUG_PARSE;
  264. return (0, $record, "FRS-NO-PARENS"); # return 0 if no parens found.
  265. }
  266. if ($record =~ m/\\+$/) {
  267. ($trailbs) = $record =~ m/(\\+$)/; # strip trailing \ so they don't foul marker
  268. $record =~ s/(\\+$)//;
  269. }
  270. $record .= '(*)'; # append marker
  271. while ( $record =~ m{
  272. ( # Start of captured result
  273. (?:[^\"\\\(\)]* # swallow chars up to " or \ or ( or )
  274. (?: # followed by 4 alternatives
  275. (?=\() # 1. positive lookahead assertion for ( ends match
  276. |(?=\)) # 2. positive lookahead assertion for ) ends match
  277. |(?:\\.) # 3. if backslash, swallow it + next char
  278. |(?:\" # 4. if leading quote then find end of quoted
  279. # string but respect backslash escape char.
  280. (?:[^\"\\]* # swallow up to next " or \ if any
  281. (?:\\. # if prev ended on \ then swallow it + next char
  282. [^\"\\]* # continue to next quote or \, if any
  283. )* # loop if we hit \
  284. )
  285. \"? # consume trailing quote, if any. could be eos
  286. )
  287. ) # end of 4 alternatives
  288. )+ # continue after quoted string or \
  289. [\(\)]? # end match with next ( or ) (if any) ends captured result
  290. | (?:[^\(]+) /? # no quotes up to next (, if any, or eos
  291. | \( # eat extra (
  292. )
  293. }gx ) {
  294. $pre = $`; $match = $+; $post = $';
  295. #
  296. # if the marker "(*)" is consumed in the match then we must be
  297. # in the middle of a quoted string so leave count unchanged.
  298. # Otherwise the marker would have been split.
  299. #
  300. if (substr($+,-3,3) ne '(*)') {
  301. if (substr($+,-1,1) eq ')') {$count--;}
  302. if (substr($+,-1,1) eq '(') {$count++;}
  303. #
  304. # remember finding a left paren if count > 0 and it wasn't
  305. # caused by a split marker.
  306. #
  307. if (($count > 0) && ($post ne '*)')) {$leftparfound = 1;}
  308. }
  309. print "## ($count) paren match:'$+'\n" if $DEBUG_PARSE;
  310. #
  311. # if the count hits zero then return balanced part.
  312. # if the count goes negative then we've seen more right parens than left parens
  313. #
  314. if ($count <= 0) {goto RETURN;}
  315. }
  316. RETURN:
  317. #
  318. # Clean off the marker.
  319. #
  320. $result = $pre . $match;
  321. if ($post =~ m/\(\*\)$/) {
  322. substr($post, -3, 3) = '';
  323. } else {
  324. if ($post eq '*)') { # check for a split marker.
  325. $post = '';
  326. substr($result, -1, 1) = '';
  327. } else {
  328. $result =~ s/\(\*\)$//;
  329. }
  330. }
  331. #
  332. # add back trailing backslashes
  333. #
  334. if ($post ne "") {
  335. $post .= $trailbs;
  336. } else {
  337. $result .= $trailbs;
  338. #
  339. # The entire string was consumed so if the Count is zero
  340. # check if we ever found an unquoted left paren. If not
  341. # then return "FRS-NO-PARENS" as the result in [2].
  342. #
  343. if (($count == 0) && ($leftparfound == 0)) {$post = "FRS-NO-PARENS";}
  344. }
  345. print "## $count, Found: $result Rest: $post \n" if $DEBUG_PARSE;
  346. return ($count, $result , $post );
  347. }
  348. sub FindBalBrace {
  349. my $record = shift;
  350. my ($pre, $match, $result);
  351. my $count = 0;
  352. my $post = '';
  353. my $trailbs = '';
  354. my $leftparfound = 0;
  355. #
  356. # Look for the first expression with balanced braces.
  357. # bracess that are part of a quoted string or escaped with a backslash are skipped.
  358. #
  359. # The return value is a 3 element array:
  360. # [0] n=-1 means extra right brace found.
  361. # n=0 means a balanced expr found or no braces at all.
  362. # n>0 means n extra Left bracess found.
  363. # [1] contains the consumed part of the input string if [0] <= 0 above.
  364. # otherwise it contains the entire input string
  365. # [2] contains the remaining part of the string if [0] <= 0 above
  366. # otherwise it is the null string except for the no parens found case.
  367. #
  368. # In the case of no parens found outside of quoted string or escaped chars
  369. # [0] is zero, [1] is the input string, [2] = "FRS-NO-BRACES".
  370. #
  371. #print $record, "\n";
  372. if (!($record =~ m/[\{\}]/)) {
  373. print "## 0, Found: $record Rest: FRS-NO-BRACES\n" if $DEBUG_PARSE;
  374. return (0, $record, "FRS-NO-BRACES"); # return 0 if no parens found.
  375. }
  376. if ($record =~ m/\\+$/) {
  377. ($trailbs) = $record =~ m/(\\+$)/; # strip trailing \ so they don't foul marker
  378. $record =~ s/(\\+$)//;
  379. }
  380. $record .= '{*}'; # append marker
  381. while ( $record =~ m&
  382. ( # Start of captured result
  383. (?:[^\"\\\{\}]* # swallow chars up to " or \ or { or }
  384. (?: # followed by 4 alternatives
  385. (?=\{) # 1. positive lookahead assertion for { ends match
  386. |(?=\}) # 2. positive lookahead assertion for } ends match
  387. |(?:\\.) # 3. if backslash, swallow it + next char
  388. |(?:\" # 4. if leading quote then find end of quoted
  389. # string but respect backslash escape char.
  390. (?:[^\"\\]* # swallow up to next " or \ if any
  391. (?:\\. # if prev ended on \ then swallow it + next char
  392. [^\"\\]* # continue to next quote or \, if any
  393. )* # loop if we hit \
  394. )
  395. \"? # consume trailing quote, if any. could be eos
  396. )
  397. ) # end of 4 alternatives
  398. )+ # continue after quoted string or \
  399. [\{\}]? # end match with next { or } (if any) ends captured result
  400. | (?:[^\{]+) /? # no quotes up to next {, if any, or eos
  401. | \{ # eat extra {
  402. )
  403. &gx ) {
  404. $pre = $`; $match = $+; $post = $';
  405. #
  406. # if the marker "(*)" is consumed in the match then we must be
  407. # in the middle of a quoted string so leave count unchanged.
  408. # Otherwise the marker would have been split.
  409. #
  410. if (substr($+,-3,3) ne '{*}') {
  411. if (substr($+,-1,1) eq '}') {$count--;}
  412. if (substr($+,-1,1) eq '{') {$count++;}
  413. #
  414. # remember finding a left paren if count > 0 and it wasn't
  415. # caused by a split marker.
  416. #
  417. if (($count > 0) && ($post ne '*}')) {$leftparfound = 1;}
  418. }
  419. print "## ($count) paren match:'$+'\n" if $DEBUG_PARSE;
  420. #
  421. # if the count hits zero then return balanced part.
  422. # if the count goes negative then we've seen more right parens than left parens
  423. #
  424. if ($count <= 0) {goto RETURN;}
  425. }
  426. RETURN:
  427. #
  428. # Clean off the marker.
  429. #
  430. $result = $pre . $match;
  431. if ($post =~ m/\{\*\}$/) {
  432. substr($post, -3, 3) = '';
  433. } else {
  434. if ($post eq '*}') { # check for a split marker.
  435. $post = '';
  436. substr($result, -1, 1) = '';
  437. } else {
  438. $result =~ s/\{\*\}$//;
  439. }
  440. }
  441. #
  442. # add back trailing backslashes
  443. #
  444. if ($post ne "") {
  445. $post .= $trailbs;
  446. } else {
  447. $result .= $trailbs;
  448. #
  449. # The entire string was consumed so if the Count is zero
  450. # check if we ever found an unquoted left paren. If not
  451. # then return "FRS-NO-PARENS" as the result in [2].
  452. #
  453. if (($count eq 0) && ($leftparfound eq 0)) {$post = "FRS-NO-BRACES";}
  454. }
  455. print "## $count, Found: $result Rest: $post \n" if $DEBUG_PARSE;
  456. return ($count, $result , $post );
  457. }
  458. sub ExtractParams {
  459. my $rest = shift;
  460. #
  461. # Starting with the input string find the next paren balanced expression
  462. # in the input stream, consuming more input as needed.
  463. # Returns a two element array. [0] balanced paren text, [1] the rest of the
  464. # last input line read.
  465. #
  466. my (@BalParen, $ParamStr);
  467. @BalParen = FindBalParens($rest);
  468. while (($BalParen[0] gt 0) || ($rest eq "FRS-NO-PARENS")) {
  469. if (!($_ = <F>)) {
  470. EmitError($_, "EOF hit looking for ')'");
  471. exit;
  472. }
  473. chop;
  474. if (m/\#/) {$_ = CleanInput($_)}; # remove trailing comment string.
  475. s/^\s+//; # remove leading & trailing white space
  476. s/\s+$//;
  477. next if ($_ eq '');
  478. &EmitComment ($_);
  479. #
  480. # If we are in a DEFSUB then scan for calling params replacement strings.
  481. # If found then insert a ref to __args hash.
  482. #
  483. if ($SubDefActive) {
  484. if ( s/\%(\w+)\%/\$__args\{$1\}/gx ) {
  485. print "## ExpandArgStr: $_\n" if $DEBUG_EXPAND;
  486. }
  487. }
  488. #
  489. # replace <foo> set refs with a lookup.
  490. #
  491. &ExpandSetRef();
  492. $rest = $rest . " " . $_;
  493. @BalParen = FindBalParens($rest);
  494. }
  495. if ($BalParen[0] < 0) {
  496. EmitError($_, "Unbalanced right paren");
  497. return ("", $rest);
  498. }
  499. $ParamStr = $BalParen[1];
  500. $rest = $BalParen[2];
  501. $ParamStr =~ s/^\s*\(\s*\/*//; # remove " ( /"
  502. $ParamStr =~ s/\s*\)\s*$//; # remove " ) "
  503. return ($ParamStr, $rest);
  504. }
  505. sub EmitCode {
  506. my $ArgStr;
  507. foreach $ArgStr (@_) {
  508. print $ArgStr;
  509. }
  510. }
  511. sub EmitComment {
  512. my $ArgStr;
  513. return if !$DEBUG_CODE;
  514. foreach $ArgStr (@_) {
  515. print "## ", $ArgStr, "\n";
  516. }
  517. }
  518. sub EmitError {
  519. my $input = shift;
  520. my $msg = shift;
  521. print STDOUT "ERROR: $main::InFile($main::inlinenumber) - $msg '", $input, "'\n";
  522. print STDERR "ERROR: $main::InFile($main::inlinenumber) - $msg '", $input, "'\n";
  523. }
  524. sub CompileFrsObject {
  525. my $func = shift;
  526. my ($lhs, $rest, @pars, $ParamStr, $p, $expansion, $switch, $rhs, @setnames, $argtype);
  527. #
  528. # Consume input until we have text with balanced parens.
  529. #
  530. ($lhs, $rest) = m/(.*)$func\s*(.*)/;
  531. ($ParamStr, $rest) = ExtractParams($rest);
  532. if ($ParamStr eq "") {
  533. EmitError("FRS object, no parameters found near: '$func' ");
  534. exit;
  535. }
  536. print "## '$ParamStr' \n" if $DEBUG_PARSE;
  537. if ($FRS_CALLS{$func}->{NSETS} != 0) {
  538. $expansion = '$__HashRef = ' . $FRS_CALLS{$func}->{INTRO};
  539. EmitCode " $expansion \n";
  540. } else {
  541. EmitCode " $FRS_CALLS{$func}->{INTRO} \n";
  542. }
  543. @pars = &ParseArgList($ParamStr);
  544. print "\n\n", "## ", join(':', (@pars)), "\n\n" if $DEBUG_EXPAND;
  545. $expansion = '';
  546. foreach $p (@pars) {
  547. $expansion .= &ExpandSwitch($func, $p) . ", ";
  548. }
  549. #
  550. # Check for inline call and remove the keys.
  551. # **NOTE** if any inline func takes more than 1 arg then add code
  552. # to place the args in the order specified by $FRS_CALLS{$func}->{ARGS}
  553. # using the arg name in the key part.
  554. #
  555. if ($FRS_CALLS{$func}->{CALL} == 2) {
  556. $expansion =~ s/\w+=>//g; #for now just remove key part.
  557. }
  558. substr($expansion, -2, 1) = ''; # remove trailing comma-space
  559. EmitCode " ".$expansion ;
  560. EmitCode "\n $FRS_CALLS{$func}->{CLOSE} \n";
  561. if ($FRS_CALLS{$func}->{NSETS} != 0) {
  562. $lhs = &Trim($lhs);
  563. $lhs =~ s/\s*:\s*/ /g;
  564. @setnames = split('\s', $lhs);
  565. foreach $p (@setnames) {
  566. #
  567. # Add set def and remember for cleanup if we are inside a SubDef.
  568. #
  569. EmitCode ' &FRSSUP::AddToSet("', $p, '", $__HashRef);', "\n";
  570. if ($SubDefActive) {push @SubDefCleanup, $p;}
  571. }
  572. }
  573. EmitCode "\n";
  574. }
  575. sub CompileFrsSubDef {
  576. my $func = shift;
  577. my ($SubName, $rest, @pars, $p, $expansion, $switch, $rhs, @setnames, $argtype);
  578. my (@SubBody, @BalBrace, $ParamStr);
  579. #
  580. # Consume input until we have text with balanced parens.
  581. #
  582. ($SubName, $rest) = m/(?:.*?)(?:$func)+? \s* (\w+?) \s* (\(.*)/x;
  583. EmitComment( "subname = '$SubName', func = '$func', rest = '$rest'\n");
  584. if (exists $FRS_CALLS{$SubName}) {
  585. EmitError("FRS_SUB subroutine name: '$SubName' ",
  586. "Conflict with builtin or previously defined name.");
  587. exit;
  588. }
  589. ($ParamStr, $rest) = ExtractParams($rest);
  590. if ($ParamStr eq "") {
  591. EmitError("FRS_SUB no parameters found near: '$SubName' ");
  592. exit;
  593. }
  594. #
  595. # Add the function name to the call table.
  596. #
  597. $FRS_CALLS{$SubName} = { CALL=>1, NSETS=>0,
  598. INTRO=>"&$SubName (",
  599. CLOSE=>');',
  600. ARGS=>[], BODY=>[] };
  601. EmitCode("sub $SubName {\n");
  602. EmitCode(' my %__args = (@_);' . "\n");
  603. EmitCode(' my ($__HashRef, $__k, $__v);' . "\n");
  604. if ($DEBUG_CHECK) {
  605. EmitCode(' print "##\n";' . "\n");
  606. EmitCode(' print "## Entering sub ' . $SubName . '\n";' . "\n");
  607. EmitCode(' while ( ($__k, $__v) = each %__args ) { print "## \t$__k => \'$__v\'\n"; }' . "\n");
  608. }
  609. EmitCode("\n\n");
  610. #EmitComment( " paramstr: $ParamStr\n");
  611. @pars = &ParseArgList($ParamStr);
  612. #EmitComment( join(':', (@pars)) . "\n\n");
  613. $expansion = '';
  614. foreach $p (@pars) {
  615. ($switch, $rhs) = FRSSUP::ParseSwitch($p);
  616. if ((!defined($rhs)) || (!exists $FRS_ARG_TYPES{$rhs})) {
  617. EmitError("FRS_SUB parameter: '$p' ",
  618. "Right hand side must have valid type code in FRS_SUB declaration");
  619. }
  620. if (exists $FRS_ARGS{$SubName."-".$switch}) {
  621. EmitError("FRS_SUB parameter: '$SubName-$switch' ",
  622. "Conflict with builtin or previously defined name.");
  623. exit;
  624. }
  625. #
  626. # Add this parameter to the argument table.
  627. #
  628. $FRS_ARGS{$SubName."-".$switch} = { TYPE => "$rhs" };
  629. #EmitComment("new FRS_SUB parameter: '$SubName-$switch' ");
  630. push @{ $FRS_CALLS{$SubName}{ARGS} }, $switch;
  631. }
  632. if ($SubList ne '') {$SubList .= "|";}
  633. $SubList .= $SubName;
  634. #EmitComment( "new sublist = '$SubList'\n");
  635. $_ = $rest;
  636. EmitCode "\n";
  637. return 1; #we are now compiling in a subdef.
  638. }
  639. sub CompileFrsEndSubDef {
  640. my $func = shift;
  641. my ($p);
  642. #
  643. # Consume input up through FRS_END_SUB
  644. #
  645. s/(?:.*?)(?:$func)+? \s*//x;
  646. #
  647. # Emit code to free the locally defined sets.
  648. #
  649. foreach $p (@SubDefCleanup) {
  650. EmitCode(' &FRSSUP::DeleteSet("' . $p . '");'. "\n");
  651. }
  652. EmitCode ("} # FRS_END_SUB\n\n\n");
  653. undef @SubDefCleanup;
  654. }
  655. sub CompileFrsFunctionCall {
  656. my $func = shift;
  657. my ($lhs, $rest, @pars, $p, $ParamStr, $expansion, $switch, $rhs, @setnames, $argtype);
  658. #
  659. # Consume input until we have text with balanced parens.
  660. #
  661. ($lhs, $rest) = m/(.*?)(?:$func)+?\s*(.*)/x;
  662. #print "\n lhs = '$lhs', func = '$func', rest = '$rest'\n";
  663. ($ParamStr, $rest) = ExtractParams($rest);
  664. if ($ParamStr eq "") {
  665. EmitError("FRS_FUNC no parameters found near: '$func' ");
  666. exit;
  667. }
  668. print "## '$ParamStr' \n" if $DEBUG_PARSE;
  669. if ($FRS_CALLS{$func}->{NSETS} ne 0) {
  670. EmitError($func, "Internal error - Function {NSETS} ne 0");
  671. exit;
  672. } else {
  673. EmitCode "$lhs $FRS_CALLS{$func}->{INTRO} \n";
  674. }
  675. @pars = &ParseArgList($ParamStr);
  676. print "\n\n", "## ", join(':', (@pars)), "\n\n" if $DEBUG_EXPAND;
  677. $expansion = '';
  678. foreach $p (@pars) {
  679. $expansion .= &ExpandSwitch($func, $p) . ", ";
  680. }
  681. #
  682. # Check for inline call and remove the keys.
  683. # **NOTE** if any inline func takes more than 1 arg then add code
  684. # to place the args in the order specified by $FRS_CALLS{$func}->{ARGS}
  685. # using the arg name in the key part.
  686. #
  687. if ($FRS_CALLS{$func}->{CALL} == 2) {
  688. $expansion =~ s/\w+=>//g; #for now just remove key part.
  689. }
  690. substr($expansion, -2, 1) = ''; # remove trailing comma-space
  691. EmitCode $expansion ;
  692. EmitCode "$FRS_CALLS{$func}->{CLOSE} \n";
  693. EmitCode "\n";
  694. $_ = $rest;
  695. }
  696. sub ExpandSetRef {
  697. #
  698. # IMPROVEMENT: Don't apply inside "".
  699. # works on $_
  700. #
  701. # First convert <foo>[x] to <foo>->[x]
  702. #
  703. if ( s/(\w) > \s* \[/$1>->[/gx ) {
  704. print "## ExpandSetRefIndex: $_\n" if $DEBUG_EXPAND;
  705. }
  706. #
  707. # replace set refs with a lookup.
  708. # scan for <foo> or <$foo> or <"foo"> or <'foo'>
  709. # No embedded whitespace allowed.
  710. # replace with {&FRSSUP::SelectSet($arg)}
  711. #
  712. if ( s/< ([\$\"\']?? \w+) >/&FRSSUP::SelectSet\($1\)/gx ) {
  713. print "## ExpandSetRef: $_\n" if $DEBUG_EXPAND;
  714. }
  715. }
  716. sub ExpandSwitch {
  717. my $SubName = shift;
  718. my $input = shift;
  719. my ($switch, $rhs, $argtype, $ArgIndex, $indexpart, $setpart);
  720. ## my $FoundFormalPar;
  721. my $result = '';
  722. #
  723. # Process the argument string "switch=rhs" based on the argtype def.
  724. #
  725. ($switch, $rhs) = FRSSUP::ParseSwitch($input);
  726. if (($main::SubList ne '') && ($SubName =~ m/$main::SubList/)) {
  727. $ArgIndex = $SubName."-".$switch; # The arg index for a user defined function.
  728. EmitComment("ArgIndex for parameter: '$ArgIndex' ") if $DEBUG_EXPAND;
  729. } else {
  730. $ArgIndex = $switch; # The arg index for a builtin function.
  731. EmitComment("ArgIndex for parameter: '$switch' ") if $DEBUG_EXPAND;
  732. }
  733. #
  734. # Get the switch argument type. default to a string.
  735. #
  736. if (exists $FRS_ARGS{$ArgIndex}) {
  737. $argtype = $FRS_ARGS{$ArgIndex}->{TYPE};
  738. EmitComment("ArgType found for parameter: '$ArgIndex' is '$argtype'") if $DEBUG;
  739. } else {
  740. $argtype = 'VALUE_STR';
  741. }
  742. #
  743. # SET_REF_SINGLE
  744. # SET_REF_SET
  745. # SET_ELEMENT
  746. # SCHEDULE
  747. # ARG_REF
  748. # VALUE_INT
  749. # VALUE_CHOICE_SINGLE
  750. # VALUE_CHOICE_LIST
  751. # VALUE_STR
  752. # VALUE_SIGN_TIME
  753. # VALUE_TIME_SINGLE
  754. # VALUE_TIME_LIST
  755. # VARCON
  756. #
  757. $result = $rhs;
  758. if (($argtype eq 'SET_ELEMENT') || ($argtype eq 'SCHEDULE')) {
  759. #
  760. # /ARG=<HUB> maps to /ARG=&SelectSet("HUB")->[0]
  761. # /ARG=<HUB>[expr] maps to &SelectSet("HUB")->[expr]
  762. # anything else is unchanged.
  763. #
  764. ### goto RETURN if ($FoundFormalPar);
  765. goto RETURN if (($argtype eq 'SCHEDULE') && ($rhs =~ m/\^s*(ON|OFF)\s*$/i));
  766. if ($rhs =~ m/SelectSet\(.*\)$/) { $rhs .= '->[0]'; }
  767. $result = $rhs;
  768. # ($setpart, $indexpart) = $rhs =~ m/([^\[]*) (.*)/x ;
  769. # $result = $FRS_ARG_TYPES{$argtype}->{INTRO} .
  770. # $setpart .
  771. # $FRS_ARG_TYPES{$argtype}->{CLOSE} .
  772. # $indexpart;
  773. goto RETURN;
  774. } elsif ($argtype eq 'SET_REF_SET') {
  775. ## goto RETURN if ($FoundFormalPar);
  776. $result = $FRS_ARG_TYPES{$argtype}->{INTRO} .
  777. $rhs .
  778. $FRS_ARG_TYPES{$argtype}->{CLOSE};
  779. goto RETURN;
  780. } elsif ($argtype eq 'SET_REF_SINGLE') {
  781. } elsif ($argtype eq 'ARG_REF') {
  782. } elsif ($argtype eq 'VALUE_CHOICE_SINGLE') {
  783. } elsif ($argtype eq 'VALUE_CHOICE_LIST') {
  784. } elsif (($argtype eq 'VALUE_STR') ||
  785. ($argtype eq 'VARCON')) {
  786. #
  787. # The following is a bad idea.
  788. # Consider "D:\"
  789. # When searching for balanced parens the backslash escapes the dbl-quote
  790. # so the trailing paren ends up as part of a quoted string.
  791. # if you double up the backslash then the line below will give you
  792. # some more. So the upshot is make the user double the backslash.
  793. #
  794. #$rhs =~ s/\\(?=[^\\])/\\\\/g; # double up the backslash
  795. $result = $FRS_ARG_TYPES{$argtype}->{INTRO} .
  796. $rhs .
  797. $FRS_ARG_TYPES{$argtype}->{CLOSE};
  798. goto RETURN;
  799. } elsif (($argtype eq 'VALUE_INT') ||
  800. ($argtype eq 'VALUE_SIGN_TIME') ||
  801. ($argtype eq 'VALUE_TIME_SINGLE') ||
  802. ($argtype eq 'VALUE_TIME_LIST')) {
  803. $result = $FRS_ARG_TYPES{$argtype}->{INTRO} .
  804. $rhs .
  805. $FRS_ARG_TYPES{$argtype}->{CLOSE};
  806. goto RETURN;
  807. } elsif ($argtype eq 'VALUE_BOOL') {
  808. $result = 'TRUE';
  809. $rhs = 'TRUE';
  810. goto RETURN;
  811. } else {
  812. &EmitError("ExpandSwitch('$SubName','$input')", "Unexpected internal error");
  813. }
  814. RETURN:
  815. EmitComment(" '$switch' = '$rhs' arg_typ: $argtype result: $result");
  816. return $switch . "=>" . $result;
  817. }
  818. sub ProcessFile {
  819. my ($modtime, $func);
  820. my ($newfile, $evalstr);
  821. local *F;
  822. local *inlinenumber;
  823. local *InFile;
  824. ($InFile) = @_;
  825. open(F, $InFile) || die "Can't open input file: $InFile\n";
  826. $modtime = (stat $InFile)[9];
  827. EmitComment("Processing file $InFile Modify Time: " . scalar localtime($modtime) . "\n\n");
  828. $infilelist = $infilelist . " " . $InFile;
  829. $inlinenumber = 0;
  830. while (<F>) {
  831. $inlinenumber++;
  832. chop;
  833. LOOP:
  834. next if (m/^\s*$|^#/); # remove blank lines and lines starting with #
  835. if (m/\#/) {$_ = CleanInput($_)}; # remove trailing comment string.
  836. next if (m/^\s*$/);
  837. &EmitComment ($_);
  838. #
  839. # Check for an include directive.
  840. # The parameter value gets EVALed.
  841. #
  842. # .FRS_INCLUDE ($CMD_VARS{"SERVERS"})
  843. # This lets you pass the file name on cmd line with -DSERVERS=filename
  844. #
  845. # .FRS_INCLUDE ("genbchoff.srv")
  846. # This lets you include a specific file.
  847. #
  848. if (m/\.FRS_INCLUDE/) {
  849. ($evalstr) = m/\.FRS_INCLUDE\s*\(\s*(.+)\s*\)$/;
  850. $newfile = eval $evalstr;
  851. if ($newfile ne "") {ProcessFile($newfile);}
  852. next;
  853. }
  854. #
  855. # .FRS_EVAL( single line perl expresion evaluated at config file compile time )
  856. #
  857. # For example, the following checks for the presence of a required compile time parameter.
  858. #
  859. # .FRS_EVAL (if (!exists $CMD_VARS{"SERVERS"}) {print STDERR "ERROR - Required parameter -DSERVERS=filename not found."; exit} )
  860. #
  861. if (m/\.FRS_EVAL/) {
  862. ($evalstr) = m/\.FRS_EVAL\s*\(\s*(.+)\s*\)$/;
  863. eval $evalstr;
  864. next;
  865. }
  866. #
  867. # If we are in a DEFSUB then scan for calling params replacement strings.
  868. # If found then insert a ref to __args hash.
  869. #
  870. if ($SubDefActive) {
  871. if ( s/\%(\w+)\%/\$__args\{$1\}/gx ) {
  872. print "## ExpandArgStr: $_\n" if $DEBUG_EXPAND;
  873. }
  874. #if ($FoundFormalPar) {
  875. # #
  876. # # look for component dereference "->" and replace with {}
  877. # #
  878. # $rhs =~ s/->(\w+)/->\{$1\}/gx;
  879. #}
  880. }
  881. #
  882. # replace <foo> set refs with a lookup.
  883. #
  884. &ExpandSetRef();
  885. #
  886. # Check for FRS object declaration
  887. #
  888. if (($func) = m/($FrsObjectNames)/xio) {
  889. CompileFrsObject($func);
  890. next;
  891. }
  892. #
  893. # Check for FRS function call
  894. #
  895. while (($func) = m/($FrsFunctionNames)/xio) {
  896. CompileFrsFunctionCall($func);
  897. }
  898. #
  899. # Check for FRS function definition
  900. #
  901. if (($func) = m/(FRS_SUB)/xi) {
  902. if ($SubDefActive) {
  903. EmitError("$_\n", "Recursive SUB DEF not allowed.\n");
  904. exit;
  905. }
  906. #
  907. # build the header and update the symbol table with the arg type
  908. # definitions. Then continue compiling the body.
  909. #
  910. $SubDefActive = CompileFrsSubDef($func);
  911. next if ($_ eq '');
  912. goto LOOP;
  913. }
  914. #
  915. # Check for end of FRS function definition
  916. #
  917. if (($func) = m/(FRS_END_SUB)/xi) {
  918. if (!$SubDefActive) {
  919. EmitError("$_\n", "FRS_END_SUB found while no SUB DEF active.\n");
  920. exit;
  921. }
  922. #
  923. # Generate the cleanup code and end the function.
  924. #
  925. CompileFrsEndSubDef($func);
  926. $SubDefActive = 0;
  927. next if ($_ eq '');
  928. goto LOOP;
  929. }
  930. #
  931. # Check for user defined object declaration
  932. #
  933. if (($main::SubList ne '') && (($func) = m/($main::SubList)/)) {
  934. CompileFrsFunctionCall($func);
  935. next;
  936. }
  937. EmitCode($_."\n");
  938. }
  939. close(F);
  940. }
  941. my $USAGE = "
  942. Usage: $0 [cmd options] infiles... \> output
  943. Process the FRS replica set definition file(s) and generate a perl output
  944. file that when executed creates the desired configuration in the DS.
  945. Command line options must be prefixed with a dash.
  946. -verbose=all : display all debug output.
  947. -verbose=code : display input interspersed with output. Note that the
  948. resulting output file may not run. For debugging.
  949. -verbose=check : Add some check code to print out argument values.
  950. -verbose=parse : display parsing results. For debugging.
  951. -verbose=expand : display variable expansion results. For debugging.
  952. To add a help message to the generated script add a usage string and then
  953. insert the following function call in the input script.
  954. FRSSUP::CheckForHelp(\%CMD_PARS, \\\$usage);
  955. where the usage string might look like:
  956. \$usage = \"
  957. The input options to this script are:
  958. -DBchID=nnnnn : to provide a value for the BchID parameter.
  959. ...\";
  960. To pass the values of command line paramters to your script use the notation
  961. -Dvarname=value on the command line when invoking the generated script.
  962. e.g. perl generated_script.prl -DBchID=0011220
  963. The input script can retrieve the value with the reference \$CMD_VARS{\"varname\"}.
  964. varname is case sensitive. In the example, \$CMD_VARS{\"BchID\"}.
  965. Other command line options can be retrived by the script using
  966. \$CMD_PARS{\"optionname\"} where optionname is in lower case.
  967. ";
  968. my ($k, $v, $k1, $v1, $HashRef, $str, $lhs, $rhs, $filename);
  969. &FRSSUP::ProcessCmdLine(\%CMD_VARS, \%CMD_PARS);
  970. die $USAGE unless @ARGV;
  971. $k = @rem; # here to suppress warning message on @rem.
  972. $argdebug = lc($CMD_PARS{"verbose"});
  973. if ($argdebug ne "") {
  974. if ($argdebug =~ m/code/) {$DEBUG_CODE=1;}
  975. if ($argdebug =~ m/all/) {$DEBUG=1; $DEBUG_PARSE=1; $DEBUG_EXPAND=1; $DEBUG_CHECK=1; $DEBUG_CODE=1;}
  976. if ($argdebug =~ m/check/) {$DEBUG_CHECK=1;}
  977. if ($argdebug =~ m/parse/) {$DEBUG_PARSE=1;}
  978. if ($argdebug =~ m/expand/) {$DEBUG_EXPAND=1;}
  979. if (($DEBUG + $DEBUG_CODE + $DEBUG_CHECK + $DEBUG_PARSE + $DEBUG_EXPAND) == 0) {
  980. print STDERR "Error: Invalid -verbose parameter: $argdebug\n";
  981. die $USAGE;
  982. }
  983. }
  984. $infilelist = '';
  985. $inlinenumber = 0;
  986. $InFile = "";
  987. EmitCode(' use frsobjsup;' . "\n");
  988. EmitCode(' package main; ' . "\n");
  989. EmitCode(' my ($__HashRef, $__k, $__v);' . "\n");
  990. EmitCode(' my (%CMD_VARS, %CMD_PARS);' . "\n\n");
  991. EmitCode(' &FRSSUP::ProcessCmdLine(\%CMD_VARS, \%CMD_PARS);' . "\n\n");
  992. foreach $filename (@ARGV) {
  993. ProcessFile($filename);
  994. }
  995. #while (<>) {
  996. #
  997. # if ($InFile ne $ARGV) {
  998. # $InFile = $ARGV;
  999. # $modtime = (stat $InFile)[9];
  1000. # EmitComment("Processing file $InFile Modify Time: " . scalar localtime($modtime) . "\n\n");
  1001. # $infilelist = $infilelist . " " . $InFile;
  1002. # $inlinenumber = 0;
  1003. # }
  1004. # $inlinenumber++;
  1005. #
  1006. # chop;
  1007. #}
  1008. EmitCode('__END__' . "\n");
  1009. exit;
  1010. #
  1011. # Todo:
  1012. #
  1013. # Implement CHOICES type and checking
  1014. # error check argument names and validate operand types.
  1015. # Add error checking with line numbers $ errors going to stderr.
  1016. # test with malformed input, e.g. missing rhs, missing (, missing ), etc.
  1017. # wrap invocation in an EVAL
  1018. #
  1019. # write func to wrap long lines for comment print.
  1020. # Write help and doc. give some examples of simple perl script commands.
  1021. # Provide runtime option to start the service on any new member.
  1022. #
  1023. # Implement set operations e.g.
  1024. # INSTALLED_BCH: FRS_SET_DIFF(/ARG1=BCH /ARG2=NOTDEPLOYED)
  1025. #
  1026. __END__
  1027. :endofperl
  1028. @rem -d -w
  1029. @perl -w %~dpn0.cmd %*
  1030. @goto :QUIT
  1031. @:QUIT