1 /* 2 * IDL Compiler 3 * 4 * Copyright 2005-2006 Eric Kohl 5 * 6 * This library is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Lesser General Public 8 * License as published by the Free Software Foundation; either 9 * version 2.1 of the License, or (at your option) any later version. 10 * 11 * This library is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * Lesser General Public License for more details. 15 * 16 * You should have received a copy of the GNU Lesser General Public 17 * License along with this library; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 19 */ 20 21 #include "config.h" 22 #include "wine/port.h" 23 24 #include <stdio.h> 25 #include <stdlib.h> 26 #ifdef HAVE_UNISTD_H 27 # include <unistd.h> 28 #endif 29 #include <string.h> 30 #include <ctype.h> 31 32 #include "widl.h" 33 #include "utils.h" 34 #include "parser.h" 35 #include "header.h" 36 37 #include "widltypes.h" 38 #include "typegen.h" 39 #include "expr.h" 40 41 static FILE* client; 42 static int indent = 0; 43 44 static void print_client( const char *format, ... ) __attribute__((format (printf, 1, 2))); 45 static void print_client( const char *format, ... ) 46 { 47 va_list va; 48 va_start(va, format); 49 print(client, indent, format, va); 50 va_end(va); 51 } 52 53 static void write_client_func_decl( const type_t *iface, const var_t *func ) 54 { 55 const char *callconv = get_attrp(func->type->attrs, ATTR_CALLCONV); 56 const var_list_t *args = type_get_function_args(func->type); 57 type_t *rettype = type_function_get_rettype(func->type); 58 59 if (!callconv) callconv = "__cdecl"; 60 write_type_decl_left(client, rettype); 61 fprintf(client, " %s ", callconv); 62 fprintf(client, "%s%s(\n", prefix_client, get_name(func)); 63 indent++; 64 if (args) 65 write_args(client, args, iface->name, 0, TRUE); 66 else 67 print_client("void"); 68 fprintf(client, ")\n"); 69 indent--; 70 } 71 72 static void write_function_stub( const type_t *iface, const var_t *func, 73 int method_count, unsigned int proc_offset ) 74 { 75 unsigned char explicit_fc, implicit_fc; 76 int has_full_pointer = is_full_pointer_function(func); 77 var_t *retval = type_function_get_retval(func->type); 78 const var_t *handle_var = get_func_handle_var( iface, func, &explicit_fc, &implicit_fc ); 79 int has_ret = !is_void(retval->type); 80 81 if (is_interpreted_func( iface, func )) 82 { 83 write_client_func_decl( iface, func ); 84 write_client_call_routine( client, iface, func, iface->name, proc_offset ); 85 return; 86 } 87 88 print_client( "struct __frame_%s%s\n{\n", prefix_client, get_name(func) ); 89 indent++; 90 print_client( "__DECL_EXCEPTION_FRAME\n" ); 91 print_client("MIDL_STUB_MESSAGE _StubMsg;\n"); 92 if (handle_var) 93 { 94 if (explicit_fc == FC_BIND_GENERIC) 95 print_client("%s %s;\n", 96 get_explicit_generic_handle_type(handle_var)->name, handle_var->name ); 97 print_client("RPC_BINDING_HANDLE _Handle;\n"); 98 } 99 100 if (has_ret && decl_indirect(retval->type)) 101 { 102 print_client("void *_p_%s;\n", retval->name); 103 } 104 indent--; 105 print_client( "};\n\n" ); 106 107 print_client( "static void __finally_%s%s(", prefix_client, get_name(func) ); 108 print_client( " struct __frame_%s%s *__frame )\n{\n", prefix_client, get_name(func) ); 109 indent++; 110 111 if (has_full_pointer) 112 write_full_pointer_free(client, indent, func); 113 114 print_client("NdrFreeBuffer(&__frame->_StubMsg);\n"); 115 116 if (explicit_fc == FC_BIND_GENERIC) 117 { 118 fprintf(client, "\n"); 119 print_client("if (__frame->_Handle)\n"); 120 indent++; 121 print_client("%s_unbind(__frame->%s, __frame->_Handle);\n", 122 get_explicit_generic_handle_type(handle_var)->name, handle_var->name); 123 indent--; 124 } 125 indent--; 126 print_client( "}\n\n" ); 127 128 write_client_func_decl( iface, func ); 129 130 /* write the functions body */ 131 fprintf(client, "{\n"); 132 indent++; 133 print_client( "struct __frame_%s%s __f, * const __frame = &__f;\n", prefix_client, get_name(func) ); 134 135 /* declare return value */ 136 if (has_ret) 137 { 138 print_client("%s", ""); 139 write_type_decl(client, retval->type, retval->name); 140 fprintf(client, ";\n"); 141 } 142 print_client("RPC_MESSAGE _RpcMessage;\n"); 143 144 if (handle_var) 145 { 146 print_client( "__frame->_Handle = 0;\n" ); 147 if (explicit_fc == FC_BIND_GENERIC) 148 print_client("__frame->%s = %s;\n", handle_var->name, handle_var->name ); 149 } 150 if (has_ret && decl_indirect(retval->type)) 151 { 152 print_client("__frame->_p_%s = &%s;\n", retval->name, retval->name); 153 } 154 fprintf(client, "\n"); 155 156 print_client( "RpcExceptionInit( 0, __finally_%s%s );\n", prefix_client, get_name(func) ); 157 158 if (has_full_pointer) 159 write_full_pointer_init(client, indent, func, FALSE); 160 161 /* check pointers */ 162 write_pointer_checks( client, indent, func ); 163 164 print_client("RpcTryFinally\n"); 165 print_client("{\n"); 166 indent++; 167 168 print_client("NdrClientInitializeNew(&_RpcMessage, &__frame->_StubMsg, &%s_StubDesc, %d);\n", 169 iface->name, method_count); 170 171 if (is_attr(func->attrs, ATTR_IDEMPOTENT) || is_attr(func->attrs, ATTR_BROADCAST)) 172 { 173 print_client("_RpcMessage.RpcFlags = ( RPC_NCA_FLAGS_DEFAULT "); 174 if (is_attr(func->attrs, ATTR_IDEMPOTENT)) 175 fprintf(client, "| RPC_NCA_FLAGS_IDEMPOTENT "); 176 if (is_attr(func->attrs, ATTR_BROADCAST)) 177 fprintf(client, "| RPC_NCA_FLAGS_BROADCAST "); 178 fprintf(client, ");\n\n"); 179 } 180 181 switch (explicit_fc) 182 { 183 case FC_BIND_PRIMITIVE: 184 print_client("__frame->_Handle = %s;\n", handle_var->name); 185 fprintf(client, "\n"); 186 break; 187 case FC_BIND_GENERIC: 188 print_client("__frame->_Handle = %s_bind(%s);\n", 189 get_explicit_generic_handle_type(handle_var)->name, handle_var->name); 190 fprintf(client, "\n"); 191 break; 192 case FC_BIND_CONTEXT: 193 { 194 /* if the context_handle attribute appears in the chain of types 195 * without pointers being followed, then the context handle must 196 * be direct, otherwise it is a pointer */ 197 int is_ch_ptr = !is_aliaschain_attr(handle_var->type, ATTR_CONTEXTHANDLE); 198 print_client("if (%s%s != 0)\n", is_ch_ptr ? "*" : "", handle_var->name); 199 indent++; 200 print_client("__frame->_Handle = NDRCContextBinding(%s%s);\n", 201 is_ch_ptr ? "*" : "", handle_var->name); 202 indent--; 203 if (is_attr(handle_var->attrs, ATTR_IN) && !is_attr(handle_var->attrs, ATTR_OUT)) 204 { 205 print_client("else\n"); 206 indent++; 207 print_client("RpcRaiseException(RPC_X_SS_IN_NULL_CONTEXT);\n"); 208 indent--; 209 } 210 fprintf(client, "\n"); 211 break; 212 } 213 case 0: /* implicit handle */ 214 if (handle_var) 215 { 216 print_client("__frame->_Handle = %s;\n", handle_var->name); 217 fprintf(client, "\n"); 218 } 219 break; 220 } 221 222 write_remoting_arguments(client, indent, func, "", PASS_IN, PHASE_BUFFERSIZE); 223 224 print_client("NdrGetBuffer(&__frame->_StubMsg, __frame->_StubMsg.BufferLength, "); 225 if (handle_var) 226 fprintf(client, "__frame->_Handle);\n\n"); 227 else 228 fprintf(client,"%s__MIDL_AutoBindHandle);\n\n", iface->name); 229 230 /* marshal arguments */ 231 write_remoting_arguments(client, indent, func, "", PASS_IN, PHASE_MARSHAL); 232 233 /* send/receive message */ 234 /* print_client("NdrNsSendReceive(\n"); */ 235 /* print_client("(unsigned char *)__frame->_StubMsg.Buffer,\n"); */ 236 /* print_client("(RPC_BINDING_HANDLE *) &%s__MIDL_AutoBindHandle);\n", iface->name); */ 237 print_client("NdrSendReceive(&__frame->_StubMsg, __frame->_StubMsg.Buffer);\n\n"); 238 239 print_client("__frame->_StubMsg.BufferStart = _RpcMessage.Buffer;\n"); 240 print_client("__frame->_StubMsg.BufferEnd = __frame->_StubMsg.BufferStart + _RpcMessage.BufferLength;\n"); 241 242 if (has_out_arg_or_return(func)) 243 { 244 fprintf(client, "\n"); 245 246 print_client("if ((_RpcMessage.DataRepresentation & 0x0000FFFFUL) != NDR_LOCAL_DATA_REPRESENTATION)\n"); 247 indent++; 248 print_client("NdrConvert(&__frame->_StubMsg, (PFORMAT_STRING)&__MIDL_ProcFormatString.Format[%u]);\n", 249 proc_offset); 250 indent--; 251 } 252 253 /* unmarshall arguments */ 254 fprintf(client, "\n"); 255 write_remoting_arguments(client, indent, func, "", PASS_OUT, PHASE_UNMARSHAL); 256 257 /* unmarshal return value */ 258 if (has_ret) 259 { 260 if (decl_indirect(retval->type)) 261 print_client("MIDL_memset(&%s, 0, sizeof(%s));\n", retval->name, retval->name); 262 else if (is_ptr(retval->type) || is_array(retval->type)) 263 print_client("%s = 0;\n", retval->name); 264 write_remoting_arguments(client, indent, func, "", PASS_RETURN, PHASE_UNMARSHAL); 265 } 266 267 indent--; 268 print_client("}\n"); 269 print_client("RpcFinally\n"); 270 print_client("{\n"); 271 indent++; 272 print_client( "__finally_%s%s( __frame );\n", prefix_client, get_name(func) ); 273 indent--; 274 print_client("}\n"); 275 print_client("RpcEndFinally\n"); 276 277 278 /* emit return code */ 279 if (has_ret) 280 { 281 fprintf(client, "\n"); 282 print_client("return %s;\n", retval->name); 283 } 284 285 indent--; 286 fprintf(client, "}\n"); 287 fprintf(client, "\n"); 288 } 289 290 static void write_serialize_function(FILE *file, const type_t *type, const type_t *iface, 291 const char *func_name, const char *ret_type) 292 { 293 enum stub_mode mode = get_stub_mode(); 294 static int emited_pickling_info; 295 296 if (iface && !type->typestring_offset) 297 { 298 /* FIXME: Those are mostly basic types. They should be implemented 299 * using NdrMesSimpleType* functions */ 300 if (ret_type) warning("Serialization of type %s is not supported\n", type->name); 301 return; 302 } 303 304 if (!emited_pickling_info && iface && mode != MODE_Os) 305 { 306 fprintf(file, "static const MIDL_TYPE_PICKLING_INFO __MIDL_TypePicklingInfo =\n"); 307 fprintf(file, "{\n"); 308 fprintf(file, " 0x33205054,\n"); 309 fprintf(file, " 0x3,\n"); 310 fprintf(file, " 0,\n"); 311 fprintf(file, " 0,\n"); 312 fprintf(file, " 0\n"); 313 fprintf(file, "};\n"); 314 fprintf(file, "\n"); 315 emited_pickling_info = 1; 316 } 317 318 /* FIXME: Assuming explicit handle */ 319 320 fprintf(file, "%s __cdecl %s_%s(handle_t IDL_handle, %s *IDL_type)%s\n", 321 ret_type ? ret_type : "void", type->name, func_name, type->name, iface ? "" : ";"); 322 if (!iface) return; /* declaration only */ 323 324 fprintf(file, "{\n"); 325 fprintf(file, " %sNdrMesType%s%s(\n", ret_type ? "return " : "", func_name, 326 mode != MODE_Os ? "2" : ""); 327 fprintf(file, " IDL_handle,\n"); 328 if (mode != MODE_Os) 329 fprintf(file, " (MIDL_TYPE_PICKLING_INFO*)&__MIDL_TypePicklingInfo,\n"); 330 fprintf(file, " &%s_StubDesc,\n", iface->name); 331 fprintf(file, " (PFORMAT_STRING)&__MIDL_TypeFormatString.Format[%u],\n", 332 type->typestring_offset); 333 fprintf(file, " IDL_type);\n"); 334 fprintf(file, "}\n"); 335 fprintf(file, "\n"); 336 } 337 338 void write_serialize_functions(FILE *file, const type_t *type, const type_t *iface) 339 { 340 if (is_attr(type->attrs, ATTR_ENCODE)) 341 { 342 write_serialize_function(file, type, iface, "AlignSize", "SIZE_T"); 343 write_serialize_function(file, type, iface, "Encode", NULL); 344 } 345 if (is_attr(type->attrs, ATTR_DECODE)) 346 { 347 write_serialize_function(file, type, iface, "Decode", NULL); 348 write_serialize_function(file, type, iface, "Free", NULL); 349 } 350 } 351 352 static void write_function_stubs(type_t *iface, unsigned int *proc_offset) 353 { 354 const statement_t *stmt; 355 const var_t *implicit_handle = get_attrp(iface->attrs, ATTR_IMPLICIT_HANDLE); 356 int method_count = 0; 357 358 if (!implicit_handle) 359 print_client("static RPC_BINDING_HANDLE %s__MIDL_AutoBindHandle;\n\n", iface->name); 360 361 LIST_FOR_EACH_ENTRY( stmt, type_iface_get_stmts(iface), const statement_t, entry ) 362 { 363 switch (stmt->type) 364 { 365 case STMT_DECLARATION: 366 { 367 const var_t *func = stmt->u.var; 368 if (stmt->u.var->stgclass != STG_NONE 369 || type_get_type_detect_alias(stmt->u.var->type) != TYPE_FUNCTION) 370 continue; 371 write_function_stub( iface, func, method_count++, *proc_offset ); 372 *proc_offset += get_size_procformatstring_func( iface, func ); 373 break; 374 } 375 case STMT_TYPEDEF: 376 { 377 const type_list_t *type_entry; 378 for (type_entry = stmt->u.type_list; type_entry; type_entry = type_entry->next) 379 write_serialize_functions(client, type_entry->type, iface); 380 break; 381 } 382 default: 383 break; 384 } 385 } 386 } 387 388 389 static void write_stubdescdecl(type_t *iface) 390 { 391 print_client("static const MIDL_STUB_DESC %s_StubDesc;\n", iface->name); 392 fprintf(client, "\n"); 393 } 394 395 396 static void write_stubdescriptor(type_t *iface, int expr_eval_routines) 397 { 398 const var_t *implicit_handle = get_attrp(iface->attrs, ATTR_IMPLICIT_HANDLE); 399 400 print_client("static const MIDL_STUB_DESC %s_StubDesc =\n", iface->name); 401 print_client("{\n"); 402 indent++; 403 print_client("(void *)& %s___RpcClientInterface,\n", iface->name); 404 print_client("MIDL_user_allocate,\n"); 405 print_client("MIDL_user_free,\n"); 406 print_client("{\n"); 407 indent++; 408 if (implicit_handle) 409 print_client("&%s,\n", implicit_handle->name); 410 else 411 print_client("&%s__MIDL_AutoBindHandle,\n", iface->name); 412 indent--; 413 print_client("},\n"); 414 print_client("0,\n"); 415 if (!list_empty( &generic_handle_list )) 416 print_client("BindingRoutines,\n"); 417 else 418 print_client("0,\n"); 419 if (expr_eval_routines) 420 print_client("ExprEvalRoutines,\n"); 421 else 422 print_client("0,\n"); 423 print_client("0,\n"); 424 print_client("__MIDL_TypeFormatString.Format,\n"); 425 print_client("1, /* -error bounds_check flag */\n"); 426 print_client("0x%x, /* Ndr library version */\n", get_stub_mode() == MODE_Oif ? 0x50002 : 0x10001); 427 print_client("0,\n"); 428 print_client("0x50100a4, /* MIDL Version 5.1.164 */\n"); 429 print_client("0,\n"); 430 print_client("%s,\n", list_empty(&user_type_list) ? "0" : "UserMarshalRoutines"); 431 print_client("0, /* notify & notify_flag routine table */\n"); 432 print_client("1, /* Flags */\n"); 433 print_client("0, /* Reserved3 */\n"); 434 print_client("0, /* Reserved4 */\n"); 435 print_client("0 /* Reserved5 */\n"); 436 indent--; 437 print_client("};\n"); 438 fprintf(client, "\n"); 439 } 440 441 442 static void write_clientinterfacedecl(type_t *iface) 443 { 444 unsigned int ver = get_attrv(iface->attrs, ATTR_VERSION); 445 const UUID *uuid = get_attrp(iface->attrs, ATTR_UUID); 446 const str_list_t *endpoints = get_attrp(iface->attrs, ATTR_ENDPOINT); 447 448 if (endpoints) write_endpoints( client, iface->name, endpoints ); 449 450 print_client("static const RPC_CLIENT_INTERFACE %s___RpcClientInterface =\n", iface->name ); 451 print_client("{\n"); 452 indent++; 453 print_client("sizeof(RPC_CLIENT_INTERFACE),\n"); 454 print_client("{{0x%08x,0x%04x,0x%04x,{0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x}},{%d,%d}},\n", 455 uuid->Data1, uuid->Data2, uuid->Data3, uuid->Data4[0], uuid->Data4[1], 456 uuid->Data4[2], uuid->Data4[3], uuid->Data4[4], uuid->Data4[5], uuid->Data4[6], 457 uuid->Data4[7], MAJORVERSION(ver), MINORVERSION(ver)); 458 print_client("{{0x8a885d04,0x1ceb,0x11c9,{0x9f,0xe8,0x08,0x00,0x2b,0x10,0x48,0x60}},{2,0}},\n"); /* FIXME */ 459 print_client("0,\n"); 460 if (endpoints) 461 { 462 print_client("%u,\n", list_count(endpoints)); 463 print_client("(PRPC_PROTSEQ_ENDPOINT)%s__RpcProtseqEndpoint,\n", iface->name); 464 } 465 else 466 { 467 print_client("0,\n"); 468 print_client("0,\n"); 469 } 470 print_client("0,\n"); 471 print_client("0,\n"); 472 print_client("0,\n"); 473 indent--; 474 print_client("};\n"); 475 if (old_names) 476 print_client("RPC_IF_HANDLE %s_ClientIfHandle DECLSPEC_HIDDEN = (RPC_IF_HANDLE)& %s___RpcClientInterface;\n", 477 iface->name, iface->name); 478 else 479 print_client("RPC_IF_HANDLE %s%s_v%d_%d_c_ifspec DECLSPEC_HIDDEN = (RPC_IF_HANDLE)& %s___RpcClientInterface;\n", 480 prefix_client, iface->name, MAJORVERSION(ver), MINORVERSION(ver), iface->name); 481 fprintf(client, "\n"); 482 } 483 484 485 static void write_implicithandledecl(type_t *iface) 486 { 487 const var_t *implicit_handle = get_attrp(iface->attrs, ATTR_IMPLICIT_HANDLE); 488 489 if (implicit_handle) 490 { 491 write_type_decl( client, implicit_handle->type, implicit_handle->name ); 492 fprintf(client, ";\n\n"); 493 } 494 } 495 496 497 static void init_client(void) 498 { 499 if (client) return; 500 if (!(client = fopen(client_name, "w"))) 501 error("Could not open %s for output\n", client_name); 502 503 print_client("/*** Autogenerated by WIDL %s from %s - Do not edit ***/\n", PACKAGE_VERSION, input_name); 504 print_client("#include <string.h>\n"); 505 print_client( "\n"); 506 print_client("#include \"%s\"\n", header_name); 507 print_client( "\n"); 508 print_client( "#ifndef DECLSPEC_HIDDEN\n"); 509 print_client( "#define DECLSPEC_HIDDEN\n"); 510 print_client( "#endif\n"); 511 print_client( "\n"); 512 } 513 514 515 static void write_client_ifaces(const statement_list_t *stmts, int expr_eval_routines, unsigned int *proc_offset) 516 { 517 const statement_t *stmt; 518 if (stmts) LIST_FOR_EACH_ENTRY( stmt, stmts, const statement_t, entry ) 519 { 520 if (stmt->type == STMT_TYPE && type_get_type(stmt->u.type) == TYPE_INTERFACE) 521 { 522 int needs_stub = 0; 523 const statement_t *stmt2; 524 type_t *iface = stmt->u.type; 525 if (!need_stub(iface)) 526 return; 527 528 fprintf(client, "/*****************************************************************************\n"); 529 fprintf(client, " * %s interface\n", iface->name); 530 fprintf(client, " */\n"); 531 fprintf(client, "\n"); 532 533 LIST_FOR_EACH_ENTRY(stmt2, type_iface_get_stmts(iface), const statement_t, entry) 534 { 535 if (stmt2->type == STMT_DECLARATION && stmt2->u.var->stgclass == STG_NONE && 536 type_get_type_detect_alias(stmt2->u.var->type) == TYPE_FUNCTION) 537 { 538 needs_stub = 1; 539 break; 540 } 541 if (stmt2->type == STMT_TYPEDEF) 542 { 543 const type_list_t *type_entry; 544 for (type_entry = stmt2->u.type_list; type_entry; type_entry = type_entry->next) 545 { 546 if (is_attr(type_entry->type->attrs, ATTR_ENCODE) 547 || is_attr(type_entry->type->attrs, ATTR_DECODE)) 548 { 549 needs_stub = 1; 550 break; 551 } 552 } 553 if (needs_stub) 554 break; 555 } 556 } 557 if (needs_stub) 558 { 559 write_implicithandledecl(iface); 560 561 write_clientinterfacedecl(iface); 562 write_stubdescdecl(iface); 563 write_function_stubs(iface, proc_offset); 564 565 print_client("#if !defined(__RPC_WIN%u__)\n", pointer_size == 8 ? 64 : 32); 566 print_client("#error Invalid build platform for this stub.\n"); 567 print_client("#endif\n"); 568 569 fprintf(client, "\n"); 570 write_stubdescriptor(iface, expr_eval_routines); 571 } 572 } 573 } 574 } 575 576 static void write_generic_handle_routine_list(void) 577 { 578 generic_handle_t *gh; 579 580 if (list_empty( &generic_handle_list )) return; 581 print_client( "static const GENERIC_BINDING_ROUTINE_PAIR BindingRoutines[] =\n" ); 582 print_client( "{\n" ); 583 indent++; 584 LIST_FOR_EACH_ENTRY( gh, &generic_handle_list, generic_handle_t, entry ) 585 { 586 print_client( "{ (GENERIC_BINDING_ROUTINE)%s_bind, (GENERIC_UNBIND_ROUTINE)%s_unbind },\n", 587 gh->name, gh->name ); 588 } 589 indent--; 590 print_client( "};\n\n" ); 591 } 592 593 static void write_client_routines(const statement_list_t *stmts) 594 { 595 unsigned int proc_offset = 0; 596 int expr_eval_routines; 597 598 if (need_inline_stubs_file( stmts )) 599 { 600 write_exceptions( client ); 601 print_client( "\n"); 602 } 603 604 write_formatstringsdecl(client, indent, stmts, need_stub); 605 expr_eval_routines = write_expr_eval_routines(client, client_token); 606 if (expr_eval_routines) 607 write_expr_eval_routine_list(client, client_token); 608 write_generic_handle_routine_list(); 609 write_user_quad_list(client); 610 611 write_client_ifaces(stmts, expr_eval_routines, &proc_offset); 612 613 fprintf(client, "\n"); 614 615 write_procformatstring(client, stmts, need_stub); 616 write_typeformatstring(client, stmts, need_stub); 617 } 618 619 void write_client(const statement_list_t *stmts) 620 { 621 if (!do_client) 622 return; 623 if (do_everything && !need_stub_files(stmts)) 624 return; 625 626 init_client(); 627 if (!client) 628 return; 629 630 write_client_routines( stmts ); 631 fclose(client); 632 } 633