~ubuntu-branches/debian/jessie/eso-midas/jessie

« back to all changes in this revision

Viewing changes to libsrc/os/vms/ospsystem.c

  • Committer: Package Import Robot
  • Author(s): Ole Streicher
  • Date: 2014-04-22 14:44:58 UTC
  • Revision ID: package-import@ubuntu.com-20140422144458-okiwi1assxkkiz39
Tags: upstream-13.09pl1.2+dfsg
ImportĀ upstreamĀ versionĀ 13.09pl1.2+dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* @(#)ospsystem.c      19.1 (ES0-DMD) 02/25/03 13:56:13 */
 
2
/*===========================================================================
 
3
  Copyright (C) 1995 European Southern Observatory (ESO)
 
4
 
 
5
  This program is free software; you can redistribute it and/or 
 
6
  modify it under the terms of the GNU General Public License as 
 
7
  published by the Free Software Foundation; either version 2 of 
 
8
  the License, or (at your option) any later version.
 
9
 
 
10
  This program 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 General Public License for more details.
 
14
 
 
15
  You should have received a copy of the GNU General Public 
 
16
  License along with this program; if not, write to the Free 
 
17
  Software Foundation, Inc., 675 Massachusetss Ave, Cambridge, 
 
18
  MA 02139, USA.
 
19
 
 
20
  Corresponding concerning ESO-MIDAS should be addressed as follows:
 
21
        Internet e-mail: midas@eso.org
 
22
        Postal address: European Southern Observatory
 
23
                        Data Management Division 
 
24
                        Karl-Schwarzschild-Strasse 2
 
25
                        D 85748 Garching bei Muenchen 
 
26
                        GERMANY
 
27
===========================================================================*/
 
28
 
 
29
/*--------------------------ospsystem--------------------------------------
 
30
.TYPE        routine
 
31
.NAME        ospsystem
 
32
.MODULE      osp (operating system independant interface, processes)
 
33
.ENVIROMENT  vms
 
34
.LANGUAGE    C
 
35
.AUTHOR      Trond Melen (IPG-ESO Garching)
 
36
.PURPOSE
 
37
  Execute a command in a shell (The Borne shell under UNIX) or to a 
 
38
  command line interpreter (DCL under VMS).
 
39
.IMPLEMENTATION
 
40
  VMS: The "system()" C runtime library call could have been used but is 
 
41
  not for two reasons. First, it is slow because spawning a process for 
 
42
  each command is very expensive under VMS. Second, a command cannot be 
 
43
  run in the background (using SPAWN/NOWAIT) since a VMS subprocess dies 
 
44
  with its parent.
 
45
 
 
46
  Instead a subprocess is created when "ospsystem()" is called for the first
 
47
  time, and it is kept alive for the rest of the execution period. To 
 
48
  run a process in the background, a SPAWN/NOWAIT command is executed in 
 
49
  this subprocess. Therfore, a background command will be aborted if the 
 
50
  program that issued the command terminates or the subprocess is 
 
51
  otherwise killed. The same holds if the user is logged out.
 
52
 
 
53
  To simulate the behaviour of a UNIX shell, the command string is 
 
54
  parsed and the symbols ';', '&', '(' and ')' are given special meanings. 
 
55
  Commands separated by semicolons are run in sequence. If a command is 
 
56
  followed by an ampersant, its run in the background. Several commands 
 
57
  enclosed in paranthesis are treated as one. This is implemented by 
 
58
  creating a command file in the SYS$SCRATCH directory holding the simple
 
59
  commands. The command file is executed using the '@file.com' construct.
 
60
 
 
61
  Nesting is not admissable.  It is only really usefull for running a
 
62
  sequence of commands in the background, and spawning a process from a
 
63
  spawned process is unsafe since the parent process might die before
 
64
  the child and thereby kill it.
 
65
 
 
66
  The parser is documented in connection with the relevant routines.
 
67
 
 
68
.RETURNS
 
69
  Error status of the command (UNIX only).
 
70
----------------------------------------------------------------------*/
 
71
 
 
72
#include <stdio.h>
 
73
 
 
74
int ospdebug = 0;         /* flag controlling debug printings */
 
75
 
 
76
 
 
77
/* The fairly complicated VMS solution */
 
78
 
 
79
#include <descrip.h>       /* character descriptor definitions */
 
80
#include <dvidef.h>        /* SYS$GETDVI definitions */
 
81
#include <iodef.h>         /* SYS$QIO definitions */
 
82
 
 
83
 
 
84
#define COMMAND_LEN  256
 
85
#define WORD_LEN      80
 
86
 
 
87
#define BIT_0(x) ((x) & 1)   /* mask returning bit zero (used for 
 
88
                                checking return status of system calls */
 
89
 
 
90
 
 
91
/* Type definitions. */
 
92
 
 
93
typedef struct dsc$descriptor_s descriptor;
 
94
 
 
95
typedef struct   /* structure used by SYS$GETDVI */
 
96
  {
 
97
    unsigned short buf_len;
 
98
    unsigned short item_code;
 
99
    char *buffer;
 
100
    short *return_len;
 
101
  } item_dsc;
 
102
 
 
103
typedef struct
 
104
  {
 
105
    long channel;   /* channel assigned to mailbox 
 
106
                       assosiated with the subprocess */
 
107
    long pid;       /* process identifier for the subprocess */
 
108
  } proc_record;
 
109
 
 
110
typedef enum   /* tokens for the parser */
 
111
  {T_WORD, T_SEMI, T_LEFT, T_RIGHT, T_AMP, T_EOS, T_ERROR, T_TOOLONG}
 
112
TOKEN;
 
113
 
 
114
 
 
115
/* Variables global to ospsystem related code. */
 
116
 
 
117
static proc_record sub_proc = {0, 0};    /* info about the subprocess */
 
118
static int sub_proc_created = 0;         /* flag, initially false */
 
119
static int exit_handler_installed = 0;   /* flag, initially false */
 
120
static char ospword[WORD_LEN + 1];       /* value only valid when 
 
121
                                            get_token returnes T_WORD */
 
122
 
 
123
 
 
124
/* A string handling routine. */
 
125
 
 
126
static char *strprecat(s1, s2)
 
127
/* Workes as strcat, but s2 is inserted at the beginning of s1. */
 
128
char s1[], s2[];
 
129
{
 
130
  int l1, l2, i;
 
131
  char *from, *to;
 
132
 
 
133
  l1 = strlen(s1);
 
134
  l2 = strlen(s2);
 
135
 
 
136
  from = &s1[l1 - 1];
 
137
  to   = &s1[l1 + l2 - 1];
 
138
  for (i = 0; i < l1; i++)
 
139
    *to-- = *from--;
 
140
  strncpy(s1, s2, l2);
 
141
  s1[l1 + l2] = '\0';
 
142
 
 
143
  return(s1);
 
144
}
 
145
 
 
146
 
 
147
/* routines to simplify access to VMS services */
 
148
 
 
149
static descriptor dsc(str)
 
150
/* Create a descriptor from a string.                                  */
 
151
/* Returning structures is not portable, but neigther are descriptors. */
 
152
/* May cause program abortion if str is not a null terminated string.  */
 
153
char str[];
 
154
{
 
155
  descriptor dsc;
 
156
 
 
157
  dsc.dsc$w_length  = (strlen(str) <= 256) ? strlen(str) : 256 ;
 
158
  dsc.dsc$a_pointer = str;
 
159
  dsc.dsc$b_class   = DSC$K_CLASS_S;
 
160
  dsc.dsc$b_dtype   = DSC$K_DTYPE_T;
 
161
 
 
162
  return(dsc);
 
163
}
 
164
 
 
165
 
 
166
static void error_msg(str, msg_id)
 
167
/* Print string and error message identified by msg_id. */
 
168
char str[];
 
169
long msg_id;
 
170
{
 
171
  long flags, status;
 
172
  short msg_len;
 
173
  descriptor msg_dsc;
 
174
  char buffer[257];
 
175
 
 
176
  msg_dsc = dsc(buffer);
 
177
  msg_dsc.dsc$w_length = 256;
 
178
  flags =  0;        /* use process default */
 
179
  /* flags = 15;        /* get all information available */
 
180
 
 
181
  if (BIT_0(status = SYS$GETMSG(msg_id, &msg_len, &msg_dsc, flags, NULL)))
 
182
    {
 
183
      buffer[msg_len] = '\0';
 
184
      fprintf(stderr, "%s: %s\n", str, buffer);
 
185
    }
 
186
  else
 
187
    {
 
188
      fprintf(stderr, 
 
189
        "%s: error_msg: SYS$GETMSG failed for msg_id %d\n", str, msg_id);
 
190
 
 
191
      msg_dsc.dsc$w_length = 256;
 
192
      if (BIT_0(status = SYS$GETMSG(status, &msg_len, &msg_dsc, flags, NULL)))
 
193
        {
 
194
          buffer[msg_len] = '\0';
 
195
          fprintf(stderr, "error_msg: %s\n", buffer);
 
196
        }
 
197
      else
 
198
        fprintf(stderr, 
 
199
          "error_msg: unable to find out why (status = %d)\n", status);
 
200
    }
 
201
}
 
202
 
 
203
 
 
204
static int write_channel(channel, buffer)
 
205
/* Write null-terminated string to VMS-type channel. */
 
206
/* Returns 0 on success, 1 on falure.                */
 
207
long channel;
 
208
char buffer[];
 
209
{
 
210
  long event_flag, function, buf_size;
 
211
  short int iosb[4];                    /* not used */
 
212
  long status;
 
213
    
 
214
  event_flag = 0;                       /* not used */
 
215
  function = IO$_WRITEVBLK | IO$M_NOW;  /* write virtual block */
 
216
  buf_size = strlen(buffer);
 
217
 
 
218
  status = SYS$QIOW(event_flag, channel, function, iosb, NULL, 0, 
 
219
                    buffer, buf_size, 0, 0, 0, 0);
 
220
  if (!BIT_0(status))
 
221
    {
 
222
      error_msg("SYS$QIOW", status);
 
223
      return(1);
 
224
    }
 
225
  return(0);
 
226
}
 
227
 
 
228
 
 
229
static char *get_dev_name(channel, str, max_len)
 
230
/* Get name of the device which the channel is assigned to. */
 
231
/* Returnes str on success, NULL on falure. */
 
232
long channel;
 
233
char str[];
 
234
int max_len;
 
235
{
 
236
  /* parameters for SYS$GETDVI */
 
237
  item_dsc item_list[2];
 
238
  long event_flag;
 
239
  short str_len;
 
240
  long status;
 
241
 
 
242
  event_flag = 0;
 
243
  item_list[0].buf_len    = max_len - 1;
 
244
  item_list[0].item_code  = DVI$_DEVNAM;
 
245
  item_list[0].buffer     = str;
 
246
  item_list[0].return_len = &str_len;
 
247
  item_list[1].buf_len    = 0;
 
248
  item_list[1].item_code  = 0;
 
249
 
 
250
  status = SYS$GETDVIW(event_flag, channel, NULL, item_list, 
 
251
                      NULL, NULL, 0, NULL);
 
252
  if (!BIT_0(status))
 
253
    {
 
254
      error_msg("SYS$GETDVI", status);
 
255
      return(NULL);
 
256
    }
 
257
 
 
258
  str[str_len] = '\0';
 
259
  return(str);
 
260
}
 
261
 
 
262
 
 
263
/* Asyncronous system traps (ASTs) end exit handler (EXH) */
 
264
 
 
265
static int ready_ast()
 
266
/* Executes in parent process when subprosses is ready for input, */
 
267
/* i.e. when previous command has compleated.                     */
 
268
{
 
269
  long status;
 
270
 
 
271
  status = SYS$WAKE(NULL, NULL);
 
272
  if (!BIT_0(status))
 
273
    {
 
274
      error_msg("SYS$WAKE", status);
 
275
      fprintf(stderr, "osp/ready_ast: cannot recover\n");
 
276
    }
 
277
 
 
278
  return(0);
 
279
}
 
280
 
 
281
 
 
282
static long termination_ast()
 
283
/* Executes in parent process when a subprocess terminates */
 
284
{
 
285
  long status;
 
286
 
 
287
  fprintf(stderr, "[subprocess terminated]\n");
 
288
 
 
289
  sub_proc_created = 0;
 
290
 
 
291
  status = SYS$WAKE(NULL, NULL);
 
292
  if (!BIT_0(status)) error_msg("SYS$WAKE", status); 
 
293
 
 
294
  return(0);
 
295
}
 
296
 
 
297
 
 
298
static int cleanup_exh()
 
299
/* Kill the subprocess if one has been created.     */
 
300
/* Executes in parent process when this is exiting. */
 
301
{
 
302
  long status;
 
303
 
 
304
  if (sub_proc_created)
 
305
    {
 
306
      status = SYS$DELPRC(&sub_proc.pid, NULL);
 
307
      if (!BIT_0(status)) error_msg("SYS$DELPRC", status);
 
308
    }
 
309
 
 
310
  return(0);
 
311
}
 
312
 
 
313
 
 
314
static int install_exit_handler()
 
315
/* Install the exithandler and set the assosiated flag */
 
316
{
 
317
  static long control_block[4];
 
318
  static long condition_value;
 
319
  long status;
 
320
 
 
321
  control_block[1] = (long) cleanup_exh;
 
322
  control_block[2] = 1;
 
323
  control_block[3] = (long) &condition_value;
 
324
 
 
325
  status = SYS$DCLEXH(control_block);
 
326
  if (!BIT_0(status))
 
327
    {
 
328
      error_msg("SYS$DCLEXH", status);
 
329
      return(1);
 
330
    }
 
331
 
 
332
  exit_handler_installed = 1;
 
333
 
 
334
  return(0);
 
335
};
 
336
 
 
337
 
 
338
/* Routines to create and initialize the subprocess. */
 
339
 
 
340
static int create_sub_proc()
 
341
/* Create a subprocess and let it take its input from a mailbox. */
 
342
/* Returns 0 on success, 1 on falure.                            */
 
343
{
 
344
  char mbx_dev_name[10];
 
345
  long status;
 
346
 
 
347
  /* Parameters for SYS$CREMBX. */
 
348
  long mbx_chan;
 
349
 
 
350
  /* Parameters for LIB$SPAWN. */
 
351
  descriptor in_file;
 
352
  long flags, spawned_pid, (* c_ast)(), c_ast_param;
 
353
 
 
354
  /* Create a mailbox.
 
355
  status = SYS$CREMBX(permanent_flag, &mbx_channel, max_msg_size,
 
356
                      buffer_size, protection_mask, access_mode, &mbx_name); */
 
357
  if (ospdebug)
 
358
    fprintf(stderr, "osp/create_sub_proc: creating mailbox\n");
 
359
  status = SYS$CREMBX(0, &mbx_chan, 0, 0, 0, 0, 0);
 
360
  if (!BIT_0(status))
 
361
    {
 
362
      error_msg("SYS$CREMBX", status);
 
363
      return(1);
 
364
    }
 
365
 
 
366
  /* Get devive name of mailbox. */
 
367
  if (ospdebug) fprintf(stderr, 
 
368
    "osp/create_sub_proc: asking system for mailbox' device name\n");
 
369
  (void) get_dev_name(mbx_chan, mbx_dev_name, sizeof(mbx_dev_name));
 
370
 
 
371
  /* Spawn a process, not waiting for it to complete.
 
372
  status = LIB$SPAWN(&command_str, &in_file, &out_file, &flags, &proc_name, 
 
373
                     &spawned_pid, &c_status, &c_efn, c_ast, c_ast_param); */
 
374
  if (ospdebug) 
 
375
    fprintf(stderr, "osp/create_sub_proc: spawning a subprocess\n");
 
376
  in_file = dsc(mbx_dev_name);  /* take input from the mailbox */
 
377
  flags = 1;                    /* NOWAIT */
 
378
  c_ast = termination_ast;      /* rutine to be run upon completion */
 
379
  status = LIB$SPAWN(0, &in_file, 0, &flags, 0, &spawned_pid, 0, 0, c_ast, 0);
 
380
  if (!BIT_0(status))
 
381
    {
 
382
      error_msg("LIB$SPAWN", status);
 
383
      return(1);
 
384
    }
 
385
 
 
386
  /* Initialize process record. */
 
387
  sub_proc.channel = mbx_chan;
 
388
  sub_proc.pid     = spawned_pid;
 
389
 
 
390
  /* Print information about the subprocess. */
 
391
  if (ospdebug)
 
392
    {
 
393
      fprintf(stderr,
 
394
              "osp/create_sub_proc: Process identifier %d\n", sub_proc.pid);
 
395
      fprintf(stderr,
 
396
              "osp/create_sub_proc: Channel number     %d\n", sub_proc.channel);
 
397
    }
 
398
  return(0);
 
399
}
 
400
 
 
401
 
 
402
static int init_sub_proc()
 
403
/* Initialize subprocess using DCL commands */
 
404
/* Returns 0 on success, 1 on falure.       */
 
405
{
 
406
  long channel;
 
407
  char user_terminal[10], command[80];
 
408
  int length;
 
409
  short str_len;
 
410
 
 
411
  /* Get channel assigned to mailbox associated with subprocess. */
 
412
  channel = sub_proc.channel;
 
413
 
 
414
  /* Assign terminal device name to I/O logical names. */
 
415
  return(
 
416
    write_channel(channel, "SET NOVERIFY") ||
 
417
    write_channel(channel, "SET NOON") ||
 
418
    write_channel(channel,
 
419
                  "DEFINE/NOLOG TT          'F$TRNLNM(\"SYS$OUTPUT\")") ||
 
420
    write_channel(channel,
 
421
                  "DEFINE/NOLOG SYS$COMMAND 'F$TRNLNM(\"SYS$OUTPUT\")") ||
 
422
    write_channel(channel,
 
423
                  "DEFINE/NOLOG SYS$INPUT   'F$TRNLNM(\"SYS$OUTPUT\")") ||
 
424
    write_channel(channel,
 
425
                  "DEFINE/NOLOG FOR$INPUT   'F$TRNLNM(\"SYS$OUTPUT\")"));
 
426
}
 
427
 
 
428
 
 
429
/* The parser */
 
430
 
 
431
static TOKEN get_token(string, pos_ptr, look_for_T_RIGHT)
 
432
/* Return the token at current position and              */
 
433
/* increment position to the beginning of next token.    */
 
434
/* If token is T_WORD, ospword will hold the characters. */
 
435
/* '(' is only interpreted as a token at the beginning   */
 
436
/* of a line or after a ';' or a '&'. ')' is only        */
 
437
/* interpreted if the flag look_for_T_RIGHT is set.      */
 
438
 
 
439
char *string;          /* string to be parsed */
 
440
int  *pos_ptr;         /* pointer to variable holding position within string */
 
441
int look_for_T_RIGHT;  /* 1 if ')' should be interpreted as a token, else 0 */
 
442
{
 
443
  enum {NEUTRAL, INWORD} state = NEUTRAL;
 
444
  int ch, prev_ch, i;
 
445
 
 
446
  i = 0;
 
447
  while (i < sizeof(ospword))
 
448
    {
 
449
      ch = string[(*pos_ptr)++];
 
450
      switch(state)
 
451
        {
 
452
          case NEUTRAL:
 
453
            switch(ch)
 
454
              {
 
455
                case ';':
 
456
                  return(T_SEMI);
 
457
                case '(':
 
458
                  if (*pos_ptr == 1)
 
459
                    return(T_LEFT);
 
460
                  prev_ch = string[(*pos_ptr) - 1];
 
461
                  if (prev_ch == ';' || prev_ch == '&')
 
462
                    return(T_LEFT);
 
463
                  else
 
464
                    {
 
465
                      state = INWORD;
 
466
                      ospword[i++] = ch;
 
467
                      continue;
 
468
                    }
 
469
                case ')':
 
470
                  if (look_for_T_RIGHT)
 
471
                    return(T_RIGHT);
 
472
                  else
 
473
                    {
 
474
                      state = INWORD;
 
475
                      ospword[i++] = ch;
 
476
                      continue;
 
477
                    }
 
478
                case '&':
 
479
                  return(T_AMP);
 
480
                case '\0':
 
481
                  return(T_EOS);
 
482
                case ' ':
 
483
                case '\t':
 
484
                  continue;
 
485
                default:
 
486
                  state = INWORD;
 
487
                  ospword[i++] = ch;
 
488
                  continue;
 
489
            }
 
490
        case INWORD:
 
491
          switch(ch)
 
492
            {
 
493
              case ';':
 
494
              case '&':
 
495
              case ' ':
 
496
              case '\t':
 
497
              case '\0':
 
498
                (*pos_ptr)--;
 
499
                ospword[i] = '\0';
 
500
                return(T_WORD);
 
501
              case ')':
 
502
                if (look_for_T_RIGHT)
 
503
                  {
 
504
                    (*pos_ptr)--;
 
505
                    ospword[i] = '\0';
 
506
                    return(T_WORD);
 
507
                  }
 
508
                else
 
509
                  {
 
510
                    ospword[i++] = ch;
 
511
                    continue;
 
512
                  }
 
513
              default:
 
514
                ospword[i++] = ch;
 
515
                continue;
 
516
            }
 
517
        }
 
518
    }
 
519
  return(T_TOOLONG);
 
520
}
 
521
 
 
522
 
 
523
static TOKEN make_simple_command(look_ahead, string,
 
524
                                 pos_ptr, dcl_command, look_for_T_RIGHT)
 
525
/*------------------------------------------------------------------
 
526
Make_simple_command builds a command using tokens from string
 
527
and places it in dcl_command.
 
528
The routine is called with look_ahead holding the first
 
529
token of the simple command, which must be T_WORD. This
 
530
word and the following are collected and copied into dcl_command.
 
531
If look_ahead is not T_WORD, dcl_command becomes the null string.
 
532
Make_simple_command returnes the first token not used in building
 
533
dcl_command.
 
534
---------------------------------------------------------------------*/
 
535
TOKEN look_ahead;      /* first token to be used  */
 
536
char string[];         /* the string being parsed */
 
537
int *pos_ptr;          /* ponter to integer holding 
 
538
                         current position whithin string */
 
539
char dcl_command[];    /* string to hold the command being build */
 
540
int look_for_T_RIGHT;  /* 1 if ')' should be interpreted as a token, else 0 */
 
541
{
 
542
  dcl_command[0] = '\0';
 
543
 
 
544
  /* Check for empty command. */
 
545
  if (look_ahead != T_WORD) return(look_ahead);
 
546
 
 
547
  /* loop over the next tokens */
 
548
  while (1)
 
549
    {
 
550
      switch (look_ahead)
 
551
        {
 
552
          case T_WORD:
 
553
            if ((strlen(dcl_command) + strlen(ospword) + 1) > COMMAND_LEN) 
 
554
              return(T_TOOLONG);
 
555
            strcat(dcl_command, ospword);
 
556
            strcat(dcl_command, " ");
 
557
            look_ahead = get_token(string, pos_ptr, look_for_T_RIGHT);
 
558
            continue;
 
559
          default:   /* token was a command terminator */
 
560
            return(look_ahead);
 
561
        }
 
562
    }
 
563
}
 
564
 
 
565
 
 
566
static TOKEN make_command(look_ahead, string, pos_ptr, 
 
567
                          dcl_command, action)
 
568
/*-------------------------------------------------------------------
 
569
Make_command builds a command using tokens from string
 
570
and places it in dcl_command.
 
571
The routine is called with look_ahead holding the first
 
572
token of the command.
 
573
* If look_ahead is T_WORD, the simple exercice of building
 
574
  the command is left to make_simple command. 
 
575
* If look_ahead is T_LEFT, a command file is created and the
 
576
  commands enclosed in paranthesis are included in the file.
 
577
* If look_ahead is a command separator or terminator, an
 
578
  empty command is build.
 
579
* For other values of look_ahead som error handling is performed.
 
580
Some effords are made to avoid creating files for a single or empty 
 
581
command.
 
582
Make_command returnes the first token not used building dcl_command.
 
583
dcl_command will hold a simple command, or the command to run the
 
584
comand file.
 
585
---------------------------------------------------------------------*/
 
586
TOKEN look_ahead;     /* first token to be used  */
 
587
char string[];        /* the string being parsed */
 
588
int *pos_ptr;         /* ponter to integer holding 
 
589
                         current position whithin string */
 
590
char dcl_command[];   /* string to hold the command being build */
 
591
int action;           /* flag, if 0 only check syntax, 
 
592
                         else create the necessary files */
 
593
{
 
594
  FILE *fd;                        /* file descriptor of file created */
 
595
  char file_name[64];              /* name of file created */
 
596
  int com_file_created = 0;        /* flag, initially false */
 
597
  int look_for_T_RIGHT = 0;        /* flag, initially false */
 
598
  char last_dcl[COMMAND_LEN + 1];  /* temporary storage for dcl_command */
 
599
 
 
600
  file_name[0] = '\0';
 
601
  last_dcl[0] = '\0';
 
602
 
 
603
  /* switch on first token of the command */
 
604
  switch (look_ahead)
 
605
    {
 
606
      case T_SEMI:
 
607
      case T_AMP:
 
608
      case T_EOS:
 
609
        dcl_command[0] = '\0';
 
610
        return(look_ahead);
 
611
      case T_WORD:
 
612
        look_ahead = make_simple_command(look_ahead, string, pos_ptr,
 
613
                                         dcl_command, look_for_T_RIGHT);
 
614
        return(look_ahead);
 
615
      case T_RIGHT:
 
616
        fprintf(stderr, "Unexpected ')'\n");
 
617
        return(T_ERROR);
 
618
      case T_ERROR:
 
619
      case T_TOOLONG:
 
620
        return(look_ahead);
 
621
      case T_LEFT:
 
622
        /* create a com-file to execute commands in paranthesis in sequence */
 
623
        look_for_T_RIGHT = 1;
 
624
 
 
625
        /* not all tokens are admissable after T_LEFT */
 
626
        look_ahead = get_token(string, pos_ptr, look_for_T_RIGHT);
 
627
        switch (look_ahead)
 
628
          {
 
629
          case T_RIGHT:
 
630
            dcl_command[0] = '\0';
 
631
            look_for_T_RIGHT = 0;
 
632
            look_ahead = get_token(string, pos_ptr, look_for_T_RIGHT);
 
633
            return(look_ahead);
 
634
          case T_LEFT:
 
635
            fprintf(stderr, "Nested paranthesis not allowed\n");
 
636
            return(T_ERROR);
 
637
          case T_AMP:
 
638
            fprintf(stderr, "Unexpexted '&'\n");
 
639
            return(T_ERROR);
 
640
          case T_EOS:
 
641
            fprintf(stderr, "Unbalanced parenthesis\n");
 
642
            return(T_ERROR);
 
643
          case T_ERROR:
 
644
          case T_TOOLONG:
 
645
            return(look_ahead);
 
646
          default:  /* T_WORD or T_SEMI */
 
647
            break;  /* ok */
 
648
          }
 
649
 
 
650
        /* loop over commands to be included in command file */
 
651
        while (1)
 
652
          {
 
653
            look_ahead = make_simple_command(look_ahead, string, pos_ptr,
 
654
                                             dcl_command, look_for_T_RIGHT);
 
655
 
 
656
            /* switch on token following the command */
 
657
            switch(look_ahead) 
 
658
              {
 
659
                case T_WORD:
 
660
                  fprintf(stderr, "osp/parser: BUG! Error in parser\n");
 
661
                  return(T_ERROR);
 
662
                case T_LEFT:
 
663
                  fprintf(stderr, "Nested paranthesis not allowed\n");
 
664
                  return(T_ERROR);
 
665
                case T_AMP:
 
666
                  fprintf(stderr, "'&' not allowed in paranthesis\n");
 
667
                  return(T_ERROR);
 
668
                case T_EOS:
 
669
                  fprintf(stderr, "Unbalanced parenthesis\n");
 
670
                  return(T_ERROR);
 
671
                case T_ERROR:
 
672
                case T_TOOLONG:
 
673
                  return(look_ahead);
 
674
                case T_SEMI:
 
675
                case T_RIGHT:
 
676
                  if (strlen(dcl_command) > 0)
 
677
                    {
 
678
                      /* if this is not the first nonempty command */
 
679
                      if (strlen(last_dcl) > 0)
 
680
                        {
 
681
                          /* if command file not yet created, do it */
 
682
                          if (!com_file_created)
 
683
                            {
 
684
                              sprintf(file_name, "sys$scratch:midas_%s.com", 
 
685
                                                 tmpnam(NULL));
 
686
                              if (action)
 
687
                                {
 
688
                                  fd = fopen(file_name, "w");
 
689
                                  fprintf(fd, "$ SET NOON\n");
 
690
                                }
 
691
                              com_file_created = 1; /* true */
 
692
                            }
 
693
 
 
694
                          /* Write last command to file */
 
695
                          if (action) 
 
696
                            fprintf(fd, "$ %s\n", last_dcl);
 
697
                        }
 
698
 
 
699
                      /* copy dcl_commnad to last_dcl */
 
700
                      strcpy(last_dcl, dcl_command);
 
701
                    }
 
702
 
 
703
                  /* if command was terminated with ';', build next command */
 
704
                  if (look_ahead == T_SEMI)
 
705
                    {
 
706
                      /* get the next token and loop to next command */
 
707
                      look_ahead = get_token(string, pos_ptr,look_for_T_RIGHT);
 
708
                      continue;
 
709
                    }
 
710
 
 
711
                  /* command was terminated with ')', close the com-file */
 
712
                  if (com_file_created)
 
713
                    {
 
714
                      if (action)
 
715
                        {
 
716
                          fprintf(fd, "$ %s\n", last_dcl);
 
717
/*                        fprintf(fd,
 
718
"$ IF f$process() .nes. f$getjpi(\"%x\", \"prcnam\") THEN \
 
719
WRITE sys$error \"Process \" + f$process() + \" terminated\"\n", sub_proc.pid);
 
720
                          fprintf(fd, "$ DELETE/NOLOG %s;\n", file_name); */
 
721
                          fclose(fd);
 
722
                        }
 
723
                      sprintf(dcl_command, "@%s", file_name);
 
724
                    }
 
725
                  else
 
726
                    strcpy(dcl_command, last_dcl);
 
727
                  /* get the next token */
 
728
                  look_for_T_RIGHT = 0;
 
729
                  look_ahead = get_token(string, pos_ptr, look_for_T_RIGHT);
 
730
                  return(look_ahead);
 
731
              }
 
732
          }
 
733
    }
 
734
}
 
735
 
 
736
 
 
737
static int run_command(command, action)
 
738
/*----------------------------------------------------------------------
 
739
run_command execute the command(s) in the command string if the flag
 
740
action is set.  It the flag is not set, only syntax checking will be
 
741
performed. Commands are separated with ';' or '&'. If a command
 
742
is terminated with an '&', dcl_command is concatenated with the string 
 
743
"SPAWN/NOWAIT " so that it will be run in the background.
 
744
run_command retuns 1 on error, 0 if command was ok.
 
745
----------------------------------------------------------------------*/
 
746
char command[];     /* the command to be parsed and executed */
 
747
int action;         /* flag, if 0 only check syntax, 
 
748
                       else create the necessary files */
 
749
{
 
750
  TOKEN look_ahead;                   /* first token not yet used  */
 
751
  char dcl_command[COMMAND_LEN + 1];  /* string to hold the 
 
752
                                         command being build */
 
753
  int pos = 0;                        /* current position whithin string */
 
754
 
 
755
  /* Get first token in command. */
 
756
  look_ahead = get_token(command, &pos, 0);
 
757
 
 
758
  /* loop over commands separated by semicolons or ampersent */
 
759
  while (1)
 
760
    {
 
761
      look_ahead = make_command(look_ahead, command, &pos, dcl_command,action);
 
762
      switch(look_ahead)
 
763
        {
 
764
          case T_SEMI:
 
765
            if (action && strlen(dcl_command) > 0)
 
766
              {
 
767
                write_channel(sub_proc.channel, dcl_command);
 
768
                write_channel(sub_proc.channel,
 
769
                              "DEFINE/NOLOG SYS$INPUT 'F$TRNLNM(\"TT\")");
 
770
              }
 
771
            look_ahead = get_token(command, &pos, 0);
 
772
            continue;
 
773
          case T_AMP:
 
774
            if (strlen(dcl_command) > 0)
 
775
              if (strlen("SPAWN/NOWAIT/NOLOG ") +
 
776
                  strlen(dcl_command) > COMMAND_LEN)
 
777
                {
 
778
                  fprintf(stderr, "Token or command too long\n");
 
779
                  return(1);
 
780
                }
 
781
              else
 
782
                {
 
783
                  strprecat(dcl_command, "SPAWN/NOWAIT/NOLOG ");
 
784
                  if (action)
 
785
                    write_channel(sub_proc.channel, dcl_command);
 
786
                }
 
787
            look_ahead = get_token(command, &pos, 0);
 
788
            continue;
 
789
          case T_EOS:
 
790
            if (action && strlen(dcl_command) > 0)
 
791
              {
 
792
                write_channel(sub_proc.channel, dcl_command);
 
793
                write_channel(sub_proc.channel,
 
794
                              "DEFINE/NOLOG SYS$INPUT 'F$TRNLNM(\"TT\")");
 
795
              }
 
796
            return(0);
 
797
          case T_LEFT:
 
798
            fprintf(stderr, "Unexpected left parenthesis\n");
 
799
            return(1);
 
800
          case T_RIGHT:
 
801
            fprintf(stderr, "Unexpected right parenthesis\n");
 
802
            return(1);
 
803
          case T_WORD:
 
804
            fprintf(stderr, "';' expected\n");
 
805
            return(1);
 
806
          case T_ERROR:
 
807
            return(1);
 
808
          case T_TOOLONG:
 
809
            fprintf(stderr, "Token or command too long\n");
 
810
            return(1);
 
811
        }
 
812
    }
 
813
}
 
814
 
 
815
 
 
816
/* The entrypoint for the VMS implementation */
 
817
 
 
818
long ospsystem(command)
 
819
/* Routine simulating the UNIX "system()" library call under VMS. */
 
820
char *command;   /* command to be executed */
 
821
{
 
822
  /* parameters for SYS$QIOW */
 
823
  long event_flag, channel, function, acc_mode, status;
 
824
 
 
825
  /* check the command syntax */
 
826
  if (run_command(command, 0))      /* if syntax error */
 
827
    return(1);
 
828
 
 
829
  /* If subprocess not yet created, do it. */
 
830
  /* Should ask system for the existance of the process, but ... */
 
831
  if (!sub_proc_created)
 
832
    {
 
833
      if (ospdebug)
 
834
        fprintf(stderr, "ospsystem: Creating a subprocess\n");
 
835
      if (create_sub_proc() != 0)
 
836
        {
 
837
          fprintf(stderr, "Unable to create subprocess\n");
 
838
          return(1);
 
839
        }
 
840
 
 
841
      /* Initialize subprocess. */
 
842
      if (ospdebug)
 
843
        fprintf(stderr, "osp/create_sub_proc: initializing subprocess\n");
 
844
      if (init_sub_proc())
 
845
        printf(stderr, "Subprocess may not be properly initialized\n");
 
846
 
 
847
      /* Intstall exit handler. */
 
848
      if (ospdebug)
 
849
        fprintf(stderr, "osp/create_sub_proc: installing exit handler\n");
 
850
      if (!exit_handler_installed && install_exit_handler())
 
851
          printf(stderr, "Subprocess may not be deleted on exit\n");
 
852
 
 
853
      if (ospdebug)
 
854
        fprintf(stderr, "ospsystem: Subprocess created\n");
 
855
      sub_proc_created = 1;  /* true */
 
856
    }
 
857
 
 
858
  /* Run the command in the subprocess. */
 
859
  (void) run_command(command, 1);
 
860
 
 
861
  /* enable mailbox to run ready_ast when dcl command has compleated */
 
862
  event_flag = 0;                          /* not used */
 
863
  channel = sub_proc.channel;              /* channel assigned to mailbox */
 
864
  function = IO$_SETMODE | IO$M_READATTN;  /* read attention ast */
 
865
 
 
866
  status = SYS$QIOW(event_flag, channel, function, NULL, 
 
867
                    NULL, 0, ready_ast, NULL, 0, 0, 0, 0);
 
868
  if (!BIT_0(status))
 
869
    {
 
870
      error_msg("SYS$QIOW", status);
 
871
      fprintf(stderr, 
 
872
        "Unable to enable ready_ast, skipping the hibernation\n");
 
873
    }
 
874
  else
 
875
    {
 
876
      /* wait for dcl command to complete, ready_ast will wake us up */
 
877
      if (ospdebug)
 
878
        fprintf(stderr, "ospsystem: caller is hibernated\n");
 
879
 
 
880
      status = SYS$HIBER();
 
881
      if (!BIT_0(status))
 
882
        {
 
883
          error_msg("SYS$HIBER", status);
 
884
          fprintf(stderr, "Hibernation failed\n");
 
885
        }
 
886
      else
 
887
        if (ospdebug) 
 
888
          fprintf(stderr, "ospsystem: Hibernated caller waked up\n");
 
889
    }
 
890
}