class ZCL_EXCEL_ABAP_ZIP definition
public
final
create public .
*"* public components of class ZCL_EXCEL_ABAP_ZIP
*"* do not include other source files here!!!
public section.
type-pools IHTTP .
types:
BEGIN OF T_FILE,
name TYPE string,
date TYPE d,
time TYPE t,
size TYPE i,
END OF T_FILE .
types:
T_FILES TYPE TABLE OF T_FILE .
types:
BEGIN OF T_SPLICE_ENTRY,
name TYPE string,
offset TYPE i,
length TYPE i,
compressed TYPE i,
END OF T_SPLICE_ENTRY .
types:
T_SPLICE_ENTRIES TYPE STANDARD TABLE OF T_SPLICE_ENTRY WITH DEFAULT KEY .
data FILES type T_FILES read-only .
type-pools ABAP .
data SUPPORT_UNICODE_NAMES type ABAP_BOOL value ABAP_FALSE. "#EC NOTEXT .
methods LOAD
importing
!ZIP type XSTRING
exceptions
ZIP_PARSE_ERROR .
methods SAVE
returning
value(ZIP) type XSTRING .
methods GET
importing
!NAME type STRING optional
value(INDEX) type I default 0
exporting
!CONTENT type XSTRING
exceptions
ZIP_INDEX_ERROR
ZIP_DECOMPRESSION_ERROR .
methods ADD
importing
!NAME type STRING
!CONTENT type XSEQUENCE .
methods DELETE
importing
!NAME type STRING optional
value(INDEX) type I default 0
exceptions
ZIP_INDEX_ERROR .
class-methods CRC32
importing
!CONTENT type XSTRING
returning
value(CRC32) type I .
class-methods SPLICE
importing
!ZIP type XSTRING
returning
value(ENTRIES) type T_SPLICE_ENTRIES .*"* protected components of class CL_ABAP_ZIP
*"* do not include other source files here!!!
protected section.*"* private components of class CL_ABAP_ZIP
*"* do not include other source files here!!!
private section.
types:
BEGIN OF T_EXT,
min_extract_version TYPE i,
gen_flags TYPE i,
compressed TYPE i,
compsize TYPE i,
crc32(4) TYPE x,
filename_len TYPE i,
filename TYPE xstring,
extra_len TYPE i,
extra TYPE xstring,
content TYPE xstring,
END OF T_EXT .
types:
T_EXTS TYPE TABLE OF T_EXT .
data EXTS type T_EXTS .
class-data CRC32_MAP type XSTRING .CLASS msdos IMPLEMENTATION.
METHOD from_date. " IMPORTING msdos_date TYPE i RETURNING value(date) TYPE d
* MS-DOS format for date:
* Bits 15:9 = year - 1980
* Bits 8:5 = month of year
* Bits 4:0 = day of month
CONSTANTS: mFE00(2) TYPE x VALUE 'FE00',
m01E0(2) TYPE x VALUE '01E0',
m001F(2) TYPE x VALUE '001F'.
DATA: x(2) TYPE x,
year TYPE i,
month TYPE i,
day TYPE i,
c4(4) TYPE c,
str TYPE string.
* Bits 15:9 = year - 1980
x = msdos_date.
x = x BIT-AND mFE00.
x = x DIV 512. " >> 9
x = x BIT-AND m001F.
year = x.
year = year + 1980.
WRITE year TO c4 USING EDIT MASK 'RR____'.
CONCATENATE str c4 INTO str.
* Bits 8:5 = month of year
x = msdos_date.
x = x BIT-AND m01E0.
x = x DIV 32. " >> 5
x = x BIT-AND m001F.
month = x.
WRITE month TO c4 USING EDIT MASK 'RR__'.
CONCATENATE str c4 INTO str.
* Bits 4:0 = day of month
x = msdos_date.
x = x BIT-AND m001F.
day = x.
WRITE day TO c4 USING EDIT MASK 'RR__'.
CONCATENATE str c4 INTO str.
* Build date
TRANSLATE str USING ' 0'.
date = str.
ENDMETHOD.
METHOD from_time. " IMPORTING msdos_time TYPE i RETURNING value(time) TYPE t.
* MS-DOS format for time:
* Bits 15:11 = hour (24-hour clock)
* Bits 10:5 = minute
* Bits 4:0 = second/2
CONSTANTS: mF100(2) TYPE x VALUE 'F100',
m07E0(2) TYPE x VALUE '07E0',
m003F(2) TYPE x VALUE '003F',
m001F(2) TYPE x VALUE '001F'.
DATA: x(2) TYPE x,
hour TYPE i,
min TYPE i,
c4(4) TYPE c,
str TYPE string.
* Bits 15:11 = hour (24-hour clock)
x = msdos_time.
x = x BIT-AND mF100.
x = x DIV 2048. " >> 11
x = x BIT-AND m001F.
hour = x.
WRITE hour TO c4 USING EDIT MASK 'RR__'.
CONCATENATE str c4 INTO str.
* Bits 10:5 = minute
x = msdos_time.
x = x BIT-AND m07E0.
x = x DIV 32. " >> 5
x = x BIT-AND m003F.
min = x.
WRITE min TO c4 USING EDIT MASK 'RR__'.
CONCATENATE str c4 INTO str.
* Bits 4:0 = second/2
CONCATENATE str '00' INTO str.
* Build time
TRANSLATE str USING ' 0'.
time = str.
ENDMETHOD.
METHOD to_date. " IMPORTING date TYPE d RETURNING value(msdos_date) TYPE i.
* MS-DOS format for date:
* Bits 15:9 = year - 1980
* Bits 8:5 = month of year
* Bits 4:0 = day of month
DATA: xdate(2) TYPE x,
x(2) TYPE x,
year TYPE i,
month TYPE i,
day TYPE i.
* Bits 15:9 = year - 1980
year = date+0(4).
x = year - 1980.
x = x * 512. " << 9
xdate = xdate BIT-OR x.
* Bits 8:5 = month of year
month = date+4(2).
x = month.
x = x * 32. " << 5
xdate = xdate BIT-OR x.
* Bits 4:0 = day of month
day = date+6(2).
x = day.
xdate = xdate BIT-OR x.
msdos_date = xdate.
ENDMETHOD.
METHOD to_time. " IMPORTING time TYPE t RETURNING value(msdos_time) TYPE i.
* MS-DOS format for time:
* Bits 15:11 = hour (24-hour clock)
* Bits 10:5 = minute
* Bits 4:0 = second/2
DATA: xtime(2) TYPE x,
x(2) TYPE x,
hour TYPE i,
min TYPE i,
sec TYPE i.
* Bits 15:11 = hour (24-hour clock)
hour = time+0(2).
x = hour.
x = x * 2048. " << 11
xtime = xtime BIT-OR x.
* Bits 10:5 = minute
min = time+2(2).
x = min.
x = x * 32. " << 5
xtime = xtime BIT-OR x.
* Bits 4:0 = seconds
sec = time+4(2).
x = sec / 2.
xtime = xtime BIT-OR x.
msdos_time = xtime.
ENDMETHOD.
ENDCLASS.CLASS msdos DEFINITION FINAL.
PUBLIC SECTION.
CLASS-METHODS: to_date IMPORTING date TYPE d RETURNING value(msdos_date) TYPE i.
CLASS-METHODS: to_time IMPORTING time TYPE t RETURNING value(msdos_time) TYPE i.
CLASS-METHODS: from_date IMPORTING msdos_date TYPE i RETURNING value(date) TYPE d.
CLASS-METHODS: from_time IMPORTING msdos_time TYPE i RETURNING value(time) TYPE t.
ENDCLASS.*"* use this source file for any macro definitions you need
*"* in the implementation part of the classABAPIHTTPMETHOD ADD.
FIELD-SYMBOLS: <file> TYPE t_file,
<ext> TYPE t_ext.
APPEND INITIAL LINE TO files ASSIGNING <file>.
APPEND INITIAL LINE TO exts ASSIGNING <ext>.
<file>-name = name.
<file>-date = sy-datum.
<file>-time = sy-uzeit.
<file>-size = XSTRLEN( content ).
* general purpose flag bit 11 (Language encoding flag (EFS)
CONSTANTS: gen_flags_unicode(2) TYPE x VALUE '0800'.
* see: http://www.pkware.com/documents/casestudies/APPNOTE.TXT, APPENDIX D
* zip normaly used IBM Code Page 437 mapped to SAP Printer EPESCP IBM 437
DATA: conv TYPE REF TO cl_abap_conv_out_ce,
conv_cp437 TYPE REF TO cl_abap_conv_out_ce,
conv_utf8 TYPE REF TO cl_abap_conv_out_ce,
cp437 TYPE abap_encoding value '1142', " IBM 437
utf8 TYPE abap_encoding value '4110'. " UTF-8
if support_unicode_names = abap_true.
conv = cl_abap_conv_out_ce=>create( encoding = utf8
ignore_cerr = abap_true
replacement = '#' ).
else.
conv = cl_abap_conv_out_ce=>create( encoding = cp437
ignore_cerr = abap_true
replacement = '#' ).
endif.
conv->convert( EXPORTING data = <file>-name IMPORTING buffer = <ext>-filename ).
<ext>-filename_len = XSTRLEN( <ext>-filename ).
<ext>-extra_len = 0.
<ext>-extra = ''.
<ext>-min_extract_version = 20.
if support_unicode_names = abap_true.
<ext>-gen_flags = gen_flags_unicode.
else.
<ext>-gen_flags = 0.
endif.
IF <file>-size > 0.
<ext>-compressed = 8. " gzip Deflate
<ext>-crc32 = crc32( content ).
cl_abap_gzip=>compress_binary(
EXPORTING raw_in = content
raw_in_len = <file>-size
IMPORTING gzip_out = <ext>-content
gzip_out_len = <ext>-compsize ).
ELSE. " folder
<ext>-compressed = 0. " gzip Stored
<ext>-crc32 = 0.
<ext>-compsize = 0.
ENDIF.
ENDMETHOD.METHOD CRC32.
* Let us ask our friendly neighbour whether there is a CRC32 in the kernel (thanks guys!)
IF cl_http_utility=>is_ict_system_call_implemented( ihttp_scid_crc32_checksum ) IS INITIAL.
SYSTEM-CALL ict "#EC CI_SYSTEMCALL
DID
ihttp_scid_crc32_checksum
PARAMETERS
content " > xstr
crc32. " < unsigned int
RETURN.
ENDIF.
* Do the calculations by hand. This is going to be slow. This is going to be a pain.
* What is a man to do?
CONSTANTS: magic_nr(4) TYPE x VALUE 'EDB88320',
mFFFFFFFF(4) TYPE x VALUE 'FFFFFFFF',
m7FFFFFFF(4) TYPE x VALUE '7FFFFFFF',
m00FFFFFF(4) TYPE x VALUE '00FFFFFF',
m000000FF(4) TYPE x VALUE '000000FF',
m000000(3) TYPE x VALUE '000000'.
IF XSTRLEN( crc32_map ) = 0.
DO 256 TIMES.
DATA: c(4) TYPE x, low_bit(4) TYPE x.
c = sy-index - 1.
DO 8 TIMES.
low_bit = '00000001'. low_bit = c BIT-AND low_bit. " c & 1
c = c DIV 2. c = c BIT-AND m7FFFFFFF. " c >> 1 (top is zero, but in ABAP signed!)
IF low_bit IS NOT INITIAL.
c = c BIT-XOR magic_nr.
ENDIF.
ENDDO.
CONCATENATE crc32_map c INTO crc32_map IN BYTE MODE.
ENDDO.
ENDIF.
DATA: len TYPE i, n TYPE i. "#EC *
DATA: crc(4) TYPE x VALUE mFFFFFFFF, x4(4) TYPE x, idx(4) TYPE x.
len = XSTRLEN( content ).
DO len TIMES.
n = sy-index - 1.
CONCATENATE m000000 content+n(1) INTO idx IN BYTE MODE.
idx = ( crc BIT-XOR idx ) BIT-AND m000000FF.
idx = idx * 4.
x4 = crc32_map+idx(4).
crc = crc DIV 256. crc = crc BIT-AND m00FFFFFF. " c >> 8
crc = x4 BIT-XOR crc.
ENDDO.
crc = crc BIT-XOR mFFFFFFFF.
crc32 = crc.
ENDMETHOD.METHOD DELETE.
IF index = 0.
READ TABLE files TRANSPORTING NO FIELDS WITH KEY name = name.
IF sy-subrc IS NOT INITIAL.
RAISE zip_index_error. "#EC RAISE_OK
ENDIF.
index = sy-tabix.
ENDIF.
IF index < 1 OR index > LINES( files ).
RAISE zip_index_error. "#EC RAISE_OK
ENDIF.
DELETE files INDEX index.
DELETE exts INDEX index.
ENDMETHOD.METHOD GET.
FIELD-SYMBOLS: <ext> TYPE t_ext.
IF index IS INITIAL.
READ TABLE files TRANSPORTING NO FIELDS WITH KEY name = name.
IF sy-subrc IS NOT INITIAL.
RAISE zip_index_error. "#EC RAISE_OK
ENDIF.
index = sy-tabix.
ENDIF.
IF index < 1 OR index > LINES( files ).
RAISE zip_index_error. "#EC RAISE_OK
ENDIF.
READ TABLE exts INDEX index ASSIGNING <ext>.
IF <ext>-compressed IS INITIAL.
content = <ext>-content.
ELSE.
cl_abap_gzip=>decompress_binary(
EXPORTING gzip_in = <ext>-content
gzip_in_len = <ext>-compsize
IMPORTING raw_out = content ).
ENDIF.
IF crc32( content ) <> <ext>-crc32.
RAISE zip_decompression_error. "#EC RAISE_OK
ENDIF.
ENDMETHOD.METHOD load.
* Documentation from: http://www.pkware.com/company/standards/appnote/appnote.txt
* Start to decode new ZIP file
CLEAR: files, exts.
REFRESH: files, exts.
* Global offset for moving through file
DATA: offset TYPE i.
DEFINE next. " move offset
offset = offset + &1.
END-OF-DEFINITION.
* DATA: l1(1) TYPE x, h1(1) TYPE x, l2(1) TYPE x, h2(1) TYPE x, xstr TYPE xstring.
DATA: w2(2) TYPE x, w4(4) TYPE x, xstr TYPE xstring.
DEFINE read2. " read two bytes as integer and move offset
* l1 = zip+offset(1). offset = offset + 1.
* h1 = zip+offset(1). offset = offset + 1.
* CONCATENATE h1 l1 INTO xstr IN BYTE MODE.
w2 = zip+offset(2).
offset = offset + 2.
concatenate w2+1(1) w2+0(1) into xstr in byte mode.
&1 = xstr.
END-OF-DEFINITION.
DEFINE read4. " read four bytes as integer and move offset
* l1 = zip+offset(1). offset = offset + 1.
* h1 = zip+offset(1). offset = offset + 1.
* l2 = zip+offset(1). offset = offset + 1.
* h2 = zip+offset(1). offset = offset + 1.
* CONCATENATE h2 l2 h1 l1 INTO xstr IN BYTE MODE.
w4 = zip+offset(4).
offset = offset + 4.
concatenate w4+3(1) w4+2(1) w4+1(1) w4+0(1) into xstr in byte mode.
&1 = xstr.
END-OF-DEFINITION.
CONSTANTS: gen_flags_encrypted(2) TYPE x VALUE '0001',
gen_flags_unicode(2) TYPE x VALUE '0800'. " general purpose flag bit 11
DATA: gen_flags(2) TYPE x.
* We convert all names from xstring into string
* see: http://www.pkware.com/documents/casestudies/APPNOTE.TXT, APPENDIX D
* zip normaly used IBM Code Page 437 mapped to SAP Printer EPESCP IBM 437
DATA: conv TYPE REF TO cl_abap_conv_in_ce,
conv_cp437 TYPE REF TO cl_abap_conv_in_ce,
conv_utf8 TYPE REF TO cl_abap_conv_in_ce,
cp437 TYPE abap_encoding VALUE '1142', " IBM 437
utf8 TYPE abap_encoding VALUE '4110'. " UTF-8
conv_cp437 = cl_abap_conv_in_ce=>create( encoding = cp437
ignore_cerr = abap_true
replacement = '#' ).
conv_utf8 = cl_abap_conv_in_ce=>create( encoding = utf8
ignore_cerr = abap_true
replacement = '#' ).
* The maximum length of the ZIP file for scanning.
DATA: max_length TYPE i.
max_length = XSTRLEN( zip ) - 4.
* Extract information about all files.
DATA: msdos_date TYPE i, msdos_time TYPE i, file_no TYPE i VALUE 0.
FIELD-SYMBOLS: <file> TYPE t_file,
<ext> TYPE t_ext.
*--------------------------------------------------------------------*
* Insert Begin - Get local file headers by central directory record
* Documentation: http://en.wikipedia.org/wiki/Zip_(file_format)
*--------------------------------------------------------------------*
* ### End of central directory record
* 0 4 End of central directory signature = 0x06054b50
* 4 2 Number of this disk
* 6 2 Disk where central directory starts
* 8 2 Number of central directory records on this disk
*10 ->2 Total number of central directory records
*12 4 Size of central directory (bytes)
*16 ->4 Offset of start of central directory,relative to start of archive
*20 2 Comment length (n)
*22 n Comment
*--------------------------------------------------------------------*
* ### Central directory file header
* 0 4 Central directory file header signature = 0x02014b50
* 4 2 Version made by
* 6 2 Version needed to extract (minimum)
* 8 2 General purpose bit flag
*10 2 Compression method
*12 2 File last modification time
*14 2 File last modification date
*16 4 CRC-32
*20 4 Compressed size
*24 4 Uncompressed size
*28 2 File name length (n)
*30 2 Extra field length (m)
*32 2 File comment length (k)
*34 2 Disk number where file starts
*36 2 Internal file attributes
*38 4 External file attributes
*42 4 Relative offset of local file header.
*46 n File name
*46+n m Extra field
*46+n+m k File comment
*--------------------------------------------------------------------*
CONSTANTS: cv_end_central_directory(4) TYPE x VALUE '504B0506'.
DATA: lt_match_results TYPE match_result_tab,
lv_match_count TYPE i,
ls_match_result LIKE LINE OF lt_match_results,
lv_entries_in_central_dir TYPE i,
lv_offset_central_dir TYPE i,
lv_offset_local_fileheader TYPE i,
lv_file_name_length TYPE i,
lv_extra_field_length TYPE i,
lv_file_comment_length TYPE i,
lv_file_length TYPE i.
* Read "end of central directory record" to find "central directory record"
FIND cv_end_central_directory IN zip IN BYTE MODE
RESULTS lt_match_results MATCH COUNT lv_match_count .
IF lv_match_count IS INITIAL.
RAISE zip_parse_error. "#EC RAISE_OK
ENDIF.
READ TABLE lt_match_results INTO ls_match_result INDEX lv_match_count.
offset = ls_match_result-offset + 10.
read2 lv_entries_in_central_dir.
offset = ls_match_result-offset + 16.
read4 lv_offset_central_dir.
offset = lv_offset_central_dir.
DO lv_entries_in_central_dir TIMES.
offset = lv_offset_central_dir + 24.
read4 lv_file_length. " uncompressed size
IF lv_file_length > 0. " Directoryentries in central file header have no local file header
*--------------------------------------------------------------------*
* Insert End - Get local file headers by central directory record
*--------------------------------------------------------------------*
* WHILE offset < max_length AND zip+offset(4) = '504B0304'. "-cdr
file_no = file_no + 1.
APPEND INITIAL LINE TO files ASSIGNING <file>.
APPEND INITIAL LINE TO exts ASSIGNING <ext>.
*--------------------------------------------------------------------*
* Insert Begin - Get local file headers by central directory record
*--------------------------------------------------------------------*
* Get position of local file header
* CRC32, filesize, compressed size are set in central directory header
* but not necessarily in local file header --> use this here
offset = lv_offset_central_dir + 16.
read4 <ext>-crc32. " crc-32
read4 <ext>-compsize. " compressed size
read4 <file>-size. " uncompressed size
ENDIF.
* Get offset of local file header to continue old coding
offset = lv_offset_central_dir + 42.
read4 lv_offset_local_fileheader.
* and prepare next central directory entry
offset = lv_offset_central_dir + 28.
read2 lv_file_name_length .
read2 lv_extra_field_length .
read2 lv_file_comment_length.
ADD 46 TO lv_offset_central_dir.
ADD lv_file_name_length TO lv_offset_central_dir.
ADD lv_extra_field_length TO lv_offset_central_dir.
ADD lv_file_comment_length TO lv_offset_central_dir.
CHECK lv_file_length > 0. " Directoryentries in central file header have no local file header
offset = lv_offset_local_fileheader.
*--------------------------------------------------------------------*
* Insert End - Get local file headers by central directory record
*--------------------------------------------------------------------*
next 4. " local file header signature
read2 <ext>-min_extract_version. " version needed to extract = 2.0 - File is compressed using Deflate
read2 <ext>-gen_flags. " general purpose bit flag
read2 <ext>-compressed. " compression method: deflated
read2 msdos_time. " last mod file time
read2 msdos_date. " last mod file date
* read4 <ext>-crc32. " crc-32 "-cdr
* read4 <ext>-compsize. " compressed size "-cdr
* read4 <file>-size. " uncompressed size "-cdr
next 12. "+cdr
read2 <ext>-filename_len. " file name length
read2 <ext>-extra_len. " extra field length
gen_flags = <ext>-gen_flags.
gen_flags = gen_flags BIT-AND gen_flags_unicode. " bit 11: Language encoding flag
IF gen_flags <> 0 AND support_unicode_names = abap_true.
conv = conv_utf8. " utf-8 filename extension
ELSE.
conv = conv_cp437. " IBM CP437
ENDIF.
<ext>-filename = zip+offset(<ext>-filename_len).
conv->convert( EXPORTING input = <ext>-filename IMPORTING data = <file>-name ).
next <ext>-filename_len.
<ext>-extra = zip+offset(<ext>-extra_len).
next <ext>-extra_len.
IF <ext>-gen_flags <> 8.
<ext>-content = zip+offset(<ext>-compsize).
next <ext>-compsize.
ELSE.
* Sometimes sequence in file and directory do not match.
* READ TABLE markers INTO marker WITH KEY no = file_no.
* DATA: cached_offset TYPE i. cached_offset = offset. offset = marker-offset + 16.
DATA result_tab TYPE match_result_tab.
FIELD-SYMBOLS <match> LIKE LINE OF result_tab.
FIND ALL OCCURRENCES OF <ext>-filename IN zip RESULTS result_tab IN BYTE MODE.
* --- start of modification:
* The following modification was necessary to handle zip-archives containing files
* where the name of one file is a sub-string of the name of another file
* --- deleted code:
* Loop till the end of the result_tab to get the entry from the Central Directory
* LOOP at result_tab ASSIGNING <match>.
* ENDLOOP .
* DATA: cached_offset TYPE i. cached_offset = offset. offset = <match>-offset - 30.
DATA: cached_offset TYPE i. cached_offset = offset.
DATA: l_filename_length TYPE i.
SORT result_tab BY offset DESCENDING.
LOOP AT result_tab ASSIGNING <match>.
offset = <match>-offset - 18.
read2 l_filename_length.
IF l_filename_length = XSTRLEN( <ext>-filename ).
EXIT.
ENDIF.
ENDLOOP .
offset = <match>-offset - 30.
* --- end of modification
read4 <ext>-crc32.
read4 <ext>-compsize.
read4 <file>-size.
next 18.
offset = cached_offset.
<ext>-content = zip+offset(<ext>-compsize).
next <ext>-compsize.
next 16. " I032850
ENDIF.
<file>-time = msdos=>from_time( msdos_time ).
<file>-date = msdos=>from_date( msdos_date ).
gen_flags = <ext>-gen_flags.
gen_flags = gen_flags BIT-AND gen_flags_encrypted.
IF NOT ( <ext>-min_extract_version <= 20 )
* OR NOT ( <ext>-gen_flags = 0 OR <ext>-gen_flags = 2 OR <ext>-gen_flags = 8 )
OR ( gen_flags = gen_flags_encrypted )
OR NOT ( <ext>-compressed = 0 OR <ext>-compressed = 8 ).
RAISE zip_parse_error. "#EC RAISE_OK
ENDIF.
* ENDWHILE. "-cdr
ENDDO. "+cdr
ENDMETHOD.METHOD SAVE.
* Documentation from: http://www.pkware.com/company/standards/appnote/appnote.txt
DATA: x2(2) TYPE x, x4(4) TYPE x.
DEFINE writeX4. " write xstring
x4 = &2.
CONCATENATE &1 x4 INTO &1 IN BYTE MODE.
END-OF-DEFINITION.
DEFINE write2. " write two bytes from integer
x2 = &2.
CONCATENATE &1 x2+1(1) x2+0(1) INTO &1 IN BYTE MODE.
END-OF-DEFINITION.
DEFINE write4. " write four bytes from integer
x4 = &2.
CONCATENATE &1 x4+3(1) x4+2(1) x4+1(1) x4+0(1) INTO &1 IN BYTE MODE.
END-OF-DEFINITION.
* Process all files. We write in parallel the zip and the central directory to use later
DATA: msdos_date TYPE i, msdos_time TYPE i.
FIELD-SYMBOLS: <file> TYPE t_file,
<ext> TYPE t_ext.
DATA: dir TYPE xstring, start_offset(4) TYPE x.
LOOP AT files ASSIGNING <file>.
READ TABLE exts INDEX sy-tabix ASSIGNING <ext>.
start_offset = XSTRLEN( zip ).
msdos_time = msdos=>to_time( <file>-time ).
msdos_date = msdos=>to_date( <file>-date ).
* zip data stream
writeX4 zip '504B0304'. " local file header signature
write2 zip <ext>-min_extract_version. " version needed to extract = 2.0 - File is compressed using Deflate
write2 zip <ext>-gen_flags. " general purpose bit flag
write2 zip <ext>-compressed. " compression method: deflated
write2 zip msdos_time. " last mod file time
write2 zip msdos_date. " last mod file date
write4 zip <ext>-crc32. " crc-32
write4 zip <ext>-compsize. " compressed size
write4 zip <file>-size. " uncompressed size
write2 zip <ext>-filename_len. " file name length
write2 zip <ext>-extra_len. " extra field length
CONCATENATE zip <ext>-filename <ext>-extra <ext>-content INTO zip IN BYTE MODE.
* central directory stream (which has a lare duplicate sequence of zip header)
DATA: dup_offset TYPE i. dup_offset = start_offset + 4.
writeX4 dir '504B0102'. " central file header signature
write2 dir 19. " version made by (== pkzip 2.04g)
CONCATENATE dir zip+dup_offset(26) INTO dir IN BYTE MODE. " part which matches exactly zip header
write2 dir 0. " file comment length
write2 dir 0. " disk number start
write2 dir 0. " internal file attributes
write4 dir 0. " external file attributes
write4 dir start_offset. " relative offset of local header
CONCATENATE dir <ext>-filename <ext>-extra INTO dir IN BYTE MODE. " file name + extra info
ENDLOOP.
* Write Central Directory
DATA: lines_files TYPE i. lines_files = LINES( files ).
DATA: xstrlen_dir TYPE i. xstrlen_dir = XSTRLEN( dir ).
DATA: offset_dir TYPE i. offset_dir = XSTRLEN( zip ).
CONCATENATE zip dir INTO zip IN BYTE MODE.
writeX4 zip '504B0506'. " End of central directory
write2 zip 0. " number of this disk
write2 zip 0. " number of the disk with the start of the central directory
write2 zip lines_files. " total number of entries in the central directory on this disk
write2 zip lines_files. " total number of entries in the central directory
write4 zip xstrlen_dir. " size of the central directory
write4 zip offset_dir. " offset of start of central directory
write2 zip 0. " ZIP file comment length
ENDMETHOD.METHOD SPLICE.
* Documentation from: http://www.pkware.com/company/standards/appnote/appnote.txt
* Local variables.
FIELD-SYMBOLS:
<entry> LIKE LINE OF entries.
DATA: filename_len TYPE i,
extra_len TYPE i,
filename TYPE xstring.
* Start to decode new ZIP file
CLEAR: entries.
REFRESH: entries.
* Global offset for moving through file
DATA: offset TYPE i.
DATA: w2(2) TYPE x, w4(4) TYPE x, xstr TYPE xstring.
DEFINE read2. "#EC NEEDED read two bytes as integer and move offset
w2 = zip+offset(2).
offset = offset + 2.
CONCATENATE w2+1(1) w2+0(1) INTO xstr IN BYTE MODE.
&1 = xstr.
END-OF-DEFINITION.
DEFINE read4. "#EC NEEDED read four bytes as integer and move offset
w4 = zip+offset(4).
offset = offset + 4.
CONCATENATE w4+3(1) w4+2(1) w4+1(1) w4+0(1) INTO xstr IN BYTE MODE.
&1 = xstr.
END-OF-DEFINITION.
* We convert all names from xstring into string
DATA: conv TYPE REF TO cl_abap_conv_in_ce.
conv = cl_abap_conv_in_ce=>create( ).
* Extract information about all files.
WHILE zip+offset(4) = '504B0304'. " local file header signature
APPEND INITIAL LINE TO entries ASSIGNING <entry>.
offset = offset + 8. " next 4=(header). read2 <ext>-min_extract_version. read2 <ext>-gen_flags.
read2 <entry>-compressed. " compression method: deflated
offset = offset + 8. " read2 msdos_time. read2 msdos_date. read4 <ext>-crc32.
read4 <entry>-length. " compressed size
offset = offset + 4. " uncompressed size
read2 filename_len. " file name length
read2 extra_len. " extra field length
filename = zip+offset(filename_len).
conv->convert( EXPORTING input = filename IMPORTING data = <entry>-name ).
<entry>-offset = offset + filename_len + extra_len.
offset = <entry>-offset + <entry>-length.
ENDWHILE.
DELETE entries WHERE length = 0.
ENDMETHOD.