~ubuntu-branches/ubuntu/precise/code-saturne/precise

« back to all changes in this revision

Viewing changes to salome/cfd_proxy/cfd_proxy_api.c

  • Committer: Package Import Robot
  • Author(s): Sylvestre Ledru
  • Date: 2011-11-24 00:00:08 UTC
  • mfrom: (6.1.9 sid)
  • Revision ID: package-import@ubuntu.com-20111124000008-2vo99e38267942q5
Tags: 2.1.0-3
Install a missing file

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
//============================================================================
 
2
// Main API functions for CFD_Proxy component
 
3
//============================================================================
 
4
 
 
5
/*
 
6
  This file is part of Code_Saturne, a general-purpose CFD tool.
 
7
 
 
8
  Copyright (C) 1998-2011 EDF S.A.
 
9
 
 
10
  This program is free software; you can redistribute it and/or modify it under
 
11
  the terms of the GNU General Public License as published by the Free Software
 
12
  Foundation; either version 2 of the License, or (at your option) any later
 
13
  version.
 
14
 
 
15
  This program is distributed in the hope that it will be useful, but WITHOUT
 
16
  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 
17
  FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 
18
  details.
 
19
 
 
20
  You should have received a copy of the GNU General Public License along with
 
21
  this program; if not, write to the Free Software Foundation, Inc., 51 Franklin
 
22
  Street, Fifth Floor, Boston, MA 02110-1301, USA.
 
23
*/
 
24
 
 
25
#include "cs_config.h"
 
26
 
 
27
//----------------------------------------------------------------------------
 
28
// System headers
 
29
//----------------------------------------------------------------------------
 
30
 
 
31
#include <assert.h>
 
32
#include <errno.h>
 
33
#include <stdio.h>
 
34
#include <string.h>
 
35
#include <unistd.h>
 
36
 
 
37
#if defined(HAVE_MPI)
 
38
#include <mpi.h>
 
39
#endif
 
40
 
 
41
#include "cfd_proxy_defs.h"
 
42
#include "cfd_proxy_comm.h"
 
43
#include "cfd_proxy_child.h"
 
44
#include "cfd_proxy_loader.h"
 
45
 
 
46
#include "cfd_proxy_api.h"
 
47
 
 
48
//----------------------------------------------------------------------------
 
49
 
 
50
#ifdef __cplusplus
 
51
extern "C" {
 
52
#if 0
 
53
} /* Fake brace to force back Emacs auto-indentation back to column 0 */
 
54
#endif
 
55
#endif /* __cplusplus */
 
56
 
 
57
//============================================================================
 
58
// Local type and structure definitions
 
59
//============================================================================
 
60
 
 
61
typedef enum {
 
62
 
 
63
  CFD_PROXY_UNDEF,    // Mode not defined yet
 
64
  CFD_PROXY_LOAD,     // Load a shared library
 
65
  CFD_PROXY_SPAWN     // Execute a sub-process
 
66
 
 
67
} cfd_proxy_mode_t;
 
68
 
 
69
// Main CFD_proxy object
 
70
 
 
71
typedef struct {
 
72
 
 
73
  cfd_proxy_mode_t  mode;        // CFD code execution mode
 
74
 
 
75
  int               n_args;
 
76
  int               n_launcher_args;
 
77
 
 
78
  char             *dirname;
 
79
  char             *filename;
 
80
  char            **args;
 
81
  char            **launcher_args;
 
82
 
 
83
} _cfd_proxy_t ;
 
84
 
 
85
//============================================================================
 
86
// Static global variables
 
87
//============================================================================
 
88
 
 
89
static _cfd_proxy_t _proxy = {CFD_PROXY_UNDEF, 0, 0, NULL, NULL, NULL, NULL};
 
90
 
 
91
//============================================================================
 
92
// Private function definitions
 
93
//============================================================================
 
94
 
 
95
//----------------------------------------------------------------------------
 
96
// Set arguments in a structure.
 
97
//
 
98
// parameters:
 
99
//   n_args   <-- number of arguments defined
 
100
//   args     <-- array of argument strings.
 
101
//   s_n_args <-> pointer to structure's n_args
 
102
//   s_args   <-> pointer to structure's args
 
103
//----------------------------------------------------------------------------
 
104
 
 
105
static void
 
