/* This is part of ODS2, originally written by Paul Nankervis, * email address: Paulnank@au1.ibm.com * ODS2 is distributed freely for all members of the * VMS community to use. However all derived works * must maintain comments in their source to acknowledge * the contributions of the original author and * subsequent contributors. This is free software; no * warranty is offered, and while we believe it to be useful, * you use it at your own risk. */ #if !defined( DEBUG ) && defined( DEBUG_COPYCMD ) #define DEBUG DEBUG_COPYCMD #else #ifndef DEBUG #define DEBUG 0 #endif #endif #include #include #ifdef _WIN32 #include #include "winfile.h" #define basename winbasename #undef fstat #define fstat _fstat #elif defined(VMS) #include "vmsfile.h" #define basename vmsbasename #else #include #include #include #endif #ifndef GLOB_BRACE #define GLOB_BRACE 0 #endif #ifndef GLOB_TILDE_CHECK #define GLOB_TILDE_CHECK 0 #endif #include "cmddef.h" static vmscond_t copy_from( options_t options, int argc, char **argv ); static vmscond_t copy_1_from( options_t options, struct FAB *fab, const char *to, size_t pause ); static vmscond_t put_prn( char ch, FILE *tof, pause_t *pause ); static vmscond_t copy_to( options_t options, int argc, char **argv ); static vmscond_t copy_1_to( options_t options, char *from, char *to ); char *output_file_name( const char *ofname, const char *ifname, int *flags ); #define OF_WILD 1 static int xpand( char **buf, size_t size[2], size_t add ); /******************************************************************* dotype() */ /* Display a file on the terminal */ #define type_page OPT_SHARED_20 #define type_one OPT_SHARED_19 #define copy_vfc OPT_SHARED_18 static uint32_t height; qual_t typequals[] = { {"page", type_page, 0, VOPT(DV(&height)), "-commands type qual_page"}, {"nopage", 0, type_page, NV, NULL}, {"onefile", type_one, 0, NV, NULL}, {"vfc_header", copy_vfc, 0, NV, "-commands type qual_vfc"}, {"novfc_header", 0, copy_vfc, NV, NULL}, {NULL, 0, 0, NV, NULL } }; param_t typepars[] = { {"filespec", REQ | NOLIM, FSPEC, NOPA, "commands type filespec"}, { NULL, 0, 0, NOPA, NULL } }; DECL_CMD(type) { vmscond_t sts; int theight; options_t options; struct FAB fab = cc$rms_fab; struct NAM nam = cc$rms_nam; char esa[NAM$C_MAXRSS + 1], rsa[NAM$C_MAXRSS + 1]; (void) termchar( NULL, &theight ); height = theight-1; if( $FAILS(sts = checkquals( &options, type_page, typequals, qualc, qualv )) ) { return sts; } if( !(options & type_page) ) { height = 0; } --argc; ++argv; while( $SUCCESSFUL(sts) && argc-- ) { nam.nam$l_esa = esa; nam.nam$b_ess = NAM$C_MAXRSS; fab.fab$l_fna = argv++[0]; fab.fab$b_fns = (uint8_t)strlen(fab.fab$l_fna); if( options & type_one ) { /* RMS test/debug: implicit parse/search */ if( $FAILS(sts = sys_open(&fab)) ) { nam.nam$l_rsa[nam.nam$b_rsl] = '\0'; printmsg( TYPE_OPENIN, 0, nam.nam$l_rsa ); sts = printmsg( sts, MSG_CONTINUE, TYPE_OPENIN ); break; } sts = copy_1_from( options & copy_vfc, &fab, NULL, height ); sys_close(&fab); continue; } fab.fab$l_nam = &nam; if( $FAILS(sts = sys_parse(&fab)) ) { fab.fab$l_fna[fab.fab$b_fns] = '\0'; printmsg( TYPE_PARSEFAIL, 0, fab.fab$l_fna ); sts = printmsg( sts, MSG_CONTINUE, TYPE_PARSEFAIL ); break; } nam.nam$l_rsa = rsa; nam.nam$b_rss = NAM$C_MAXRSS; fab.fab$l_fop = 0; while( $SUCCESSFUL(sts) ) { if( $FAILS(sts = sys_search( &fab )) ) { if( $MATCHCOND(sts, RMS$_NMF) ) sts = SS$_NORMAL; nam.nam$l_esa[nam.nam$b_esl] = '\0'; printmsg( TYPE_SEARCHFAIL, 0, nam.nam$l_esa ); sts = printmsg( sts, MSG_CONTINUE, TYPE_SEARCHFAIL ); break; } fab.fab$b_fac = FAB$M_GET; if( nam.nam$l_fnb & NAM$M_WILDCARD ) printf( "\n%.*s\n\n", nam.nam$b_rsl, rsa ); if( $FAILS(sts = sys_open(&fab)) ) { nam.nam$l_rsa[nam.nam$b_rsl] = '\0'; printmsg( TYPE_OPENIN, 0, nam.nam$l_rsa ); sts = printmsg( sts, MSG_CONTINUE, TYPE_OPENIN ); break; } sts = copy_1_from( options & copy_vfc, &fab, NULL, height ); sys_close(&fab); } } printf( "\n" ); fab.fab$b_fns = nam.nam$b_ess = fab.fab$b_dns = 0; nam.nam$l_rlf = NULL; nam.nam$b_nop = NAM$M_SYNCHK; (void) sys_parse( &fab ); /* Release context */ if( $MATCHCOND( sts, RMS$_EOF ) ) sts = SS$_NORMAL | STS$M_INHIB_MSG; return sts; } /******************************************************************* docopy() */ /* Copy files to and from Files-11 volume */ #define copy_binary OPT_GENERIC_1 #define copy_log OPT_GENERIC_2 #define copy_tof11 OPT_GENERIC_3 #define copy_confirm OPT_GENERIC_4 #define copy_override OPT_GENERIC_5 #define copy_owner OPT_GENERIC_6 #define copy_protect OPT_GENERIC_7 #define copy_volume OPT_GENERIC_8 #define copy_verlim OPT_GENERIC_9 #define copy_recfmt (OPT_GENERIC_12|OPT_GENERIC_11|OPT_GENERIC_10) #define copy_v_recfmt (OPT_V_GENERIC + 9) /* GENERIC option space is full */ #define copy_b4 (OPT_SHARED_2|OPT_SHARED_1) #define copy_v_b4 0 #define copy_after (OPT_SHARED_4|OPT_SHARED_3) #define copy_v_after 2 #define copy_prn_none 0 #define copy_prn_nl 1 #define copy_prn_c0 2 #define copy_prn_vfu 3 #define copy_prn OPT_SHARED_5 #define copy_ftn OPT_SHARED_6 #define copy_cr OPT_SHARED_7 #define copy_span OPT_SHARED_8 #define copy_append OPT_SHARED_9 /* Type uses OPT_SHARED from the high end */ static uint32_t b4n, aftn; static uint32_t vfclen; static uint32_t reclen; static uint32_t verlimit; #if 0 static uint32_t volume; #endif static uint32_t protection; static uint32_t ownuic; /* Print CC */ static qual_t prnb4[] = { {"none", copy_prn_none << copy_v_b4, copy_b4, NV, "commands copy qual_cc print b4 none"}, {"nl", copy_prn_nl << copy_v_b4, copy_b4, DV(&b4n), "commands copy qual_cc print b4 nl"}, {"ctl", copy_prn_c0, copy_b4, DV(&b4n), "commands copy qual_cc print b4 ctl"}, {"vfu", copy_prn_vfu << copy_v_b4, copy_b4, DV(&b4n), "commands copy qual_cc print b4 vfu"}, {NULL, 0, 0, NV, NULL} }; static qual_t /* Same keywords as prnb4. Use same help. */ prnafter[] = { {"none", copy_prn_none << copy_v_after, copy_after, NV, "commands copy qual_cc print b4 none"}, {"nl", copy_prn_nl << copy_v_after, copy_after, DV(&aftn), "commands copy qual_cc print b4 nl"}, {"ctl", copy_prn_c0, copy_b4, DV(&aftn), "commands copy qual_cc print b4 ctl"}, {"vfu", copy_prn_vfu << copy_v_after, copy_after, DV(&aftn), "commands copy qual_cc print b4 vfu"}, {NULL, 0, 0, NV, NULL} }; static qual_t prnkwds[] = { {"before", 0, copy_b4, KV(prnb4), "commands copy qual_cc print b4"}, {"after", 0, copy_after, KV(prnafter), "commands copy qual_cc print after"}, {NULL, OPT_NOSORT, 0, NV, NULL} }; /* Carriage control record attributes */ static qual_t cckwds[] = { {"fortran", copy_ftn, copy_cr|copy_prn, NV, "commands copy qual_cc fortran"}, {"carriage_return", copy_cr, copy_ftn|copy_prn, NV, "commands copy qual_cc cr"}, {"print", copy_prn|(FAB$C_VFC<qualc, copy_defopt->qualv )) ) { return sts; } } else options = copy_defaults; if( $FAILS(sts = checkquals( &options, options, copyquals, qualc, qualv )) ) { return sts; } if( options & copy_tof11 ) return copy_to( options, argc, argv ); return copy_from( options, argc, argv ); } /******************************************************************* copy_from() */ static vmscond_t copy_from( options_t options, int argc, char **argv ) { vmscond_t sts; struct NAM nam; struct FAB fab; char res[NAM$C_MAXRSS + 1], rsa[NAM$C_MAXRSS + 1]; int i, filecount = 0; vmscond_t confirm = ODS2_CONFIRM_ALL; if( options & copy_confirm ) confirm = COPY_CONFIRM; sts = SS$_NORMAL; nam = cc$rms_nam; fab = cc$rms_fab; for( i = 1; i < argc -1; i++ ) { nam.nam$l_esa = res; nam.nam$b_ess = NAM$C_MAXRSS; fab.fab$l_nam = &nam; fab.fab$l_fna = argv[1]; fab.fab$b_fns = (uint8_t)strlen(fab.fab$l_fna); if( $FAILS(sts = sys_parse(&fab)) ) { printmsg( COPY_PARSEFAIL, 0, fab.fab$l_fna ); sts = printmsg( sts, MSG_CONTINUE, COPY_PARSEFAIL ); break; } res[nam.nam$b_esl] = '\0'; nam.nam$l_rsa = rsa; nam.nam$b_rss = NAM$C_MAXRSS; fab.fab$l_fop = 0; while( $SUCCESSFUL(sts) ) { char name[NAM$C_MAXRSS + 1]; char *out, *inp; int dot = FALSE; uint32_t directory = 0; uint16_t retlen; struct XABPRO pro; struct XABDAT dat; struct XABITM itm; struct item_list xitems[] = { { XAB$_UCHAR_DIRECTORY, sizeof(directory), NULL, 0 }, { 0, 0, NULL, 0 } }; if( $FAILS(sts = sys_search( &fab )) ) { if( $MATCHCOND(sts, RMS$_NMF) ) { sts = SS$_NORMAL; break; } nam.nam$l_esa[nam.nam$b_esl] = '\0'; printmsg( COPY_SEARCHFAIL, 0, nam.nam$l_esa ); sts = printmsg( sts, MSG_CONTINUE, COPY_SEARCHFAIL ); break; } rsa[nam.nam$b_rsl] = '\0'; pro = cc$rms_xabpro; dat = cc$rms_xabdat; itm = cc$rms_xabitm; dat.xab$l_nxt = &pro; itm.xab$l_nxt = &dat; xitems[0].buffer = &directory; xitems[0].retlen = &retlen; itm.xab$b_mode = XAB$K_SENSEMODE; itm.xab$l_itemlist = xitems; fab.fab$l_xab = &itm; fab.fab$b_fac = FAB$M_GET; sts = sys_open(&fab); fab.fab$l_xab = NULL; rsa[nam.nam$b_rsl] = '\0'; if( $FAILED(sts) ) { int err; err = errno; printmsg( COPY_OPENIN, 0, rsa ); sts = printmsg( sts, MSG_CONTINUE, COPY_OPENIN ); if( err ) printmsg( ODS2_OSERROR, MSG_CONTINUE, COPY_OPENIN, strerror( err ) ); break; } if( directory ) { if( !(options & copy_override) ) { printmsg( COPY_DIRNOTCOPIED, 0, rsa ); sys_close( &fab ); continue; } } out = name; inp = argv[2]; while (*inp != '\0') { if (*inp == '*') { inp++; if (dot) { memcpy(out, nam.nam$l_type + 1, nam.nam$b_type - 1); out += nam.nam$b_type - 1; } else { size_t length; length = nam.nam$b_name; if (*inp == '\0') length += nam.nam$b_type; memcpy( out, nam.nam$l_name, length ); out += length; } continue; } if (*inp == '.') { dot = TRUE; } else { if (strchr(":]\\/", *inp)) dot = FALSE; } *out++ = *inp++; } *out++ = '\0'; if( $MATCHCOND( confirm, COPY_CONFIRM) ) confirm = confirm_cmd( confirm, rsa, name ); if( !$MATCHCOND( confirm, ODS2_CONFIRM_ALL ) ) { if( $MATCHCOND( confirm, ODS2_CONFIRM_YES ) ) { confirm = COPY_CONFIRM; } else if( $MATCHCOND( confirm, ODS2_CONFIRM_NO ) ) { confirm = COPY_CONFIRM; sys_close( &fab ); continue; } else { /* ODS2_CONFIRM_QUIT */ sys_close( &fab ); sts = confirm; break; } } fab.fab$l_xab = &itm; if( $SUCCESSFUL(sts = copy_1_from( options, &fab, name, 0 )) ) filecount++; fab.fab$l_xab = NULL; sys_close(&fab); } /* search */ if( $VMS_STATUS_SEVERITY(sts) > STS$K_WARNING || $MATCHCOND(sts, ODS2_CONFIRM_QUIT) ) break; } /* arg */ if( $SUCCESSFUL(sts) || $MATCHCOND(sts, ODS2_CONFIRM_QUIT) ) { if (filecount > 0) { if( options & copy_log ) sts = printmsg( COPY_COPIED, 0, filecount ); } else { sts = printmsg( COPY_NOFILES, 0 ); } } return sts; } /******************************************************************* copy_1_from() */ static vmscond_t copy_1_from( options_t options, struct FAB *fab, const char *to, size_t dopause ) { vmscond_t sts; uint32_t records = 0; int stream = 0, var = 0; FILE *tof; char *rec; pause_t *pause = NULL, pausectl; struct RAB rab = cc$rms_rab; if( dopause ) { pausectl.interval = dopause; pausectl.lineno = 0; pausectl.prompt = TYPE_CONTINUE; pause = &pausectl; } rab.rab$l_fab = fab; if( $FAILS(sts = sys_connect(&rab)) ) { printmsg( COPY_OPENIN, 0, fab->fab$l_fna ); sts = printmsg( sts, MSG_CONTINUE, COPY_OPENIN ); return sts; } if( (rec = malloc( MAXREC + 2 )) == NULL ) { sys_disconnect( &rab ); printmsg( COPY_OPENIN, 0, fab->fab$l_fna ); sts = printmsg( SS$_INSFMEM, MSG_CONTINUE, COPY_OPENIN ); return sts; } rab.rab$l_ubf = rec; rab.rab$w_usz = MAXREC; rab.rab$l_rhb = NULL; if( options & copy_binary ) { fab->fab$b_rfm = FAB$C_UDF; fab->fab$b_rat = 0; } else { switch( fab->fab$b_rfm ) { case FAB$C_STMLF: stream = '\n'; break; case FAB$C_STMCR: stream = '\r'; break; case FAB$C_STM: stream = EOF; break; case FAB$C_VFC: var = 1; if( fab-> fab$b_fsz != 0 ) { ++var; rab.rab$l_rhb = malloc( fab->fab$b_fsz ); if( rab.rab$l_rhb == NULL ) { free( rec ); sys_disconnect( &rab ); printmsg( COPY_OPENIN, 0, fab->fab$l_fna ); sts = printmsg( sts, MSG_CONTINUE, COPY_OPENIN ); return sts; } } break; case FAB$C_VAR: var = 1; break; case FAB$C_FIX: break; } } if( to == NULL ) tof = stdout; else { if( (stream || (fab->fab$b_rat & (FAB$M_CR | FAB$M_PRN | FAB$M_FTN))) && !(options & copy_binary) ) { tof = openf( to, (options & copy_append)? "a": "w" ); } else { tof = openf( to, (options & copy_append)? "ab": "wb" ); } } if (tof == NULL) { int err; err = errno; free( rec ); free( rab.rab$l_rhb ); sys_disconnect( &rab ); printmsg( COPY_OPENOUT, 0, to ); return printmsg( ODS2_OSERROR, MSG_CONTINUE, COPY_OPENOUT, strerror( err ) ); } while( $SUCCESSFUL(sts) ) { char *rp; unsigned rsz; if( ferror( tof ) ) { int err; err = errno; printmsg( COPY_WRITEERR, 0, (to == NULL)? "tty": to ); sts = printmsg( ODS2_OSERROR, MSG_CONTINUE, COPY_WRITEERR, strerror(err) ); break; } if( $FAILS(sts = sys_get( &rab )) ) { if( $MATCHCOND(sts, RMS$_EOF) ) sts |= STS$M_INHIB_MSG; else { printmsg( COPY_READERR, 0, fab->fab$l_fna ); sts = printmsg( sts, MSG_CONTINUE, COPY_READERR, rab.rab$l_stv); } break; } rp = rec; rsz = rab.rab$w_rsz; if( (options & copy_binary) || fab->fab$b_rfm == FAB$C_UDF ) { if( rsz != 0 ) { if( $FAILS(sts = put_strn( rp, rsz, tof, pause )) ) break; if( fab->fab$b_rfm == FAB$C_UDF ) records += rsz; else records++; } continue; } if( stream ) { if( $FAILS(sts = put_strn( rp, rsz, tof, pause )) ) break; if( fab->fab$b_rat & (FAB$M_CR|FAB$M_FTN) ) { if( stream == EOF ) { if( $FAILS(sts = put_strn( "\r\n", 2, tof, pause )) ) break; } else { if( $FAILS(sts = put_c( stream, tof, pause )) ) break; } } ++records; continue; } if( fab->fab$b_rat & FAB$M_FTN ) { --rsz; switch( *rp++ ) { /* AA-D034E-TE Table 8-5 */ case '+': /* Overprint current line and return to left margin */ if( $FAILS(sts = put_c( '\r', tof, pause )) ) break; if( $FAILS(sts = put_strn( rp, rsz, tof, pause )) ) break; if( $FAILS(sts = put_c( '\r', tof, pause )) ) break; break; case ' ': /* Single space: output to beginning of next line */ default: if( $FAILS(sts = put_c( '\n', tof, pause )) ) break; if( $FAILS(sts = put_strn(rp, rsz, tof, pause )) ) break; if( $FAILS(sts = put_c( '\r', tof, pause )) ) break; break; case '0': /* Double space: skip a line before starting output */ if( $FAILS(sts = put_strn( "\n\n", 2, tof, pause )) ) break; if( $FAILS(sts = put_strn( rp, rsz, tof, pause )) ) break; if( $FAILS(sts = put_c( '\r', tof, pause )) ) break; break; case '1': /* Paging: start output at top of a new page */ if( $FAILS(sts = put_c( '\f', tof, pause )) ) break; if( $FAILS(sts = put_strn( rp, rsz, tof, pause )) ) break; if( $FAILS(sts = put_c( '\r', tof, pause )) ) break; break; case '$': /* Prompting: Start a beginning of next line and suppress \r */ if( $FAILS(sts = put_c( '\n', tof, pause )) ) break; if( $FAILS(sts = put_strn( rp, rsz, tof, pause )) ) break; break; case '\0': /* Overprinting with no advance - no \r at end */ if( $FAILS(sts = put_c( '\r', tof, pause )) ) break; if( $FAILS(sts = put_strn( rp, rsz, tof, pause )) ) break; break; } if( $FAILED(sts) ) break; ++records; continue; } if( (fab->fab$b_rat & FAB$M_CR) && $FAILS(sts = put_c( '\n', tof, pause )) ) break; if( var == 2 ) { if( (fab->fab$b_rat & FAB$M_PRN) && fab->fab$b_fsz == 2 ) { if( $FAILS(sts = put_prn( rab.rab$l_rhb[0], tof, pause )) ) break; if( $FAILS(sts = put_strn( rp, rsz, tof, pause )) ) break; if( $FAILS(sts = put_prn( rab.rab$l_rhb[1], tof, pause )) ) break; ++records; continue; } if( (options & copy_vfc) && $FAILS(sts = put_strn( rab.rab$l_rhb, fab->fab$b_fsz, tof, pause )) ) break; } if( $FAILS(sts = put_strn( rp, rsz, tof, pause )) ) break; if( fab->fab$b_rat & FAB$M_CR ) fputc( '\r', tof ); ++records; } sys_disconnect( &rab ); if( var == 2 ) { free( rab.rab$l_rhb ); rab.rab$l_rhb = NULL; } free( rec ); rec = NULL; if( to == NULL ) { /* stdout */ if( $FAILED(sts) ) sts = printmsg( sts, 0, rab.rab$l_stv ); return sts; } if( fclose( tof ) ) { int err; err = errno; printmsg( COPY_CLOSEOUT, 0, (to == NULL)? "tty": to ); sts = printmsg( ODS2_OSERROR, MSG_CONTINUE, COPY_CLOSEOUT, strerror( err ) ); return sts; } do { struct XABDAT *dat; time_t posixtime; unsigned short timvec[7]; char *tz; struct tm tm; #ifdef _WIN32 struct _utimbuf tv; #else struct timeval tv[2]; #endif for( dat = (struct XABDAT *)fab->fab$l_xab; dat != NULL && dat->xab$b_cod != XAB$C_DAT; dat = dat->xab$l_nxt ) ; if( dat == NULL ) break; if( $FAILS(sys_numtim( timvec, dat->xab$q_rdt )) ) break; tm.tm_sec = timvec[5]; tm.tm_min = timvec[4]; tm.tm_hour = timvec[3]; tm.tm_mday = timvec[2]; tm.tm_mon = timvec[1] -1; tm.tm_year = timvec[0] - 1900; tz = get_env( "TZ" ); setenv( "TZ", "", 1 ); tzset(); posixtime = mktime( &tm ); if( posixtime != (time_t)-1 ) { #ifdef _WIN32 tv.actime = tv.modtime = posixtime; (void)_utime( to, &tv ); #else tv[0].tv_sec = posixtime; tv[0].tv_usec = timvec[6] * 10000; tv[1] = tv[0]; /* Set mtime to atime */ (void) utimes( to, tv ); #endif } if( tz != NULL ) setenv( "TZ", tz, 1 ); else unsetenv( "TZ" ); tzset(); free( tz ); } while( 0 ); #ifndef _WIN32 do { struct XABPRO *pro; mode_t mode; uint16_t prot; for( pro = (struct XABPRO *)fab->fab$l_xab; pro != NULL && pro->xab$b_cod != XAB$C_PRO; pro = pro->xab$l_nxt ) ; if( pro == NULL ) break; mode = (S_IRUSR|S_IWUSR|S_IXUSR| S_IRGRP|S_IWGRP|S_IXGRP| S_IROTH|S_IWOTH|S_IXOTH); prot = pro->xab$w_pro; if( prot & ((xab$m_noread << xab$v_system) | (xab$m_noread << xab$v_owner)) ) mode &= ~S_IRUSR; if( prot & (((xab$m_nodel|xab$m_nowrite) << xab$v_system) | ((xab$m_nodel|xab$m_nowrite) << xab$v_owner)) ) mode &= ~S_IWUSR; if( prot & ( (xab$m_noexe << xab$v_system) | (xab$m_noexe << xab$v_owner) ) ) mode &= ~S_IXUSR; if( prot & (xab$m_noread << xab$v_group) ) mode &= ~S_IRGRP; if( prot & (xab$m_nowrite << xab$v_group) ) mode &= ~S_IWGRP; if( prot & (xab$m_noexe << xab$v_group) ) mode &= ~S_IXGRP; if( prot & (xab$m_noread << xab$v_world) ) mode &= ~S_IROTH; if( prot & (xab$m_nowrite << xab$v_world) ) mode &= ~S_IWOTH; if( prot & (xab$m_noexe << xab$v_world) ) mode &= ~S_IXOTH; if( chmod( to, mode ) != 0 ) { int err; err = errno; printmsg( COPY_PROTERR, 0, to ); sts = printmsg( ODS2_OSERROR, MSG_CONTINUE, COPY_PROTERR, strerror( err ) ); } } while( 0 ); #endif if( !$MATCHCOND(sts, RMS$_EOF) ) return sts; if( options & copy_log ) { sts = (fab->fab$b_rfm == FAB$C_UDF)? COPY_COPIEDU: COPY_COPIEDR; sts = printmsg( sts, 0, fab->fab$l_nam->nam$l_rsa, (to == NULL)? "tty": to, records ); } return SS$_NORMAL; } /******************************************************************* put_prn() */ static vmscond_t put_prn( char ch, FILE *tof, pause_t *pause ) { vmscond_t sts; size_t n; /* 7 6 5 4 */ if( !(ch & 0x80) ) { /* 0 x x x */ n = ch & 0x7F; while( n-- ) if( $FAILS(sts = put_c( '\n', tof, pause )) ) return sts; return SS$_NORMAL; } /* 1 x x x */ if( !(ch & 0x40) ) { /* 1 0 x x */ if( !(ch & 0x20) ) /* 1 0 0 x */ return put_c( ch & 0x1f, tof, pause ); /* C0 */ /* 1 0 1 x */ return put_c( (ch & 0x1f) | 0x80, tof, pause ); /* C1/reserved */ } /* 1 1 x x */ if( !(ch & 0x30) ) { /* 1 1 0 0 */ static const char vfuch[16] = { '\f', '\020', '\021', '\022', /* CH 1 - 4 */ '\023', '\024', '\v', '\n', /* CH 5 - 8 */ '\n', '\n', '\n', '\n', /* CH 9 - 12 */ '\n', '\n', '\n', '\n' }; /* CH 13 - 16 */ if( tof == stdout ) return put_c( '\n', tof, pause ); /* type: 1 LF */ return put_c( vfuch[ch & 0xf], tof, pause ); /* VFU channel 1+<3:0> */ } /* 1 1 y y : yy == 01, 10, 11 */ return put_c( ch, tof, pause ); /* Device-specific/reserved -- Agrave - yumlaut */ } /******************************************************************* copy_to() */ static vmscond_t copy_to( options_t options, int argc, char **argv ) { glob_t globs; int i, err, gflags = GLOB_MARK | GLOB_BRACE | GLOB_TILDE_CHECK; size_t n, filecount = 0; vmscond_t sts; vmscond_t confirm = ODS2_CONFIRM_ALL; memset( &globs, 0, sizeof( globs ) ); if( options & copy_confirm ) confirm = COPY_CONFIRM; sts = SS$_NORMAL; for( i = 1; i < argc -1; i++ ) { err = glob( argv[i], gflags, NULL, &globs ); gflags |= GLOB_APPEND; switch( err ) { case 0: sts = SS$_NORMAL; break; case GLOB_NOSPACE: sts = printmsg( COPY_NOMEM, 0, argv[i] ); break; case GLOB_ABORTED: sts = printmsg( COPY_GLOBABT, 0, argv[i] ); break; case GLOB_NOMATCH: sts = printmsg( COPY_NOMATCH, 0, argv[i] ); break; default: sts = printmsg( COPY_GLOBERR, 0, err, argv[i] ); break; } if( $FAILED(sts) ) { globfree( &globs ); return sts; } } for( n = 0; n < globs.gl_pathc; n++ ) { char *to; if( (to = output_file_name( argv[ argc - 1], globs.gl_pathv[n], NULL )) == NULL ) { sts = printmsg( COPY_NOMEM, 0, globs.gl_pathv[n] ); globfree( &globs ); return sts; } if( $MATCHCOND( confirm, COPY_CONFIRM) ) confirm = confirm_cmd( confirm, globs.gl_pathv[n], to ); if( !$MATCHCOND( confirm, ODS2_CONFIRM_ALL ) ) { if( $MATCHCOND( confirm, ODS2_CONFIRM_YES ) ) { confirm = COPY_CONFIRM; } else if( $MATCHCOND( confirm, ODS2_CONFIRM_NO ) ) { confirm = COPY_CONFIRM; free( to ); continue; } else { /* ODS2_CONFIRM_QUIT */ sts = confirm; free( to ); break; } } sts = copy_1_to( options, globs.gl_pathv[n], to ); free( to ); if( $SUCCESSFUL(sts ) ) ++filecount; else if( $VMS_STATUS_SEVERITY(sts) > STS$K_WARNING || $MATCHCOND(sts, ODS2_CONFIRM_QUIT) ) break; } /* glob */ if( $SUCCESSFUL(sts) || $MATCHCOND(sts, ODS2_CONFIRM_QUIT) ) { if( filecount > 0 ) { if( options & copy_log ) sts = printmsg( COPY_COPIED, 0, filecount ); } else { sts = printmsg( COPY_NOFILES, 0 ); } } globfree( &globs ); return sts; } /******************************************************************* copy_1_to() */ static vmscond_t copy_1_to( options_t options, char *from, char *to ) { vmscond_t sts; size_t records = 0, bufsize = 80; char *buf = NULL; FILE *fromf; struct FAB fab; struct RAB rab; struct NAM nam; struct XABDAT dat; struct XABPRO pro; char vfc[2], rsbuf[NAM$C_MAXRSS+1]; #ifdef _WIN32 struct _stat statbuf; #else struct stat statbuf; #endif fab = cc$rms_fab; rab = cc$rms_rab; nam = cc$rms_nam; dat = cc$rms_xabdat; pro = cc$rms_xabpro; if ( *from == '\0' ) { return printmsg( COPY_NONAME, 0 ); } fromf = openf( from, (options & copy_binary)? "rb": "r" ); if( fromf == NULL ) { int err; err = errno; sts = printmsg( COPY_OPENIN, 0, from ); if( err ) sts = printmsg( ODS2_OSERROR, MSG_CONTINUE, COPY_OPENIN, strerror(err) ); return sts; } if( fstat( Fileno( fromf ), &statbuf ) != 0 ) statbuf.st_size = 0; else { unsigned short tvec[7]; #ifdef _WIN32 struct tm tmd; struct tm *tm = &tmd; (void) gmtime_s( &tmd, &statbuf.st_mtime ); #else struct tm *tm; tm = gmtime( &statbuf.st_mtime ); #endif tvec[0] = tm->tm_year + 1900; tvec[1] = tm->tm_mon + 1; tvec[2] = tm->tm_mday; tvec[3] = tm->tm_hour; tvec[4] = tm->tm_min; tvec[5] = tm->tm_sec; tvec[6] = 0; if( $SUCCESSFUL(lib_cvt_vectim( tvec, dat.xab$q_rdt )) ) { #ifdef _WIN32 (void) gmtime_s( &tmd, &statbuf.st_ctime ); tvec[0] = tm->tm_year + 1900; tvec[1] = tm->tm_mon + 1; tvec[2] = tm->tm_mday; tvec[3] = tm->tm_hour; tvec[4] = tm->tm_min; tvec[5] = tm->tm_sec; tvec[6] = 0; (void) lib_cvt_vectim( tvec, dat.xab$q_cdt ); #else /* No local create time */ memcpy( &dat.xab$q_cdt, dat.xab$q_rdt, sizeof( dat.xab$q_cdt ) ); #endif fab.fab$l_xab = &dat; } } nam.nam$l_rsa = rsbuf; nam.nam$b_rss = (uint8_t)(sizeof( rsbuf ) -1); if( options & (copy_protect|copy_owner) ) { pro.xab$l_nxt = fab.fab$l_xab; fab.fab$l_xab = &pro; if( options & copy_protect ) { pro.xab$w_pro = (uint16_t) ((pro.xab$w_pro & ~(protection >> 16)) | (uint16_t)protection); } if( options & copy_owner ) { pro.xab$l_uic = ownuic; } } fab.fab$l_nam = &nam; fab.fab$b_fac = FAB$M_PUT; fab.fab$l_fop = FAB$M_OFP | FAB$M_TEF; fab.fab$b_org = FAB$C_SEQ; #if 0 if( options & copy_binary ) { fab.fab$w_mrs = 512; fab.fab$b_rfm = FAB$C_FIX; } else { fab.fab$b_rat = 0; fab.fab$b_rfm = FAB$C_STMLF; } #endif if( !(options & copy_recfmt) ) { options |= (FAB$C_VAR +1) << copy_v_recfmt; if( !(options & (copy_prn|copy_ftn|copy_cr)) ) options |= copy_cr; } fab.fab$b_rfm = ((options & copy_recfmt) >> copy_v_recfmt) -1; switch( fab.fab$b_rfm ) { case FAB$C_STM: case FAB$C_STMLF: case FAB$C_STMCR: fab.fab$b_rat |= FAB$M_CR; break; } if( !(options & copy_span) ) fab.fab$b_rat |= FAB$M_BLK; if( options & copy_ftn ) fab.fab$b_rat |= FAB$M_FTN; if( options & copy_cr ) fab.fab$b_rat |= FAB$M_CR; if( options & copy_prn ) { fab.fab$b_rat |= FAB$M_PRN; fab.fab$b_fsz = 2; fab.fab$b_rfm = FAB$C_VFC; switch( (options & copy_b4) >> copy_v_b4 ) { case copy_prn_none: vfc[0] = 0; break; case copy_prn_nl: if( b4n < 1 || b4n > 127 ) return printmsg( COPY_INVALIDNL, 0 ); vfc[0] = b4n & 0x7f; break; case copy_prn_c0: if( b4n > 31 ) return printmsg( COPY_INVALIDC0, 0 ); vfc[0] = 0x80 | b4n; break; case copy_prn_vfu: if( b4n < 1 || b4n > 16 ) return printmsg( COPY_INVALIDVFU, 0 ); vfc[0] = (0xC0 | (b4n -1)) & 0xff; } switch( (options & copy_after) >> copy_v_after ) { case copy_prn_none: vfc[1] = 0; break; case copy_prn_nl: if( aftn < 1 || aftn > 127 ) return printmsg( COPY_INVALIDNL, 0 ); vfc[1] = aftn & 0x7f; break; case copy_prn_c0: if( aftn > 31 ) return printmsg( COPY_INVALIDC0, 0 ); vfc[1] = 0x80 | aftn; break; case copy_prn_vfu: if( aftn < 1 || aftn > 16 ) return printmsg( COPY_INVALIDVFU, 0 ); vfc[1] = (0xC0 | (aftn -1)) & 0xff; } } /* prn */ fab.fab$l_alq = (statbuf.st_size + 511) / 512; fab.fab$w_mrs = (uint16_t)reclen; fab.fab$l_fna = to; fab.fab$b_fns = (uint8_t)strlen( fab.fab$l_fna ); do { if( options & copy_append ) { sts = sys_open( &fab ); rab.rab$l_rop = RAB$M_EOF; } else sts = sys_create( &fab ); rsbuf[nam.nam$b_rsl] = '\0'; if( $FAILED(sts) ) { fclose( fromf ); printmsg( COPY_OPENOUT, 0, (nam.nam$b_rsl? rsbuf:to) ); return printmsg( sts, MSG_CONTINUE, COPY_OPENOUT ); } rab.rab$l_fab = &fab; rab.rab$b_rac = RAB$C_SEQ; if( $FAILS(sts = sys_connect( &rab )) ) { sts = printmsg( sts, 0 ); break; } if( fab.fab$b_rfm == FAB$C_VFC ) { if( options & copy_prn ) rab.rab$l_rhb = vfc; } else options &= ~(copy_prn|copy_vfc); if( options & copy_binary ) { char *buf; uint16_t len; if( (buf = malloc( 512 )) == NULL ) { sts = printmsg( COPY_NOMEM, 0, from ); break; } rab.rab$l_rbf = buf; while( (len = (uint16_t)fread( buf, 1, 512, fromf )) > 0 ) { if( len != 512 ) memset( buf + len, 0, 512 - len ); rab.rab$w_rsz = 512; if( $FAILS(sts = sys_put( &rab )) ) { printmsg( COPY_WRITEERR, 0, rsbuf ); sts = printmsg( sts, MSG_CONTINUE, COPY_WRITEERR ); break; } ++records; } free( buf ); buf = NULL; break; } while( (rab.rab$l_rbf = fgetline( fromf, TRUE, &buf, &bufsize )) != NULL ) { size_t len; char * rp; rp = rab.rab$l_rbf; len = strlen( rp ); if( (options & (copy_prn | copy_vfc)) == copy_vfc ) { if( len < fab.fab$b_fsz ) { sts = RMS$_RSZ; break; } rab.rab$l_rhb = rp; rab.rab$l_rbf = rp + fab.fab$b_fsz; len -= fab.fab$b_fsz; } do { rab.rab$w_rsz = (uint16_t) (len % (MAXREC + 1)); sts = sys_put( &rab ); rab.rab$l_rbf += rab.rab$w_rsz; len -= rab.rab$w_rsz; } while( len > 0 && $SUCCESSFUL( sts ) ); ++records; if( $FAILED(sts) ) { printmsg( COPY_WRITEERR, 0, rsbuf ); sts = printmsg( sts, MSG_CONTINUE, COPY_WRITEERR ); break; } } } while( 0 ); if( buf != NULL ) free( buf ); sys_disconnect( &rab ); { vmscond_t clsts; if( $FAILS(clsts = sys_close( &fab )) && $SUCCESSFUL(sts) ) { printmsg( COPY_CLOSEOUT, 0, rsbuf ); clsts = printmsg( clsts, MSG_CONTINUE, COPY_CLOSEOUT ); } if( $SUCCESSFUL(sts) ) sts = clsts; } fclose( fromf ); if( options & copy_log ) { sts = (options & copy_binary)? COPY_COPIEDB: COPY_COPIEDR; sts = printmsg( sts, 0, from, fab.fab$l_nam->nam$l_rsa, records ); } return sts; } /******************************************************************* output_file_name() */ char *output_file_name( const char *ofname, const char *ifname, int *flags ) { char *ofn, *from, *fname; const char *p, *q, *e; size_t len, bufsize[2]; if( flags != NULL ) *flags = 0; ofn= NULL; bufsize[0] = bufsize[1] = 0; len = strlen( ifname ) +1; if( (from = malloc( len )) == NULL ) { return NULL; } memcpy( from, ifname, len ); #ifdef _WIN32 for( fname = from; *fname != '\0'; fname++ ) if( *fname == '/' ) *fname = '\\'; #endif fname = basename( from ); e = strrchr( fname, '.' ); p = strrchr( ofname, ']' ); if( p == NULL ) p = strrchr( ofname, ':' ); if( p == NULL ) len = 0; else len = 1 + (size_t)( p - ofname ); if( !xpand( &ofn, bufsize, len ) ) { free( from ); return NULL; } memcpy( ofn, ofname, len ); if( p == NULL ) p = ofname; else ++p; if( p[0] == '*' && (!p[1] || p[1] == '.' || p[1] == ';') ) { if( flags ) *flags |= OF_WILD; len = (e == NULL)? strlen(fname) : (size_t)(e - fname); if( !xpand( &ofn, bufsize, len ) ) { free( from ); return NULL; } memcpy( ofn+ bufsize[1] -len, fname, len ); ++p; } else { q = strchr( p, '.' ); if( q == NULL ) q = strrchr( p, ';' ); if( q == NULL ) q = p + strlen( p ); len = (size_t)(q - p); if( !xpand( &ofn, bufsize, len ) ) { free( from ); return NULL; } memcpy( ofn + bufsize[1] - len, p, len ); p += len; } if( p[0] == '.' ) { q = strrchr( p+1, ';' ); if( q == NULL ) q = strrchr( p+1, '.' ); if( q == NULL || q <= p ) q = p + strlen( p ); len = (size_t)(q - p ); if( len == 2 && p[1] == '*' ) { if( flags ) *flags |= OF_WILD; if( e != NULL ) { len = strlen( e ); if( !xpand( &ofn, bufsize, len ) ) { free( from ); return NULL; } memcpy( ofn+ bufsize[1] - len, e, len ); } p += 2; } else { if( !xpand( &ofn, bufsize, len ) ) { free( from ); return NULL; } memcpy( ofn+ bufsize[1] - len, p, len ); p += len; } } if( *p == ';' || *p == '.' ) { len = strlen( p ); if( !xpand( &ofn, bufsize, len ) ) { free( from ); return NULL; } ofn[bufsize[1] - len] = ';'; memcpy( ofn+ bufsize[1] + 1 - len, p+1, len -1 ); p += len; } if( *p ) abort(); ofn[bufsize[1]] = '\0'; free( from ); return ofn; } /******************************************************************* xpand() */ static int xpand( char **buf, size_t size[2], size_t add ) { char *nbuf; size_t need; need = (size[1] += add) + 1; if( need < size[0] ) return 1; nbuf = realloc( *buf, need + 32 + 1 ); if( nbuf == NULL ) { free( *buf ); *buf = NULL; return 0; } *buf = nbuf; size[0] = need + 32 + 1; return 1; }