source: appstream-generator/build/girepo/gio/Subprocess.d @ 4841

Last change on this file since 4841 was 4841, checked in by Juanma, 2 years ago

Initial release

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