106
_set_args_by_list(int           n_args,
 
107
                  const char   *args[],
 
108
                  int          *s_n_args,
 
109
                  char       ***s_args)
 
110
{
 
111
  int i;
 
112
  size_t args_size = 0;
 
113
 
 
114
  int _n_args = *s_n_args;
 
115
  char **_args = *s_args;
 
116
 
 
117
  if (_args != NULL) {
 
118
    CFDP_FREE(_args[0]);
 
119
    CFDP_FREE(_args);
 
120
  }
 
121
 
 
122
  for (i = 0; i < n_args; i++) {
 
123
    if (args[i] == NULL)
 
124
      break;
 
125
    args_size += (strlen(args[i]) + 1);
 
126
    _n_args = i+1;
 
127
  }
 
128
 
 
129
  CFDP_MALLOC(_args, _n_args, char*);
 
130
  CFDP_MALLOC(_args[0], args_size, char);
 
131
 
 
132
  if (_n_args == 0)
 
133
    return;
 
134
 
 
135
  for (i = 1, args_size = strlen(args[0]) + 1;
 
136
       i < _n_args;
 
137
       i++) {
 
138
    _args[i] = _args[0] + args_size;
 
139
    args_size += (strlen(args[i]) + 1);
 
140
  }
 
141
 
 
142
  for (i = 0, args_size; i < _n_args; i++)
 
143
    strcpy(_args[i], args[i]);
 
144
 
 
145
  // Set return values
 
146
 
 
147
  *s_n_args = _n_args;
 
148
  *s_args = _args;
 
149
}
 
150
 
 
151
//----------------------------------------------------------------------------
 
152
// Set arguments in a structure, using a complete arguments string.
 
153
//
 
154
// parameters:
 
155
//   args     <-- argument string
 
156
//   s_n_args <-> pointer to structure's n_args
 
157
//   s_args   <-> pointer to structure's args
 
158
//----------------------------------------------------------------------------
 
159
 
 
160
static void
 
161
_set_args_by_string(const char   *args,
 
162
                    int          *s_n_args,
 
163
                    char       ***s_args)
 
