source: asp3_tinet_ecnl_rx/trunk/asp3_dcre/tecsgen/tecsgen.rb@ 374

Last change on this file since 374 was 374, checked in by coas-nagasima, 5 years ago

mbed関連を更新
シリアルドライバをmbedのHALを使うよう変更
ファイルディスクリプタの処理を更新

  • Property svn:eol-style set to native
  • Property svn:mime-type set to text/x-ruby;charset=UTF-8
File size: 22.1 KB
Line 
1#!/usr/bin/env ruby
2# -*- coding: utf-8 -*-
3#
4# TECS Generator
5# Generator for TOPPERS Embedded Component System
6#
7# Copyright (C) 2008-2019 by TOPPERS Project
8#--
9# 上記著作権者は,以下の(1)~(4)の条件を満たす場合に限り,本ソフトウェ
10# ア(本ソフトウェアを改変したものを含む.以下同じ)を使用・複製・改
11# 変・再配布(以下,利用と呼ぶ)することを無償で許諾する.
12# (1) 本ソフトウェアをソースコードの形で利用する場合には,上記の著作
13# 権表示,この利用条件および下記の無保証規定が,そのままの形でソー
14# スコード中に含まれていること.
15# (2) 本ソフトウェアを,ライブラリ形式など,他のソフトウェア開発に使
16# 用できる形で再配布する場合には,再配布に伴うドキュメント(利用
17# 者マニュアルなど)に,上記の著作権表示,この利用条件および下記
18# の無保証規定を掲載すること.
19# (3) 本ソフトウェアを,機器に組み込むなど,他のソフトウェア開発に使
20# 用できない形で再配布する場合には,次のいずれかの条件を満たすこ
21# と.
22# (a) 再配布に伴うドキュメント(利用者マニュアルなど)に,上記の著
23# 作権表示,この利用条件および下記の無保証規定を掲載すること.
24# (b) 再配布の形態を,別に定める方法によって,TOPPERSプロジェクトに
25# 報告すること.
26# (4) 本ソフトウェアの利用により直接的または間接的に生じるいかなる損
27# 害からも,上記著作権者およびTOPPERSプロジェクトを免責すること.
28# また,本ソフトウェアのユーザまたはエンドユーザからのいかなる理
29# 由に基づく請求からも,上記著作権者およびTOPPERSプロジェクトを
30# 免責すること.
31#
32# 本ソフトウェアは,無保証で提供されているものである.上記著作権者お
33# よびTOPPERSプロジェクトは,本ソフトウェアに関して,特定の使用目的
34# に対する適合性も含めて,いかなる保証も行わない.また,本ソフトウェ
35# アの利用により直接的または間接的に生じたいかなる損害に関しても,そ
36# の責任を負わない.
37#
38# $Id$
39#++
40
41#= tecsgen : TECS のジェネレータ
42#
43#Authors:: 石川 拓也(HRP2Plugin)
44#Authors:: 原  拓(動的結合仕様)
45#Authors:: 河田 智明(NotifierPlugin)
46#Authors:: 高田 広章(ASP3 への TECS 組込み)
47#Authors:: 高木 信尚(ランタイムヘッダ)
48#Authors:: 成瀬 有美(TECS flow 実装)
49#Authors:: 鵜飼 敬幸(ATK1Plugin)
50#Authors:: 大山 博司(ジェネレータ実装, RPCPlugin, TracePlugin, MrubyBridgePlugin)
51#Authors:: 山本 将也(ジェネレータ初期プロトタイプ実装)
52#Authors:: 小南 靖雄(テストコードの一部)
53#Authors:: 安積 卓也(ASP+TECS, EV3RT+TECS, mruby on TECS等実装)
54# Authors list is in i-ro-ha order.
55#Version:: see version.rb
56$Copyright = "Copyright(c) 2008-2019, TOPPERS project. All rights reserved."
57$License = "TOPPERS License"
58
59# This doesn't work as expected in exerb version (Ruby 1.8.7?)
60$tecsgen_base_path = File.dirname( File.expand_path __FILE__ )
61
62
63###
64#= Array initializer の '{', '}' で囲まれた場合
65# mikan AggregateInitializer など、クラスを変更すべきである
66class Array
67 #=== CDL の文字列を生成する
68 def to_CDL_str
69 str = '{ '
70 delim = ''
71 self.each{ |v|
72 str += delim + v.to_CDL_str
73 delim = ', '
74 }
75 str += ' }'
76 return str
77 end
78
79 def show_tree( indent )
80 indent.times{ print " " }
81 print( "Array\n" )
82 self.each{ |m|
83 m.show_tree( indent+1 )
84 }
85 end
86end
87
88#=== RUBY ライブラリをロードする
89#
90# -L, $RUBYLIB, システム(/usr/lib/rub..など) の順にサーチが行われる
91# exerb 対応のため、上記パスにファイルが見つからない場合 require を実行してみる
92#
93# プラグインの場合は b_fatal = false を指定。ファイルがなくてもエラー出力後、処理続行
94# b_fatal = false の場合 tecslib/core がサーチパスに追加される
95#RETURN::Bool : true=成功、 false=失敗 失敗した場合、Generator.error は呼び元で出力する
96def require_tecsgen_lib( fname, b_fatal = true )
97 dbgPrint( "require_lib: #{fname}\n")
98 set_kcode $KCODE_TECSGEN
99 begin
100 b_require = false
101 b_exception = false
102
103 # -L 、 $RUBYLIB で指定されたパスおよびシステムのパスからサーチ
104 # exerb では $LOAD_PATH は ["."] のみ入っているようだ
105 ($library_path+$LOAD_PATH).each{ |path|
106 [ "", "tecslib/plugin/" ].each { |lp|
107 lib = File.expand_path( path ) + '/' + lp + fname
108
109 if File.exist? lib then # ファイル存否と他のエラーを区別するため存在確認をする
110 begin
111 require( lib )
112 b_require = true
113 rescue Exception => evar
114 b_exception = true
115 print_exception( evar )
116 end
117 break
118 end
119 }
120 if b_require then
121 break
122 end
123 }
124
125 if b_require == false && b_exception == false then
126 # exerb 対応 "." をサーチパスの最初に加える
127 # "tecslib/" は RPCPlugin.rb, TracePlugin.rb のために用意してある
128 # RPCPlugin.rb, TracePlugin.rb が tecslib 下でなければ不要になるが、このようにしておく
129 ["","tecslib/plugin/"].each{ |lp|
130 path = lp + fname
131 begin
132 require path
133 return true
134 rescue LoadError => e
135 # p "LoadError to load #{fname}"
136 # 2015.12.18 exerb 版でプラグインのロードでエラーが出るので、無視する. おそらく昔からエラーは出ていた
137 # print_exception( e )
138 rescue Exception => e
139 b_exception = true
140 # 文法エラーなどが発生
141 print_exception( e )
142 break
143 end
144 }
145 end
146
147 if b_require == false then
148 # 見つからなかった
149 if b_exception == false then
150 STDERR << "tecsgen: Fail to load #{fname}. Check $RUBYLIB environment variable or -L option\n"
151 end
152 # tecsgen を構成するファイルの場合は中止する
153 if b_fatal then
154 STDERR << "tecsgen: Exit because of unrecoverble error\n"
155 exit 1
156 end
157 return false
158 end
159 return true
160 ensure
161 # $KCODE を CDL の文字コードに戻しておく
162 set_kcode $KCODE_CDL
163 end
164end
165
166#=== 例外の表示
167#evar:: Exception
168def print_exception( evar )
169# もしスタックトレースが出るまでい時間がかかるようならば、次をコメントアウトしてみるべし
170 print "*** Begin Ruby exception message ***\n"
171 puts( evar.to_s )
172
173 if $debug then
174 puts "#### stack trace ####"
175 pp evar.backtrace
176 end
177 print "*** End Ruby exception message ***\n"
178end
179
180def dbgPrint( str )
181 if $debug then
182 print str
183 end
184end
185
186def dbgPrintf( *param )
187 if $debug then
188 printf *param
189 end
190end
191
192#=== エラーおよび警告のレポート
193def print_report
194 msg = nil
195
196 if Generator.get_n_error != 0 then
197 msg = "#{Generator.get_n_error} error"
198 msg = "#{msg}s" if Generator.get_n_error >= 2
199 end
200
201 if Generator.get_n_warning != 0 then
202 msg = "#{msg} " if msg
203 msg = "#{msg}#{Generator.get_n_warning} warning"
204 msg = "#{msg}s" if Generator.get_n_warning >= 2
205 end
206
207 puts msg if msg
208end
209
210#=== $KCODE を設定
211def set_kcode kcode
212 if ! $b_no_kcode then
213 $KCODE = kcode
214 end
215end
216#----- class TECSGEN -------#
217class TECSGEN
218
219 @@current_tecsgen = nil
220
221 def self.init( addtional_option_parser = nil )
222 initialize_global_var
223 analyze_option addtional_option_parser
224 load_modules
225 if ! $TECSFLOW then
226 setup
227 end
228
229 dbgPrint "tecspath: #{$tecsgen_base_path}, __FILE__=#{__FILE__}\n"
230 dbgPrint "ARGV(remained): #{ARGV}, argments=#{$arguments}\n"
231 end
232
233 #----- initialize -------#
234 def initialize
235 @cell_list = nil
236 @cell_list2 = nil
237 @celltype_list = nil
238 @root_namespace = nil
239
240 #--- obsolete ---# replaced to TOOL_INFO
241 @cell_location_list = []
242 @join_location_list = []
243 end
244
245 def run1
246 @@current_tecsgen = self
247
248 syntax_analisys ARGV
249 semantics_analisys_1
250 semantics_analisys_2
251
252 @celltype_list = Celltype.get_celltype_list
253 @cell_list = Cell.get_cell_list
254 @cell_list2 = Cell.get_cell_list2
255
256 @@current_tecsgen = nil
257 end
258
259 def run2
260 @@current_tecsgen = self
261
262 optimize_and_generate
263 finalize
264
265 @@current_tecsgen = nil
266 end
267
268 #----- initialize_global_var -----#
269 def self.initialize_global_var
270
271 require 'optparse'
272 #2.0 require 'runit/assert.rb'
273 require 'kconv'
274 $b_no_kcode = RUBY_VERSION >= "1.9.0" ? true : false
275 # Use Ruby 1.9 M17N code (use Ruby 1.8 code if false).
276 if ! $b_no_kcode then
277 require 'jcode'
278 end
279 require 'pp'
280 # include RUNIT::Assert
281
282 ### グローバル変数定義 ###
283
284 # コマンドライン引数  (Makefile.templ へ出力)
285 $arguments = ""
286 ARGV.each { |a| $arguments += " " + a }
287
288 $unopt = false # bool: disable optimizing both call and entry port
289 $unopt_entry= false # bool: disable optimizing entry port
290 $gen_base = "gen" # string: folder path to place generated files
291 $gen = $gen_base # string: folder path to place generated files
292 $generate_all_template = false # bool: generarete template files for all celltypes (if non cell exist or system celltypes)
293 $generate_no_template = false # bool: generarete no template file (neither celltype code nor Makefile)
294 $idx_is_id = false # bool: all components are idx_is_id
295 $unique_id = false # bool: assign unique id to each cell (otherwise begin from 1 for each celltype)
296 $debug = false # bool: tecsgen debug message
297 $dryrun = false # bool: dryrun mode: syntax is checked, but not generate any files
298 $show_tree = false # bool: show parsing tree
299 $verbose = false # bool: verbose mode: show some messages
300 $yydebug = false # bool: yydebug: parser debug mode (need bnf-deb.tab.rb)
301 $run_dir = Dir.pwd # string: tecsgen/tecscde start up directory
302 $base_dir = { } # string=>bool: base dir for import_path (key:base_dir, val:actually used or specified directly)
303 $import_path = [ "." ] # string array : import/import_C path
304 $import_path_opt = [] # [String]
305 $library_path = [ $tecsgen_base_path ] # string array : path to dir where tecsgen.rb placed
306 $define = [ ] # string array : define
307 $ram_initializer = false # bool: generate ram initializer
308 $region_list = {} #string array : region path which is generated
309 $generating_region = nil # Region: Region to optimisze & generate code # コマンドラインオプションではない
310 # Cell#is_generate? にて参照される
311 $unit_test = false # bool: unit test verification
312 $kcode = nil # nil | String: Kanji code type "euc"|"sjis"|"none"|"utf8"
313 $force_overwrite = false # bool: force overwrite all files if file contents not differ
314 $no_banner = false # bool: not print banner
315 $print_version = false # bool: print version
316 $target = "tecs" # String: target name, ARGV[0] から再設定する("tecs" は仮のターゲット)
317 $no_default_import_path = false # bool: no default import path
318 $c_suffix = "c" # suffix for C progorams (for C++ source)
319 $h_suffix = "h" # suffix for C progoram headers (for C++ source)
320
321 if ENV['TECSGEN_DEFAULT_RAM'] then
322 rom_ram_defalult = "ram"
323 else
324 rom_ram_defalult = "rom"
325 end
326 if rom_ram_defalult == "rom" then
327 $rom = true # bool: ROM support : generate CB separately
328 else
329 $rom = false # bool: ROM support : generate CB separately
330 end
331 $b_cpp_specified = false
332 if $cpp == nil then
333 $cpp = "gcc -E -DTECSGEN"
334 end
335 if ENV[ 'TECS_CPP' ]then
336 $cpp = ENV[ 'TECS_CPP' ]
337 $b_cpp_specified = true
338 end
339 if ENV[ 'TECSPATH' ] then
340 $tecspath = ENV[ 'TECSPATH' ]
341 else
342 $tecspath = "#{$tecsgen_base_path}/tecs"
343 end
344
345 # # 文字コードの設定
346 if $IN_EXERB then
347 # KCODE_CDL, $KCONV_CDL を仮に設定する (tecs_lang.rb ですぐに再設定される)
348 $KCODE_CDL = "SJIS" # string: "EUC" | "SJIS" | "NONE" | "UTF8"
349 $KCONV_CDL = Kconv::SJIS # const: NONE には ASCII を対応させる
350 else
351 $KCODE_CDL = "EUC" # string: "EUC" | "SJIS" | "NONE" | "UTF8"
352 $KCONV_CDL = Kconv::EUC # const: NONE には ASCII を対応させる
353 end
354 # $KCODE_TECSGEN, $KCONV_TECSGEN を仮に設定する (tecs_lang.rb ですぐに再設定される)
355 $KCODE_TECSGEN = "UTF8" # string: "EUC" このファイルの文字コード(オプションではなく定数)
356 $KCONV_TECSGEN = Kconv::UTF8 # const:
357 set_kcode( $KCODE_TECSGEN ) # このファイルの文字コードを設定
358
359 end # initialize_global_var
360
361 def self.analyze_option( additional_option_parser )
362
363 ### tecsgen コマンドオプション解析 ###
364 ARGV.options {|parser|
365 parser.banner = "Usage: tecsgen [options] files"
366 parser.on('-D', '--define=def', 'define cpp symbol for import_C') { |define|
367 $define << define
368 }
369 parser.on('-G', '--generate-region=path', 'generate region') { |path|
370 if path =~ /^::/ then
371 gen_path = path
372 else
373 gen_path = "::" + path
374 end
375 $region_list[ gen_path ] = true
376 }
377 parser.on('-I', '--import-path=path', 'imoprt/import_C path') { |path|
378 $import_path << path
379 $import_path_opt << path
380 }
381 parser.on('-L', '--library-path=path', 'path to dir where tecsgen.rb (obsolete, unnecessary to specify -L, those passes are gotten from tecsgen.rb') { |path|
382 $library_path << path
383 }
384 parser.on('-R', '--RAM-initializer', 'generate RAM initializer. INITIALIZE_TECS() must be called before running any TECS code.' ){
385 $ram_initializer = true
386 }
387 parser.on('-U', '--unoptimize', 'unoptimize') {
388 $unopt = true
389 }
390 parser.on('--unoptimize-entry', 'unoptimize entry port') {
391 $unopt_entry = true
392 }
393 parser.on('-c', '--cpp=cpp_cmd', 'C pre-processor command used import_C (default: gcc -E -DTECSGEN), you can also specify by environment variable TECS_CPP' ){
394 |arg|
395 $cpp = arg
396 $b_cpp_specified = true
397 }
398 parser.on('-d', '--dryrun', 'dryrun' ){
399 $dryrun = true
400 }
401 parser.on('-f', '--force-overwrite', 'force overwrite all files') {
402 $force_overwrite = true
403 }
404 parser.on('-g', '--gen=dir', 'generate dir') { |dir|
405 $gen = $gen_base = dir
406 }
407 parser.on('-i', '--idx_is_id', 'set idx_is_id to all celltypes') {
408 $idx_is_id = true
409 }
410 # parser.on('-k', '--kcode=code', 'set kanji code: euc|sjis|none|utf8, none is default') { |code|
411 parser.on('-k', '--kcode=code', "set kanji code: euc|sjis|none|utf8") { |code|
412 $kcode = code
413 }
414 # old_mode は V1.0.C.22 で廃止
415 # parser.on('-o', '--old-mode', 'old mode' ){
416 # $old_mode = true
417 # }
418 parser.on('-r', '--ram', 'RAM only' ){
419 $rom = false
420 }
421 parser.on('-s', '--show-tree', 'show parsing tree' ){
422 $show_tree = true
423 }
424 parser.on('-t', '--generator-debug', 'generator debug' ){
425 $debug = true
426 $verbose = true
427 }
428 parser.on('-u', '--unique-id', 'assign unique id for each cell' ){
429 $unique_id = true
430 }
431 parser.on('-v', '--verbose', 'verbose mode' ){
432 $verbose = true
433 }
434 parser.on('-y', '--yydebug', 'yydebug' ){
435 $yydebug = true
436 }
437 parser.on( '--no-banner', 'not display banner') {
438 $no_banner = true
439 }
440 parser.on( '--version', 'print version') {
441 $print_version = true
442 }
443 parser.on( '--unit-test', 'unit verification (test tecsgen itself)') {
444 $unit_test = true
445 }
446 parser.on( '--generate-all-template', 'generate all celltypes\' templates') {
447 $generate_all_template = true
448 }
449 parser.on( '--generate-no-template', 'generate no template') {
450 $generate_no_template = true
451 }
452 parser.on( '--no-default-import-path', 'no default import path' ){
453 $no_default_import_path = true
454 }
455 parser.on( '--c-suffix=c', 'C program suffix (default: c)' ){
456 | suffix |
457 $c_suffix = suffix
458 }
459 parser.on( '--h-suffix=h', 'C program header suffix (default: h)' ){
460 | suffix |
461 $h_suffix = suffix
462 }
463 # parser.on( '--include_path_opt_format', 'cpp include path option format, default: "-I %s"' ){
464 # }
465 parser.version = #{$version}
466 parser.release = nil
467 if additional_option_parser
468 additional_option_parser.call( parser )
469 end
470 parser.parse!
471 }
472
473 if ARGV.empty? && ! $print_version && ! $unit_test && ! $TECSFLOW
474 ARGV.options{|parser|
475 puts parser.help
476 exit 1
477 }
478 end
479
480 end # analyze_option
481
482 def self.load_modules
483 ### tecsgen モジュールのロード ####
484 # -L でパス指定可能としたため、ここからロードを開始する
485
486 # tecsgen バージョンファイルのロード
487 # これを実行するまで tecsgen のバージョンを表示できない
488 # このファイルを誤って読み込むと、異なるバージョン名を表示してしまう
489 require_tecsgen_lib 'tecslib/version.rb'
490 if $title then
491 STDERR << "#{$title} version #{$tool_version} (tecsgen version #{$version}) #{$Copyright}\n"
492 elsif ! $no_banner || $print_version
493 STDERR << "tecsgen version #{$version} #{$Copyright}\n"
494 end
495 if $verbose then
496 STDERR << "ruby #{RUBY_VERSION} (#{RUBY_RELEASE_DATE} patchlevel #{RUBY_PATCHLEVEL}) [#{RUBY_PLATFORM}]\n"
497 end
498 if $print_version && ARGV.empty? && ! $TECSFLOW
499 exit
500 end
501
502 # 文字コード決定のため最初に読みこむ
503 require_tecsgen_lib 'tecslib/core/tecs_lang.rb'
504
505 unless $yydebug then
506 require_tecsgen_lib 'tecslib/core/bnf.tab.rb'
507 else
508 require_tecsgen_lib 'tecslib/core/bnf-deb.tab.rb'
509 end
510
511 # syntaxobj.rb には Node が定義されているので、早い段階で require
512 require_tecsgen_lib 'tecslib/core/syntaxobj.rb'
513 require_tecsgen_lib 'tecslib/core/pluginModule.rb'
514 require_tecsgen_lib 'tecslib/core/plugin.rb'
515 require_tecsgen_lib 'tecslib/core/messages.rb'
516 require_tecsgen_lib 'tecslib/core/types.rb'
517 require_tecsgen_lib 'tecslib/core/value.rb'
518 require_tecsgen_lib 'tecslib/core/componentobj.rb'
519 require_tecsgen_lib 'tecslib/core/expression.rb'
520 require_tecsgen_lib 'tecslib/core/optimize.rb'
521 require_tecsgen_lib 'tecslib/core/tecsgen.rb'
522 require_tecsgen_lib 'tecslib/core/generate.rb'
523 require_tecsgen_lib 'tecslib/core/gen_xml.rb'
524 require_tecsgen_lib 'tecslib/core/tool_info.rb'
525 require_tecsgen_lib 'tecslib/core/tecsinfo.rb'
526 require_tecsgen_lib 'tecslib/core/unjoin_plugin.rb'
527 require_tecsgen_lib 'tecslib/plugin/CelltypePlugin.rb'
528 require_tecsgen_lib 'tecslib/plugin/CompositePlugin.rb'
529 require_tecsgen_lib 'tecslib/plugin/CellPlugin.rb'
530 require_tecsgen_lib 'tecslib/plugin/SignaturePlugin.rb'
531 require_tecsgen_lib 'tecslib/plugin/ThroughPlugin.rb'
532 require_tecsgen_lib 'tecslib/plugin/DomainPlugin.rb'
533 require_tecsgen_lib 'tecslib/plugin/MultiPlugin.rb'
534
535 # C 言語パーサ
536 require_tecsgen_lib 'tecslib/core/C_parser.tab.rb'
537 require_tecsgen_lib 'tecslib/core/ctypes.rb'
538
539 if $unit_test then
540 exit 1
541 end
542
543 end # load_modules
544
545 def self.setup
546 # $import_path, $tecspath を調整
547 TECSGEN.adjust_exerb_path
548
549 # $import_path に環境変数 $TECSGEN およびその直下を追加
550 if $no_default_import_path == false then
551 # $TECSGEN および、その直下のディレクトリをパスに追加
552 if $tecspath != "." then
553 TECSGEN.add_import_path $tecspath
554 dir = nil
555 begin
556 Dir.foreach( $tecspath ){ |f|
557 if f != "." && f != ".." && File.directory?( $tecspath + '/' + f ) then
558 TECSGEN.add_import_path( $tecspath + '/' + f )
559 end
560 }
561 rescue
562 # 無視
563 end
564 end
565 end
566
567 # デフォルト設定
568 TECSGEN.set_default_config
569
570 # $target の設定
571 $target = ARGV[0]
572 pos = $target.rindex( /[:\\\/]/ )
573 if pos then
574 $target = $target[pos+1..-1] # ディレクトリ区切りを除いた文字列
575 end
576 pos = $target.rindex( /\./ )
577 if pos then
578 $target = $target[0..pos-1] # 拡張子を取り除いた文字列
579 end
580
581 # gen ディレクトリの作成
582 begin
583 if ! File.directory?( $gen_base ) then
584 Dir.mkdir( $gen_base )
585 end
586 rescue
587 print( "Cannot mkdir #{$gen_base}. If the path has hierarchy, please create directory by manual.\n" )
588 exit 1
589 end
590 end # setup
591
592 #=== TECSGEN#get_celltype_list
593 def get_celltype_list
594 @celltype_list
595 end
596
597 #=== TECSGEN#get_cell_list
598 def get_cell_list
599 @cell_list
600 end
601
602 def get_root_namespace
603 @root_namespace
604 end
605end # TECSGEN
606
607# 複数のジェネレータインスタンスを生成することは、可能だが、以下の問題がある
608# クラス変数のリセットを確実に行う必要がある
609
610if $TECSCDE != true && $TECSFLOW != true then
611 begin
612 TECSGEN.init
613 tecsgen = TECSGEN.new
614 tecsgen.run1
615 tecsgen.run2
616 tecsgen.dump_tecsgen_rbdmp
617 rescue => evar
618 print_exception( evar )
619 STDERR << "tecsgen: exit because of unrecoverble error.\n"
620 STDERR << " please retry after resolve early error.\n"
621 STDERR << " if no error has occured, please report to TOPPERS TECS WG (users@toppers.jp or com-wg@toppers.jp).\n"
622 exit 1
623 end
624end
Note: See TracBrowser for help on using the repository browser.