-+-+-+-+-+-+-+-+ START OF PART 3 -+-+-+-+-+-+-+-+ X match_node_pointer = `5Ematch_node_type ; X X VAR X archive : tar_file_type ; X archive_temp : tar_file_type ; X output_filespec, archive_filespec : filespec_type ; X i, j : integer ; `7B miscellaneous counters `7D X last_char : integer ; `7B points to last char buffere Vd `7D X eof_mark_found : boolean ; `7B by write_temp `7D X archive_temp_open : boolean ; X confirm : boolean ; X match_node : match_node_pointer ; X match_all : boolean ; X option : small_string ; X opening_archive_input : `5BVOLATILE`5D boolean ; X creating_archive_output : `5BVOLATILE`5D boolean ; X inspecting_header : `5BVOLATILE`5D boolean ; X full_archive_spec : `5BVOLATILE`5D filespec_type ; X X default_header : tar_record_type ; X X UNIX_epoch_time : lib_date_type ; X delta_seconds : integer ; `7B Delta time from GMT `7D X X VALUE X default_header := (0, X (100 OF null), `7B name `7D X (' 644', space, null), `7B mode `7D X (' 0', space, null), `7B uid `7D X (' 0', space, null), `7B gid `7D X ' 0 ', `7B siz `7D X ' 4241462038 ', `7B mtime - 10-MAY-1988 12:00 EST `7D X (' 0', null, space), `7B chksum - nul/spc back to front, I kn Vow `7D X X null, `7B linkflag `7D X (100 OF null), `7B linkname `7D X (175 OF null)) ; `7B filler `7D X X UNIX_epoch_time := (0, %x4BEB4000, %x007C9567) ; X `7B which equals 1-JAN-1970 00:00:00.00 `7D X inspecting_header := false ; X opening_archive_input := false ; X archive_temp_open := false ; X match_all := false ; X X X `5BASYNCHRONOUS`5D FUNCTION tar_handler ( `7B TAR condition handler ` V7D X VAR sigargs : lib_sigargs_type ; X VAR mechargs: lib_mechargs_type) : sts_type ; X X VAR X i, j : integer ; X condition : STS$TYPE ; X convert : boolean ; X X PROCEDURE bad_archive ; X VAR X descriptor : `5BSTATIC`5D PACKED RECORD X maxlen : lib_word_type ; X dtype, class : lib_byte_type ; X pointer : integer END ; X X BEGIN X sigargs.param_count := 4 ; X sigargs.condition := tar__badarchive ; X sigargs.parameter`5B1`5D := 1 ; X descriptor.maxlen := length (full_archive_spec) ; X descriptor.class := DSC$K_CLASS_VS ; X descriptor.dtype := DSC$K_DTYPE_VT ; X descriptor.pointer := iaddress (full_archive_spec) ; X sigargs.parameter`5B2`5D := iaddress (descriptor) END ; X X BEGIN `7B tar_handler `7D X tar_handler := SS$_RESIGNAL ; X convert := false ; X CASE sigargs.condition OF X PAS$_ERRDUROPE, PAS$_FILNOTFOU, PAS$_ERRDURREW : BEGIN X IF opening_archive_input THEN X sigargs.condition := tar__openin X ELSE IF creating_archive_output THEN X sigargs.condition := tar__createrr X ELSE X convert := true ; X IF NOT convert THEN BEGIN X sigargs.parameter`5B1`5D := 1 ; X j := int (sigargs.param_count) - 3 ; X FOR i := 2 TO j DO X sigargs.parameter`5Bi`5D := sigargs.parameter`5Bi+2`5D V ; X sigargs.param_count := sigargs.param_count - 2 END END ; X PAS$_ACCMETINC, `7B Access method inconsistent `7D X PAS$_RECLENINC, `7B Record length inconsistent `7D X PAS$_RECTYPINC : `7B Record type inconsistent `7D X IF opening_archive_input THEN X bad_archive X ELSE X convert := true ; X PAS$_ERRDURGET : `7B Error during GET `7D X sigargs.condition := tar__errread ; X PAS$_INVSYNOCT : `7B Invalid syntax in octal value - Somethi Vng X might have blown up while reading a heade Vr `7D X IF inspecting_header THEN X bad_archive X ELSE X convert := true ; X OTHERWISE X convert := true END ; X X IF convert THEN BEGIN X condition := (sigargs.condition)::STS$TYPE ; X IF (condition.STS$V_FAC_NO = PAS$_FACILITY) THEN BEGIN X `7B Report condition encountered as "internal error" `7D X LIB$STOP (tar__internerr, 1, sigargs.condition) ; X END END ; X END ; `7B tar_handler `7D X X X FUNCTION lowercase ( X VAR inp_string : `5BREADONLY`5D VARYING `5Bn1`5D OF char ; X start_pos : integer := 1) : medium_string ; X X VAR X i : integer ; X result : medium_string ; X X BEGIN X result := inp_string ; X FOR i := start_pos TO inp_string.length DO X IF inp_string`5Bi`5D IN `5B'A'..'Z'`5D THEN X result`5Bi`5D := chr (ord (inp_string`5Bi`5D) + 32) ; X lowercase := result ; X END ; X X X FUNCTION uppercase ( `7B Wrote my own cos it looks neater with "lowerc Vase" `7D X VAR inp_string : `5BREADONLY`5D VARYING `5Bn1`5D OF char) : medium_str Ving ; X X VAR X result : medium_string ; X i : integer ; X X BEGIN X result := inp_string ; X FOR i := 1 TO inp_string.length DO X IF inp_string`5Bi`5D IN `5B'a'..'z'`5D THEN X result`5Bi`5D := chr (ord (inp_string`5Bi`5D) - 32) ; X uppercase := result ; X END ; X X X PROCEDURE convert_zstr ( `7B Convert a null-terminated string to VARY VING `7D X VAR z_string : `5BREADONLY`5D PACKED ARRAY `5Bl1..u1:integer`5D OF cha Vr ; X VAR vs_string : VARYING `5Bn1`5D OF char) ; X X BEGIN X vs_string := substr (z_string, 1, index (z_string, null) - 1) ; X END ; X X X FUNCTION february_days (year : integer) : integer ; X BEGIN X IF year REM 4 = 0 THEN X IF year REM 100 = 0 THEN X IF year REM 400 = 0 THEN X february_days := 29 X ELSE X february_days := 28 X ELSE X february_days := 29 X ELSE X february_days := 28 END ; X X X FUNCTION get_timezone : integer ; X VAR X return, hours, minutes, i : integer ; X timezone_str : small_string ; X X BEGIN X return := 1 ; X IF failure ($TRNLNM (, lnm$file_dev_kt, 'TAR_TIMEZONE',, X lib_item_list (lib_out_item (LNM$_STRING, %DESCR timezone_str))) V) X THEN X IF failure ($TRNLNM (, lnm$file_dev_kt, 'SYS$TIME_ZONE',, X lib_item_list (lib_out_item (LNM$_STRING, X %DESCR timezone_str)))) THEN X return := 0 ; X IF return <> 0 THEN BEGIN X IF failure (OTS$CVT_TI_L ((timezone_str), i)) THEN X LIB$STOP (tar__invtimzon, 1, %STDESCR (timezone_str)) ; X hours := i DIV 100 ; X minutes := i - hours * 100 ; X IF (abs (hours) > 18) OR (abs (minutes) > 59) THEN X return := 0 X ELSE X return := minutes * 60 + hours * 3600 END ; X get_timezone := return ; X END ; X X X FUNCTION add_timezone (`09`7B converts from GMT to local time `7D X VAR UNIX_time : unsigned) : boolean ; X X BEGIN X add_timezone := true ; X X IF delta_seconds < 0 THEN X IF (-1 * delta_seconds) > UNIX_time THEN BEGIN X UNIX_time := 0 ; X add_timezone := false END`09`7B indicates over/underflow `7D X ELSE X UNIX_time := UNIX_time + delta_seconds X ELSE X IF uint (lib_k_maxlong) - delta_seconds > UNIX_time THEN X UNIX_time := UNIX_time + delta_seconds X ELSE BEGIN X UNIX_time := lib_k_maxlong ; X add_timezone := false END END ; X X X PROCEDURE break_up_UNIX_time ( X UNIX_time : unsigned ; X VAR time : broken_time_type) ; X X CONST X mar =`0931 ; X apr =`0930 + mar ; X may =`0931 + apr ; X jun =`0930 + may ; X jul =`0931 + jun ; X aug =`0931 + jul ; X sep =`0930 + aug ; X oct =`0931 + sep ; X nov =`0930 + oct ; X dec =`0931 + nov ; X jan =`0931 + dec ; X X seconds_per_day =`0986400 ; X seconds_per_hour =`093600 ; X seconds_per_minute =`0960 ; X weekday_epoch =`094 ; X days_to_eoy =`09`09306 ;`09`7B Days from 1/3 to 1/1 next year `7D X X VAR X UNIX_time_l : unsigned ; X days, m_day, temp : unsigned ; X X BEGIN`09`7B break_up_UNIX_time `7D X UNIX_time_l := UNIX_time ; X X days := int (UNIX_time_l DIV seconds_per_day) ; X X UNIX_time_l := UNIX_time_l - days * seconds_per_day ; X time.hour := int (UNIX_time_l DIV seconds_per_hour) ; X X UNIX_time_l := UNIX_time_l - time.hour * seconds_per_hour ; X time.minute := int (UNIX_time_l DIV seconds_per_minute) ; X X time.second := int (UNIX_time_l - time.minute * seconds_per_minute) V ; X X days := days + 2133 ;`09`09`7B Now relative to 1/3/1964 `7D X X `7B Find remainder of days / 365.25 `7D X temp := days * 4 DIV 1461 ; X m_day := days - temp * 1461 DIV 4 ; X X `7B m_day now contains the day of the year, relative to 1st March `7D X X time.year := -6 ;`09`09`7B Year will then be relative to 1970 `7D X X IF m_day = 0 THEN BEGIN`09`7B It's actually the 29th of Feb! `7D X time.month := 2 ; X time.day := 29 END X ELSE`09`09`09`09`7B Right, figure out month and day `7D X IF m_day > aug THEN X IF m_day > nov THEN X IF m_day > dec THEN BEGIN X IF m_day > jan THEN BEGIN`09`09`7B February `7D X time.month := 2 ; X time.day := int (m_day - jan) END X ELSE BEGIN`09`09`09`09`7B January `7D X time.month := 1 ; X time.day := int (m_day - dec) END ; X time.year := -5 END X ELSE BEGIN X time.month := 12 ; X time.day := int (m_day - nov) END X ELSE X IF m_day > sep THEN X IF m_day > oct THEN BEGIN`09`09`7B November `7D X time.month := 11 ; X time.day := int (m_day - oct) END X ELSE BEGIN`09`09`09`09`7B October `7D X time.month := 10 ; X time.day := int (m_day - sep) END X ELSE BEGIN`09`09`09`09`7B September `7D X time.month := 9 ; X time.day := int (m_day - aug) END X ELSE X IF m_day > may THEN X IF m_day > jun THEN X IF m_day > jul THEN BEGIN`09`09`7B August `7D X time.month := 8 ; X time.day := int (m_day - jul) END X ELSE BEGIN`09`09`09`09`7B July `7D X time.month := 7 ; X time.day := int (m_day - jun) END X ELSE BEGIN`09`09`09`09`7B June `7D X time.month := 6 ; X time.day := int (m_day - may) END X ELSE X IF m_day > mar THEN X IF m_day > apr THEN BEGIN`09`09`7B May `7D X time.month := 5 ; X time.day := int (m_day - apr) END X ELSE BEGIN`09`09`09`09`7B April `7D X time.month := 4 ; X time.day := int (m_day - mar) END X ELSE BEGIN`09`09`09`09`7B March `7D X time.month := 3 ; X time.day := int (m_day) END ; X X `7B Last of all, what year is it? `7D X X time.year := int (time.year + days * 4 DIV 1461) ; X END ; X X X FUNCTION UNIX_time_to_str ( X UNIX_time : unsigned ; X delta_seconds : integer := 0) : medium_string ; X X VAR X time : broken_time_type ; X UNIX_time_local : unsigned ; X months : `5BSTATIC`5D ARRAY `5B1..12`5D OF PACKED ARRAY `5B1..3`5D V OF char := X ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', X 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec') ; X return_string : VARYING `5B17`5D OF char ; X X BEGIN X add_timezone (UNIX_time) ; X UNIX_time_local := UNIX_time ; X break_up_UNIX_time (UNIX_time_local, time) ; X lib_sigiferr (LIB$SYS_FAO ('!2SL !AS !SL !2ZL:!2ZL',, return_string V, X time.day, %STDESCR (months`5Btime.month`5D), time.year + 1970, X time.hour, time.minute)) ; X UNIX_time_to_str := return_string ; X END ; X X X FUNCTION UNIX_to_VMS_time ( X VAR UNIX_time : unsigned ; X delta_seconds : integer := 0) : lib_date_type ; X X VAR X h_time : quadruple ; X return_time : lib_date_type ; X X BEGIN X h_time := UNIX_time * 1q7 ; X h_time := h_time + UNIX_epoch_quad ; X `7B Convert to local time `7D X h_time := h_time + delta_seconds * 1q7 ; X return_time.hi := trunc (h_time / power_32_quad) ; X h_time := h_time - return_time.hi * power_32_quad ; X return_time.lo := utrunc (h_time) ; X UNIX_to_VMS_time := return_time ; X END ; X `20 X X FUNCTION VMS_to_UNIX_time ( X VAR VMS_time : lib_date_type ; `7B Should be READONLY `7D X delta_seconds : integer := 0) : unsigned ; X X VAR X VMS_rel_UNIX_epoch : lib_date_type ; X `7B delta VMS time relative to 1-JAN-1970 0:0:0.0 `7D X h_time : quadruple ; `7B quadruple precision, or H_FLOAT ` V7D X result_time : unsigned ; X X BEGIN X VMS_to_UNIX_time := 0 ; X IF success (LIB$SUBX ( X VMS_time, UNIX_epoch_time, VMS_rel_UNIX_epoch)) THEN X IF VMS_rel_UNIX_epoch.hi > 0 THEN BEGIN X h_time := X VMS_rel_UNIX_epoch.hi * power_32_quad ; X h_time := h_time + quad (VMS_rel_UNIX_epoch.lo) ; X h_time := h_time / 1q7 ; `7B convert to seconds `7D X h_time := h_time - delta_seconds ; `7B convert to GMT `7D X IF h_time < 0 THEN X VMS_to_UNIX_time := 0 X ELSE X IF h_time > (power_32_quad - 1) THEN X VMS_to_UNIX_time := %xFFFFFFFF `7B 2`5E32 - 1 `7D X ELSE X VMS_to_UNIX_time := uround (h_time) END END ; X X X FUNCTION get_prompted_string ( X prompt : VARYING `5Bn1`5D OF char ; X VAR string : VARYING `5Bn2`5D OF char ; X default : VARYING `5Bn3`5D OF char := '') X : sts_type ; X X VAR X status : sts_type ; X X BEGIN X status := LIB$GET_INPUT (string, prompt) ; X IF success (status) THEN X IF string = '' THEN string := default ; X get_prompted_string := status END ; X X X PROCEDURE set_this_false ( X VAR bool_to_set_false : boolean) ; X X BEGIN X bool_to_set_false := false END ; X X X FUNCTION confirm_operation ( X VAR op_to_confirm : `5BREADONLY`5D VARYING `5Bn1`5D OF char ; X VAR conf_filespec : `5BREADONLY`5D filespec_type ; X confirmed : boolean := false ; X PROCEDURE eof_routine (VAR p1 : boolean) ; X VAR eof_routine_p1 : boolean) : boolean ; X X VAR X decision : small_string ; X status : sts_type ; X X BEGIN X IF confirmed THEN confirm_operation := true X ELSE BEGIN X status := get_prompted_string ( X lowercase (op_to_confirm, 2) + space + conf_filespec + X '? (Y/N) `5BY`5D: ', decision, 'Y') ; X IF success (status) THEN X confirm_operation := decision`5B1`5D IN `5B'Y', 'y', ' '`5D X ELSE X IF status = RMS$_EOF THEN BEGIN X eof_routine (eof_routine_p1) ; X confirm_operation := false END X ELSE X LIB$SIGNAL (status) END END ; X X X FUNCTION VMS_filespec ( X VAR UNIX_filespec : `5BREADONLY`5D filespec_type ; X VAR absolute : boolean) : medium_string ; X X VAR X UNIX_dir : small_string ; X temp_ch : char ; X i, j : integer ; X UNIX_filespec_l : filespec_type ; X device, directory, name : medium_string ; X dot_found, device_name, start_name, more : boolean ; X X BEGIN X absolute := false ; X i := UNIX_filespec.length ; X IF i = 0 THEN BEGIN X name := '' ; X UNIX_dir := '' END X ELSE BEGIN X UNIX_filespec_l := UNIX_filespec ; X IF i > 1 THEN X IF substr (UNIX_filespec, 1, 2) = './' `7B redundant noise `7 VD X THEN BEGIN X UNIX_filespec_l := substr (UNIX_filespec_l, 3, X UNIX_filespec_l.length - 2) ; X i := i - 2 END ; X more := true ; X WHILE more DO X IF i > 0 THEN X IF UNIX_filespec_l`5Bi`5D <> slash THEN X i := i - 1 X ELSE X more := false X ELSE X more := false ; X name := X substr (UNIX_filespec_l, i + 1, UNIX_filespec_l.length - i) ; X IF i > 1 THEN X UNIX_dir := substr (UNIX_filespec_l, 1, i - 1) X ELSE X UNIX_dir := '' ; X j := length (name) ; X dot_found := false ; X FOR i := j DOWNTO 1 DO X CASE name`5Bi`5D OF X '.' : X IF dot_found THEN X name`5Bi`5D := '-' X ELSE X dot_found := true ; X 'a'..'z' : X name`5Bi`5D := chr (ord (name`5Bi`5D) - 32) ; X 'A'..'Z', '0'..'9', '$', '_', '-' : ; X ',', '+', '`7E', '#', '*' : name`5Bi`5D := '_' ; X OTHERWISE name`5Bi`5D := '$' ; X END END ; X device := '' ; X directory := '' ; X IF length (UNIX_dir) > 0 THEN BEGIN X j := length (UNIX_dir) ; X start_name := true ; X device_name := false ; X i := 0 ; X WHILE i < j DO BEGIN X i := i + 1 ; X CASE UNIX_dir`5Bi`5D OF X slash : X IF device_name THEN BEGIN X start_name := true ; X device_name := false END X ELSE IF i = 1 THEN BEGIN X absolute := true ; X device_name := true END X ELSE BEGIN X start_name := true ; X directory := directory + '.' END ; X 'A'..'Z', '0'..'9', '$', '_' : BEGIN X IF device_name THEN X device := device + UNIX_dir`5Bi`5D X ELSE X directory := directory + UNIX_dir`5Bi`5D ; X start_name := false END ; X 'a'..'z' : BEGIN X IF device_name THEN X device := device + chr (ord (UNIX_dir`5Bi`5D) - 32) X ELSE X directory := directory + chr (ord (UNIX_dir`5Bi`5D) V - 32) ; X start_name := false END ; X '-' : BEGIN X IF start_name OR device_name THEN X temp_ch := '_' X ELSE X temp_ch := '-' ; X IF device_name THEN X device := device + temp_ch X ELSE X directory := directory + temp_ch END ; X '.' : X IF device_name THEN X device := device + '_' X ELSE X IF (j > i) AND (UNIX_dir`5Bi+1`5D = '.') THEN BEGIN X directory := directory + '-' ; X i := i + 1 END X ELSE X directory := directory + '_' ; X OTHERWISE BEGIN X IF device_name THEN X device := device + '$' X ELSE X directory := directory + '$' ; X start_name := false END ; X END ; `7B CASE `7D X END ; `7B WHILE `7D X END ; `7B IF `7D X IF device = '' THEN X IF directory = '' THEN X VMS_filespec := '`5B`5D' + name X ELSE X VMS_filespec := '`5B.' + directory + '`5D' + name X ELSE X VMS_filespec := device + ':`5B' + directory + '`5D' + name X END ; X X X FUNCTION UNIX_filespec ( X VAR VMS_filespec : `5BREADONLY`5D filespec_type ; `7BALL fields expe Vcted `7D X map_mode : map_mode_type ; X prefix_length : integer := 0 ; X retain_version : boolean := false) : medium_string ; X X VAR X device, name, type_, version : small_string ; X d, n, t, v, e, i : integer ; X directory, temp_result : medium_string ; X X BEGIN X e := length (VMS_filespec) ; X v := e ; X WHILE VMS_filespec`5Bv`5D <> ';' DO X v := v - 1 ; X t := v ; X WHILE VMS_filespec`5Bt`5D <> '.' DO X t := t - 1 ; X n := t ; X WHILE VMS_filespec`5Bn`5D <> '`5D' DO X n := n - 1 ; X d := 1 ; X WHILE VMS_filespec`5Bd`5D <> ':' DO X d := d + 1 ; X device := substr (VMS_filespec, 1, d - 1) ; X directory := substr (VMS_filespec, d + 2, n - d - 2) ; X X `7B Take out a leading 000000 directory if present (irrelevant) `7D X X IF directory.length > 8 THEN X IF substr (directory, 1, 8) = '000000.' THEN X directory := substr (directory, 8, length (directory) - 7) X ELSE X ELSE X IF directory = '000000' THEN X directory := '' ; X X name := substr (VMS_filespec, n + 1, t - n - 1) ; X type_ := substr (VMS_filespec, t, v - t) ; X IF type_ = '.' THEN X type_ := '' ; X version := substr (VMS_filespec, v, e - v + 1) ; X X temp_result := '' ; X IF map_mode IN `5Babsolute_mode, prefix_mode`5D THEN X temp_result := slash + device + slash ; X IF map_mode <> single_dir_mode THEN BEGIN X d := length (directory) ; X IF d > 0 THEN BEGIN X FOR i := 1 TO d DO X IF directory`5Bi`5D = '.' THEN X directory`5Bi`5D := slash ; X temp_result := temp_result + directory + slash END END ; X temp_result := temp_result + name + type_ ; X IF retain_version THEN X temp_result := temp_result + version ; X IF map_mode = prefix_mode THEN X temp_result := substr (temp_result, prefix_length + 1, X length (temp_result) - prefix_length) ; X UNIX_filespec := lowercase (temp_result) ; X END ; X X X FUNCTION find_file_spec ( X VAR file_buf : `5BUNSAFE`5D text) : filespec_type ; X X `7B This routine returns the full file specification of the file opened w Vith X the passed file-variable. This routine does assume that a NAM block ha Vs X been used by Pascal (which is usually the case), and that the file is X open. `7D X X VAR X fab : fab_pointer ; X nam : nam_pointer ; X nam_rsa : nam_rsa_type ; X X BEGIN X fab := PAS$FAB (file_buf) ; X nam := (fab`5E.FAB$L_NAM)::nam_pointer ; X nam_rsa := (nam`5E.NAM$L_RSA)::nam_rsa_type ; X find_file_spec := substr (nam_rsa`5E, 1, nam`5E.NAM$B_RSL) ; X END ; X X X PROCEDURE open_archive_input (`09`7B Opens an archive file for input `7D X VAR filespec : `5BREADONLY`5D filespec_type ; X share : boolean := false) ; X X BEGIN X opening_archive_input := true ; X IF share THEN X open (archive, filespec, history := OLD, sharing := READONLY, X default := dot_tar_kt) X ELSE X open (archive, filespec, history := OLD, default := dot_tar_kt) V ; X reset (archive) ; X opening_archive_input := false ; X END ; X X X FUNCTION eof_archive : boolean ; X BEGIN eof_archive := eof (archive) OR eof_mark_found END ; X X X PROCEDURE bad_header ( X VAR header : `5BREADONLY`5D tar_record_type) ; X X BEGIN +-+-+-+-+-+-+-+- END OF PART 3 +-+-+-+-+-+-+-+-