164
{
 
165
  size_t i, tok_len, start_quote_id;
 
166
  char *s;
 
167
  int protected; // 0 for unprotected, 1 for protected char, 2 for substring,
 
168
                 // 3 for protected char in substring
 
169
  size_t args_size = 0, s_size = 0;
 
170
 
 
171
  int _n_args = *s_n_args;
 
172
  char **_args = *s_args;
 
173
 
 
174
  if (_args != NULL) {
 
175
    CFDP_FREE(_args[0]);
 
176
    CFDP_FREE(_args);
 
177
  }
 
178
 
 
179
  if (args == NULL) {
 
180
    *s_n_args = 0;
 
181
    *s_args = _args;
 
182
    return;
 
183
  }
 
184
 
 
185
  args_size = strlen(args);
 
186
 
 
187
  // Estimate number of arguments (may overestimate, not underestimate)
 
188
 
 
189
  for (i = 0, _n_args = 1; i < args_size; i++) {
 
190
    char c = args[i];
 
191
    if (c == ' ' || c == '\t' || c == '\n'|| c == '\r')
 
192
      _n_args++;
 
193
  }
 
194
 
 
195
  // Prepare tokenization of the arguments string
 
196
 
 
197
  CFDP_MALLOC(_args, _n_args, char*);
 
198
  CFDP_MALLOC(_args[0], args_size + 1, char);
 
199
 
 
200
  s = _args[0];
 
201
 
 
202
  // Tokenize
 
203
 
 
204
  _n_args = 0;
 
205
 
 
206
  i = 0;                      // String position marker
 
207
  start_quote_id = args_size; // Start position marker for quoted strings
 
208
                              // (unset if j == j, set if j < args_size)
 
209
 
 
210
  protected = 0;
 
211
  tok_len = 0;
 
212
 
 
213
  while (i < args_size) {
 
214
 
 
215
    char c = args[i];
 
216
 
 
217
    // Regular case, where previous character was not protected
 
218
 
 
219
    if (protected == 0) {
 
220
 
 
221
      // Protection character
 
222
 
 
223
      if (c == '\\')
 
224
        protected = 1;
 
225
 
 
226
      // Fully protected string
 
227
 
 
228
      else if (c == '"' || c == '\'') {
 
229
        protected = 2;
 
230
        start_quote_id = i;
 
231
      }
 
232
 
 
233
      // Whitespace
 
234
 
 
235
      else if (c == ' ' || c == '\t' || c == '\n'|| c == '\r') {
 
236
        if (tok_len > 0) { // Finish previous token
 
237
          _args[_n_args] = s + s_size;
 
238
          s[s_size + tok_len] = '\0';
 
239
          _n_args += 1;
 
240
          s_size += tok_len+1;
 
241
          tok_len = 0;
 
242
        }
 
243
      }
 
244
 
 
245
      // Regular characters (token)
 
246
 
 
247
      else {
 
248
        s[s_size + tok_len] = args[i];
 
249
        if (tok_len == 0)
 
250
          _args[_n_args] = s + s_size;
 
251
        tok_len++;
 
252
      }
 
253
 
 
254
    }
 
255
 
 
256
    // Cases where previous character was protected
 
257
 
 
258
    else if (protected == 1) {
 
259
 
 
260
      protected = 0;
 
261
      s[s_size + tok_len] = args[i];
 
262
      if (tok_len == 0)
 
263
        _args[_n_args] = s + s_size;
 
264
      tok_len++;
 
265
 
 
266
    }
 
267
 
 
268
    else if (protected == 2) {
 
269
 
 
270
      // Single protection character
 
271
 
 
272
      if (c == '\\')
 
273
        protected = 3;
 
274
 
 
275
      // End of string protection
 
276
 
 
277
      else if (c == args[start_quote_id]) {
 
278
        protected = 0;
 
279
        start_quote_id = args_size;
 
280
      }
 
281
 
 
282
      else {
 
283
        s[s_size + tok_len] = args[i];
 
284
        if (tok_len == 0)
 
285
          _args[_n_args] = s + s_size;
 
286
        tok_len++;
 
287
      }
 
288
 
 
289
    }
 
290
 
 
291
    else { // if (protected == 3)
 
292
 
 
293
      s[s_size + tok_len] = args[i];
 
294
      if (tok_len == 0)
 
295
        _args[_n_args] = s + s_size;
 
296
      tok_len++;
 
297
      protected = 2;
 
298
 
 
299
    }
 
300
 
 
301
    i+= 1;
 
302
 
 
303
  } /* End of loop in infix string characters */
 
304
 
 
305
  if (tok_len > 0) { /* Finish previous token */
 
306
    _args[_n_args] = s + s_size;
 
307
    s[s_size + tok_len] = '\0';
 
308
    _n_args += 1;
 
309
    s_size += tok_len+1;
 
310
    tok_len = 0;
 
311
  }
 
312
 
 
313
  if (protected == 1)
 
314
    cfd_proxy_error(__FILE__, __LINE__, 0,
 
315
                    _("Error tokenizing expression:\n"
 
316
                      "%s\n"
 
317
                      "Missing character after \\\n"),
 
318
                    args);
 
319
  else if (protected >= 2)
 
320
    cfd_proxy_error(__FILE__, __LINE__, 0,
 
321
                    _("Error tokenizing expression:\n"
 
322
                      "%s\n"
 
323
                      "Missing closing quote for subexpression:\n"
 
324
                      "%s\n"),
 
325
                    args, args + start_quote_id);
 
326
 
 
327
  // Resize to adjusted size (do not resize _args[0], as _args[i]
 
328
  // would then need to be updated, and extra size is limited.
 
329
 
 
330
  CFDP_REALLOC(_args, _n_args, char *);
 
331
 
 
332
  for (i = s_size; i < args_size; i++)
 
333
    s[i] = '\0';
 
334
 
 
335
  // Set return values
 
336
 
 
337
  *s_n_args = _n_args;
 
338
  *s_args = _args;
 
339
}
 
340
 
 
341
//----------------------------------------------------------------------------
 
342
// Set working directory.
 
343
//
 
344
// parameters:
 
345
//   path <-- new working directory
 
346
//
 
347
// returns:
 
348
//   0 on success, -1 on error
 
349
//----------------------------------------------------------------------------
 
350
 
 
351
static int
 
352
_change_dir(const char  *path)
 
