1 /*
2  * Author:	George Carrette, <GJC@MITECH.COM> in November, 1990.
3  *
4  * @(#)$Header: /mm2/home/cvs/bc-src/tgif/vms_comp.c,v 1.1 2004/06/18 23:19:10 william Exp $
5  */
6 
7 /* vms_comp routines, generally useful functions
8    to aid in porting/running various common Unix style code under VAX/VMS.
9 
10    This code has been sufficient to run programs from sources
11    such as COMP.SOURCES.X, in particular XV and TGIF.
12 
13    Some routines are for direct unix compatibility and have the same
14    exact calling sequence and names as in various versions of unix.
15    Some are vms specific and are intended to be called from generally
16    unix-style programs inside a minimal amount of {#ifdef VMS #endif}
17    conditionalized code.
18 
19    **  Written by George Carrette, <GJC@MITECH.COM> in November, 1990.
20    **  Includes modified VMS readdir() routines.
21    **  Written by Rich $alz, <rsalz@bbn.com> in August, 1990.
22    **  some additions by Rolf Niepraschk, niepraschk@ptb.de, 1997/11/24
23 
24 To use:
25 
26 #include "vms_comp.h"
27 
28 Used generally where you see #include <sys/dir.h>.
29 
30 Contains:
31  bcopy    ... full functionality
32  rindex   ... full functionality
33  lstat    ... full functionality, just a name change
34  fork     ... dummy, returns 1.
35  getwd    ... full functionality, returns unix style pathname.
36  unlink   ... full functionality(?), just a name change
37  popen    ... ?.
38  pclose   ... ?.
39  opendir  ... almost full functionality.
40  closedir ... almost full functionality.
41  readdir  ... almost full functionality.
42  telldir  ... almost full functionality.
43  seekdir  ... almost full functionality.
44  ioctl    ... dummy, returns 0. -- RN --
45  fcntl    ... dummy, returns 0. -- RN --
46 
47  do_vms_wildcard ... non-unix. but put this into your MAIN program to
48                      get limited wildcard processing on the command line.
49                      Currently returns vms style pathnames.
50 
51 */
52 
53 /* some of these unix compat functions are just dummy
54    versions to get the code running. some are fully functional.
55    the use of fork and popen/pclose in particular
56    assume unix command shell as used in the main code.
57    And since this sort of thing is generally done for the purposes
58    such as printing, spooling mail, etc, which are best done
59    in considerably different ways under VMS, it is better to
60    dummy this stuff up now and then restructure the code that
61    calls this sort of thing to be more abstract/subroutinized
62    and then conditionalize it specifically for VMS
63 */
64 
65 #include "vms_comp.h"
66 
67 #ifdef VMS
68 
69 #if 0                    /* RN */
70 bcopy(x,y,n)
71      char *x,*y;
72      long n;
73 {memmove(x,y,n);}
74 
75 char *rindex(p,c)
76      char *p;
77      int c;
78 {return(strrchr(p,c));}
79 
80 int lstat(x,y)
81 {return(stat(x,y));}
82 #endif                   /* RN */
83 
fork()84 fork()
85 {return(1);}
86 
87 static char *getwd_tmp = NULL;
88 
getwd(p)89 char *getwd(p)
90      char *p;
91 {int c;
92  char *l1,*l2;
93  if (getwd_tmp == NULL)
94    getwd_tmp = (char *) malloc(512);
95  l1 = getwd_tmp;
96  getcwd(l1,512);
97  l2 = p;
98  *l2++ = '/';
99  while((c = *l1++))
100    switch(c)
101      {case '.':
102       case '[':
103 	*l2++ = '/';
104 	break;
105       case ']':
106       case ':':
107 	break;
108       default:
109 	*l2++ = c;}
110  *l2 = 0;
111  return(p);}
112 
113 #if 0                    /* RN */
114 unlink(p)
115      char *p;
116 {delete(p);}
117 #endif                   /* RN */
118 
119 static vms_wild_putargs(),vms_wild_put_one(),vms_wild_put_wild();
120 static struct dsc$descriptor *set_dsc();
121 static struct dsc$descriptor *set_dsc_cst();
122 
do_vms_wildcard(pargc,pargv)123 do_vms_wildcard(pargc,pargv)
124      int *pargc;
125      char ***pargv;
126 {int j,vsize;
127  int argc; char **argv;
128  argc = *pargc;
129  argv = *pargv;
130  *pargc = 0;
131  vsize = 3;
132  *pargv = (char **) malloc(sizeof (char *) * vsize);
133  for(j=0;j<argc;++j)
134    vms_wild_putargs(argv[j],pargc,pargv,&vsize);}
135 
vms_wild_putargs(s,pargc,pargv,pvsize)136 static vms_wild_putargs(s,pargc,pargv,pvsize)
137      char *s; int *pargc; char ***pargv; int *pvsize;
138 {if (!strchr(s,'*'))
139    vms_wild_put_one(s,pargc,pargv,pvsize);
140  else
141    vms_wild_put_wild(s,pargc,pargv,pvsize);}
142 
vms_wild_put_one(s,pargc,pargv,pvsize)143 static vms_wild_put_one(s,pargc,pargv,pvsize)
144      char *s; int *pargc; char ***pargv; int *pvsize;
145 {int nvsize,i;
146  char ** nargv;
147  if (*pargc == *pvsize)
148    {nvsize = 2 * *pvsize;
149     nargv = (char **) malloc(sizeof (char *) * nvsize);
150     for(i=0;i < *pargc; ++i) nargv[i] = (*pargv)[i];
151     free(*pargv);
152     *pargv = nargv;
153     *pvsize = nvsize;}
154  (*pargv)[(*pargc)++] = s;}
155 
156 
set_dsc(x,buff,len)157 static struct dsc$descriptor *set_dsc(x,buff,len)
158      struct dsc$descriptor *x;
159      char *buff;
160      int len;
161 {(*x).dsc$w_length = len;
162  (*x).dsc$a_pointer = buff;
163  (*x).dsc$b_class = DSC$K_CLASS_S;
164  (*x).dsc$b_dtype = DSC$K_DTYPE_T;
165  return(x);}
166 
set_dsc_cst(x,buff)167 static struct dsc$descriptor *set_dsc_cst(x,buff)
168      struct dsc$descriptor *x;
169      char *buff;
170 {(*x).dsc$w_length = strlen(buff);
171  (*x).dsc$a_pointer = buff;
172  (*x).dsc$b_class = DSC$K_CLASS_S;
173  (*x).dsc$b_dtype = DSC$K_DTYPE_T;
174  return(x);}
175 
vms_wild_put_wild(s,pargc,pargv,pvsize)176 static vms_wild_put_wild(s,pargc,pargv,pvsize)
177      char *s; int *pargc; char ***pargv; int *pvsize;
178 {struct dsc$descriptor fnamed,foutd,rfnamed;
179  char *ns,*p;
180  int rval;
181  long context;
182  set_dsc_cst(&rfnamed,";");
183  set_dsc_cst(&fnamed,s);
184  set_dsc(&foutd,0,0);
185  foutd.dsc$b_class = DSC$K_CLASS_D;
186  context = 0;
187  while(1)
188   {rval = lib$find_file(&fnamed,&foutd,&context,0,&rfnamed,0,0);
189    if (rval == RMS$_NMF) break;
190    if (rval == RMS$_FNF) break;
191    if (rval != RMS$_NORMAL) exit(rval);
192    ns = (char *) malloc(foutd.dsc$w_length + 1);
193    ns[foutd.dsc$w_length] = 0;
194    memcpy(ns,foutd.dsc$a_pointer,foutd.dsc$w_length);
195    if (p = strchr(ns,']')) ns = p+1;
196    if (p = strchr(ns,';')) *p = 0;
197    vms_wild_put_one(ns,pargc,pargv,pvsize);}
198  if (foutd.dsc$a_pointer) lib$sfree1_dd(&foutd);
199  if (context)
200    {rval = lib$find_file_end(&context);
201     if (rval != SS$_NORMAL) exit(rval);}}
202 
203 /* globalvalue CLI$M_NOWAIT; -- RN */
204 
205 static int create_mbx();
206 
207 #define mailbox_size (512)
208 #define mailbox_byte_quota (3*mailbox_size)
209 #define mailbox_protection_mask (0x0000F000)
210 
211 struct popen_cell
212 {FILE *fp;
213  char *mbx_name;
214  short mbx_chan;
215  long pid;
216  long completed;
217  long comp_status;
218  struct popen_cell *next;
219  struct popen_cell *prev;};
220 
221 #if __VMS_VER < 70000000 /* RN */
222 static struct popen_cell *popen_list = NULL;
223 
find_popen_cell(fp)224 static struct popen_cell *find_popen_cell(fp)
225      FILE *fp;
226 {struct popen_cell *l;
227  for(l=popen_list;l != NULL; l = l->next)
228    if (l->fp == fp) return(l);
229  return(NULL);}
230 
p_describe(fp)231 void p_describe(fp)
232      FILE *fp;
233 {struct popen_cell *cell;
234  if (!(cell = find_popen_cell(fp)))
235    {printf("File pointer is not from popen, or it has been closed\n");
236     return;}
237  printf("FILE *fp                = %08X\n",cell->fp);
238  printf("char *mbx_name          = %s\n",cell->mbx_name);
239  printf("short mbx_chan          = %d\n",cell->mbx_chan);
240  printf("long pid                = %08X\n",cell->pid);
241  printf("long completed          = %d\n",cell->completed);
242  printf("long comp_status        = %d\n",cell->comp_status);
243  printf("struct popen_cell *next = %08X\n",cell->next);
244  printf("struct popen_cell *prev = %08X\n",cell->prev);}
245 
proc_exit_ast(cell)246 static void proc_exit_ast(cell)
247      struct popen_cell *cell;
248 {cell->completed = 1;}
249 
pclose_cleanup(cell)250 static void pclose_cleanup(cell)
251      struct popen_cell *cell;
252 {sys$dassgn(cell->mbx_chan);
253  free(cell->mbx_name);
254  if (!cell->completed)
255    sys$delprc(&cell->pid,0);
256  memset(cell,0,sizeof(struct popen_cell));
257  free(cell);}
258 
pclose_delq(cell)259 static void pclose_delq(cell)
260      struct popen_cell *cell;
261 {if (cell->prev)
262    {cell->prev->next = cell->next;
263     if (cell->next)
264       cell->next->prev = cell->prev;}
265  else
266    {popen_list = cell->next;
267     if (cell->next)
268       cell->next->prev = NULL;}}
269 
popen_push(cell)270 static void popen_push(cell)
271      struct popen_cell *cell;
272 {if (popen_list)
273    popen_list->prev = cell;
274  cell->prev = NULL;
275  cell->next = popen_list;
276  popen_list = cell;}
277 
278 /* 17-APR-1991 -GJC@MITECH.COM  version 1.0
279                Implement unix popen and pclose in vms by using mailboxes.
280  */
281 
282 /* popen starts a subprocess and opens a pipe to its stdout or stdin
283    (mode = "r" its stdout, = "w" its stdin)
284    */
285 
popen(command,mode)286 FILE *popen(command,mode)
287      char *command,*mode;
288 {char *temp;
289  struct popen_cell *cell;
290  int readp,n,mask,ret;
291  char *name,*prompt,*in,*out;
292  struct dsc$descriptor comm_d,in_d,out_d,name_d,prompt_d;
293 
294  if (strcmp(mode,"r") == 0)
295    readp = 1;
296  else if (strcmp(mode,"w") == 0)
297    readp = 0;
298  else
299    return(NULL);
300 
301  temp = mktemp("POPEN_MB_XXXXXXXXXX");
302  n = strlen(temp);
303   cell =  (struct popen_cell *) malloc(sizeof(struct popen_cell));
304  cell->mbx_name = (char *) malloc(n+1);
305  strcpy(cell->mbx_name,temp);
306  if ((cell->mbx_chan = create_mbx(cell->mbx_name)) < 0)
307    {cell->completed = 1;
308     pclose_cleanup(cell);
309     return(NULL);}
310 
311  if (readp)
312    {in = "NL:";
313     out = cell->mbx_name;}
314  else
315    {in = cell->mbx_name;
316     out = "NL:";}
317 
318  name = 0;
319  prompt = 0;
320  mask = CLI$M_NOWAIT;
321 
322  cell->completed = 0;
323 
324  ret = lib$spawn((command) ? set_dsc_cst(&comm_d,command) : 0,
325                  (in) ? set_dsc_cst(&in_d,in) : 0,
326                  (out) ? set_dsc_cst(&out_d,out) : 0,
327                  &mask,
328                  (name)  ? set_dsc_cst(&name_d,name) : 0,
329                  &cell->pid,
330                  &cell->comp_status,
331                  0, /* event flag */
332 		 proc_exit_ast,
333 		 cell,
334                  (prompt) ? set_dsc_cst(&prompt_d,prompt) : 0,
335                  0 /* cli */
336                  );
337 
338  if (ret != SS$_NORMAL)
339    {cell->completed = 1;
340     pclose_cleanup(cell);
341     return(NULL);}
342 
343  if (!(cell->fp = fopen(cell->mbx_name,mode)))
344    {pclose_cleanup(cell);
345     return(NULL);}
346 
347  popen_push(cell);
348 
349  return(cell->fp);}
350 
pclose(fp)351 pclose(fp)
352      FILE *fp;
353 {int i;
354  struct popen_cell *cell;
355  i = fclose(fp);
356  if (cell = find_popen_cell(fp))
357    {pclose_delq(cell);
358     pclose_cleanup(cell);}
359  return(i);}
360 
create_mbx(name)361 static int create_mbx(name)
362   char *name;
363 {short chan;
364  int prmflg,maxmsg,bufquo,promsk,acmode,iflag,retval;
365  struct dsc$descriptor lognam;
366  prmflg = 0;
367  maxmsg = mailbox_size;
368  bufquo = mailbox_byte_quota;
369  promsk = mailbox_protection_mask;
370  acmode = 0;
371  set_dsc_cst(&lognam,name);
372  retval = sys$crembx(prmflg,&chan,maxmsg,bufquo,promsk,acmode,&lognam);
373  if (retval != SS$_NORMAL) return(-1);
374  return(chan);}
375 
376 #endif /* __VMS_VER < 70000000 */ /* RN */
377 
378 /*
379 **  VMS readdir() routines.
380 **  Written by Rich $alz, <rsalz@bbn.com> in August, 1990.
381 **  This code has no copyright.
382 */
383 
384 /* 12-NOV-1990 added d_namlen field and special case "." name -GJC@MITECH.COM
385                added unlink and getwd, also typically needed to port
386                unix style code that does directory manipulation.
387    26-MAR-1991 added lowercasing of d_name. -GJC@MITECH.COM typically
388                strcmp and other operations may be done on the names.
389                added a lot more hair to fixunixname.
390  */
391 
392     /* Uncomment the next line to get a test routine. */
393 /*#define TEST*/
394 
395     /* Number of elements in vms_versions array */
396 #define VERSIZE(e)	(sizeof e->vms_versions / sizeof e->vms_versions[0])
397 
fixunixname(name)398 static char *fixunixname(name)
399      char *name;
400      /* This handles special cases such as "." and ".."
401 	and also undoes some of the stuff that getwd does.
402 	*/
403 {FILE *f;
404  char *p;
405  static char *tmp = NULL;
406  if (strcmp(".",name) == 0) return("");
407  if (strcmp("..",name) == 0) return("[-]");
408  if (strchr(name,':') ||
409      strchr(name,'[') ||
410      strchr(name,']'))
411    return(name);
412  if (tmp == NULL)
413    tmp = (char *) malloc(128);
414  if (name[0] != '/')
415    /* foo/bar/baz => [.foo.bar.baz] */
416    {strcpy(tmp,"[.");
417     strcat(tmp,name);
418     p = &tmp[strlen(tmp) - 1];
419     if (*p == '/') *p = 0;
420     strcat(tmp,"]");
421     for(p=tmp;*p;++p) if (*p == '/') *p = '.';
422     return(tmp);}
423  if ((name[0] == '/') && !strchr(name+1,'/'))
424    /* /foo => foo:[000000] */
425    {strcpy(tmp,name+1);
426     strcat(tmp,":[000000]");
427     return(tmp);}
428  /* /foo/bar/baz => foo:[bar.baz] */
429  p = strchr(name+1,'/');
430  *p = 0;
431  strcpy(tmp,name+1);
432  strcat(tmp,":[");
433  strcat(tmp,p+1);
434  p = &tmp[strlen(tmp) - 1];
435  if (*p == '/') *p = 0;
436  strcat(tmp,"]");
437  for(p=tmp;*p;++p) if (*p == '/') *p = '.';
438  return(tmp);}
439 
440 
441 /*
442 **  Open a directory, return a handle for later use.
443 */
444 
445 static char *opendir_pat = "*.*";
446 
opendir(name)447 DIR *opendir(name)
448     char	*name;
449 {DIR *dd;
450  /* Get memory for the handle, and the pattern. */
451  if ((dd = (DIR *)malloc(sizeof *dd)) == NULL)
452    {errno = ENOMEM;
453     return NULL;}
454 
455  name = fixunixname(name);
456 
457  dd->pattern = (char *) malloc((unsigned int)(strlen(name) +
458 					      strlen(opendir_pat) +
459 					      1));
460  if (dd->pattern == NULL)
461    {free((char *)dd);
462     errno = ENOMEM;
463     return NULL;}
464 
465  /* Fill in the fields; mainly playing with the descriptor. */
466  strcpy(dd->pattern,name);
467  strcat(dd->pattern,opendir_pat);
468  dd->context = 0;
469  dd->vms_wantversions = 0;
470  set_dsc_cst(&dd->pat,dd->pattern);
471  return dd;}
472 
473 
474 /*
475 **  Set the flag to indicate we want versions or not.
476 */
477 
vmsreaddirversions(dd,flag)478 void vmsreaddirversions(dd, flag)
479      DIR		*dd;
480      int		flag;
481 {dd->vms_wantversions = flag;}
482 
483 
484 /*
485 **  Free up an opened directory.
486 */
487 
closedir(dd)488 void closedir(dd)
489     DIR		*dd;
490 {if (dd->context)
491    lib$find_file_end(&dd->context);
492  free(dd->pattern);
493  free((char *)dd);}
494 
495 /*
496 **  Collect all the version numbers for the current file.
497 */
498 
collectversions(dd)499 static void collectversions(dd)
500      DIR *dd;
501 {struct dsc$descriptor_s pat;
502  struct dsc$descriptor_s res;
503  struct dirent *e;
504  char *p;
505  char buff[sizeof dd->entry.d_name];
506  int i;
507  char *text;
508  long  context;
509 
510  /* Convenient shorthand. */
511  e = &dd->entry;
512 
513  /* Add the version wildcard, ignoring the "*.*" put on before */
514  i = strlen(dd->pattern);
515  text = (char *) malloc((unsigned int)(i + strlen(e->d_name)+ 2 + 1));
516  if (text == NULL)
517    return;
518  (void)strcpy(text, dd->pattern);
519  text[i - strlen(opendir_pat)] = 0;
520  strcat(text,e->d_name);
521  strcat(text,";*");
522 
523  /* Set up the pattern descriptor. */
524  set_dsc_cst(&pat,text);
525 
526  /* Set up result descriptor. */
527  set_dsc(&res,buff,sizeof buff - 2);
528 
529  /* Read files, collecting versions. */
530  for (context = 0; e->vms_verscount < VERSIZE(e); e->vms_verscount++)
531    {if (lib$find_file(&pat, &res, &context) == RMS$_NMF || context == 0)
532       break;
533     buff[sizeof buff - 1] = '\0';
534     if (p = strchr(buff, ';'))
535       e->vms_versions[e->vms_verscount] = atoi(p + 1);
536     else
537       e->vms_versions[e->vms_verscount] = -1;}
538 
539  lib$find_file_end(&context);
540 
541  free(text);}
542 
543 
strtolower(s)544 static strtolower(s)
545      char *s;
546 {int c;
547  char *p;
548  p = s;
549  while(c = *p)
550    {if ((c > 'Z') || (c < 'A'))
551       ++p;
552     else
553       *p++ = c + ('a' - 'A');}}
554 
555 
556 /*
557 **  Read the next entry from the directory.
558 */
readdir(dd)559 struct dirent *readdir(dd)
560     DIR				*dd;
561 {struct dsc$descriptor_s	res;
562  char			*p;
563  char			buff[sizeof dd->entry.d_name];
564  int				i;
565  int status;
566 
567  /* Set up result descriptor, and get next file. */
568  set_dsc(&res,buff,sizeof buff - 2);
569  status = lib$find_file(&dd->pat, &res, &dd->context);
570  if (status == RMS$_NMF || dd->context == 0L)
571    /* None left... */
572    return NULL;
573 
574  if ((status != SS$_NORMAL) && (status != RMS$_NORMAL))
575    /* May be something like RMS$_SYN for bad syntax patterns */
576    return NULL;
577 
578  /* Force the buffer to end with a NUL. */
579  buff[sizeof buff - 1] = '\0';
580  for (p = buff; !isspace(*p); p++);
581  *p = '\0';
582 
583  /* Skip any directory component and just copy the name. */
584  if (p = strchr(buff, ']'))
585    (void)strcpy(dd->entry.d_name, p + 1);
586  else
587    (void)strcpy(dd->entry.d_name, buff);
588 
589  /* Clobber the version. */
590  if (p = strchr(dd->entry.d_name, ';'))
591    *p = '\0';
592 
593  dd->entry.d_namlen = strlen(dd->entry.d_name);
594 
595  dd->entry.vms_verscount = 0;
596  if (dd->vms_wantversions)
597    collectversions(dd);
598 
599  /* force to lowercase, since many unix programs look for
600     specific file extensions, etc, which are almost always in
601     lowercase */
602 
603  strtolower(dd->entry.d_name);
604 
605  return &dd->entry;}
606 
607 
608 /*
609 **  Return something that can be used in a seekdir later.
610 */
611 
telldir(dd)612 long telldir(dd)
613      DIR		*dd;
614 {return dd->context;}
615 
616 
617 /*
618 **  Return to a spot where we used to be.
619 */
620 
seekdir(dd,pos)621 void seekdir(dd, pos)
622      DIR		*dd;
623      long	pos;
624 {dd->context = pos;}
625 
ioctl(int fildes,int request,...)626 int ioctl(int fildes, int request,... /* arg */)
627 {
628   fprintf(stderr, "\"ioctl\" not implemented (VMS)\n");
629   return 0;
630 }
631 
fcntl(int filedes,int request,int argument)632 int fcntl (int filedes, int request , int argument)
633 {
634   fprintf(stderr, "\"fcntl\" not implemented (VMS)\n");
635   return 0;
636 }
637 
638 
639 
640 #ifdef	TEST
main()641 main()
642 {
643     char		buff[256];
644     DIR			*dd;
645     struct dirent	*dp;
646     int			i;
647     int			j;
648 
649     for ( ; ; ) {
650 	printf("\n\nEnter dir:  ");
651 	(void)fflush(stdout);
652 	(void)gets(buff);
653 	if (buff[0] == '\0')
654 	    break;
655 	if ((dd = opendir(buff)) == NULL) {
656 	    perror(buff);
657 	    continue;
658 	}
659 
660 	/* Print the directory contents twice, the second time print
661 	 * the versions. */
662 	for (i = 0; i < 2; i++) {
663 	    while (dp = readdir(dd)) {
664 		printf("%s%s", i ? "\t" : "    ", dp->d_name);
665 		for (j = 0; j < dp->vms_verscount; j++)
666 		    printf("  %d", dp->vms_versions[j]);
667 		printf("\n");
668 	    }
669 	    rewinddir(dd);
670 	    vmsreaddirversions(dd, 1);
671 	}
672 	closedir(dd);
673     }
674     exit(0);
675 }
676 #endif	/* TEST */
677 
678 #endif  /* VMS */
679