This is vms.c in view mode; [Download] [Up]
/* * Copyright (c) 1992, 1995 John E. Davis (davis@space.mit.edu) * All Rights Reserved. */ #include <config.h> #include <stdio.h> #include <string.h> #include "sysdep.h" #include <ssdef.h> #include <rmsdef.h> #include <dvidef.h> #include <jpidef.h> #include <descrip.h> #include <iodef.h> #include <ttdef.h> #include <rms.h> #include <errno.h> #include <tt2def.h> #ifdef __DECC #include <starlet.h> #include <lib$routines.h> #include <unixlib.h> #include <unixio.h> #endif /* #include <libdef.h> */ typedef struct { /* I/O status block */ short i_cond; /* Condition value */ short i_xfer; /* Transfer count */ long i_info; /* Device information */ } Iosb_Type; typedef struct { /* Terminal characteristics */ char t_class; /* Terminal class */ char t_type; /* Terminal type */ short t_width; /* Terminal width in characters */ long t_mandl; /* Terminal's mode and length */ long t_extend; /* Extended terminal characteristics */ } TermChar_Type; TermChar_Type Old_Term_Char, New_Term_Char; int Abort_Char = 7; /* scan code for G (control) */ /* This serves to identify the channel we are reading input from. */ short This_Term; typedef struct { short buflen; short item_code; int *buf_addr; int *len_addr; } item_list_3; static int TTY_Inited; void vms_exit_handler(int not_used) { if (TTY_Inited == 0) exit(0); auto_save_all(); reset_display(); reset_tty(); exit(1); } /* * Exit Handler Control Block */ static struct argument_block { int forward_link; int (*exit_routine)(); int arg_count; int *status_address; int exit_status; } exit_block = { 0, NULL, 1, &exit_block.exit_status, 0 }; void vms_cancel_exithandler() { sys$canexh(exit_block); } #undef USING_INPUT_BUFFER #undef DONE_WITH_INPUT_BUFFER int vms_input_buffer; static struct vms_ast_iosb { short status; short offset; short termlen; short term; } vms_ast_iosb; extern void vms_que_key_ast(); static int Ast_Fired_Event_Flag; static int Timer_Event_Flag; static int Event_Flag_Mask; static int Ast_Stop_Input; static int Waiting_For_Ast; static int Using_Keyboard_Buffer_Event_Flag; #define USING_INPUT_BUFFER\ sys$setast(0); #define DONE_WITH_INPUT_BUFFER\ sys$setast(1); static int getkey_ast(int not_used) { unsigned int c = 1000; if (vms_ast_iosb.offset) { c = (unsigned int) vms_input_buffer; } if (c <= 255) { if (c == Abort_Char) { if (Ignore_User_Abort == 0) SLang_Error = 2; SLKeyBoard_Quit = 1; } USING_INPUT_BUFFER if (Input_Buffer_Len < MAX_INPUT_BUFFER_LEN - 3) Input_Buffer[Input_Buffer_Len++] = c; DONE_WITH_INPUT_BUFFER } if (Waiting_For_Ast) sys$setef (Ast_Fired_Event_Flag); Waiting_For_Ast = 0; vms_que_key_ast(); return (1); } void vms_que_key_ast() { static int trmmsk [2] = { 0, 0 }; int status; if (Ast_Stop_Input) return; status = sys$qio (0, This_Term, IO$_READVBLK | IO$M_NOECHO | IO$_TTYREADALL, &vms_ast_iosb, getkey_ast, 1, &vms_input_buffer, 1, 0, trmmsk, 0, 0); } static char TTY_Name[8]; static int This_Process_Pid; void init_tty() { Iosb_Type iostatus; int tmp, name_len, status, lastppid, ppid; item_list_3 itmlst[3]; $DESCRIPTOR ( term, TTY_Name); itmlst[0].buflen = sizeof(int); itmlst[0].item_code = JPI$_PID; itmlst[0].buf_addr = &This_Process_Pid; itmlst[0].len_addr = &tmp; itmlst[1].buflen = 7; itmlst[1].item_code = JPI$_TERMINAL; itmlst[1].buf_addr = (int *) TTY_Name; itmlst[1].len_addr = &name_len; itmlst[2].buflen = 0; itmlst[2].item_code = 0; itmlst[2].buf_addr = 0; itmlst[2].len_addr = 0; TTY_Inited = 1; ppid = 0, lastppid = -1; /* Here I get this process pid then I get the master process pid and use the controlling terminal of that process. */ while (1) { status = sys$getjpiw(0, /* event flag */ &ppid, /* pid address */ 0, /* proc name address */ itmlst, 0, 0, 0); if (status != SS$_NORMAL) { fprintf(stderr, "PID: %X, status: %X\n", This_Process_Pid, status); exit(1); } if (lastppid == ppid) break; lastppid = ppid; itmlst[0].item_code = JPI$_MASTER_PID; itmlst[0].buf_addr = &ppid; } if (Batch) return; if (X_Init_Term_Hook != NULL) { (void) (*X_Init_Term_Hook) (); return; } term.dsc$w_length = name_len; status = sys$assign ( &term, &This_Term, 0, 0 ); if (status != SS$_NORMAL) { fprintf(stderr,"Unable to assign input channel\n"); fprintf(stderr,"PID: %X, DEV %s, status: %d\n", This_Process_Pid, TTY_Name, status); exit(0); } if (NULL == exit_block.exit_routine) { exit_block.exit_routine = (int (*)()) vms_exit_handler; sys$dclexh(&exit_block); } /* allocate an event flag and clear it--- used by ast routines. Since * I am only using a few local event flags, there is really no need to * worry about freeing these. * * The event flags are used to avoid timing problems with the getkey AST * as well as for a form of time out. */ if (!Ast_Fired_Event_Flag) lib$get_ef (&Ast_Fired_Event_Flag); sys$clref (Ast_Fired_Event_Flag); if (!Timer_Event_Flag) lib$get_ef (&Timer_Event_Flag); sys$clref (Timer_Event_Flag); /* When no thread is using the keyboard buffer, this ev is set. It * gets cleared when the buffer is in use. */ if (!Using_Keyboard_Buffer_Event_Flag) lib$get_ef (&Using_Keyboard_Buffer_Event_Flag); sys$setef (Using_Keyboard_Buffer_Event_Flag); /* The working assumption here is that the event flags are in the same * cluster. They need not be but it is very likely that they are. */ Event_Flag_Mask = ((unsigned) 1 << (Ast_Fired_Event_Flag % 32)); Event_Flag_Mask |= ((unsigned) 1 << (Timer_Event_Flag % 32)); Waiting_For_Ast = 0; Ast_Stop_Input = 0; /* Get the startup terminal characteristics */ status = sys$qiow(0, /* Wait on event flag zero */ This_Term, /* Channel to input terminal */ IO$_SENSEMODE, /* Get current characteristic */ &iostatus, /* Status after operation */ 0, 0, /* No AST service */ &Old_Term_Char, /* Terminal characteristics buf */ sizeof(Old_Term_Char),/* Size of the buffer */ 0, 0, 0, 0); New_Term_Char = Old_Term_Char; New_Term_Char.t_mandl |= TT$M_EIGHTBIT | TT$M_NOECHO; New_Term_Char.t_extend |= TT2$M_PASTHRU | TT2$M_XON; status = sys$qiow(0, /* Wait on event flag zero */ This_Term, /* Channel to input terminal */ IO$_SETMODE, /* Set current characteristic */ &iostatus, /* Status after operation */ 0, 0, /* No AST service */ &New_Term_Char, /* Terminal characteristics buf */ sizeof(New_Term_Char),/* Size of the buffer */ 0, 0, 0, 0); /* Enable the Application Keypad */ (*tt_write_string) ("\033=\033[?1l"); /* application keys/cursor keys */ vms_que_key_ast(); /* set up the key ast */ } void sys_flush(int fd) { } static void cancel_ast (void) { if (TTY_Inited == 0) return; /* stop the keyboard ast */ sys$setast (0); /* disable AST delivery */ sys$clref (Ast_Fired_Event_Flag); Waiting_For_Ast = 1; Ast_Stop_Input = 1; /* cancel all i/o on this channel. This canels pending, as well as those * already in progress and queued. In particular, according to the * manuals, cancelling I/O on the channel will cause the getkey AST * to fire even though the SYS$QIO call was aborted. This is crucial * because below we wait for the AST to set the event flag. */ sys$cancel (This_Term); sys$setast (1); /* enable ASTs again */ sys$waitfr (Ast_Fired_Event_Flag); /* sleep until it fires */ Waiting_For_Ast = 0; } static int keypad_state = 0; void reset_tty() { Iosb_Type iostatus; if (Batch) return; if (!TTY_Inited) return; if (X_Init_Term_Hook != NULL) { if (X_Reset_Term_Hook != NULL) (*X_Reset_Term_Hook) (); TTY_Inited = 0; return; } cancel_ast (); TTY_Inited = 0; /* reset the terminal characteristics */ sys$qiow(0, /* event flag 0 */ This_Term, /* Channel to input terminal */ IO$_SETMODE, /* Set current characteristic */ &iostatus, /* Status after operation */ 0, 0, /* No AST service */ &Old_Term_Char, /* Terminal characteristics buf */ sizeof(Old_Term_Char), /* Size of the buffer */ 0, 0, 0, 0); /* unused */ if (keypad_state) SLtt_write_string("\033=\033[?1l"); else SLtt_write_string("\033>"); } unsigned char sys_getkey() { unsigned char c; int tsecs; if (SLKeyBoard_Quit) return((int) Abort_Char); /* Under DECWIndows, I do not know how to timeout so this will have to do for now */ if (X_Read_Hook != NULL) return (c = X_Read_Hook ()); /* On VMS, the keyboard ast routine should be stuffing the buffer, so do nothing except sleep */ /* clear the flag which ast will set */ Waiting_For_Ast = 0; if (Input_Buffer_Len) return(my_getkey()); tsecs = 450; /* 45 seconds */ while (!sys_input_pending(&tsecs, 0)) { /* update status line incase user is displaying time */ if (Display_Time) { JWindow->trashed = 1; update((Line *) NULL, 0, 1); } } c = my_getkey(); return(c); } /* waits *secs tenth of seconds for input */ static int sys_input_pending(int *secs, int unused) { unsigned long daytim[2]; if (Batch) return(0); if (Input_Buffer_Len) return(Input_Buffer_Len); if (X_Input_Pending_Hook != NULL) { if ((*X_Input_Pending_Hook) ()) return 1; /* I need to make this work with DECWIndows */ return 0; } if (*secs) { /* takes a quad word time. If negative, use a relative time. */ daytim[1] = 0xFFFFFFFF; daytim[0] = -(*secs * 1000 * 1000); /* 1000 * 1000 is a tenth of a sec */ sys$clref (Ast_Fired_Event_Flag); /* SYS$CLREF (Timer_Event_Flag); SYS$SETIMR call clears this */ /* set up a flag for the ast so it knows to set the event flag */ Waiting_For_Ast = 1; sys$setimr(Timer_Event_Flag, daytim, 0, 1); /* this will return when ast does its job or timer expires. * The first argument simply serves to identify the cluster for * the event flag and that is all. The second argument serves * to identify the event flags to wait for. */ sys$wflor (Ast_Fired_Event_Flag, Event_Flag_Mask); Waiting_For_Ast = 0; /* cancel the timer */ sys$cantim(1, 3); /* 3 is user mode */ } return (Input_Buffer_Len); } /* This is to get the size of the terminal */ int get_term_dimensions(int *cols, int *rows) { int status, junk; Iosb_Type iostatus; $DESCRIPTOR(devnam, TTY_Name); item_list_3 itmlst[4]; itmlst[0].buflen = sizeof (*rows); itmlst[0].item_code = DVI$_TT_PAGE; itmlst[0].buf_addr = rows; itmlst[0].len_addr = &junk; itmlst[1].buflen = sizeof(*cols); itmlst[1].item_code = DVI$_DEVBUFSIZ; itmlst[1].buf_addr = cols; itmlst[1].len_addr = &junk; itmlst[2].buflen = sizeof (keypad_state); itmlst[2].item_code = DVI$_TT_APP_KEYPAD; itmlst[2].buf_addr = &keypad_state; itmlst[2].len_addr = &junk; itmlst[3].buflen = 0; itmlst[3].item_code = 0; itmlst[3].buf_addr = 0; itmlst[3].len_addr = 0; if (X_Get_Term_Size_Hook != NULL) { (*X_Get_Term_Size_Hook)(cols, rows); return; } if (*rows <= 0) *rows = *tt_Screen_Rows; if (*cols <= 0) *cols = *tt_Screen_Cols; /* Get current terminal characteristics */ status = sys$getdviw(0, /* Wait on event flag zero */ 0, /* Channel to input terminal */ &devnam, /* device name */ &itmlst, /* Item descriptor List */ &iostatus, /* Status after operation */ 0, 0, /* No AST service */ 0); /* nullarg */ if (status&1) status = iostatus.i_cond; /* Jump out if bad status */ if ((status & 1) == 0) exit(status); return 0; } /* returns 0 on failure, 1 on sucess */ int sys_delete_file(char *filename) { return (1 + delete(filename)); /* 0: sucess; -1 failure */ } int sys_rename(char *from, char *to) { return(-1); } /* This routine converts unix type names to vms names */ int locate(char ch, char *string) { int i; char c; i = 0; while (c = string[i++], (c != ch) && (c != '\0')); if (c == ch) return(i); else return (0); } char *unix2vms(char *file) { int i,device,j,first,last; static char vms_name[80]; char ch; if (locate('[',file)) return(file); /* vms_name syntax */ if (!locate('/',file)) return(file); /* vms_name syntax */ /* search for the ':' which means a device is present */ device = locate(':',file); i = 0; if (device) { while (ch = file[i], i < device) vms_name[i++] = ch; } j = i; /* go from the end looking for a '/' and mark it */ i = strlen(file) - 1; while(ch = file[i], ch != '/' && i-- >= 0); if (ch == '/') { file[i] = ']'; last = 0; } else last = 1; i = j; vms_name[j++] = '['; vms_name[j++] = '.'; first = 0; while(ch = file[i++], ch != '\0') { switch (ch) { case '.': if (last) vms_name[j++] = '.'; if (last) break; ch = file[i++]; if (ch == '.') { if (!first) j--; /* overwrite the dot */ vms_name[j++] = '-'; } else if (ch == '/'); /* './' combinations-- do nothing */ else if (ch == ']') { last = 1; if (vms_name[j-1] == '.') j--; vms_name[j++] = ']'; } else vms_name[j++] = '.'; break; case '/': if (first) { vms_name[j++] = '.'; } else { first = 1; /* if '/' is first char or follows a colon do nothing */ if ((i!=1) && (file[i-2] != ':')) { vms_name[j++] = '.'; } else j--; /* overwrite the '.' following '[' */ } break; case ']': last = 1; if (vms_name[j-1] == '.') j--; vms_name[j++] = ']'; break; default: vms_name[j++] = ch; } } return (vms_name); } void define_logical_name (char *varname, char *string) { struct dsc$descriptor_s strdsc, envdsc, lnmdsc; strdsc.dsc$w_length = strlen (string); strdsc.dsc$b_dtype = DSC$K_DTYPE_T; strdsc.dsc$b_class = DSC$K_CLASS_S; strdsc.dsc$a_pointer = string; envdsc.dsc$w_length = strlen (varname); envdsc.dsc$b_dtype = DSC$K_DTYPE_T; envdsc.dsc$b_class = DSC$K_CLASS_S; envdsc.dsc$a_pointer = varname; lnmdsc.dsc$w_length = 7; lnmdsc.dsc$b_dtype = DSC$K_DTYPE_T; lnmdsc.dsc$b_class = DSC$K_CLASS_S; lnmdsc.dsc$a_pointer = "LNM$JOB"; lib$set_logical (&envdsc, &strdsc, &lnmdsc, 0, 0); } void delete_logical_name (char *varname) { struct dsc$descriptor_s envdsc, lnmdsc; envdsc.dsc$w_length = strlen (varname); envdsc.dsc$b_dtype = DSC$K_DTYPE_T; envdsc.dsc$b_class = DSC$K_CLASS_S; envdsc.dsc$a_pointer = varname; lnmdsc.dsc$w_length = 7; lnmdsc.dsc$b_dtype = DSC$K_DTYPE_T; lnmdsc.dsc$b_class = DSC$K_CLASS_S; lnmdsc.dsc$a_pointer = "LNM$JOB"; lib$delete_logical (&envdsc, &lnmdsc); } int do_attach_cmd() { unsigned long pid; char *pidstr; if((pidstr = getenv("JED_ATTACH_TO")) != NULL) { delete_logical_name("JED_ATTACH_TO"); (void) sscanf(pidstr,"%X",&pid); if (lib$attach(&pid) == SS$_NORMAL) return(1); else return(0); } else return(0); } unsigned long SHELL_PID = 0; /* here we try to attach to the parent otherwise just spawn a new one */ void sys_suspend() { unsigned long parent_pid; unsigned long status = 0; char str[80]; cancel_ast (); parent_pid = getppid(); /* try to attach to different process */ if (do_attach_cmd()) status = SS$_NORMAL; else if (parent_pid && parent_pid != 0xffffffff) /* we attach to parent */ status = lib$attach(&parent_pid); else if (SHELL_PID && SHELL_PID != 0xffffffff) /* try to attach to previous shell */ status = lib$attach (&SHELL_PID); if (status != SS$_NORMAL) /* others fail so spawn a new shell */ { status = 0; SLtt_write_string("Spawning Subprocess...\n"); flush_output (); status = lib$spawn(0,0,0,0,0,&SHELL_PID,0); /* if we attach back, status may come back unchanged */ if (!(status & 1)) /* Thanks to Hunter Goatley for this suggestion */ { sprintf(str,"Unable to spawn subprocess. Error = X%X (%d)", status, status); msg_error(str); return; } } /* if((file = getenv("JED_FILE_NAME")) != NULL) { find_file_cmd(file); delete_logical_name ("JED_FILE_NAME"); } */ } /* returns 0 on success, -1 on syntax error -2 on bad dir, -3 on bad device, 1 if something else */ int vms_parse_file(char *old) { struct FAB fab = cc$rms_fab; struct NAM nam = cc$rms_nam; char neew[256]; int status; fab.fab$l_fna = old; fab.fab$b_fns = strlen(old); fab.fab$b_dns = 0; fab.fab$w_ifi = 0; fab.fab$l_nam = &nam; nam.nam$l_esa = neew; nam.nam$b_ess = 255; nam.nam$b_nop = NAM$V_SYNCHK; /* syntax only */ nam.nam$l_rlf = 0; status = sys$parse(&fab); neew[nam.nam$b_esl] = 0; strcpy(old, neew); while(*old != 0) { if ((*old == ';') && (*(old + 1) == 0)) *old = 0; else { if ((*old >= 'A') && (*old <= 'Z')) *old |= 0x20; old++; } } switch(status) { case RMS$_NORMAL: return 0; case RMS$_SYN: return -1; case RMS$_DNF: return -2; case RMS$_DEV: return -3; return 1; } } char *expand_filename(char *file) { char *p, *p1, *dir; static char work[300]; int len; strcpy(work, get_cwd()); strcat(work, file); file = work; /* start at end and look for ']' then look for ':' */ if (0 == (len = strlen(file))) return(file); p = file + (len - 1); while (p >= file) if (*p-- == ':') { while((p >= file) && (*p != ':') && (*p != ']')) p--; p++; p1 = file; while(*p) *p1++ = *p++; *p1 = 0; break; } dir = p = p1 = file; /* look for the start of the path */ while (*p != 0) { if (*p == ':') { p++; if (*p == ':') { p++; } dir = p; break; } if (*p == '[') { dir = p++; break; } p++; } p1 = p = dir; while (*p != 0) { if (*p == '[') { /* if (*(p + 1) == '-') */ p1 = dir; } if (p1 != p) *p1 = *p; p1++; p++; } *p1 = 0; switch(vms_parse_file(file)) { case 0: break; case -1: msg_error("Filename syntax error."); break; case -2: msg_error("Directory does not exist!"); msg_error (file); break; case -3: msg_error("Bad device name."); break; default: msg_error ("Unknown directory error:"); msg_error (file); } p = file; while(*p != 0) if (*p++ == ']') { if ((*p == '.') && (*(p + 1) == 0)) *p = 0; break; } return(file); } int vms_expand_filename(char *file,char *expanded_file) { static int context = 0; static char inputname[256] = ""; $DESCRIPTOR(file_desc,inputname); $DESCRIPTOR(default_dsc,"SYS$DISK:[]*.*;"); static struct dsc$descriptor_s result = {0, DSC$K_DTYPE_T, DSC$K_CLASS_D, NULL}; if (strcmp(inputname, file)) { if (context) { lib$find_file_end(&context); } context = 0; strcpy(inputname, file); file_desc.dsc$w_length = strlen(inputname); } if (RMS$_NORMAL == lib$find_file(&file_desc,&result,&context, &default_dsc,0,0,&Number_Zero)) { MEMCPY(expanded_file, result.dsc$a_pointer, result.dsc$w_length); expanded_file[result.dsc$w_length] = '\0'; return (1); } else { /* expanded_file[0] = '\0'; */ /* so file comes back as zero width */ return(0); } } static int context = 0; static char inputname[256] = ""; $DESCRIPTOR(file_desc,inputname); $DESCRIPTOR(default_dsc,"SYS$DISK:[]*.*;"); static char *VMS_Star; int sys_findnext(char *file) { char *f; static struct dsc$descriptor_s result = {0, DSC$K_DTYPE_T, DSC$K_CLASS_D, NULL}; if (RMS$_NORMAL == lib$find_file(&file_desc,&result,&context, &default_dsc,0,0,&Number_Zero)) { MEMCPY(file, result.dsc$a_pointer, result.dsc$w_length); file[result.dsc$w_length] = 0; f = &file[result.dsc$w_length]; if (*VMS_Star) { while ((f > file) && (*f != ';')) f--; if (*f == ';') *f = 0; } return (1); } else return(0); } int sys_findfirst(char *thefile) { char *f, *f1, *s2 = "*.*"; char *file; file = thefile; f = extract_file(file); while (*f && (*f != '*')) f++; VMS_Star = ""; /* let user choose what to match with or take mine */ if (! *f) { f = extract_file(file); while (*f && (*f != '.')) f++; if (*f) VMS_Star = "*"; else VMS_Star = s2; file = expand_filename(thefile); f = f1 = extract_file(file); if (VMS_Star == s2) { while (*f && (*f != '.')) f++; *f = 0; } else { while (*f1 && (*f1 != ';')) f1++; *f1 = 0; } strcpy(thefile, file); } strcpy(inputname, file); if (*VMS_Star) strcat(inputname, VMS_Star); file_desc.dsc$w_length = strlen(inputname); if (context) lib$find_file_end(&context); context = 0; return sys_findnext(thefile); } #include <stat.h> /* returns 0 if file does not exist, 1 if it is not a dir, 2 if it is */ int sys_chmod(char *file, int what, int *mode, short *uid, short *gid) { struct stat buf; int m; if (stat(file, &buf) < 0) switch (errno) { case EACCES: return(-1); /* es = "Access denied."; break; */ case ENOENT: return(0); /* ms = "File does not exist."; */ case ENOTDIR: return(-2); /* es = "Invalid Path."; */ default: return(-3); /* "stat: unknown error."; break;*/ } m = buf.st_mode; (void) uid; (void) gid; *mode = m & 0777; if (m & S_IFDIR) return (2); return(1); } #if 0 switch(vms_parse_file(file)) { case 0: break; case -1: /* Filename syntax error. */ case -2: return(-2); /* Directory does not exist! */ case 1: case -3: return(-3); /* Bad device name. */ default: return(-3); } return(1); #endif unsigned long sys_file_mod_time(char *file) { struct stat buf; if (stat(file, &buf) < 0) return(0); return((unsigned long) buf.st_mtime); } int rmdir (char *d) { return(-1); }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.