353
{
 
354
  int retval = 0;
 
355
 
 
356
  if (path != NULL) {
 
357
 
 
358
    retval = chdir(path);
 
359
 
 
360
    if (retval != 0)
 
361
      cfd_proxy_error(__FILE__, __LINE__, errno,
 
362
                      _("Error setting the working directory to:\n"
 
363
                        "%s"),
 
364
                      path);
 
365
  }
 
366
 
 
367
  return retval;
 
368
}
 
369
 
 
370
//============================================================================
 
371
// Public functions
 
372
//============================================================================
 
373
 
 
374
//----------------------------------------------------------------------------
 
375
// Set the proxy's associated supervisable SALOME component.
 
376
//
 
377
// Multiple components may be asociated with the proxy if necessary,
 
378
// using a different id for each (0 for the first). This may be useful
 
379
// in threaded cases used for multiple couplings.
 
380
//
 
381
// parameters:
 
382
//   component    <-- pointer of type Superv_Component_i* to the
 
383
//                    supervisable SALOME component
 
384
//   component_id <-- id of component (0 by default, >= 0 if multiple
 
385
//                    components are managed in the same process space,
 
386
//                    which may be possible with multiple threads)
 
387
//----------------------------------------------------------------------------
 
388
 
 
389
void
 
390
cfd_proxy_set_component(void  *component,
 
391
                        int    component_id)
 
392
{
 
393
  if (component_id < 0)
 
394
    return;
 
395
 
 
396
  if (component_id > cfd_proxy_glob_n_components) {
 
397
 
 
398
    if (cfd_proxy_glob_n_components == 1)
 
399
      // pointed to stack value, see cfd_proxy_defs.c
 
400
      cfd_proxy_glob_component = NULL;
 
401
 
 
402
    CFDP_REALLOC(cfd_proxy_glob_component, component_id+1, void *);
 
403
 
 
404
  }
 
405
 
 
406
  cfd_proxy_glob_component[component_id] = component;
 
407
}
 
408
 
 
409
//----------------------------------------------------------------------------
 
410
// Set command-line arguments for execution, using an argument array.
 
411
//
 
412
// Only user arguments need to be defined (i.e. the executable file
 
413
// at argv[0] is set automatically, so only argv[1] to argv[argc-1]
 
414
// need to be defined here.
 
415
//
 
416
// parameters:
 
417
//   n_args <-- number of arguments defined
 
418
//   args   <-- array of argument strings.
 
419
//----------------------------------------------------------------------------
 
420
 
 
421
void
 
422
cfd_proxy_set_args_by_list(int          n_args,
 
423
                           const char  *args[])
 
424
{
 
425
  _set_args_by_list(n_args, args, &(_proxy.n_args), &(_proxy.args));
 
426
}
 
427
 
 
428
//----------------------------------------------------------------------------
 
429
// Set command-line arguments for execution, using a single string.
 
430
//
 
431
// Arguments are separated by whitespace. Whitespaces may be protected
 
432
// using couples of " or ' quotes, or single \ escape characters.
 
433
//
 
434
// Only user arguments need to be defined (i.e. the executable file
 
435
// at argv[0] is set automatically, so only argv[1] to argv[argc-1]
 
436
// need to be defined here.
 
437
//
 
438
// parameters:
 
439
//   args <-- arguments string.
 
440
//----------------------------------------------------------------------------
 
441
 
 
442
void
 
443
cfd_proxy_set_args(const char  *args)
 
444
{
 
445
  _set_args_by_string(args, &(_proxy.n_args), &(_proxy.args));
 
446
}
 
447
 
 
448
//----------------------------------------------------------------------------
 
449
// Set working directory.
 
450
//
 
451
// parameters:
 
452
//   path <-- new working directory
 
453
//----------------------------------------------------------------------------
 
454
 
 
455
void
 
456
cfd_proxy_set_dir(const char  *path)
 
457
{
 
458
  if (path != NULL) {
 
459
    CFDP_REALLOC(_proxy.dirname, strlen(path) + 1, char);
 
460
    strcpy(_proxy.dirname, path);
 
461
  }
 
462
}
 
463
 
 
464
//----------------------------------------------------------------------------
 
465
// Set shared library (also setting the shared library proxy mode)
 
466
//
 
467
// parameters:
 
468
//   filename <-- name of dynamic library file
 
469
//----------------------------------------------------------------------------
 
470
 
 
471
void
 
472
cfd_proxy_set_lib(const char  *filename)
 
