1 /* 2 * This file is part of gtkD. 3 * 4 * gtkD is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU Lesser General Public License 6 * as published by the Free Software Foundation; either version 3 7 * of the License, or (at your option) any later version, with 8 * some exceptions, please read the COPYING file. 9 * 10 * gtkD is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU Lesser General Public License for more details. 14 * 15 * You should have received a copy of the GNU Lesser General Public License 16 * along with gtkD; if not, write to the Free Software 17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, USA 18 */ 19 20 // generated automatically - do not change 21 // find conversion definition on APILookup.txt 22 // implement new conversion functionalities on the wrap.utils pakage 23 24 25 module gio.Subprocess; 26 27 private import gio.AsyncResultIF; 28 private import gio.Cancellable; 29 private import gio.InitableIF; 30 private import gio.InitableT; 31 private import gio.InputStream; 32 private import gio.OutputStream; 33 private import gio.c.functions; 34 public import gio.c.types; 35 private import glib.Bytes; 36 private import glib.ConstructionException; 37 private import glib.ErrorG; 38 private import glib.GException; 39 private import glib.Str; 40 private import gobject.ObjectG; 41 public import gtkc.giotypes; 42 43 44 /** 45 * #GSubprocess allows the creation of and interaction with child 46 * processes. 47 * 48 * Processes can be communicated with using standard GIO-style APIs (ie: 49 * #GInputStream, #GOutputStream). There are GIO-style APIs to wait for 50 * process termination (ie: cancellable and with an asynchronous 51 * variant). 52 * 53 * There is an API to force a process to terminate, as well as a 54 * race-free API for sending UNIX signals to a subprocess. 55 * 56 * One major advantage that GIO brings over the core GLib library is 57 * comprehensive API for asynchronous I/O, such 58 * g_output_stream_splice_async(). This makes GSubprocess 59 * significantly more powerful and flexible than equivalent APIs in 60 * some other languages such as the `subprocess.py` 61 * included with Python. For example, using #GSubprocess one could 62 * create two child processes, reading standard output from the first, 63 * processing it, and writing to the input stream of the second, all 64 * without blocking the main loop. 65 * 66 * A powerful g_subprocess_communicate() API is provided similar to the 67 * `communicate()` method of `subprocess.py`. This enables very easy 68 * interaction with a subprocess that has been opened with pipes. 69 * 70 * #GSubprocess defaults to tight control over the file descriptors open 71 * in the child process, avoiding dangling-fd issues that are caused by 72 * a simple fork()/exec(). The only open file descriptors in the 73 * spawned process are ones that were explicitly specified by the 74 * #GSubprocess API (unless %G_SUBPROCESS_FLAGS_INHERIT_FDS was 75 * specified). 76 * 77 * #GSubprocess will quickly reap all child processes as they exit, 78 * avoiding "zombie processes" remaining around for long periods of 79 * time. g_subprocess_wait() can be used to wait for this to happen, 80 * but it will happen even without the call being explicitly made. 81 * 82 * As a matter of principle, #GSubprocess has no API that accepts 83 * shell-style space-separated strings. It will, however, match the 84 * typical shell behaviour of searching the PATH for executables that do 85 * not contain a directory separator in their name. 86 * 87 * #GSubprocess attempts to have a very simple API for most uses (ie: 88 * spawning a subprocess with arguments and support for most typical 89 * kinds of input and output redirection). See g_subprocess_new(). The 90 * #GSubprocessLauncher API is provided for more complicated cases 91 * (advanced types of redirection, environment variable manipulation, 92 * change of working directory, child setup functions, etc). 93 * 94 * A typical use of #GSubprocess will involve calling 95 * g_subprocess_new(), followed by g_subprocess_wait_async() or 96 * g_subprocess_wait(). After the process exits, the status can be 97 * checked using functions such as g_subprocess_get_if_exited() (which 98 * are similar to the familiar WIFEXITED-style POSIX macros). 99 * 100 * Since: 2.40 101 */ 102 public class Subprocess : ObjectG, InitableIF 103 { 104 /** the main Gtk struct */ 105 protected GSubprocess* gSubprocess; 106 107 /** Get the main Gtk struct */ 108 public GSubprocess* getSubprocessStruct(bool transferOwnership = false) 109 { 110 if (transferOwnership) 111 ownedRef = false; 112 return gSubprocess; 113 } 114 115 /** the main Gtk struct as a void* */ 116 protected override void* getStruct() 117 { 118 return cast(void*)gSubprocess; 119 } 120 121 /** 122 * Sets our main struct and passes it to the parent class. 123 */ 124 public this (GSubprocess* gSubprocess, bool ownedRef = false) 125 { 126 this.gSubprocess = gSubprocess; 127 super(cast(GObject*)gSubprocess, ownedRef); 128 } 129 130 // add the Initable capabilities 131 mixin InitableT!(GSubprocess); 132 133 134 /** */ 135 public static GType getType() 136 { 137 return g_subprocess_get_type(); 138 } 139 140 /** 141 * Create a new process with the given flags and argument list. 142 * 143 * The argument list is expected to be %NULL-terminated. 144 * 145 * Params: 146 * argv = commandline arguments for the subprocess 147 * flags = flags that define the behaviour of the subprocess 148 * 149 * Returns: A newly created #GSubprocess, or %NULL on error (and @error 150 * will be set) 151 * 152 * Since: 2.40 153 * 154 * Throws: GException on failure. 155 * Throws: ConstructionException GTK+ fails to create the object. 156 */ 157 public this(string[] argv, GSubprocessFlags flags) 158 { 159 GError* err = null; 160 161 auto p = g_subprocess_newv(Str.toStringzArray(argv), flags, &err); 162 163 if (err !is null) 164 { 165 throw new GException( new ErrorG(err) ); 166 } 167 168 if(p is null) 169 { 170 throw new ConstructionException("null returned by newv"); 171 } 172 173 this(cast(GSubprocess*) p, true); 174 } 175 176 /** 177 * Communicate with the subprocess until it terminates, and all input 178 * and output has been completed. 179 * 180 * If @stdin_buf is given, the subprocess must have been created with 181 * %G_SUBPROCESS_FLAGS_STDIN_PIPE. The given data is fed to the 182 * stdin of the subprocess and the pipe is closed (ie: EOF). 183 * 184 * At the same time (as not to cause blocking when dealing with large 185 * amounts of data), if %G_SUBPROCESS_FLAGS_STDOUT_PIPE or 186 * %G_SUBPROCESS_FLAGS_STDERR_PIPE were used, reads from those 187 * streams. The data that was read is returned in @stdout and/or 188 * the @stderr. 189 * 190 * If the subprocess was created with %G_SUBPROCESS_FLAGS_STDOUT_PIPE, 191 * @stdout_buf will contain the data read from stdout. Otherwise, for 192 * subprocesses not created with %G_SUBPROCESS_FLAGS_STDOUT_PIPE, 193 * @stdout_buf will be set to %NULL. Similar provisions apply to 194 * @stderr_buf and %G_SUBPROCESS_FLAGS_STDERR_PIPE. 195 * 196 * As usual, any output variable may be given as %NULL to ignore it. 197 * 198 * If you desire the stdout and stderr data to be interleaved, create 199 * the subprocess with %G_SUBPROCESS_FLAGS_STDOUT_PIPE and 200 * %G_SUBPROCESS_FLAGS_STDERR_MERGE. The merged result will be returned 201 * in @stdout_buf and @stderr_buf will be set to %NULL. 202 * 203 * In case of any error (including cancellation), %FALSE will be 204 * returned with @error set. Some or all of the stdin data may have 205 * been written. Any stdout or stderr data that has been read will be 206 * discarded. None of the out variables (aside from @error) will have 207 * been set to anything in particular and should not be inspected. 208 * 209 * In the case that %TRUE is returned, the subprocess has exited and the 210 * exit status inspection APIs (eg: g_subprocess_get_if_exited(), 211 * g_subprocess_get_exit_status()) may be used. 212 * 213 * You should not attempt to use any of the subprocess pipes after 214 * starting this function, since they may be left in strange states, 215 * even if the operation was cancelled. You should especially not 216 * attempt to interact with the pipes while the operation is in progress 217 * (either from another thread or if using the asynchronous version). 218 * 219 * Params: 220 * stdinBuf = data to send to the stdin of the subprocess, or %NULL 221 * cancellable = a #GCancellable 222 * stdoutBuf = data read from the subprocess stdout 223 * stderrBuf = data read from the subprocess stderr 224 * 225 * Returns: %TRUE if successful 226 * 227 * Since: 2.40 228 * 229 * Throws: GException on failure. 230 */ 231 public bool communicate(Bytes stdinBuf, Cancellable cancellable, out Bytes stdoutBuf, out Bytes stderrBuf) 232 { 233 GBytes* outstdoutBuf = null; 234 GBytes* outstderrBuf = null; 235 GError* err = null; 236 237 auto p = g_subprocess_communicate(gSubprocess, (stdinBuf is null) ? null : stdinBuf.getBytesStruct(), (cancellable is null) ? null : cancellable.getCancellableStruct(), &outstdoutBuf, &outstderrBuf, &err) != 0; 238 239 if (err !is null) 240 { 241 throw new GException( new ErrorG(err) ); 242 } 243 244 stdoutBuf = new Bytes(outstdoutBuf); 245 stderrBuf = new Bytes(outstderrBuf); 246 247 return p; 248 } 249 250 /** 251 * Asynchronous version of g_subprocess_communicate(). Complete 252 * invocation with g_subprocess_communicate_finish(). 253 * 254 * Params: 255 * stdinBuf = Input data, or %NULL 256 * cancellable = Cancellable 257 * callback = Callback 258 * userData = User data 259 */ 260 public void communicateAsync(Bytes stdinBuf, Cancellable cancellable, GAsyncReadyCallback callback, void* userData) 261 { 262 g_subprocess_communicate_async(gSubprocess, (stdinBuf is null) ? null : stdinBuf.getBytesStruct(), (cancellable is null) ? null : cancellable.getCancellableStruct(), callback, userData); 263 } 264 265 /** 266 * Complete an invocation of g_subprocess_communicate_async(). 267 * 268 * Params: 269 * result = Result 270 * stdoutBuf = Return location for stdout data 271 * stderrBuf = Return location for stderr data 272 * 273 * Throws: GException on failure. 274 */ 275 public bool communicateFinish(AsyncResultIF result, out Bytes stdoutBuf, out Bytes stderrBuf) 276 { 277 GBytes* outstdoutBuf = null; 278 GBytes* outstderrBuf = null; 279 GError* err = null; 280 281 auto p = g_subprocess_communicate_finish(gSubprocess, (result is null) ? null : result.getAsyncResultStruct(), &outstdoutBuf, &outstderrBuf, &err) != 0; 282 283 if (err !is null) 284 { 285 throw new GException( new ErrorG(err) ); 286 } 287 288 stdoutBuf = new Bytes(outstdoutBuf); 289 stderrBuf = new Bytes(outstderrBuf); 290 291 return p; 292 } 293 294 /** 295 * Like g_subprocess_communicate(), but validates the output of the 296 * process as UTF-8, and returns it as a regular NUL terminated string. 297 * 298 * On error, @stdout_buf and @stderr_buf will be set to undefined values and 299 * should not be used. 300 * 301 * Params: 302 * stdinBuf = data to send to the stdin of the subprocess, or %NULL 303 * cancellable = a #GCancellable 304 * stdoutBuf = data read from the subprocess stdout 305 * stderrBuf = data read from the subprocess stderr 306 * 307 * Throws: GException on failure. 308 */ 309 public bool communicateUtf8(string stdinBuf, Cancellable cancellable, out string stdoutBuf, out string stderrBuf) 310 { 311 char* outstdoutBuf = null; 312 char* outstderrBuf = null; 313 GError* err = null; 314 315 auto p = g_subprocess_communicate_utf8(gSubprocess, Str.toStringz(stdinBuf), (cancellable is null) ? null : cancellable.getCancellableStruct(), &outstdoutBuf, &outstderrBuf, &err) != 0; 316 317 if (err !is null) 318 { 319 throw new GException( new ErrorG(err) ); 320 } 321 322 stdoutBuf = Str.toString(outstdoutBuf); 323 stderrBuf = Str.toString(outstderrBuf); 324 325 return p; 326 } 327 328 /** 329 * Asynchronous version of g_subprocess_communicate_utf8(). Complete 330 * invocation with g_subprocess_communicate_utf8_finish(). 331 * 332 * Params: 333 * stdinBuf = Input data, or %NULL 334 * cancellable = Cancellable 335 * callback = Callback 336 * userData = User data 337 */ 338 public void communicateUtf8Async(string stdinBuf, Cancellable cancellable, GAsyncReadyCallback callback, void* userData) 339 { 340 g_subprocess_communicate_utf8_async(gSubprocess, Str.toStringz(stdinBuf), (cancellable is null) ? null : cancellable.getCancellableStruct(), callback, userData); 341 } 342 343 /** 344 * Complete an invocation of g_subprocess_communicate_utf8_async(). 345 * 346 * Params: 347 * result = Result 348 * stdoutBuf = Return location for stdout data 349 * stderrBuf = Return location for stderr data 350 * 351 * Throws: GException on failure. 352 */ 353 public bool communicateUtf8Finish(AsyncResultIF result, out string stdoutBuf, out string stderrBuf) 354 { 355 char* outstdoutBuf = null; 356 char* outstderrBuf = null; 357 GError* err = null; 358 359 auto p = g_subprocess_communicate_utf8_finish(gSubprocess, (result is null) ? null : result.getAsyncResultStruct(), &outstdoutBuf, &outstderrBuf, &err) != 0; 360 361 if (err !is null) 362 { 363 throw new GException( new ErrorG(err) ); 364 } 365 366 stdoutBuf = Str.toString(outstdoutBuf); 367 stderrBuf = Str.toString(outstderrBuf); 368 369 return p; 370 } 371 372 /** 373 * Use an operating-system specific method to attempt an immediate, 374 * forceful termination of the process. There is no mechanism to 375 * determine whether or not the request itself was successful; 376 * however, you can use g_subprocess_wait() to monitor the status of 377 * the process after calling this function. 378 * 379 * On Unix, this function sends %SIGKILL. 380 * 381 * Since: 2.40 382 */ 383 public void forceExit() 384 { 385 g_subprocess_force_exit(gSubprocess); 386 } 387 388 /** 389 * Check the exit status of the subprocess, given that it exited 390 * normally. This is the value passed to the exit() system call or the 391 * return value from main. 392 * 393 * This is equivalent to the system WEXITSTATUS macro. 394 * 395 * It is an error to call this function before g_subprocess_wait() and 396 * unless g_subprocess_get_if_exited() returned %TRUE. 397 * 398 * Returns: the exit status 399 * 400 * Since: 2.40 401 */ 402 public int getExitStatus() 403 { 404 return g_subprocess_get_exit_status(gSubprocess); 405 } 406 407 /** 408 * On UNIX, returns the process ID as a decimal string. 409 * On Windows, returns the result of GetProcessId() also as a string. 410 */ 411 public string getIdentifier() 412 { 413 return Str.toString(g_subprocess_get_identifier(gSubprocess)); 414 } 415 416 /** 417 * Check if the given subprocess exited normally (ie: by way of exit() 418 * or return from main()). 419 * 420 * This is equivalent to the system WIFEXITED macro. 421 * 422 * It is an error to call this function before g_subprocess_wait() has 423 * returned. 424 * 425 * Returns: %TRUE if the case of a normal exit 426 * 427 * Since: 2.40 428 */ 429 public bool getIfExited() 430 { 431 return g_subprocess_get_if_exited(gSubprocess) != 0; 432 } 433 434 /** 435 * Check if the given subprocess terminated in response to a signal. 436 * 437 * This is equivalent to the system WIFSIGNALED macro. 438 * 439 * It is an error to call this function before g_subprocess_wait() has 440 * returned. 441 * 442 * Returns: %TRUE if the case of termination due to a signal 443 * 444 * Since: 2.40 445 */ 446 public bool getIfSignaled() 447 { 448 return g_subprocess_get_if_signaled(gSubprocess) != 0; 449 } 450 451 /** 452 * Gets the raw status code of the process, as from waitpid(). 453 * 454 * This value has no particular meaning, but it can be used with the 455 * macros defined by the system headers such as WIFEXITED. It can also 456 * be used with g_spawn_check_exit_status(). 457 * 458 * It is more likely that you want to use g_subprocess_get_if_exited() 459 * followed by g_subprocess_get_exit_status(). 460 * 461 * It is an error to call this function before g_subprocess_wait() has 462 * returned. 463 * 464 * Returns: the (meaningless) waitpid() exit status from the kernel 465 * 466 * Since: 2.40 467 */ 468 public int getStatus() 469 { 470 return g_subprocess_get_status(gSubprocess); 471 } 472 473 /** 474 * Gets the #GInputStream from which to read the stderr output of 475 * @subprocess. 476 * 477 * The process must have been created with 478 * %G_SUBPROCESS_FLAGS_STDERR_PIPE. 479 * 480 * Returns: the stderr pipe 481 * 482 * Since: 2.40 483 */ 484 public InputStream getStderrPipe() 485 { 486 auto p = g_subprocess_get_stderr_pipe(gSubprocess); 487 488 if(p is null) 489 { 490 return null; 491 } 492 493 return ObjectG.getDObject!(InputStream)(cast(GInputStream*) p); 494 } 495 496 /** 497 * Gets the #GOutputStream that you can write to in order to give data 498 * to the stdin of @subprocess. 499 * 500 * The process must have been created with 501 * %G_SUBPROCESS_FLAGS_STDIN_PIPE. 502 * 503 * Returns: the stdout pipe 504 * 505 * Since: 2.40 506 */ 507 public OutputStream getStdinPipe() 508 { 509 auto p = g_subprocess_get_stdin_pipe(gSubprocess); 510 511 if(p is null) 512 { 513 return null; 514 } 515 516 return ObjectG.getDObject!(OutputStream)(cast(GOutputStream*) p); 517 } 518 519 /** 520 * Gets the #GInputStream from which to read the stdout output of 521 * @subprocess. 522 * 523 * The process must have been created with 524 * %G_SUBPROCESS_FLAGS_STDOUT_PIPE. 525 * 526 * Returns: the stdout pipe 527 * 528 * Since: 2.40 529 */ 530 public InputStream getStdoutPipe() 531 { 532 auto p = g_subprocess_get_stdout_pipe(gSubprocess); 533 534 if(p is null) 535 { 536 return null; 537 } 538 539 return ObjectG.getDObject!(InputStream)(cast(GInputStream*) p); 540 } 541 542 /** 543 * Checks if the process was "successful". A process is considered 544 * successful if it exited cleanly with an exit status of 0, either by 545 * way of the exit() system call or return from main(). 546 * 547 * It is an error to call this function before g_subprocess_wait() has 548 * returned. 549 * 550 * Returns: %TRUE if the process exited cleanly with a exit status of 0 551 * 552 * Since: 2.40 553 */ 554 public bool getSuccessful() 555 { 556 return g_subprocess_get_successful(gSubprocess) != 0; 557 } 558 559 /** 560 * Get the signal number that caused the subprocess to terminate, given 561 * that it terminated due to a signal. 562 * 563 * This is equivalent to the system WTERMSIG macro. 564 * 565 * It is an error to call this function before g_subprocess_wait() and 566 * unless g_subprocess_get_if_signaled() returned %TRUE. 567 * 568 * Returns: the signal causing termination 569 * 570 * Since: 2.40 571 */ 572 public int getTermSig() 573 { 574 return g_subprocess_get_term_sig(gSubprocess); 575 } 576 577 /** 578 * Sends the UNIX signal @signal_num to the subprocess, if it is still 579 * running. 580 * 581 * This API is race-free. If the subprocess has terminated, it will not 582 * be signalled. 583 * 584 * This API is not available on Windows. 585 * 586 * Params: 587 * signalNum = the signal number to send 588 * 589 * Since: 2.40 590 */ 591 public void sendSignal(int signalNum) 592 { 593 g_subprocess_send_signal(gSubprocess, signalNum); 594 } 595 596 /** 597 * Synchronously wait for the subprocess to terminate. 598 * 599 * After the process terminates you can query its exit status with 600 * functions such as g_subprocess_get_if_exited() and 601 * g_subprocess_get_exit_status(). 602 * 603 * This function does not fail in the case of the subprocess having 604 * abnormal termination. See g_subprocess_wait_check() for that. 605 * 606 * Cancelling @cancellable doesn't kill the subprocess. Call 607 * g_subprocess_force_exit() if it is desirable. 608 * 609 * Params: 610 * cancellable = a #GCancellable 611 * 612 * Returns: %TRUE on success, %FALSE if @cancellable was cancelled 613 * 614 * Since: 2.40 615 * 616 * Throws: GException on failure. 617 */ 618 public bool wait(Cancellable cancellable) 619 { 620 GError* err = null; 621 622 auto p = g_subprocess_wait(gSubprocess, (cancellable is null) ? null : cancellable.getCancellableStruct(), &err) != 0; 623 624 if (err !is null) 625 { 626 throw new GException( new ErrorG(err) ); 627 } 628 629 return p; 630 } 631 632 /** 633 * Wait for the subprocess to terminate. 634 * 635 * This is the asynchronous version of g_subprocess_wait(). 636 * 637 * Params: 638 * cancellable = a #GCancellable, or %NULL 639 * callback = a #GAsyncReadyCallback to call when the operation is complete 640 * userData = user_data for @callback 641 * 642 * Since: 2.40 643 */ 644 public void waitAsync(Cancellable cancellable, GAsyncReadyCallback callback, void* userData) 645 { 646 g_subprocess_wait_async(gSubprocess, (cancellable is null) ? null : cancellable.getCancellableStruct(), callback, userData); 647 } 648 649 /** 650 * Combines g_subprocess_wait() with g_spawn_check_exit_status(). 651 * 652 * Params: 653 * cancellable = a #GCancellable 654 * 655 * Returns: %TRUE on success, %FALSE if process exited abnormally, or 656 * @cancellable was cancelled 657 * 658 * Since: 2.40 659 * 660 * Throws: GException on failure. 661 */ 662 public bool waitCheck(Cancellable cancellable) 663 { 664 GError* err = null; 665 666 auto p = g_subprocess_wait_check(gSubprocess, (cancellable is null) ? null : cancellable.getCancellableStruct(), &err) != 0; 667 668 if (err !is null) 669 { 670 throw new GException( new ErrorG(err) ); 671 } 672 673 return p; 674 } 675 676 /** 677 * Combines g_subprocess_wait_async() with g_spawn_check_exit_status(). 678 * 679 * This is the asynchronous version of g_subprocess_wait_check(). 680 * 681 * Params: 682 * cancellable = a #GCancellable, or %NULL 683 * callback = a #GAsyncReadyCallback to call when the operation is complete 684 * userData = user_data for @callback 685 * 686 * Since: 2.40 687 */ 688 public void waitCheckAsync(Cancellable cancellable, GAsyncReadyCallback callback, void* userData) 689 { 690 g_subprocess_wait_check_async(gSubprocess, (cancellable is null) ? null : cancellable.getCancellableStruct(), callback, userData); 691 } 692 693 /** 694 * Collects the result of a previous call to 695 * g_subprocess_wait_check_async(). 696 * 697 * Params: 698 * result = the #GAsyncResult passed to your #GAsyncReadyCallback 699 * 700 * Returns: %TRUE if successful, or %FALSE with @error set 701 * 702 * Since: 2.40 703 * 704 * Throws: GException on failure. 705 */ 706 public bool waitCheckFinish(AsyncResultIF result) 707 { 708 GError* err = null; 709 710 auto p = g_subprocess_wait_check_finish(gSubprocess, (result is null) ? null : result.getAsyncResultStruct(), &err) != 0; 711 712 if (err !is null) 713 { 714 throw new GException( new ErrorG(err) ); 715 } 716 717 return p; 718 } 719 720 /** 721 * Collects the result of a previous call to 722 * g_subprocess_wait_async(). 723 * 724 * Params: 725 * result = the #GAsyncResult passed to your #GAsyncReadyCallback 726 * 727 * Returns: %TRUE if successful, or %FALSE with @error set 728 * 729 * Since: 2.40 730 * 731 * Throws: GException on failure. 732 */ 733 public bool waitFinish(AsyncResultIF result) 734 { 735 GError* err = null; 736 737 auto p = g_subprocess_wait_finish(gSubprocess, (result is null) ? null : result.getAsyncResultStruct(), &err) != 0; 738 739 if (err !is null) 740 { 741 throw new GException( new ErrorG(err) ); 742 } 743 744 return p; 745 } 746 }