summaryrefslogtreecommitdiff
path: root/macros/latex/contrib/csvsimple/csvsimple-l3.sty
diff options
context:
space:
mode:
Diffstat (limited to 'macros/latex/contrib/csvsimple/csvsimple-l3.sty')
-rw-r--r--macros/latex/contrib/csvsimple/csvsimple-l3.sty1260
1 files changed, 1260 insertions, 0 deletions
diff --git a/macros/latex/contrib/csvsimple/csvsimple-l3.sty b/macros/latex/contrib/csvsimple/csvsimple-l3.sty
new file mode 100644
index 0000000000..da7e62f47d
--- /dev/null
+++ b/macros/latex/contrib/csvsimple/csvsimple-l3.sty
@@ -0,0 +1,1260 @@
+%% The LaTeX package csvsimple - version 2.0.0 (2021/06/29)
+%% csvsimple-l3.sty: Simple LaTeX CSV file processing (LaTeX3)
+%%
+%% -------------------------------------------------------------------------------------------
+%% Copyright (c) 2008-2021 by Prof. Dr. Dr. Thomas F. Sturm <thomas dot sturm at unibw dot de>
+%% -------------------------------------------------------------------------------------------
+%%
+%% This work may be distributed and/or modified under the
+%% conditions of the LaTeX Project Public License, either version 1.3
+%% of this license or (at your option) any later version.
+%% The latest version of this license is in
+%% http://www.latex-project.org/lppl.txt
+%% and version 1.3 or later is part of all distributions of LaTeX
+%% version 2005/12/01 or later.
+%%
+%% This work has the LPPL maintenance status `author-maintained'.
+%%
+%% This work consists of all files listed in README.md
+%%
+\ProvidesExplPackage{csvsimple-l3}{2021/06/29}{2.0.0}
+ {LaTeX3 CSV file processing}
+
+
+
+%---- check package
+
+\cs_if_exist:NT \c__csvsim_package_expl_bool
+ {
+ \msg_new:nnn { csvsimple }{ l3 / package-loaded }
+ { Package~'csvsimple-legacy'~seems~already~be~loaded!~
+ 'csvsimple-l3'~cannot~be~loaded~simultaneously.~
+ Therefore,~loading~of~'csvsimple-l3'~stops~now.}
+ \msg_warning:nn { csvsimple }{ l3 / package-loaded }
+ \tex_endinput:D
+ }
+\bool_const:Nn \c__csvsim_package_expl_bool { 1 }
+
+
+
+%---- declarations and expl3 variants
+
+\bool_new:N \g__csvsim_check_column_count_bool
+\bool_new:N \g__csvsim_firstline_bool
+\bool_new:N \g__csvsim_head_bool
+\bool_new:N \g__csvsim_head_to_colnames_bool
+\bool_new:N \g__csvsim_line_accepted_bool
+\bool_new:N \l__csvsim_respect_and_bool
+\bool_new:N \l__csvsim_respect_backslash_bool
+\bool_new:N \l__csvsim_respect_circumflex_bool
+\bool_new:N \l__csvsim_respect_dollar_bool
+\bool_new:N \l__csvsim_respect_leftbrace_bool
+\bool_new:N \l__csvsim_respect_percent_bool
+\bool_new:N \l__csvsim_respect_rightbrace_bool
+\bool_new:N \l__csvsim_respect_sharp_bool
+\bool_new:N \l__csvsim_respect_tab_bool
+\bool_new:N \l__csvsim_respect_tilde_bool
+\bool_new:N \l__csvsim_respect_underscore_bool
+
+\int_new:N \g__csvsim_col_int
+\int_new:N \g__csvsim_colmax_int
+\int_new:N \g_csvsim_inputline_int
+\int_new:N \g_csvsim_row_int
+\int_new:N \g_csvsim_columncount_int
+
+\seq_new:N \g__csvsim_colname_seq
+\seq_new:N \g__csvsim_line_seq
+\seq_new:N \g__csvsim_range_seq
+
+\str_new:N \g__csvsim_curfilename_str
+\str_new:N \g__csvsim_filename_str
+\str_new:N \l__csvsim_csvsorter_command_str
+\str_new:N \l__csvsim_csvsorter_configpath_str
+\str_new:N \l__csvsim_csvsorter_log_str
+\str_new:N \l__csvsim_csvsorter_token_str
+\str_new:N \l__csvsim_ppfilename_str
+\str_new:N \l__csvsim_temp_filename_str
+
+\tl_const:Nn \c__csvsim_par_tl { \par }
+
+\tl_new:N \g__csvsim_after_table_tl
+\tl_new:N \g__csvsim_before_table_tl
+\tl_new:N \g__csvsim_body_tl
+\tl_new:N \g__csvsim_catcode_tl
+
+\tl_new:N \g__csvsim_columnnames_tl
+\tl_new:N \g__csvsim_filter_tl
+\tl_new:N \g__csvsim_headname_prefix_tl
+\tl_new:N \g__csvsim_hook_after_first_line_tl
+\tl_new:N \g__csvsim_hook_after_head_tl
+\tl_new:N \g__csvsim_hook_after_line_tl
+\tl_new:N \g__csvsim_hook_after_reading_tl
+\tl_new:N \g__csvsim_hook_before_filter_tl
+\tl_new:N \g__csvsim_hook_before_first_line_tl
+\tl_new:N \g__csvsim_hook_before_line_tl
+\tl_new:N \g__csvsim_hook_before_reading_tl
+\tl_new:N \g__csvsim_hook_columncounterror_tl
+\tl_new:N \g__csvsim_hook_late_after_first_line_tl
+\tl_new:N \g__csvsim_hook_late_after_head_tl
+\tl_new:N \g__csvsim_hook_late_after_last_line_tl
+\tl_new:N \g__csvsim_hook_late_after_line_tl
+\tl_new:N \g__csvsim_hook_table_begin_tl
+\tl_new:N \g__csvsim_hook_table_end_tl
+\tl_new:N \g__csvsim_preprocessor_tl
+\tl_new:N \g__csvsim_separator_tl
+\tl_new:N \g__csvsim_table_foot_tl
+\tl_new:N \g__csvsim_table_head_tl
+
+\group_begin:
+ \char_set_catcode_other:n { 9 }
+ \str_const:Nn \c__csvsim_tab_str { ^^I }
+\group_end:
+
+\regex_const:Nn \c__csvsim_integer_regex {\A\d+\Z}
+
+\cs_generate_variant:Nn \seq_gset_split:Nnn { NVV }
+
+
+%---- messages
+
+\msg_new:nnnn { csvsimple }{ column-name }
+ { Unknown~column~key~'#1'. }
+ { The~key~'#1'~you~used~in~'column~names'~is~unknown.\\
+ Therefore,~the~macro~#2 is~not~defined.
+ }
+
+\msg_new:nnn { csvsimple }{ empty-head }
+ { File~'#1'~starts~with~an~empty~line~(empty~head)!.}
+
+\msg_new:nnn { csvsimple }{ file-error }
+ { File~'#1'~not~existent,~not~readable,~or~empty!}
+
+\msg_new:nnn { csvsimple }{ column-wrong-count }
+ { #1~instead~of~#2~columns~for~input~line~#3~of~file~'#4'}
+
+\msg_new:nnn { csvsimple }{ sort-info }
+ { Sort~'#1'~by~'#2' }
+
+\msg_new:nnn { csvsimple }{ sort-shell-escape }
+ { You~need~to~use~'-shell-escape'~to~run~CSV-Sorter }
+
+\msg_new:nnnn { csvsimple }{ sort-error }
+ { Call~of~CSV-Sorter~failed! }
+ { See~log~file~'\l__csvsim_csvsorter_log_str'. }
+
+
+
+%---- core loop processing
+
+\cs_new_protected_nopar:Npn \__csvsim_read_line:
+ {
+ \group_begin:
+ \g__csvsim_catcode_tl
+ \ior_get:NNTF \g__csvsim_ior \l_tmpa_tl
+ {
+ \tl_gset_eq:NN \csvline \l_tmpa_tl
+ \int_gincr:N \g_csvsim_inputline_int
+ }
+ {
+ \msg_error:nnx { csvsimple }{ file-error }{ \g__csvsim_curfilename_str }
+ }
+ \group_end:
+ }
+
+
+\cs_new_protected_nopar:Npn \__csvsim_scan_line:
+ {
+ \int_gzero:N \g__csvsim_col_int
+ \seq_gset_split:NVV \g__csvsim_line_seq \g__csvsim_separator_tl \csvline
+ \seq_map_inline:Nn \g__csvsim_line_seq
+ {
+ \int_gincr:N \g__csvsim_col_int
+ \tl_gset:cn {csvcol \int_to_roman:n \g__csvsim_col_int}{##1}
+ }
+ \int_compare:nNnT \g__csvsim_colmax_int < \g__csvsim_col_int
+ {
+ \int_gset_eq:NN \g__csvsim_colmax_int \g__csvsim_col_int
+ }
+ \int_compare:nNnT \g_csvsim_columncount_int < \c_one_int
+ {
+ \int_gset_eq:NN \g_csvsim_columncount_int \g__csvsim_col_int
+ }
+ }
+
+
+\cs_new_protected_nopar:Npn \__csvsim_process_head_name:n #1
+ {
+ \tl_set:No \l_tmpa_tl {\cs:w csvcol\int_to_roman:n{#1} \cs_end:}
+ \exp_args:NnV \cs_set_nopar:cpn {__csvsim__/\l_tmpa_tl} \l_tmpa_tl
+
+ \bool_if:NT \g__csvsim_head_to_colnames_bool
+ {
+ \tl_set:No \l_tmpb_tl {\cs:w \g__csvsim_headname_prefix_tl \l_tmpa_tl \cs_end:}
+ \tl_put_right:NV \l_tmpb_tl \l_tmpa_tl
+ \exp_args:NNV \seq_gput_right:Nn \g__csvsim_colname_seq \l_tmpb_tl
+ }
+ }
+
+
+\cs_new_protected_nopar:Npn \__csvsim_read_head:
+ {
+ \__csvsim_read_line:
+ \tl_if_eq:NNTF \csvline \c__csvsim_par_tl
+ {
+ \msg_error:nnx { csvsimple }{ empty-head }{ \g__csvsim_filename_str }
+ }
+ {
+ \int_zero:N \g_csvsim_columncount_int
+ \__csvsim_scan_line:
+ \int_step_function:nN \g_csvsim_columncount_int \__csvsim_process_head_name:n
+ }
+ }
+
+
+\cs_new_protected_nopar:Npn \__csvsim_process_colname:nn #1#2
+ {
+ \cs_if_exist:cTF {__csvsim__/#1}
+ {
+ \tl_set:Nv \l_tmpa_tl {__csvsim__/#1}
+ \tl_put_left:Nn \l_tmpa_tl {#2}
+ \exp_args:NNV \seq_gput_right:Nn \g__csvsim_colname_seq \l_tmpa_tl
+ }
+ {
+ \regex_match:NnTF \c__csvsim_integer_regex {#1}
+ {
+ \tl_set:No \l_tmpa_tl {\cs:w csvcol\int_to_roman:n{#1} \cs_end:}
+ \tl_put_left:Nn \l_tmpa_tl {#2}
+ \exp_args:NNV \seq_gput_right:Nn \g__csvsim_colname_seq \l_tmpa_tl
+ }
+ {
+ \str_set:Nn \l_tmpb_str {#2}
+ \msg_error:nnxx { csvsimple }{ column-name }{ #1 }{ \l_tmpb_str }
+ }
+ }
+ }
+
+
+\cs_new_protected_nopar:Npn \__csvsim_set_colnames:
+ {
+ \seq_map_inline:Nn \g__csvsim_colname_seq
+ {
+ \tl_gset_eq:NN ##1
+ }
+ }
+
+
+\cs_new_protected_nopar:Npn \__csvsim_loop:
+ {
+ % preprocess
+ \tl_if_empty:NTF \l__csvsim_preprocessor_tl
+ {
+ \str_gset_eq:NN \g__csvsim_curfilename_str \g__csvsim_filename_str
+ }
+ {
+ \l__csvsim_preprocessor_tl \g__csvsim_filename_str \l__csvsim_ppfilename_str
+ \str_gset_eq:NN \g__csvsim_curfilename_str \l__csvsim_ppfilename_str
+ }
+
+ % initialize
+ \cs_if_exist:NF \g__csvsim_ior
+ {
+ \ior_new:N \g__csvsim_ior
+ }
+ \__csvsim_setup_catcode_list:
+ \seq_gclear:N \g__csvsim_colname_seq
+ \int_gzero:N \g_csvsim_inputline_int
+ \int_gzero:N \g_csvsim_row_int
+ \int_gset_eq:NN \g__csvsim_colmax_int \c_one_int
+
+ % open file
+ \g__csvsim_hook_before_reading_tl
+ \g__csvsim_hook_table_begin_tl
+ \ior_open:Nn \g__csvsim_ior { \g__csvsim_curfilename_str }
+
+ % read head line
+ \bool_if:NT \g__csvsim_head_bool
+ {
+ \__csvsim_read_head:
+ }
+ \exp_args:NNNV \keyval_parse:NNn \use_none:n
+ \__csvsim_process_colname:nn \g__csvsim_columnnames_tl
+ \bool_if:NT \g__csvsim_head_bool
+ {
+ \g__csvsim_hook_after_head_tl
+ }
+
+ % read body lines
+ \bool_gset_true:N \g__csvsim_line_firstline_bool
+ \bool_until_do:nn {\ior_if_eof_p:N \g__csvsim_ior}
+ {
+ \__csvsim_read_line:
+ \tl_if_eq:NNF \csvline \c__csvsim_par_tl
+ {
+ \bool_gset_true:N \g__csvsim_line_accepted_bool
+ \__csvsim_scan_line:
+ \__csvsim_set_colnames:
+ \bool_if:NT \g__csvsim_check_column_count_bool
+ {
+ \int_compare:nNnF \g__csvsim_col_int = \g_csvsim_columncount_int
+ {
+ \bool_gset_false:N \g__csvsim_line_accepted_bool
+ \g__csvsim_hook_columncounterror_tl
+ }
+ }
+ \bool_if:NT \g__csvsim_line_accepted_bool
+ {
+ \g__csvsim_hook_before_filter_tl
+ \g__csvsim_filter_tl
+ \bool_if:NT \g__csvsim_line_accepted_bool
+ {
+ \int_gincr:N \g_csvsim_row_int
+ \__csvsim_check_range:
+ \bool_if:NT \g__csvsim_line_accepted_bool
+ {
+ \bool_if:NTF \g__csvsim_line_firstline_bool
+ {
+ \bool_if:NT \g__csvsim_head_bool
+ {
+ \g__csvsim_hook_late_after_head_tl
+ }
+ \g__csvsim_hook_before_first_line_tl
+ \g__csvsim_body_tl
+ \g__csvsim_hook_after_first_line_tl
+ \bool_gset_false:N \g__csvsim_line_firstline_bool
+ }
+ {
+ \g__csvsim_hook_late_after_line_tl
+ \g__csvsim_hook_before_line_tl
+ \g__csvsim_body_tl
+ \g__csvsim_hook_after_line_tl
+ }
+ }
+ }
+ }
+ }
+ }
+
+ % close file
+ \ior_close:N \g__csvsim_ior
+
+ % clear macros
+ \int_step_inline:nn \g__csvsim_colmax_int
+ {
+ \tl_set:No \l_tmpa_tl {\cs:w csvcol\int_to_roman:n{##1} \cs_end:}
+ \use:x
+ {
+ \exp_not:N\tl_gclear:N \exp_not:V\l_tmpa_tl
+ }
+ }
+ \__csvsim_set_colnames:
+ \seq_gclear:N \g__csvsim_colname_seq
+ \bool_if:NF \g__csvsim_line_firstline_bool
+ {
+ \g__csvsim_hook_late_after_last_line_tl
+ }
+ \g__csvsim_hook_table_end_tl
+ \g__csvsim_hook_after_reading_tl
+ }
+
+
+\NewDocumentCommand \csvloop { +m }
+ {
+ \keys_set:nn { csvsim } { default, every~csv, #1}
+ \__csvsim_loop:
+ }
+
+
+\NewDocumentCommand \csvreader { +O{} m m +m }
+ {
+ \keys_set:nn { csvsim } { default, every~csv, #1, file={#2}, column~names={#3} }
+ \tl_gset:Nn \g__csvsim_body_tl {#4}
+ \__csvsim_loop:
+ }
+
+
+
+%---- auxiliary user macros
+
+\NewDocumentCommand \csvlinetotablerow { }
+ {
+ \tl_clear:N \l_tmpa_tl
+ \bool_set_false:N \l_tmpa_bool
+ \seq_map_inline:Nn \g__csvsim_line_seq
+ {
+ \bool_if:NTF \l_tmpa_bool
+ {
+ \tl_put_right:Nn \l_tmpa_tl { & ##1 }
+ }
+ {
+ \tl_put_right:Nn \l_tmpa_tl { ##1 }
+ \bool_set_true:N \l_tmpa_bool
+ }
+ }
+ \l_tmpa_tl
+ }
+
+
+\NewExpandableDocumentCommand \thecsvrow { }
+ {
+ \int_use:N \g_csvsim_row_int
+ }
+
+
+\NewExpandableDocumentCommand \thecsvcolumncount { }
+ {
+ \int_use:N \g_csvsim_columncount_int
+ }
+
+
+\NewExpandableDocumentCommand \thecsvinputline { }
+ {
+ \int_use:N \g_csvsim_inputline_int
+ }
+
+
+\NewExpandableDocumentCommand \ifcsvfirstrow { }
+ {
+ \bool_if:NTF \g__csvsim_firstline_bool
+ }
+
+% deprecated
+\NewExpandableDocumentCommand \csviffirstrow { }
+ {
+ \bool_if:NTF \g__csvsim_firstline_bool
+ }
+
+
+\NewExpandableDocumentCommand \ifcsvoddrow { }
+ {
+ \int_if_odd:nTF {\g_csvsim_row_int}
+ }
+
+% deprecated
+\NewExpandableDocumentCommand \csvifoddrow { }
+ {
+ \int_if_odd:nTF {\g_csvsim_row_int}
+ }
+
+
+\NewExpandableDocumentCommand \ifcsvstrcmp { m m }
+ {
+ \str_compare:eNeTF {#1} = {#2}
+ }
+
+
+\NewExpandableDocumentCommand \ifcsvnotstrcmp { m m +m +m }
+ {
+ \ifcsvstrcmp{#1}{#2}{#4}{#3}
+ }
+
+
+\NewDocumentCommand \ifcsvstrequal { m m }
+ {
+ \tl_set:Nx \l_tmpa_tl {#1}
+ \tl_set:Nx \l_tmpb_tl {#2}
+ \tl_if_eq:NNTF \l_tmpa_tl \l_tmpb_tl
+ }
+
+
+\NewDocumentCommand \ifcsvprostrequal { m m }
+ {
+ \protected@edef \l_tmpa_tl {#1}
+ \protected@edef \l_tmpb_tl {#2}
+ \tl_if_eq:NNTF \l_tmpa_tl \l_tmpb_tl
+ }
+
+
+\NewExpandableDocumentCommand \ifcsvfpcmp { m }
+ {
+ \fp_compare:nTF {#1}
+ }
+
+
+\NewExpandableDocumentCommand \ifcsvintcmp { m }
+ {
+ \int_compare:nTF {#1}
+ }
+
+
+%---- filename functions
+
+\cs_new_protected_nopar:Npn \__csvsim_set_temp_filename:nnn #1#2#3
+ {
+ \str_set:Nn \l__csvsim_temp_filename_str {#2#3}
+ \str_if_empty:NTF \l__csvsim_temp_filename_str
+ {
+ \str_set:Nn \l__csvsim_temp_filename_str {#1}
+ }
+ {
+ \str_set:Nn \l_tmpa_str {#1}
+ \str_if_empty:NF \l_tmpa_str
+ {
+ \str_compare:eNeF { \str_item:Nn \l_tmpa_str {-1} } = { / }
+ {
+ \str_put_right:Nn \l_tmpa_str {/}
+ }
+ \str_concat:NNN \l__csvsim_temp_filename_str
+ \l_tmpa_str \l__csvsim_temp_filename_str
+ }
+ }
+ }
+
+
+\cs_new_protected_nopar:Npn \__csvsim_set_temp_filename:n #1
+ {
+ \file_parse_full_name_apply:nN { #1 } \__csvsim_set_temp_filename:nnn
+ }
+
+
+\cs_new_protected_nopar:Npn \__csvsim_set_filename:Nn #1#2
+ {
+ \__csvsim_set_temp_filename:n { #2 }
+ \str_set_eq:NN #1 \l__csvsim_temp_filename_str
+ }
+
+
+\cs_new_protected_nopar:Npn \__csvsim_gset_filename:Nn #1#2
+ {
+ \__csvsim_set_temp_filename:n { #2 }
+ \str_gset_eq:NN #1 \l__csvsim_temp_filename_str
+ }
+
+
+
+%---- keys
+
+\NewDocumentCommand \csvset { +m }
+ {
+ \keys_set:nn { csvsim } { #1 }
+ }
+
+
+\NewDocumentCommand \csvstyle { m +m }
+ {
+ \keys_define:nn { csvsim }
+ {
+ #1 .meta:n = { #2 }
+ }
+ }
+
+
+\NewDocumentCommand \csvnames { m m }
+ {
+ \keys_define:nn { csvsim }
+ {
+ #1 .meta:n = { column~names={#2} }
+ }
+ }
+
+
+\keys_define:nn { csvsim }
+ {
+ file .code:n = \__csvsim_gset_filename:Nn \g__csvsim_filename_str {#1},
+ column~names~reset .code:n = \tl_gclear:N \g__csvsim_columnnames_tl,
+ column~names .code:n =
+ {
+ \tl_if_empty:NTF \g__csvsim_columnnames_tl
+ {
+ \tl_gset:Nn \g__csvsim_columnnames_tl {#1}
+ }
+ {
+ \tl_gput_right:Nn \g__csvsim_columnnames_tl {,#1}
+ }
+ },
+ command .tl_gset:N = \g__csvsim_body_tl,
+ check~column~count .bool_gset:N = \g__csvsim_check_column_count_bool,
+ on~column~count~error .tl_gset:N = \g__csvsim_hook_columncounterror_tl,
+ head .bool_gset:N = \g__csvsim_head_bool,
+ head~to~column~names~prefix .tl_gset:N = \g__csvsim_headname_prefix_tl,
+ head~to~column~names .bool_gset:N = \g__csvsim_head_to_colnames_bool,
+ column~count .int_gset:N = \g_csvsim_columncount_int,
+ separator .choice:,
+ separator/comma .code:n =
+ {
+ \tl_gset:Nn \g__csvsim_separator_tl {,}
+ },
+ separator/semicolon .code:n =
+ {
+ \tl_gset:Nn \g__csvsim_separator_tl {;}
+ },
+ separator/pipe .code:n =
+ {
+ \tl_gset:Nn \g__csvsim_separator_tl {|}
+ },
+ separator/tab .code:n =
+ {
+ \tl_gset:NV \g__csvsim_separator_tl \c__csvsim_tab_str
+ \csvset{respect~tab}
+ },
+ every~csv .meta:n = {},
+ no~head .meta:n = { head=false },
+ no~check~column~count .meta:n = { check~column~count=false },
+ warn~on~column~count~error .meta:n = { on~column~count~error=
+ {
+ \msg_warning:nnxxxx { csvsimple }{ column-wrong-count }
+ { \int_use:N\g__csvsim_col_int }
+ { \int_use:N\g_csvsim_columncount_int }
+ { \int_use:N\g_csvsim_inputline_int }
+ { \g__csvsim_filename_str }
+ }},
+ }
+
+
+%---- hooks
+
+\keys_define:nn { csvsim }
+ {
+ before~reading .tl_gset:N = \g__csvsim_hook_before_reading_tl,
+ after~head .tl_gset:N = \g__csvsim_hook_after_head_tl,
+ before~filter .tl_gset:N = \g__csvsim_hook_before_filter_tl,
+ late~after~head .tl_gset:N = \g__csvsim_hook_late_after_head_tl,
+ late~after~first~line .tl_gset:N = \g__csvsim_hook_late_after_first_line_tl,
+ late~after~last~line .tl_gset:N = \g__csvsim_hook_late_after_last_line_tl,
+ before~first~line .tl_gset:N = \g__csvsim_hook_before_first_line_tl,
+ after~first~line .tl_gset:N = \g__csvsim_hook_after_first_line_tl,
+ after~reading .tl_gset:N = \g__csvsim_hook_after_reading_tl,
+ late~after~line .code:n =
+ {
+ \tl_gset:Nn \g__csvsim_hook_late_after_line_tl {#1}
+ \tl_gset_eq:NN \g__csvsim_hook_late_after_first_line_tl \g__csvsim_hook_late_after_line_tl
+ \tl_gset_eq:NN \g__csvsim_hook_late_after_last_line_tl \g__csvsim_hook_late_after_line_tl
+ },
+ before~line .code:n =
+ {
+ \tl_gset:Nn \g__csvsim_hook_before_line_tl {#1}
+ \tl_gset_eq:NN \g__csvsim_hook_before_first_line_tl \g__csvsim_hook_before_line_tl
+ },
+ after~line .code:n =
+ {
+ \tl_gset:Nn \g__csvsim_hook_after_line_tl {#1}
+ \tl_gset_eq:NN \g__csvsim_hook_after_first_line_tl \g__csvsim_hook_after_line_tl
+ },
+ }
+
+
+%---- filter
+
+\cs_new_protected_nopar:Npn \__csvsim_set_filter:n #1
+ {
+ \tl_gset:Nn \g__csvsim_filter_tl
+ {
+ #1
+ }
+ }
+
+
+\NewDocumentCommand \csvfilteraccept { }
+ {
+ \__csvsim_set_filter:n
+ {
+ \bool_gset_true:N \g__csvsim_line_accepted_bool
+ }
+ }
+
+
+\NewDocumentCommand \csvfilterreject { }
+ {
+ \__csvsim_set_filter:n
+ {
+ \bool_gset_false:N \g__csvsim_line_accepted_bool
+ }
+ }
+
+
+\keys_define:nn { csvsim }
+ {
+ no~filter .code:n =
+ {
+ \csvfilteraccept
+ },
+ filter~reject~all .code:n =
+ {
+ \csvfilterreject
+ },
+ filter~accept~all .code:n =
+ {
+ \csvfilteraccept
+ },
+ full~filter. tl_gset:N = \g__csvsim_hook_before_filter_tl,
+ filter~test .code:n =
+ {
+ \__csvsim_set_filter:n
+ {
+ #1
+ { \bool_gset_true:N \g__csvsim_line_accepted_bool }
+ { \bool_gset_false:N \g__csvsim_line_accepted_bool }
+ }
+ },
+ filter~bool .code:n =
+ {
+ \__csvsim_set_filter:n
+ {
+ \bool_gset:Nn \g__csvsim_line_accepted_bool { #1 }
+ }
+ },
+ filter~fp .code:n =
+ {
+ \__csvsim_set_filter:n
+ {
+ \bool_gset:Nn \g__csvsim_line_accepted_bool { \fp_compare_p:n{#1} }
+ }
+ },
+ filter~strcmp .meta:n = { filter~test=\ifcsvstrcmp #1 },
+ filter~not~strcmp .meta:n = { filter~test=\ifcsvnotstrcmp #1 },
+ }
+
+
+\NewDocumentCommand \csvfilterbool { m m }
+ {
+ \keys_define:nn { csvsim }
+ {
+ #1 .meta:n = { filter~bool={#2} }
+ }
+ }
+
+
+% ifthen
+\keys_define:nn { csvsim }
+ {
+ filter~ifthen .code:n =
+ {
+ \__csvsim_set_filter:n
+ {
+ \ifthenelse{#1}
+ { \bool_gset_true:N \g__csvsim_line_accepted_bool }
+ { \bool_gset_false:N \g__csvsim_line_accepted_bool }
+ }
+ },
+ filter~equal .meta:n = { filter~ifthen=\equal #1 },
+ filter~not~equal .meta:n = { filter~ifthen=\not\equal #1 },
+ }
+
+
+% etoolbox
+\keys_define:nn { csvsim }
+ {
+ filter~expr .code:n =
+ {
+ \__csvsim_set_filter:n
+ {
+ \ifboolexpr{#1}
+ { \bool_gset_true:N \g__csvsim_line_accepted_bool }
+ { \bool_gset_false:N \g__csvsim_line_accepted_bool }
+ }
+ },
+ }
+
+
+
+%---- range
+
+
+\cs_new_protected_nopar:Npn \__csvsim_add_range:n #1
+ {
+ \tl_if_in:nnTF {#1}{-}
+ {
+ \seq_set_split:Nnn \l_tmpa_seq {-} {#1}
+ \seq_pop_left:NN \l_tmpa_seq \l_tmpa_tl
+ \seq_pop_left:NN \l_tmpa_seq \l_tmpb_tl
+ \tl_if_empty:NTF \l_tmpa_tl
+ {
+ \int_set_eq:NN \l_tmpa_int \c_one_int
+ }
+ {
+ \int_set:Nn \l_tmpa_int { \l_tmpa_tl }
+ }
+ \tl_if_empty:NTF \l_tmpb_tl
+ {
+ \int_set_eq:NN \l_tmpb_int \c_max_int
+ }
+ {
+ \int_set:Nn \l_tmpb_int { \l_tmpb_tl }
+ }
+ }
+ {
+ \tl_if_in:nnTF {#1}{+}
+ {
+ \seq_set_split:Nnn \l_tmpa_seq {+} {#1}
+ \seq_pop_left:NN \l_tmpa_seq \l_tmpa_tl
+ \seq_pop_left:NN \l_tmpa_seq \l_tmpb_tl
+ \tl_if_empty:NTF \l_tmpa_tl
+ {
+ \int_set:Nn \l_tmpa_int { 1 }
+ }
+ {
+ \int_set:Nn \l_tmpa_int { \l_tmpa_tl }
+ }
+ \tl_if_empty:NTF \l_tmpb_tl
+ {
+ \int_set_eq:NN \l_tmpb_int \l_tmpa_int
+ }
+ {
+ \int_set:Nn \l_tmpb_int { \l_tmpa_int + \l_tmpb_tl - 1 }
+ }
+ }
+ {
+ \int_set:Nn \l_tmpa_int {#1}
+ \int_set_eq:NN \l_tmpb_int \l_tmpa_int
+ }
+ }
+ \seq_gput_right:Nx \g__csvsim_range_seq {{\int_use:N \l_tmpa_int}{\int_use:N \l_tmpb_int}}
+ }
+
+
+\cs_new_protected_nopar:Npn \__csvsim_set_range:n #1
+ {
+ \seq_gclear:N \g__csvsim_range_seq
+ \keyval_parse:NNn
+ \__csvsim_add_range:n
+ \use_none:nn
+ { #1 }
+ }
+
+
+\keys_define:nn { csvsim }
+ {
+ range .code:n =
+ {
+ \__csvsim_set_range:n {#1}
+ },
+ }
+
+
+\prg_new_conditional:Npnn \__csvsim_if_in_range:nn #1#2 { p , T }
+ {
+ \if_int_compare:w #1 > \g_csvsim_row_int
+ \prg_return_false:
+ \else:
+ \if_int_compare:w #2 < \g_csvsim_row_int
+ \prg_return_false:
+ \else:
+ \prg_return_true:
+ \fi:
+ \fi:
+ }
+
+
+\cs_new_protected_nopar:Npn \__csvsim_check_range:
+ {
+ \seq_if_empty:NF \g__csvsim_range_seq
+ {
+ \bool_gset_false:N \g__csvsim_line_accepted_bool
+ \seq_map_inline:Nn \g__csvsim_range_seq
+ {
+ \__csvsim_if_in_range:nnT ##1
+ {
+ \bool_gset_true:N \g__csvsim_line_accepted_bool
+ \seq_map_break:
+ }
+ }
+ }
+ }
+
+
+
+%---- catcodes
+
+\cs_new_protected_nopar:Npn \__csvsim_setup_catcode_list:
+ {
+ \tl_gclear:N \g__csvsim_catcode_tl
+ \bool_if:NT \l__csvsim_respect_tab_bool
+ {
+ \tl_gput_right:Nn \g__csvsim_catcode_tl { \char_set_catcode_other:n { 9 } }
+ }
+ \bool_if:NT \l__csvsim_respect_percent_bool
+ {
+ \tl_gput_right:Nn \g__csvsim_catcode_tl { \char_set_catcode_other:n { 37 } }
+ }
+ \bool_if:NT \l__csvsim_respect_sharp_bool
+ {
+ \tl_gput_right:Nn \g__csvsim_catcode_tl { \char_set_catcode_other:n { 35 } }
+ }
+ \bool_if:NT \l__csvsim_respect_dollar_bool
+ {
+ \tl_gput_right:Nn \g__csvsim_catcode_tl { \char_set_catcode_other:n { 36 } }
+ }
+ \bool_if:NT \l__csvsim_respect_and_bool
+ {
+ \tl_gput_right:Nn \g__csvsim_catcode_tl { \char_set_catcode_other:n { 38 } }
+ }
+ \bool_if:NT \l__csvsim_respect_backslash_bool
+ {
+ \tl_gput_right:Nn \g__csvsim_catcode_tl { \char_set_catcode_other:n { 92 } }
+ }
+ \bool_if:NT \l__csvsim_respect_underscore_bool
+ {
+ \tl_gput_right:Nn \g__csvsim_catcode_tl { \char_set_catcode_other:n { 95 } }
+ }
+ \bool_if:NT \l__csvsim_respect_tilde_bool
+ {
+ \tl_gput_right:Nn \g__csvsim_catcode_tl { \char_set_catcode_other:n { 126 } }
+ }
+ \bool_if:NT \l__csvsim_respect_circumflex_bool
+ {
+ \tl_gput_right:Nn \g__csvsim_catcode_tl { \char_set_catcode_other:n { 94 } }
+ }
+ \bool_if:NT \l__csvsim_respect_leftbrace_bool
+ {
+ \tl_gput_right:Nn \g__csvsim_catcode_tl { \char_set_catcode_other:n { 123 } }
+ }
+ \bool_if:NT \l__csvsim_respect_rightbrace_bool
+ {
+ \tl_gput_right:Nn \g__csvsim_catcode_tl { \char_set_catcode_other:n { 125 } }
+ }
+ }
+
+
+\keys_define:nn { csvsim }
+ {
+ respect~tab .bool_set:N = \l__csvsim_respect_tab_bool,
+ respect~percent .bool_set:N = \l__csvsim_respect_percent_bool,
+ respect~sharp .bool_set:N = \l__csvsim_respect_sharp_bool,
+ respect~dollar .bool_set:N = \l__csvsim_respect_dollar_bool,
+ respect~and .bool_set:N = \l__csvsim_respect_and_bool,
+ respect~backslash .bool_set:N = \l__csvsim_respect_backslash_bool,
+ respect~underscore .bool_set:N = \l__csvsim_respect_underscore_bool,
+ respect~tilde .bool_set:N = \l__csvsim_respect_tilde_bool,
+ respect~circumflex .bool_set:N = \l__csvsim_respect_circumflex_bool,
+ respect~leftbrace .bool_set:N = \l__csvsim_respect_leftbrace_bool,
+ respect~rightbrace .bool_set:N = \l__csvsim_respect_rightbrace_bool,
+ respect~all .meta:n =
+ {
+ respect~tab, respect~percent, respect~sharp, respect~dollar,
+ respect~and, respect~backslash, respect~underscore, respect~tilde,
+ respect~circumflex, respect~leftbrace, respect~rightbrace
+ },
+ respect~none .meta:n =
+ {
+ respect~tab=false, respect~percent=false, respect~sharp=false,
+ respect~dollar=false, respect~and=false, respect~backslash=false,
+ respect~underscore=false, respect~tilde=false, respect~circumflex=false,
+ respect~leftbrace=false, respect~rightbrace=false
+ },
+ }
+
+
+
+%---- tables
+
+\cs_new_protected_nopar:Npn \__csvsim_key_table:nn #1#2
+ {
+ \tl_gset:Nn \g__csvsim_hook_table_begin_tl {#1}
+ \tl_gset:Nn \g__csvsim_hook_table_end_tl {#2}
+ }
+
+
+\keys_define:nn { csvsim }
+ {
+ before~table .tl_gset:N = \g__csvsim_before_table_tl,
+ after~table .tl_gset:N = \g__csvsim_after_table_tl,
+ table~head .tl_gset:N = \g__csvsim_table_head_tl,
+ table~foot .tl_gset:N = \g__csvsim_table_foot_tl,
+ _table_ .code:n = \__csvsim_key_table:nn #1,
+ no~table .meta:n = { _table_={}{} },
+ tabular .meta:n =
+ {
+ _table_ = { \g__csvsim_before_table_tl\begin{tabular}{#1}\g__csvsim_table_head_tl }
+ { \g__csvsim_table_foot_tl\end{tabular}\g__csvsim_after_table_tl },
+ late~after~line = \\
+ },
+ centered~tabular .meta:n =
+ {
+ _table_ = { \begin{center}\g__csvsim_before_table_tl\begin{tabular}{#1}\g__csvsim_table_head_tl }
+ { \g__csvsim_table_foot_tl\end{tabular}\g__csvsim_after_table_tl\end{center} },
+ late~after~line = \\
+ },
+ longtable .meta:n =
+ {
+ _table_ = { \g__csvsim_before_table_tl\begin{longtable}{#1}\g__csvsim_table_head_tl }
+ { \g__csvsim_table_foot_tl\end{longtable}\g__csvsim_after_table_tl },
+ late~after~line = \\
+ },
+ tabbing .meta:n =
+ {
+ _table_ = { \g__csvsim_before_table_tl\begin{tabbing}\g__csvsim_table_head_tl }
+ { \g__csvsim_table_foot_tl\end{tabbing}\g__csvsim_after_table_tl },
+ late~after~line = \\,
+ late~after~last~line =
+ },
+ centered~tabbing .meta:n =
+ {
+ _table_ = { \begin{center}\g__csvsim_before_table_tl\begin{tabbing}\g__csvsim_table_head_tl }
+ { \g__csvsim_table_foot_tl\end{tabbing}\g__csvsim_after_table_tl\end{center} },
+ late~after~line = \\,
+ late~after~last~line =
+ },
+ _autotab_ .meta:n =
+ {
+ file = #1,
+ late~after~line = \\,
+ command = \csvlinetotablerow
+ },
+ _autotabular_ .meta:n =
+ {
+ _autotab_ = #1,
+ late~after~last~line = \g__csvsim_table_foot_tl
+ \end{tabular}
+ \g__csvsim_after_table_tl,
+ },
+ autotabular .meta:n =
+ {
+ _autotabular_ = #1,
+ head,
+ after~head = \g__csvsim_before_table_tl
+ \begin{tabular}{|*{\int_use:N\g__csvsim_col_int}{l|}}
+ \g__csvsim_table_head_tl,
+ table~head = \hline\csvlinetotablerow\\\hline,
+ table~foot = \\\hline,
+ },
+ autotabular* .meta:n =
+ {
+ _autotabular_ = #1,
+ no~head,
+ before~first~line = \g__csvsim_before_table_tl
+ \begin{tabular}{|*{\int_use:N\g__csvsim_col_int}{l|}}
+ \g__csvsim_table_head_tl,
+ table~head = \hline,
+ table~foot = \\\hline,
+ },
+ autobooktabular .meta:n =
+ {
+ _autotabular_ = #1,
+ head,
+ after~head = \g__csvsim_before_table_tl
+ \begin{tabular}{*{\int_use:N\g__csvsim_col_int}{l}}
+ \g__csvsim_table_head_tl,
+ table~head = \toprule\csvlinetotablerow\\\midrule,
+ table~foot = \\\bottomrule,
+ },
+ autobooktabular* .meta:n =
+ {
+ _autotabular_ = #1,
+ no~head,
+ before~first~line = \g__csvsim_before_table_tl
+ \begin{tabular}{*{\int_use:N\g__csvsim_col_int}{l}}
+ \g__csvsim_table_head_tl,
+ table~head = \toprule,
+ table~foot = \\\bottomrule,
+ },
+ _autolongtable_ .meta:n =
+ {
+ _autotab_ = #1,
+ late~after~last~line = \end{longtable}
+ \g__csvsim_after_table_tl,
+ },
+ autolongtable .meta:n =
+ {
+ _autolongtable_ = #1,
+ head,
+ after~head = \g__csvsim_before_table_tl
+ \begin{longtable}{|*{\int_use:N\g__csvsim_col_int}{l|}}
+ \g__csvsim_table_head_tl,
+ table~head = \hline\csvlinetotablerow\\\hline\endhead
+ \hline\endfoot,
+ },
+ autolongtable* .meta:n =
+ {
+ _autolongtable_ = #1,
+ no~head,
+ before~first~line = \g__csvsim_before_table_tl
+ \begin{longtable}{|*{\int_use:N\g__csvsim_col_int}{l|}}
+ \g__csvsim_table_head_tl,
+ table~head = \hline\endhead
+ \hline\endfoot,
+ },
+ autobooklongtable .meta:n =
+ {
+ _autolongtable_ = #1,
+ head,
+ after~head = \g__csvsim_before_table_tl
+ \begin{longtable}{*{\int_use:N\g__csvsim_col_int}{l}}
+ \g__csvsim_table_head_tl,
+ table~head = \toprule\csvlinetotablerow\\\midrule\endhead
+ \bottomrule\endfoot,
+ },
+ autobooklongtable* .meta:n =
+ {
+ _autolongtable_ = #1,
+ no~head,
+ before~first~line = \g__csvsim_before_table_tl
+ \begin{longtable}{*{\int_use:N\g__csvsim_col_int}{l}}
+ \g__csvsim_table_head_tl,
+ table~head = \toprule\endhead
+ \bottomrule\endfoot,
+ },
+ }
+
+
+\NewDocumentCommand \csvautotabular { s +O{} m }
+ {
+ \IfBooleanTF {#1}
+ {
+ \keys_set:nn { csvsim } { default, every~csv, autotabular*={#3}, #2}
+ }
+ {
+ \keys_set:nn { csvsim } { default, every~csv, autotabular={#3}, #2}
+ }
+ \__csvsim_loop:
+ }
+
+
+\NewDocumentCommand \csvautolongtable { s +O{} m }
+ {
+ \IfBooleanTF {#1}
+ {
+ \keys_set:nn { csvsim } { default, every~csv, autolongtable*={#3}, #2}
+ }
+ {
+ \keys_set:nn { csvsim } { default, every~csv, autolongtable={#3}, #2}
+ }
+ \__csvsim_loop:
+ }
+
+
+\NewDocumentCommand \csvautobooktabular { s +O{} m }
+ {
+ \IfBooleanTF {#1}
+ {
+ \keys_set:nn { csvsim } { default, every~csv, autobooktabular*={#3}, #2}
+ }
+ {
+ \keys_set:nn { csvsim } { default, every~csv, autobooktabular={#3}, #2}
+ }
+ \__csvsim_loop:
+ }
+
+
+\NewDocumentCommand \csvautobooklongtable { s +O{} m }
+ {
+ \IfBooleanTF {#1}
+ {
+ \keys_set:nn { csvsim } { default, every~csv, autobooklongtable*={#3}, #2}
+ }
+ {
+ \keys_set:nn { csvsim } { default, every~csv, autobooklongtable={#3}, #2}
+ }
+ \__csvsim_loop:
+ }
+
+
+
+%---- sorting
+
+\cs_new_protected_nopar:Npn \__csvsim_key_new_sorting_rule:nn #1#2
+ {
+ \keys_define:nn { csvsim }
+ {
+ sort~by~#1 .meta:n = { sort~by={#2} },
+ }
+ }
+
+
+\NewDocumentCommand \csvsortingrule { }
+ {
+ \__csvsim_key_new_sorting_rule:nn
+ }
+
+
+\keys_define:nn { csvsim }
+ {
+ preprocessor .tl_gset:N = \l__csvsim_preprocessor_tl,
+ preprocessed~file .code:n = \__csvsim_set_filename:Nn \l__csvsim_ppfilename_str {#1},
+ csvsorter~command .code:n = \__csvsim_set_filename:Nn \l__csvsim_csvsorter_command_str {#1},
+ csvsorter~configpath .code:n = \__csvsim_set_filename:Nn\l__csvsim_csvsorter_configpath_str {#1},
+ csvsorter~log .code:n = \__csvsim_set_filename:Nn \l__csvsim_csvsorter_log_str {#1},
+ csvsorter~token .code:n = \__csvsim_set_filename:Nn \l__csvsim_csvsorter_token_str {#1},
+ no~preprocessing .meta:n = { preprocessor= },
+ sort~by .meta:n =
+ {
+ preprocessor=
+ {
+ \__csvsim_processor_csvsorter:nnn {#1}
+ }
+ },
+ new~sorting~rule .code:n = \__csvsim_key_new_sorting_rule:nn #1,
+ new~sorting~rule .value_required:n = true ,
+}
+
+
+\keys_set:nn { csvsim }
+ {
+ preprocessed~file = \c_sys_jobname_str _sorted._csv,
+ csvsorter~command = csvsorter,
+ csvsorter~configpath = .,
+ csvsorter~log = csvsorter.log,
+ csvsorter~token = \c_sys_jobname_str.csvtoken,
+ }
+
+
+\cs_new_protected_nopar:Npn \__csvsim_processor_csvsorter:nnn #1#2#3
+ {
+ \sys_if_shell_unrestricted:TF
+ {
+ \__csvsim_set_temp_filename:n { #1 }
+ \msg_note:nnxx { csvsimple }{ sort-info }{ #2 }{ \l__csvsim_temp_filename_str }
+ \cs_if_exist:NF \g__csvsim_iow
+ {
+ \iow_new:N \g__csvsim_iow
+ }
+ \iow_open:Nn \g__csvsim_iow { \l__csvsim_csvsorter_token_str }
+ \iow_now:Nn \g__csvsim_iow { \ExplSyntaxOn \msg_error:nn { csvsimple }{ sort-error } \ExplSyntaxOff }
+ \iow_close:N \g__csvsim_iow
+ \sys_shell_now:x
+ {
+ "\l__csvsim_csvsorter_command_str" \c_space_tl
+ -c~ "\l__csvsim_csvsorter_configpath_str/\l__csvsim_temp_filename_str" \c_space_tl
+ -l~ "\l__csvsim_csvsorter_log_str" \c_space_tl
+ -t~ "\l__csvsim_csvsorter_token_str" \c_space_tl
+ -i~ "#2" \c_space_tl
+ -o~ "#3" \c_space_tl
+ -q~1
+ }
+ \file_input:n { \l__csvsim_csvsorter_token_str }
+ }
+ {
+ \msg_error:nn { csvsimple }{ sort-shell-escape }
+ }
+ }
+
+
+
+%---- default
+
+\keys_define:nn { csvsim }
+ {
+ % default for reset
+ default .meta:n =
+ {
+ file = unknown.csv,
+ no~preprocessing,
+ command = \csvline,
+ column~names~reset,
+ head,
+ check~column~count,
+ head~to~column~names~prefix = ,
+ head~to~column~names = false,
+ column~count = 0,
+ on~column~count~error =,
+ no~filter,
+ before~filter =,
+ before~line =,
+ after~line =,
+ late~after~line =,
+ after~head =,
+ late~after~head =,
+ before~reading =,
+ after~reading =,
+ before~table =,
+ after~table =,
+ table~head =,
+ table~foot =,
+ no~table,
+ separator = comma,
+ respect~none,
+ },
+ }
+
+\keys_set:nn { csvsim }
+ {
+ default
+ }