473
{
 
474
#if defined(HAVE_DLOPEN)
 
475
 
 
476
  size_t alloc_size = strlen(filename) + 1;
 
477
 
 
478
  // Avoid valgrind warning on Linux which seems to be due to dlopen
 
479
  // reading in multiples of size_t by allocating a slightly larger buffer.
 
480
 
 
481
  if (alloc_size % sizeof(size_t))
 
482
    alloc_size += sizeof(size_t) - (alloc_size % sizeof(size_t));
 
483
 
 
484
  _proxy.mode = CFD_PROXY_LOAD;
 
485
 
 
486
  CFDP_REALLOC(_proxy.filename, alloc_size, char);
 
487
  memset(_proxy.filename, '\0', alloc_size);
 
488
 
 
489
  strcpy(_proxy.filename, filename);
 
490
 
 
491
#endif
 
492
}
 
493
 
 
494
//----------------------------------------------------------------------------
 
495
// Set executable (also setting the child/IPC proxy mode)
 
496
//
 
497
// parameters:
 
498
//   filename <-- name of executable file
 
499
//----------------------------------------------------------------------------
 
500
 
 
501
void
 
502
cfd_proxy_set_exe(const char  *filename)
 
503
{
 
504
  _proxy.mode = CFD_PROXY_SPAWN;
 
505
 
 
506
  CFDP_REALLOC(_proxy.filename, strlen(filename) + 1, char);
 
507
  strcpy(_proxy.filename, filename);
 
508
}
 
509
 
 
510
//----------------------------------------------------------------------------
 
511
// Define intermediate launcher and associated arguments (only of use for
 
512
// the child/IPC proxy mode), using an argument array.
 
513
//
 
514
// This allows running the child executable through another process, such
 
515
// as mpiexec (for parallel runs), or a debugger.
 
516
//
 
517
// parameters:
 
518
//   n_launcher_args  <-- number of arguments defined
 
519
//   launcher_args    <-- array of string arguments (the first of which
 
520
//                        should be the launcher executable file name)
 
521
//----------------------------------------------------------------------------
 
522
 
 
523
void
 
524
cfd_proxy_set_launcher_by_list(int          n_launcher_args,
 
525
                               const char  *launcher_args[])
 
526
{
 
527
  _set_args_by_list(n_launcher_args,
 
528
                    launcher_args,
 
529
                    &(_proxy.n_launcher_args),
 
530
                    &(_proxy.launcher_args));
 
531
}
 
532
 
 
533
//----------------------------------------------------------------------------
 
534
// Define intermediate launcher and associated arguments (only of use for
 
535
// the child/IPC proxy mode), using a single string.
 
536
//
 
537
// This allows running the child executable through another process, such
 
538
// as mpiexec (for parallel runs), or a debugger.
 
539
//
 
540
// Arguments are separated by whitespace. Whitespaces may be protected
 
541
// using couples of " or ' quotes, or single \ escape characters.
 
542
//
 
543
// parameters:
 
544
//   launcher_args <-- string of launcher arguments (the first of which
 
545
//                     should be the launcher executable file name)
 
546
//----------------------------------------------------------------------------
 
547
 
 
548
void
 
549
cfd_proxy_set_launcher(const char  *launcher_args)
 
550
{
 
551
  _set_args_by_string(launcher_args,
 
552
                      &(_proxy.n_launcher_args),
 
553
                      &(_proxy.launcher_args));
 
554
}
 
555
 
 
556
//----------------------------------------------------------------------------
 
557
// Run full execution of the CFD code
 
558
//
 
559
// returns:
 
560
//   execution's return value (or prior error code if execution impossible);
 
561
//----------------------------------------------------------------------------
 
562
 
 
563
int
 
564
cfd_proxy_run_all()
 
