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