source: azure_iot_hub/trunk/asp3_dcre/tecsgen/tecslib/plugin/lib/GenOpaqueMarshaler.rb@ 388

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

Azure IoT Hub Device C SDK を使ったサンプルの追加

  • Property svn:eol-style set to native
  • Property svn:mime-type set to text/x-ruby
File size: 40.6 KB
Line 
1# -*- coding: utf-8 -*-
2#
3# TECS Generator
4# Generator for TOPPERS Embedded Component System
5#
6# Copyright (C) 2008-2018 by TOPPERS Project
7#--
8# 上記著作権者
9は,以下の(1)~(4)の条件を満たす場合に限り,本ソフトウェ
10# ア(本ソフトウェアを改変したものを含む.以下同じ)を使用・複製・改
11# 変・再é…
12å¸ƒï¼ˆä»¥ä¸‹ï¼Œåˆ©ç”¨ã¨å‘¼ã¶ï¼‰ã™ã‚‹ã“とを無償で許諾する.
13# (1) 本ソフトウェアをソースコードの形で利用する場合には,上記の著作
14# 権表示,この利用条件および下記の無保証規定が,そのままの形でソー
15# スコード中に含まれていること.
16# (2) 本ソフトウェアを,ライブラリ形式など,他のソフトウェア開発に使
17# 用できる形で再é…
18å¸ƒã™ã‚‹å ´åˆã«ã¯ï¼Œå†é…
19å¸ƒã«ä¼´ã†ãƒ‰ã‚­ãƒ¥ãƒ¡ãƒ³ãƒˆï¼ˆåˆ©ç”¨
20# 者
21マニュアルなど)に,上記の著作権表示,この利用条件および下記
22# の無保証規定を掲載すること.
23# (3) 本ソフトウェアを,機器に組み込むなど,他のソフトウェア開発に使
24# 用できない形で再é…
25å¸ƒã™ã‚‹å ´åˆã«ã¯ï¼Œæ¬¡ã®ã„ずれかの条件を満たすこ
26# と.
27# (a) 再é…
28å¸ƒã«ä¼´ã†ãƒ‰ã‚­ãƒ¥ãƒ¡ãƒ³ãƒˆï¼ˆåˆ©ç”¨è€…
29マニュアルなど)に,上記の著
30# 作権表示,この利用条件および下記の無保証規定を掲載すること.
31# (b) 再é…
32å¸ƒã®å½¢æ…
33‹ã‚’,別に定める方法によって,TOPPERSプロジェクトに
34# 報告すること.
35# (4) 本ソフトウェアの利用により直接的または間接的に生じるいかなる損
36# 害からも,上記著作権者
37およびTOPPERSプロジェクトをå…
38è²¬ã™ã‚‹ã“と.
39# また,本ソフトウェアのユーザまたはエンドユーザからのいかなる理
40# 由に基づく請求からも,上記著作権者
41およびTOPPERSプロジェクトを
42# å…
43è²¬ã™ã‚‹ã“と.
44#
45# 本ソフトウェアは,無保証で提供されているものである.上記著作権者
46お
47# よびTOPPERSプロジェクトは,本ソフトウェアに関して,特定の使用目的
48# に対する適合性も含めて,いかなる保証も行わない.また,本ソフトウェ
49# アの利用により直接的または間接的に生じたいかなる損害に関しても,そ
50# の責任を負わない.
51#
52# $Id$
53#++
54
55#== GenOpaqueMarshaler
56# OpaqueRPCPlugin, sharedOpaqueRPCPlugin å…
57±é€šã®è¦ç´ ã‚’集めたモジュール
58module GenOpaqueMarshaler
59
60 # プラグイン引数名と Proc
61 RPCPluginArgProc = {
62 "clientChannelCelltype" => Proc.new { |obj,rhs| obj.set_clientChannelCelltype rhs },
63 "serverChannelCelltype" => Proc.new { |obj,rhs| obj.set_serverChannelCelltype rhs },
64 "clientChannelCell" => Proc.new { |obj,rhs| obj.set_clientChannelCell rhs },
65 "serverChannelCell" => Proc.new { |obj,rhs| obj.set_serverChannelCell rhs },
66 "clientChannelInitializer" => Proc.new { |obj,rhs| obj.set_clientChannelInitializer rhs },
67 "serverChannelInitializer" => Proc.new { |obj,rhs| obj.set_serverChannelInitializer rhs },
68 "clientSemaphoreCelltype" => Proc.new { |obj,rhs| obj.set_clientSemaphoreCelltype rhs },
69 "clientSemaphoreInitializer" => Proc.new { |obj,rhs| obj.set_clientSemaphoreInitializer rhs },
70 "clientErrorHandler" => Proc.new { |obj,rhs| obj.set_clientErrorHandler rhs },
71 "serverErrorHandler" => Proc.new { |obj,rhs| obj.set_serverErrorHandler rhs },
72 "TDRCelltype" => Proc.new { |obj,rhs| obj.set_TDRCelltype rhs },
73 "PPAllocatorSize" => Proc.new { |obj,rhs| obj.set_PPAllocatorSize rhs },
74 "substituteAllocator" => Proc.new { |obj,rhs| obj.set_substituteAllocator rhs },
75 "noServerChannelOpenerCode" => Proc.new { |obj,rhs| obj.set_noServerChannelOpenerCode rhs },
76 "taskCelltype" => Proc.new { |obj,rhs| obj.set_taskCelltype rhs },
77 "taskPriority" => Proc.new { |obj,rhs| obj.set_taskPriority rhs },
78 "stackSize" => Proc.new { |obj,rhs| obj.set_stackSize rhs },
79 }
80
81 ##### プラグイン引数チェック関数
82
83 #=== プラグイン引数 taskPriority のチェック
84 def set_taskPriority( rhs )
85 @taskPriority = rhs
86 end
87
88 #=== プラグイン引数 serverChannelCelltype のチェック
89 def set_serverChannelCelltype( rhs )
90 @serverChannelCelltype = rhs.to_sym
91 # path = [ "::", @serverChannelCelltype ]
92 # obj = Namespace.find( path )
93 nsp = NamespacePath.analyze( @serverChannelCelltype.to_s )
94 obj = Namespace.find( nsp )
95 if ! obj.instance_of?( Celltype ) && ! obj.instance_of?( CompositeCelltype ) then
96 cdl_error( "RPCPlugin: serverChannelCelltype '#{rhs}' not celltype or not defined" )
97 end
98 end
99
100 #=== プラグイン引数 clientChannelCelltype のチェック
101 def set_clientChannelCelltype( rhs )
102 @clientChannelCelltype = rhs.to_sym
103 # path = [ "::", @clientChannelCelltype ]
104 # obj = Namespace.find( path )
105 nsp = NamespacePath.analyze( @clientChannelCelltype.to_s )
106 obj = Namespace.find( nsp )
107 if ! obj.instance_of?( Celltype ) && ! obj.instance_of?( CompositeCelltype ) then
108 cdl_error( "RPCPlugin: clientChanneclCelltype '#{rhs}' not celltype or not defined" )
109 end
110 end
111
112 #=== プラグイン引数 serverChannelCell のチェック
113 def set_serverChannelCell( rhs )
114 @serverChannelCell = rhs.to_sym
115 # ChannelCell はプラグインで生成されるため、ここではチェックできない
116 # path = [ "::", @serverChannelCell ]
117 # obj = Namespace.find( path )
118 # if ! obj.instance_of?( Cell ) then
119 # cdl_error( "RPCPlugin: serverChanneclCell '#{rhs}' not cell or not defined" )
120 # end
121 end
122
123 #=== プラグイン引数 clientChannelCell のチェック
124 def set_clientChannelCell( rhs )
125 @clientChannelCell = rhs.to_sym
126 # ChannelCell はプラグインで生成されるため、ここではチェックできない
127 # path = [ "::", @clientChannelCell ]
128 # obj = Namespace.find( path )
129 # if ! obj.instance_of?( Cell ) then
130 # cdl_error( "RPCPlugin: clientChanneclCell '#{rhs}' not cell or not defined" )
131 # end
132 end
133
134 #=== プラグイン引数 serverChannelInitializer のチェック
135 def set_serverChannelInitializer( rhs )
136 @serverChannelInitializer = rhs.to_sym
137 end
138
139 #=== プラグイン引数 clientChannelInitializer のチェック
140 def set_clientChannelInitializer( rhs )
141 @clientChannelInitializer = rhs.to_sym
142 end
143
144 #=== タスクタイプ taskCellype のチェック
145 def set_taskCelltype( rhs )
146 @taskCelltype = rhs.to_sym
147 # path = [ "::", @taskCelltype ]
148 # obj = Namespace.find( path )
149 nsp = NamespacePath.analyze( @taskCelltype.to_s )
150 obj = Namespace.find( nsp )
151 if ! obj.instance_of?( Celltype ) && ! obj.instance_of?( CompositeCelltype ) then
152 cdl_error( "RPCPlugin: taskCelltype '#{rhs}' not celltype or not defined" )
153 end
154 end
155
156 #=== タスクタイプ stack\size のチェック
157 def set_stackSize( rhs )
158 @stackSize = rhs
159 end
160
161 #=== プラグイン引数 PPAllocatorSize のチェック
162 def set_PPAllocatorSize( rhs )
163 @PPAllocatorSize = rhs
164 end
165
166 #=== プラグイン引数 TDRCelltype のチェック
167 def set_TDRCelltype( rhs )
168 @TDRCelltype = rhs.to_sym
169 # path = [ "::", @TDRCelltype ]
170 # obj = Namespace.find( path )
171 nsp = NamespacePath.analyze( @TDRCelltype.to_s )
172 obj = Namespace.find( nsp )
173 if ! obj.instance_of?( Celltype ) && ! obj.instance_of?( CompositeCelltype ) then
174 cdl_error( "RPCPlugin: TDRCelltype '#{rhs}' not celltype or not found" )
175 end
176 end
177
178 #=== プラグイン引数 substituteAllocator のチェック
179 # オプション引数が、以下の形式であることをチェック
180 # substituteAllocator(Alloc.eAlloc=>Subst.eAlloc,Alloc2.eAlloc=>Subst2.eAlloc)
181 def set_substituteAllocator( rhs )
182 #str::String : 破壊される(マッチした残りになる)。str.empty? で空になったことをチェックできる
183 #regexp::Regexp : 期待
184するトークンにマッチする正規表現。 "\A" 出始める
185 #expected::String: 期待
186するトークン、regexp が出現しなかった場合にエラーメッセージとして表示
187 def optparse (str,regexp,expected)
188 str.strip!
189 token = nil
190 res = str.sub!( regexp ){ |matched| token = matched; "" }
191 if ! token then
192 cdl_error( "syntax error in substituteAllocator option near '#{str}', expected '#{expected}'" )
193 end
194 return token
195 end
196
197 opt = rhs.dup
198 ident_rexpr = /\A(\w[\w\d]*)/
199
200 # "Alloc.eAlloc=>CAlloc.eAlloc" の形式になっていることをチェック
201 while true
202 lhs_alloc_cell = optparse( opt, ident_rexpr, "allocator cell name" )
203 break if ! lhs_alloc_cell
204
205 res = optparse( opt, /\A\./, "." )
206 break if ! res
207
208 lhs_alloc_ent = optparse( opt, ident_rexpr, "allocator cell entry name" )
209 break if ! lhs_alloc_ent
210
211 res = optparse( opt, /\A\=\>/, "=>" )
212 break if ! res
213
214 rhs_alloc_cell = optparse( opt, ident_rexpr, "allocator cell name" )
215 break if ! rhs_alloc_cell
216
217 res = optparse( opt, /\A\./, "." )
218 break if ! res
219
220 rhs_alloc_ent = optparse( opt, ident_rexpr, "allocator cell entry name" )
221 break if ! rhs_alloc_ent
222
223# ここでは、右辺のチェックはできない。右辺のセルは前方参ç…
224§ã¨ãªã‚‹
225# path = [ "::", rhs_alloc_cell.to_sym ] # mikan namespace
226# obj = Namespace.find( path )
227# if ! obj.instance_of?( Cell ) || obj.get_region.get_path_string != @clientRegion then
228# cdl_error( "RPCPlugin: substituteAllocator: '#{rhs_alloc_cell}' not cell or not found in client region" )
229# else
230# ct = obj.get_celltype
231# if ct # nil なら既にエラー
232# ent = ct.find rhs_alloc_ent
233# if ! ent.instance_of? Port || ent.get_port_type != :ENTRY || ent.get_signature == nil || ! ent.get_signature.is_allocator?
234# cdl_error( "RPCPlugin: substituteAllocator: '#{rhs_alloc_cell}.#{rhs_alloc_ent}' not entry port or not have alllocator signature" )
235# end
236# end
237# end
238
239 @substituteAllocator[ "#{lhs_alloc_cell}.#{lhs_alloc_ent}".to_sym ] =
240 [ lhs_alloc_cell, lhs_alloc_ent, rhs_alloc_cell, rhs_alloc_ent ]
241
242# p "substituteAllocator: #{lhs_alloc_cell}.#{lhs_alloc_ent}=>#{rhs_alloc_cell}.#{rhs_alloc_ent}"
243
244 break if opt.empty?
245
246 res = optparse( opt, /\A\,/, "," )
247 break if ! res
248 end
249
250 end
251
252 #=== プラグイン引数 noServerChannelOpenerCode のチェック
253 def set_noServerChannelOpenerCode( rhs )
254 rhs = rhs.to_sym
255 if rhs == :true
256 @noServerChannelOpenerCode = true
257 elsif rhs == :false then
258 @noServerChannelOpenerCode = false
259 else
260 cdl_error( "RPCPlugin: specify true or false for noServerChannelOpenerCode" )
261 end
262 end
263
264 #=== プラグイン引数 clientSemaphoreCelltype のチェック
265 def set_clientSemaphoreCelltype rhs
266 @semaphoreCelltype = rhs.to_sym
267 nsp = NamespacePath.analyze( @semaphoreCelltype.to_s )
268 obj = Namespace.find( nsp )
269 if ! obj.instance_of?( Celltype ) && ! obj.instance_of?( CompositeCelltype ) then
270 cdl_error( "RPCPlugin: clientSemaphoreCelltype '#{rhs}' not celltype or not defined" )
271 end
272 end
273
274 #=== プラグイン引数 clientSemaphoreInitializer のチェック
275 def set_clientSemaphoreInitializer rhs
276 @semaphoreInitializer = rhs.to_sym
277 end
278
279 #=== プラグイン引数 clientErrorHandler のチェック
280 def set_clientErrorHandler rhs
281 @clientErrorHandler = rhs.to_sym
282 end
283
284 #=== プラグイン引数 serverErrorHandler のチェック
285 def set_serverErrorHandler rhs
286 @serverErrorHandler = rhs.to_sym
287 end
288
289
290 #=== セルの名前を得る
291 # ThroughPlugin::get_cell_name plugin.rb をオーバーライド
292 def get_cell_name
293 @cell_name
294 # @clientChannelCell
295 end
296
297 #=== marshaler のセルタイプ名を設定する
298 def initialize_opaque_marshaler
299
300 # オプション設定される変数のデフォルトを設定
301 @taskPriority = 11
302 @stackSize = 4096
303 @serverChannelCelltype = :"tSocketServer"
304 @clientChannelCelltype = :"tSocketClient"
305 @serverChannelCell = :"#{@cell_name}Server"
306 @clientChannelCell = :"#{@cell_name}Client"
307 @serverChannelInitializer = subst_name( "portNo=8931+$count$;" ).to_sym
308 @clientChannelInitializer = subst_name( "portNo=8931+$count$; serverAddr=\"127.0.0.1\"; " ).to_sym
309 @taskCelltype = :"tTask"
310 @PPAllocatorSize = nil
311 # @TDRCelltype = :"tTDR" # "tNBOTDR" に変更の予定
312 @TDRCelltype = :"tNBOTDR"
313 @substituteAllocator = {}
314 @noServerChannelOpenerCode = false
315 @semaphoreCelltype = :"tSemaphore"
316 @semaphoreInitializer = :"count = 1; attribute = C_EXP( \"TA_NULL\" ); ";
317 @clientErrorHandler = nil
318 @serverErrorHandler = nil
319 @b_genOpener = false
320 @taskMainCelltype = :"tRPCDedicatedTaskMain"
321
322 @marshaler_celltype_name = :"tOpaqueMarshaler_#{@signature.get_global_name}"
323 @unmarshaler_celltype_name = :"tOpaqueUnmarshaler_#{@signature.get_global_name}"
324 @marshaler_celltype_file_name = "#{$gen}/#{@marshaler_celltype_name}.cdl"
325
326 # signature で対応できないものをチェック
327 @signature.each_param{ |func_decl, param_decl|
328 if param_decl.get_direction == :OUT then
329 if param_decl.get_count && ! param_decl.get_size then
330 cdl_error( "#{@signature.get_namespace_path}.#{func_decl.get_name}.#{param_decl.get_name}: size_is must be specified for out parameter of Opaque RPC" )
331 end
332 if param_decl.get_string == -1 then
333 cdl_error( "#{@signature.get_namespace_path}.#{func_decl.get_name}.#{param_decl.get_name}: string length must be specified for out parameter of Opaque RPC" )
334 end
335 end
336 }
337 end
338
339 #=== GenOpaqueMarshaler# Opener Code の生成時のチェック
340 def check_opener_code
341 # サーバーチャンネルセルタイプが entry sServerChannelOpener eOpener を持つかどうかをチェック
342 # mikan entry か (call でないか) をチェックしていない
343 # scct = Namespace.find ["::", @serverChannelCelltype] # mikan namespace
344 nsp = NamespacePath.analyze( @serverChannelCelltype.to_s )
345 scct = Namespace.find nsp
346 if scct then
347 obj = scct.find( :"eOpener" )
348 if obj.instance_of? Port then
349 if obj.get_signature.get_name.to_sym == :sServerChannelOpener then
350 if @noServerChannelOpenerCode == false then
351 @b_genOpener = true
352 @taskMainCelltype = :"tRPCDedicatedTaskMainWithOpener"
353 end
354 end
355 end
356 end
357 if @noServerChannelOpenerCode == false && @taskMainCelltype != :"tRPCDedicatedTaskMainWithOpener" then
358 cdl_warning( "O9999 ServerChannelOpener code not generated, not found 'entry sServerChannelOpener eOpener'")
359 end
360 end
361
362 #=== GenOpaqueMarshaler# PPAllocator の必
363要性をチェックする
364 def check_PPAllocator
365 if @signature.need_PPAllocator?(true) then
366 if @PPAllocatorSize == nil then
367 cdl_error( "PPAllocatorSize must be speicified for size_is array" )
368 end
369 end
370 end
371
372 #####
373
374 def gen_marshaler_celltype
375 f = CFile.open( @marshaler_celltype_file_name, "w" )
376 # 同じ内
377容を二度書く可能性あり (AppFile は不可)
378
379 if @PPAllocatorSize then
380 alloc_call_port = " call sPPAllocator cPPAllocator;\n"
381 else
382 alloc_call_port = ""
383 end
384
385 f.print <<EOT
386
387celltype #{@marshaler_celltype_name} {
388 entry #{@signature.get_namespace_path} eClientEntry;
389 call sTDR cTDR;
390 [optional]
391 call sSemaphore cLockChannel;
392 [optional]
393 call sRPCErrorHandler cErrorHandler;
394};
395celltype #{@unmarshaler_celltype_name} {
396 call #{@signature.get_namespace_path} cServerCall;
397 call sTDR cTDR;
398 [optional]
399 call sRPCErrorHandler cErrorHandler;
400 entry sUnmarshalerMain eService;
401#{alloc_call_port}};
402EOT
403 f.close
404 end
405
406 #=== 受け口関数の本体コードを生成(頭部と末尾は別途出力)
407 #ct_name:: Symbol (through プラグインで生成された) セルタイプ名 .Symbol として送られてくる(らしい)
408 def gen_ep_func_body( file, b_singleton, ct_name, global_ct_name, sig_name, ep_name, func_name, func_global_name, func_type, params )
409
410 # unmarshaler クラスか?
411 if ct_name == @unmarshaler_celltype_name.to_sym then
412 gen_ep_func_body_unmarshal( file, b_singleton, ct_name, global_ct_name, sig_name, ep_name, func_name, func_global_name, func_type, params )
413 else
414 gen_ep_func_body_marshal( file, b_singleton, ct_name, global_ct_name, sig_name, ep_name, func_name, func_global_name, func_type, params )
415 end
416 end
417
418 #=== marshal コードの生成
419 def gen_ep_func_body_marshal( file, b_singleton, ct_name, global_ct_name, sig_name, ep_name, func_name, func_global_name, func_type, params )
420
421 b_void = false
422 b_ret_er = false
423
424 # 関数の戻り値のå…
425ƒã®åž‹ã‚’å¾—ã‚‹(typedef されている場合)
426 type = func_type.get_type.get_original_type
427
428 # 戻り値記憶用の変数を出力(void 型の関数では出力しない)
429 if ! type.is_void? then
430 file.print( "\t#{func_type.get_type.get_type_str}\t\tretval_;\n" )
431 if func_type.get_type.kind_of?( DefinedType ) && ( func_type.get_type.get_type_str == "ER" || func_type.get_type.get_type_str == "ER_INT" ) then
432 b_ret_er = true
433 end
434 else
435 b_void = true
436 end
437
438 file.print( "\tER\t\tercd_;\n" )
439 file.print( "\tint16_t\tstate_;\n" )
440
441 # 関数 ID (整数値)
442 func_id = "FUNCID_#{@signature.get_global_name}_#{func_name}".upcase
443 fid = @signature.get_id_from_func_name( func_name )
444 file.print( "\tint16_t\tfunc_id_ = #{func_id}; /* (id of '#{func_name}') = #{fid}*/\n" )
445
446 # シングルトンでないか?
447 if ! b_singleton then
448
449 # singleton でなければ p_cellcb 取得コードを出力
450 file.print <<EOT
451 #{ct_name}_CB *p_cellcb;
452
453 if( VALID_IDX( idx ) ){
454 p_cellcb = GET_CELLCB(idx);
455EOT
456
457 # エラーを返すか?
458 if b_ret_er then
459 file.print <<EOT
460 }else{
461 return ERCD( E_RPC, E_ID );
462 }
463EOT
464 else
465 file.print <<EOT
466 }else{
467 /* エラー処理コードをここに記述 */
468 }
469
470EOT
471 end
472 end
473
474 if func_type.has_receive? then
475 file.print " /* initialize receive parameters */\n"
476 params.each{ |param|
477 if param.get_direction == :RECEIVE then
478 file.print " *#{param.get_name} = 0;\n"
479 end
480 }
481 end
482
483 # channel lock コード
484 file.print <<EOT
485
486 /* Channel Lock */
487 SET_RPC_STATE( state_, RPCSTATE_CLIENT_GET_SEM );
488 if( is_cLockChannel_joined() ){
489 if( (ercd_=cLockChannel_wait()) != E_OK )
490 goto error_reset;
491 }
492EOT
493
494 # SOP を送信
495 file.print " /* SOPの送出 */\n"
496 file.print " SET_RPC_STATE( state_, RPCSTATE_CLIENT_SEND_SOP );\n"
497 file.print " if( ( ercd_ = cTDR_sendSOP( true ) ) != E_OK )\n"
498 file.print " goto error_reset;\n"
499
500 # func_id を送信
501 file.print " /* 関数 id の送出 */\n"
502 file.print " if( ( ercd_ = cTDR_putInt16( func_id_ ) ) != E_OK )\n"
503 file.print " goto error_reset;\n"
504
505 # p "celltype_name, sig_name, func_name, func_global_name"
506 # p "#{ct_name}, #{sig_name}, #{func_name}, #{func_global_name}"
507
508 b_get = false # marshal なら put
509 b_marshal = true # marshal
510
511 # in 方向のå…
512¥å‡ºåŠ›ã‚’出力
513 if func_type.has_inward? then
514 file.print " /* å…
515¥åŠ›å¼•æ•°é€å‡º */\n"
516 file.print " SET_RPC_STATE( state_, RPCSTATE_CLIENT_SEND_BODY );\n"
517 print_params( params, file, 1, b_marshal, b_get, true, "eClientEntry", func_name )
518 print_params( params, file, 1, b_marshal, b_get, false, "eClientEntry", func_name )
519 end
520 print_out_nullable( params, file, 1, b_marshal );
521
522 if ! func_type.is_oneway? then
523 b_continue = "true"
524 else
525 b_continue = "false"
526 end
527 file.print " /* EOPの送出(パケットの掃きだし) */\n"
528 file.print " SET_RPC_STATE( state_, RPCSTATE_CLIENT_SEND_EOP );\n"
529 file.print " if( (ercd_=cTDR_sendEOP(#{b_continue})) != E_OK )\n"
530 file.print " goto error_reset;\n\n"
531
532 # send のメモリをデアロケート
533 if func_type.has_send? then
534 file.print " /* dealloc send parameter while executing */\n"
535 file.print " SET_RPC_STATE( state_, RPCSTATE_CLIENT_EXEC );\n"
536 dir = :SEND; nest = 1; dealloc_cp = "eClientEntry_#{func_name}"
537 dealloc_for_params( params, file, nest, dir, dealloc_cp )
538 file.print "\n"
539 end
540
541 if ! func_type.is_oneway? then
542
543 file.print " /* パケットの始まりをチェック */\n"
544 file.print " SET_RPC_STATE( state_, RPCSTATE_CLIENT_RECV_SOP );\n"
545 file.print " if( (ercd_=cTDR_receiveSOP( true )) != E_OK )\n"
546 file.print " goto error_reset;\n"
547
548 b_get = true # marshaler は get
549 file.print " /* 戻り値の受け取り */\n"
550 print_param( "retval_", func_type.get_type, file, 1, :RETURN, nil, nil, b_marshal, b_get )
551
552 if func_type.has_outward? then
553 if b_ret_er then
554 indent_level = 2
555 file.print " if( MERCD( retval_ ) != E_RPC ){\n"
556 else
557 indent_level = 1
558 end
559 indent = " " * indent_level
560
561 file.print "#{indent}/* 出力値の受け取り */\n"
562 file.print "#{indent}SET_RPC_STATE( state_, RPCSTATE_CLIENT_RECV_BODY );\n"
563 print_params( params, file, indent_level, b_marshal, b_get, true, "eClientEntry", func_name )
564 print_params( params, file, indent_level, b_marshal, b_get, false, "eClientEntry", func_name )
565
566 if b_ret_er then
567 file.print " }\n"
568 end
569 end
570
571 file.print "\n /* パケットの終わりをチェック */\n"
572 file.print " SET_RPC_STATE( state_, RPCSTATE_CLIENT_RECV_EOP );\n"
573 file.print " if( (ercd_=cTDR_receiveEOP(false)) != E_OK )\n" # b_continue = false
574 file.print " goto error_reset;\n"
575
576 end # ! func_type.is_oneway?
577
578 # channel lock コード
579 file.print <<EOT
580 /* Channel Unlock */
581 SET_RPC_STATE( state_, RPCSTATE_CLIENT_RELEASE_SEM );
582 if( is_cLockChannel_joined() ){
583 if( (ercd_=cLockChannel_signal()) != E_OK )
584 goto error_reset;
585 }
586EOT
587
588 file.print <<EOT
589 /* state_ is not used in normal case */
590 /* below is to avoid 'set but not used' warnning */
591 (void)state_;
592EOT
593
594 if( b_void == false )then
595 # 呼びå…
596ƒã«æˆ»ã‚Šå€¤ã‚’リターン
597 file.print( " return retval_;\n" )
598 else
599 file.print( " return;\n" )
600 end
601
602 file.print <<EOT
603
604error_reset:
605EOT
606 # send のメモリをデアロケート
607 if func_type.has_send? then
608 file.print " /* dealloc send parameter */\n"
609 file.print " if( state_ < RPCSTATE_CLIENT_EXEC ){\n"
610 dir = :SEND; nest = 2; dealloc_cp = "eClientEntry_#{func_name}"
611 dealloc_for_params( params, file, nest, dir, dealloc_cp )
612 file.print " }\n"
613 end
614
615 # receive のメモリをデアロケート
616 if func_type.has_receive? then
617 file.print( " /* receive parameter */\n" )
618 dir = :RECEIVE; nest = 1; dealloc_cp = "eClientEntry_#{func_name}"
619 dealloc_for_params( params, file, nest, dir, dealloc_cp, true )
620 end
621
622 file.print <<EOT
623 if( MERCD( ercd_ ) != E_RESET )
624 (void)cTDR_reset();
625EOT
626
627 # channel lock コード
628 file.print <<EOT
629 /* Channel Unlock */
630 if( is_cLockChannel_joined() )
631 cLockChannel_signal();
632
633 if( ercd_ != E_OK && is_cErrorHandler_joined() )
634 cErrorHandler_errorOccured( func_id_, ercd_, state_ );
635EOT
636
637 if( b_ret_er != false )then
638 # 呼びå…
639ƒã«æˆ»ã‚Šå€¤ã‚’リターン
640 file.print( " return ERCD( E_RPC, MERCD( ercd_ ) );\n" )
641 else
642 file.print( " return;\n" )
643 end
644
645 end
646
647 #=== unmarshal コードの生成
648 def gen_ep_func_body_unmarshal( file, b_singleton, ct_name, global_ct_name, sig_name, ep_name, func_name, func_global_name, func_type, params )
649
650 b_ret_er = true
651
652 # func_id を得るコードを生成
653 file.print <<EOT
654
655 int16_t func_id_;
656 ER ercd_ = E_OK;
657 int16_t state_;
658
659 #{ct_name}_CB *p_cellcb;
660
661 if( VALID_IDX( idx ) ){
662 p_cellcb = GET_CELLCB(idx);
663EOT
664
665 if b_ret_er then
666 file.print <<EOT
667 }else{
668 return E_ID;
669 }
670EOT
671 else
672 file.print <<EOT
673 }else{
674 /* エラー処理コードをここに記述 */
675 }
676EOT
677 end
678
679 file.print <<EOT
680
681#ifdef RPC_DEBUG
682 syslog(LOG_INFO, "Entering RPC service loop" );
683#endif
684
685 /* SOPのチェック */
686 SET_RPC_STATE( state_, RPCSTATE_SERVER_RECV_SOP );
687 if( (ercd_=cTDR_receiveSOP( false )) != E_OK )
688 goto error_reset;
689 /* func_id の取得 */
690 if( (ercd_=cTDR_getInt16( &func_id_ )) != E_OK )
691 goto error_reset;
692
693#ifdef RPC_DEBUG
694 syslog(LOG_INFO, "unmarshaler task: func_id: %d", func_id_ );
695#endif
696 switch( func_id_ ){
697EOT
698
699 # signature に含まれる すべての関数について
700 @signature.get_function_head_array.each { |f|
701 f_name = f.get_name
702 f_type = f.get_declarator.get_type
703 func_id = "FUNCID_#{@signature.get_global_name}_#{f_name}".upcase
704 fid = @signature.get_id_from_func_name( f_name )
705
706 file.print " case #{func_id}: /* (id of '#{f_name}') = #{fid} */ \n"
707 file.print " ercd_ = tOpaqueUnmarshaler_#{@signature.get_global_name}_#{f_name}( p_cellcb, &state_ );\n"
708 file.print " break;\n"
709
710 } #
711
712 if @PPAllocatorSize then
713 ppallocator_dealloc_str = " /* PPAllocator のすべてを解放 */\n cPPAllocator_dealloc_all();"
714 else
715 ppallocator_dealloc_str = ""
716 end
717
718 file.print <<EOT
719 default:
720 syslog(LOG_INFO, "unmarshaler task: ERROR: unknown func_id: %d", func_id_ );
721 ercd_ = E_ID;
722 };
723error_reset: /* OK cases also come here */
724#{ppallocator_dealloc_str}
725 if( ercd_ == E_OK )
726 return ercd_;
727 if( is_cErrorHandler_joined() )
728 ercd_ = cErrorHandler_errorOccured( func_id_, ercd_, state_ );
729 if( MERCD( ercd_ ) != E_RESET )
730 (void)cTDR_reset();
731 return ercd_;
732EOT
733
734 end
735
736 #=== PREAMBLE 部のコード生成
737 # アンマーシャラセルタイプの場合、アンマーシャラ関数のプロトタイプ宣言を生成
738 def gen_preamble file, b_singleton, ct_name, global_name
739 if ct_name != @unmarshaler_celltype_name.to_sym then
740 return
741 end
742
743 # string.h の include (memset, strlen のため)
744 file.print "/* header file (strlen, memset) */\n"
745 file.print "#include\t<string.h>\n\n"
746
747 file.print "/* アンマーシャラ関数のプロトタイプ宣言 */\n"
748 # signature に含まれる すべての関数について
749 @signature.get_function_head_array.each { |f|
750 f_name = f.get_name
751 f_type = f.get_declarator.get_type
752 id = @signature.get_id_from_func_name( f_name )
753 file.print "static ER tOpaqueUnmarshaler_#{@signature.get_global_name}_#{f_name}(CELLCB *p_cellcb, int16_t *state);\t/* func_id: #{id} */\n"
754 }
755 file.print "\n"
756 end
757
758 #=== POSTAMBLE 部のコード生成
759 # アンマーシャラセルタイプの場合、個々
760のアンマーシャラ関数の生成
761 def gen_postamble file, b_singleton, ct_name, global_name
762 if ct_name != @unmarshaler_celltype_name.to_sym then
763 return
764 end
765
766 file.print "\n/*** アンマーシャラ関数 ***/\n\n"
767 @signature.get_function_head_array.each { |f|
768 f_name = f.get_name
769 f_type = f.get_declarator.get_type
770 id = @signature.get_id_from_func_name( f_name )
771
772 # 関数は返り値を持つか?
773 b_ret_er = false
774 init_retval = ""
775 if f_type.get_type.is_void? then
776 b_void = true
777 else
778 b_void = false
779 if f_type.get_type.get_type_str == "ER" || f_type.get_type.get_type_str == "ER_INT" then
780 b_ret_er = true
781 init_retval = " = E_OK"
782 end
783 end
784
785 file.print <<EOT
786/*
787 * name: #{f_name}
788 * func_id: #{id}
789 */
790EOT
791 file.print "static ER\n"
792 file.print "tOpaqueUnmarshaler_#{@signature.get_global_name}_#{f_name}(CELLCB *p_cellcb, int16_t *state_)\t\t\n"
793 file.print "{\n"
794 file.print " ER ercd_;\n"
795
796 # 引数を受取る変数の定義
797 params = f.get_declarator.get_type.get_paramlist.get_items
798 # FuncHead-> Decl-> FuncType->ParamList
799 params.each{ |par|
800 name = par.get_name
801 type = par.get_type.get_original_type
802
803 dir = par.get_direction
804 if( dir == :RECEIVE )then
805 # type は PtrType で、それを取り除いた型
806 type = type.get_type
807 end
808 if( dir == :SEND || dir == :RECEIVE )then
809 init = " = 0"
810 else
811 init = ""
812 end
813
814 if type.kind_of? ArrayType then
815 type = type.get_type
816 aster = "(*"
817 aster2 = ")"
818 else
819 aster = ""
820 aster2 = ""
821 end
822
823 type_str = type.get_type_str.gsub( /\bconst\b */, "" ) # "const" を外す
824 file.printf( " %-12s %s%s%s%s%s;\n", type_str, aster, name, aster2, type.get_type_str_post, init )
825
826 if dir == :OUT && type.is_nullable? then
827 file.print( " int8_t\tb_#{name}_null_;\n" )
828 end
829 }
830
831 if ! b_void then
832 file.printf( " %-12s retval_%s%s;\n", f_type.get_type.get_type_str, f_type.get_type.get_type_str_post, init_retval )
833 end
834
835 # in 方向のå…
836¥å‡ºåŠ›ã‚’å…
837¥åŠ›
838 file.print "\n /* å…
839¥åŠ›å¼•æ•°å—取 */\n"
840 file.print " SET_RPC_STATE( *state_, RPCSTATE_SERVER_RECV_BODY );\n"
841 b_get = true # unmarshal では get
842 b_marshal = false
843 print_params( params, file, 1, b_marshal, b_get, true, "cServerCall", f_name )
844 print_params( params, file, 1, b_marshal, b_get, false, "cServerCall", f_name )
845 print_out_nullable( params, file, 1, b_marshal );
846
847
848 # パケットの受信完了
849 file.print " /* パケット終わりをチェック */\n"
850 file.print " SET_RPC_STATE( *state_, RPCSTATE_SERVER_RECV_EOP );\n"
851 if ! f_type.is_oneway? then
852 b_continue = "true"
853 else
854 b_continue = "false"
855 end
856 file.print " if( (ercd_=cTDR_receiveEOP(#{b_continue})) != E_OK )\n"
857 file.print " goto error_reset;\n\n"
858
859 # out のメモリをアロケート
860 dir = :OUT; alloc_cp = "cPPAllocator_alloc"; alloc_cp_extra = nil; nest = 1
861 alloc_for_out_params( params, file, nest, dir, alloc_cp, alloc_cp_extra )
862
863 # 対象関数を呼出す
864 file.print " /* 対象関数の呼出し */\n"
865 file.print " SET_RPC_STATE( *state_, RPCSTATE_SERVER_EXEC );\n"
866 if b_void then
867 file.print( " cServerCall_#{f_name}(" )
868 else
869 file.print( " retval_ = cServerCall_#{f_name}(" )
870 end
871
872 delim = " "
873 params.each{ |par|
874 file.print delim
875 delim = ", "
876 if par.get_direction == :RECEIVE then
877 file.print "&"
878 end
879 file.print par.get_name
880 }
881 file.print( " );\n" )
882
883 # 戻り値、出力引数の受取コードの生成
884
885 # oneway の場合出力、戻り値が無く、受取を待
886たない(非同期な呼出し)
887 if ! f.is_oneway? then
888
889 file.print "\n /* SOPの送出 */\n"
890 file.print " SET_RPC_STATE( *state_, RPCSTATE_SERVER_SEND_SOP );\n"
891
892 file.print " if( ( ercd_ = cTDR_sendSOP( false ) ) != E_OK )\n"
893 file.print " goto error_reset;\n"
894
895 b_get = false # unmarshaler は put
896 if( ! b_void )then
897 file.print " /* 戻り値の送出 */\n"
898 print_param( "retval_", f_type.get_type, file, 1, :RETURN, nil, nil, b_marshal, b_get )
899 end
900
901 if f_type.has_outward? then
902 if b_ret_er then
903 indent_level = 2
904 file.print " if( MERCD( retval_ ) != E_RPC ){\n"
905 else
906 indent_level = 1
907 end
908 indent = " " * indent_level
909
910 file.print "#{indent}/* 出力値の送出 */\n"
911 file.print "#{indent}SET_RPC_STATE( *state_, RPCSTATE_SERVER_SEND_BODY );\n"
912 print_params( params, file, indent_level, b_marshal, b_get, true, "cServerCall", f_name )
913 print_params( params, file, indent_level, b_marshal, b_get, false, "cServerCall", f_name )
914
915 # receive のメモリをデアロケート
916 if f_type.has_receive? then
917 file.print "#{indent}/* dealloc receive parameter */\n"
918 dir = :RECEIVE; dealloc_cp = "cServerCall_#{f_name}"
919 dealloc_for_params( params, file, indent_level, dir, dealloc_cp )
920 end
921
922 if b_ret_er then
923 file.print " }\n"
924 end
925 end
926
927 file.print " /* パケットの終わり(掃きだし) */\n"
928 file.print " SET_RPC_STATE( *state_, RPCSTATE_SERVER_SEND_EOP );\n"
929 file.print " if( (ercd_=cTDR_sendEOP(false)) != E_OK )\n" # b_continue = false
930 file.print " goto error_reset;\n"
931 end # ! f.is_oneway?
932
933 file.print " return E_OK;\n"
934 file.print <<EOT
935
936error_reset:
937EOT
938 # send のリセット用デアロケート
939 if f_type.has_send? then
940 file.print " /* dealloc send parameter */\n"
941 file.print " if( *state_ < RPCSTATE_SERVER_EXEC ){\n"
942 dir = :SEND; indent_level = 2; dealloc_cp = "cServerCall_#{f_name}"
943 dealloc_for_params( params, file, indent_level, dir, dealloc_cp, true )
944 file.print " }\n"
945 end
946
947 # receive のメモリをデアロケート
948 if f_type.has_receive? && b_ret_er then
949 file.print " /* dealloc receive parameter */\n"
950 file.print " if( MERCD( retval_ ) != E_RPC ){\n"
951 dir = :RECEIVE; indent_level = 2; dealloc_cp = "cServerCall_#{f_name}"
952 dealloc_for_params( params, file, indent_level, dir, dealloc_cp )
953 file.print " }\n"
954 end
955
956 file.print " return ERCD( E_RPC, MERCD( ercd_ ) );\n"
957 file.print "}\n\n"
958
959 # ここ(個々
960の関数)ではエラーハンドラーは呼び出さない。呼びå…
961ƒï¼ˆã‚µãƒ¼ãƒãƒ¼ã®ãƒ¡ã‚¤ãƒ³é–¢æ•°ï¼‰ã§å‘¼ã³å‡ºã™ã€‚
962 }
963 end
964
965
966 #b_marshal:: bool
967 #b_get:: bool
968 # b_marshal = true && b_get == false : マーシャラでå…
969¥åŠ›å¼•æ•°é€å‡º
970 # b_marshal = true && b_get == true : マーシャラで出力引数受取
971 # b_marshal = false && b_get == false : アンマーシャラでå…
972¥åŠ›å¼•æ•°å—取
973 # b_marshal = false && b_get == true : アンマーシャラで出力引数送出
974 #b_referenced:: size_is, count_is, string で参ç…
975§ã•ã‚Œã¦ã„るものを出力
976 def print_params( params, file, nest, b_marshal, b_get, b_referenced, port_name, func_name )
977 params.each{ |param|
978# p "#{param.get_name}: b_marshal: #{b_marshal} b_get: #{b_get}"
979 if ! ( b_referenced == param.is_referenced? ) then
980 next
981 end
982
983 dir = param.get_direction
984 if( b_get == false && b_marshal == true || b_get == true && b_marshal == false )then
985 case dir
986 when :IN, :INOUT
987 alloc_cp = "cPPAllocator_alloc"
988 alloc_cp_extra = nil
989 print_param( param.get_name, param.get_type, file, nest, dir, nil, nil, b_marshal, b_get, alloc_cp, alloc_cp_extra )
990 when :SEND
991 alloc_cp = "#{port_name}_#{func_name}_#{param.get_name}_alloc"
992 alloc_cp_extra = nil
993 print_param( param.get_name, param.get_type, file, nest, dir, nil, nil, b_marshal, b_get, alloc_cp, alloc_cp_extra )
994 end
995 else
996 case dir
997 when :OUT, :INOUT
998 alloc_cp = nil # inout の b_get==true&&b_marsha==true のときアロケータコードは不用
999 alloc_cp_extra = nil
1000 print_param( param.get_name, param.get_type, file, nest, dir, nil, nil, b_marshal, b_get, alloc_cp, alloc_cp_extra )
1001 when :RECEIVE
1002 alloc_cp = "#{port_name}_#{func_name}_#{param.get_name}_alloc"
1003 alloc_cp_extra = nil
1004 if b_get then
1005 outer = "(*" # マーシャラ側では、ポインタが (send と比べ) 一つ多い
1006 outer2 = ")"
1007 else
1008 outer = nil # アンマーシャラ側では、ポインタが一つ外されている
1009 outer2 = nil
1010 end
1011 type = param.get_type.get_referto
1012 print_param( param.get_name, type, file, nest, dir, outer, outer2, b_marshal, b_get, alloc_cp, alloc_cp_extra )
1013 end
1014 end
1015 }
1016 end
1017
1018 #=== アロケータコードを生成 (out のアンマーシャラ用)
1019 def alloc_for_out_params( params, file, nest, dir, alloc_cp, alloc_cp_extra )
1020 params.each{ |param|
1021 dir = param.get_direction
1022 if dir == :OUT then
1023 alloc_for_out_param( param.get_name, param.get_type, file, nest, nil, nil, alloc_cp, alloc_cp_extra )
1024 end
1025 }
1026 end
1027
1028 #=== アロケータコードを生成 (out のアンマーシャラ用個別パラメータの生成)
1029 def alloc_for_out_param( name, type, file, nest, outer, outer2, alloc_cp, alloc_cp_extra )
1030 org_type = type.get_original_type
1031 if org_type.is_nullable? then
1032 indent = "\t" * nest
1033 file.print "#{indent}if( ! b_#{name}_null_ ){\n"
1034 nest += 1
1035 end
1036 case org_type
1037 when PtrType
1038 indent = "\t" * nest
1039 count = type.get_count; size = type.get_size; string = type.get_string
1040 if count || size || string then
1041 loop_counter_type = IntType.new(16) # mikan 方を size_is, count_is の引数の型とする
1042 if count then
1043 len = type.get_count.to_s
1044 elsif size then
1045 len = type.get_size.to_s
1046 elsif string then
1047 if type.get_string.instance_of? Expression then
1048 len = type.get_string.to_s
1049 else
1050 raise "unsuscripted string used for out parameter #{name}"
1051 end
1052 end
1053
1054 # size_is に max 指定がある場合、length が max を超
1055えているかチェックするコードを生成
1056 if org_type.get_max != nil && string == nil then
1057 file.print "#{indent}if( #{len} > #{type.get_max} ){\t/* GenOpaqueMarshaler max check 2 */\n"
1058 file.print "#{indent} ercd_ = E_PAR;\n"
1059 file.print "#{indent} goto error_reset;\n"
1060 file.print "#{indent}}\n"
1061 end
1062
1063 file.print <<EOT
1064#{indent}if((ercd_=#{alloc_cp}(sizeof(#{type.get_type.get_type_str}#{type.get_type.get_type_str_post})*#{len},(void **)&#{outer}#{name}#{outer2}#{alloc_cp_extra}))!=E_OK)\t/* GenOpaqueMarshaler 1 */
1065#{indent} goto error_reset;
1066EOT
1067
1068 if type.get_type.kind_of? PtrType then
1069 file.print "#{indent}{\n"
1070 file.print "#{indent} #{loop_counter_type.get_type_str} i__#{nest}, length__#{nest} = #{len};\n"
1071 file.print "#{indent} for( i__#{nest} = 0; i__#{nest} < length__#{nest}; i__#{nest}++ ){\n"
1072 alloc_for_out_param( name, type.get_type, file, nest + 2, outer, "#{outer2}[i__#{nest}]", alloc_cp, alloc_cp_extra )
1073 file.print "#{indent} }\n"
1074 file.print "#{indent}}\n"
1075 end
1076
1077 else
1078 file.print <<EOT
1079#{indent}if((ercd_=#{alloc_cp}(sizeof(#{type.get_type.get_type_str}#{type.get_type.get_type_str_post}),(void **)&#{outer}#{name}#{outer2}#{alloc_cp_extra}))!=E_OK)\t/* GenOpaqueMarshaler 2 */
1080#{indent} goto error_reset;
1081EOT
1082 end
1083 end
1084 if org_type.is_nullable? then
1085 nest -= 1
1086 indent = "\t" * nest
1087 file.print "#{indent}} else {\n"
1088 file.print "#{indent} #{name} = NULL;\n"
1089 file.print "#{indent}}\n"
1090 nest += 1
1091 end
1092 end
1093
1094 #=== 引数の一括デアロケートコードの生成
1095 # send:マーシャラの最後、receive:アンマーシャラの最後で一括して引数をデアロケートする
1096 def dealloc_for_params( params, file, nest, dir, dealloc_cp, b_reset = false )
1097 if b_reset then
1098 reset_str = "_reset"
1099 else
1100 reset_str = ""
1101 end
1102
1103 params.each{ |param|
1104 if dir == param.get_direction then
1105 indent = "\t" * nest
1106 type = param.get_type.get_original_type
1107 aster = ""
1108 if dir == :RECEIVE then
1109 type = type.get_type.get_original_type # ポインタを一つ外す
1110 if b_reset then
1111 aster = "*"
1112 end
1113 end
1114 count = type.get_count; size = type.get_size
1115 if (size || count) && type.get_type.has_pointer? then
1116 if count then
1117 len = ", #{type.get_count.to_s}"
1118 elsif size then
1119 len = ", #{type.get_size.to_s}"
1120 end
1121 else
1122 len = ""
1123 end
1124 cp = "#{dealloc_cp}_#{param.get_name}_dealloc#{reset_str}".upcase
1125 file.print "#{indent}#{cp}(#{aster}#{param.get_name}#{len});\n"
1126 end
1127 }
1128 end
1129
1130 #== out で nullable な引数の情
1131報を渡す
1132 # out nullable の場合、in, send, receive のように、値を渡す直前ではなく、呼出し時に渡す
1133 def print_out_nullable( params, file, nest, b_marshal );
1134 indent = "\t" * nest
1135 params.each{ |param|
1136 next if param.get_direction != :OUT
1137 next if ! param.is_nullable?
1138 if b_marshal then
1139 file.print "#{indent}if( (ercd_=cTDR_putInt8( (int8_t)(#{param.get_name} == NULL) )) != E_OK )\n"
1140 file.print "#{indent}\tgoto error_reset;\n"
1141 else
1142 # 呼びå…
1143ˆã¯ alloc_for_out_param で nullable の対応する
1144 file.print "#{indent}if( (ercd_=cTDR_getInt8( &b_#{param.get_name}_null_)) != E_OK )\n"
1145 file.print "#{indent}\tgoto error_reset;\n"
1146 end
1147 }
1148 end
1149end
Note: See TracBrowser for help on using the repository browser.