565
{
 
566
  int n_args = 0;
 
567
  int arg_id = 0;
 
568
  char *old_wd = NULL;
 
569
 
 
570
  int retval = 0;
 
571
 
 
572
  extern char **environ;
 
573
 
 
574
  if (_proxy.dirname != NULL) {
 
575
 
 
576
    size_t old_wd_size = 128;
 
577
    char *wd = NULL;
 
578
 
 
579
    // Save old working directory
 
580
 
 
581
    CFDP_MALLOC(old_wd, old_wd_size, char);
 
582
 
 
583
    wd = getcwd(old_wd, old_wd_size);
 
584
    while (wd == NULL && errno == ERANGE) {
 
585
      old_wd_size *= 2;
 
586
      CFDP_REALLOC(old_wd, old_wd_size, char);
 
587
      wd = getcwd(old_wd, old_wd_size);
 
588
    }
 
589
    if (wd != NULL)
 
590
      CFDP_REALLOC(old_wd, strlen(old_wd) + 1, char);
 
591
    else {
 
592
      cfd_proxy_warn();
 
593
      cfd_proxy_printf
 
594
        (_("Could not obtain and save current working directory.\n"));
 
595
    }
 
596
 
 
597
    // Switch to working directory
 
598
 
 
599
    retval = _change_dir(_proxy.dirname);
 
600
    if (retval != 0)
 
601
      return retval;
 
602
  }
 
603
 
 
604
  // Handle shared library mode
 
605
  //---------------------------
 
606
 
 
607
#if defined(HAVE_DLOPEN)
 
608
 
 
609
  if (_proxy.mode == CFD_PROXY_LOAD) {
 
610
 
 
611
    char **_argv = NULL;
 
612
 
 
613
    CFDP_MALLOC(_argv, 1 + _proxy.n_args + 1, char *);
 
614
 
 
615
    _argv[n_args++] = _proxy.filename;
 
616
 
 
617
    for (arg_id = 0; arg_id < _proxy.n_args; arg_id++)
 
618
      _argv[n_args++] = _proxy.args[arg_id];
 
619
 
 
620
    _argv[n_args] = NULL;
 
621
 
 
622
    retval = cfd_proxy_loader_init(_proxy.filename);
 
623
 
 
624
    if (retval == 0)
 
625
      retval = cfd_proxy_loader_run(1 + _proxy.n_args, _argv);
 
626
 
 
627
    cfd_proxy_loader_finalize();
 
628
 
 
629
    CFDP_FREE(_argv);
 
630
  }
 
631
 
 
632
#endif // defined(HAVE_DLOPEN)
 
633
 
 
634
  // Handle spawn mode
 
635
  //------------------
 
636
 
 
637
#if defined(HAVE_POSIX_SPAWN) || defined(HAVE_FORK_EXECVE)
 
638
 
 
639
  if (_proxy.mode == CFD_PROXY_SPAWN) {
 
640
 
 
641
    cfd_proxy_child_t *child = NULL;
 
642
    char **child_argv = NULL;
 
643
 
 
644
    CFDP_MALLOC(child_argv,
 
645
                _proxy.n_launcher_args + 1 + _proxy.n_args + 1,
 
646
                char *);
 
647
 
 
648
    for (arg_id = 0; arg_id < _proxy.n_launcher_args; arg_id++)
 
649
      child_argv[n_args++] = _proxy.launcher_args[arg_id];
 
650
 
 
651
    child_argv[n_args++] = _proxy.filename;
 
652
 
 
653
    for (arg_id = 0; arg_id < _proxy.n_args; arg_id++)
 
654
      child_argv[n_args++] = _proxy.args[arg_id];
 
655
 
 
656
    child_argv[n_args] = NULL;
 
657
 
 
658
    child = cfd_proxy_child_start(child_argv[0],
 
659
                                  child_argv, environ,
 
660
                                  CFD_PROXY_COMM_TYPE_SOCKET, 10);
 
661
 
 
662
    if (child != NULL) {
 
663
 
 
664
      cfd_proxy_child_forward_all(child);
 
665
 
 
666
      retval = cfd_proxy_child_stop(&child);
 
667
 
 
668
    }
 
669
 
 
670
    else
 
671
      retval = 1;
 
672
 
 
673
    CFDP_FREE(child_argv);
 
674
  }
 
675
 
 
676
#endif // if defined(HAVE_POSIX_SPAWN) || defined(HAVE_FORK_EXECVE)
 
677
 
 
678
  if (old_wd != NULL) {
 
679
    _change_dir(old_wd);
 
680
    CFDP_FREE(old_wd);
 
681
  }
 
682
 
 
683
  return retval;
 
684
}
 
685
 
 
686
//----------------------------------------------------------------------------
 
687
 
 
688
#ifdef __cplusplus
 
689
}
 
690
#endif /* __cplusplus */
 
691