@@ -574,7 +574,61 @@ char **strv_env_clean_with_callback(char **e, void (*invalid_callback)(const cha
574
574
return e ;
575
575
}
576
576
577
- char * replace_env_n (const char * format , size_t n , char * * env , ReplaceEnvFlags flags ) {
577
+ static int strv_extend_with_length (char * * * l , const char * s , size_t n ) {
578
+ char * c ;
579
+
580
+ c = strndup (s , n );
581
+ if (!c )
582
+ return - ENOMEM ;
583
+
584
+ return strv_consume (l , c );
585
+ }
586
+
587
+ static int strv_env_get_n_validated (
588
+ char * * env ,
589
+ const char * name ,
590
+ size_t l ,
591
+ ReplaceEnvFlags flags ,
592
+ char * * ret , /* points into the env block! do not free! */
593
+ char * * * unset_variables , /* updated in place */
594
+ char * * * bad_variables ) { /* ditto */
595
+
596
+ char * e ;
597
+ int r ;
598
+
599
+ assert (l == 0 || name );
600
+ assert (ret );
601
+
602
+ if (env_name_is_valid_n (name , l )) {
603
+ e = strv_env_get_n (env , name , l , flags );
604
+ if (!e && unset_variables ) {
605
+ r = strv_extend_with_length (unset_variables , name , l );
606
+ if (r < 0 )
607
+ return r ;
608
+ }
609
+ } else {
610
+ e = NULL ; /* Resolve invalid variable names the same way as unset ones */
611
+
612
+ if (bad_variables ) {
613
+ r = strv_extend_with_length (bad_variables , name , l );
614
+ if (r < 0 )
615
+ return r ;
616
+ }
617
+ }
618
+
619
+ * ret = e ;
620
+ return !!e ;
621
+ }
622
+
623
+ int replace_env_full (
624
+ const char * format ,
625
+ size_t n ,
626
+ char * * env ,
627
+ ReplaceEnvFlags flags ,
628
+ char * * ret ,
629
+ char * * * ret_unset_variables ,
630
+ char * * * ret_bad_variables ) {
631
+
578
632
enum {
579
633
WORD ,
580
634
CURLY ,
@@ -585,14 +639,21 @@ char *replace_env_n(const char *format, size_t n, char **env, ReplaceEnvFlags fl
585
639
ALTERNATE_VALUE ,
586
640
} state = WORD ;
587
641
642
+ _cleanup_strv_free_ char * * unset_variables = NULL , * * bad_variables = NULL ;
588
643
const char * e , * word = format , * test_value = NULL ; /* test_value is initialized to appease gcc */
589
- char * k ;
590
644
_cleanup_free_ char * s = NULL ;
645
+ char * * * pu , * * * pb , * k ;
591
646
size_t i , len = 0 ; /* len is initialized to appease gcc */
592
- int nest = 0 ;
647
+ int nest = 0 , r ;
593
648
594
649
assert (format );
595
650
651
+ if (n == SIZE_MAX )
652
+ n = strlen (format );
653
+
654
+ pu = ret_unset_variables ? & unset_variables : NULL ;
655
+ pb = ret_bad_variables ? & bad_variables : NULL ;
656
+
596
657
for (e = format , i = 0 ; * e && i < n ; e ++ , i ++ )
597
658
switch (state ) {
598
659
@@ -605,27 +666,28 @@ char *replace_env_n(const char *format, size_t n, char **env, ReplaceEnvFlags fl
605
666
if (* e == '{' ) {
606
667
k = strnappend (s , word , e - word - 1 );
607
668
if (!k )
608
- return NULL ;
669
+ return - ENOMEM ;
609
670
610
671
free_and_replace (s , k );
611
672
612
673
word = e - 1 ;
613
674
state = VARIABLE ;
614
675
nest ++ ;
676
+
615
677
} else if (* e == '$' ) {
616
678
k = strnappend (s , word , e - word );
617
679
if (!k )
618
- return NULL ;
680
+ return - ENOMEM ;
619
681
620
682
free_and_replace (s , k );
621
683
622
684
word = e + 1 ;
623
685
state = WORD ;
624
686
625
- } else if (flags & REPLACE_ENV_ALLOW_BRACELESS && strchr (VALID_BASH_ENV_NAME_CHARS , * e )) {
687
+ } else if (FLAGS_SET ( flags , REPLACE_ENV_ALLOW_BRACELESS ) && strchr (VALID_BASH_ENV_NAME_CHARS , * e )) {
626
688
k = strnappend (s , word , e - word - 1 );
627
689
if (!k )
628
- return NULL ;
690
+ return - ENOMEM ;
629
691
630
692
free_and_replace (s , k );
631
693
@@ -638,12 +700,14 @@ char *replace_env_n(const char *format, size_t n, char **env, ReplaceEnvFlags fl
638
700
639
701
case VARIABLE :
640
702
if (* e == '}' ) {
641
- const char * t ;
703
+ char * t ;
642
704
643
- t = strv_env_get_n (env , word + 2 , e - word - 2 , flags );
705
+ r = strv_env_get_n_validated (env , word + 2 , e - word - 2 , flags , & t , pu , pb );
706
+ if (r < 0 )
707
+ return r ;
644
708
645
709
if (!strextend (& s , t ))
646
- return NULL ;
710
+ return - ENOMEM ;
647
711
648
712
word = e + 1 ;
649
713
state = WORD ;
@@ -685,18 +749,37 @@ char *replace_env_n(const char *format, size_t n, char **env, ReplaceEnvFlags fl
685
749
686
750
nest -- ;
687
751
if (nest == 0 ) {
688
- const char * t ;
752
+ _cleanup_strv_free_ char * * u = NULL , * * b = NULL ;
689
753
_cleanup_free_ char * v = NULL ;
754
+ char * t = NULL ;
755
+
756
+ r = strv_env_get_n_validated (env , word + 2 , len , flags , & t , pu , pb );
757
+ if (r < 0 )
758
+ return r ;
690
759
691
- t = strv_env_get_n (env , word + 2 , len , flags );
760
+ if (t && state == ALTERNATE_VALUE ) {
761
+ r = replace_env_full (test_value , e - test_value , env , flags , & v , pu ? & u : NULL , pb ? & b : NULL );
762
+ if (r < 0 )
763
+ return r ;
692
764
693
- if (t && state == ALTERNATE_VALUE )
694
- t = v = replace_env_n (test_value , e - test_value , env , flags );
695
- else if (!t && state == DEFAULT_VALUE )
696
- t = v = replace_env_n (test_value , e - test_value , env , flags );
765
+ t = v ;
766
+ } else if (!t && state == DEFAULT_VALUE ) {
767
+ r = replace_env_full (test_value , e - test_value , env , flags , & v , pu ? & u : NULL , pb ? & b : NULL );
768
+ if (r < 0 )
769
+ return r ;
770
+
771
+ t = v ;
772
+ }
773
+
774
+ r = strv_extend_strv (& unset_variables , u , /* filter_duplicates= */ true);
775
+ if (r < 0 )
776
+ return r ;
777
+ r = strv_extend_strv (& bad_variables , b , /* filter_duplicates= */ true);
778
+ if (r < 0 )
779
+ return r ;
697
780
698
781
if (!strextend (& s , t ))
699
- return NULL ;
782
+ return - ENOMEM ;
700
783
701
784
word = e + 1 ;
702
785
state = WORD ;
@@ -707,12 +790,14 @@ char *replace_env_n(const char *format, size_t n, char **env, ReplaceEnvFlags fl
707
790
assert (flags & REPLACE_ENV_ALLOW_BRACELESS );
708
791
709
792
if (!strchr (VALID_BASH_ENV_NAME_CHARS , * e )) {
710
- const char * t ;
793
+ char * t = NULL ;
711
794
712
- t = strv_env_get_n (env , word + 1 , e - word - 1 , flags );
795
+ r = strv_env_get_n_validated (env , word + 1 , e - word - 1 , flags , & t , & unset_variables , & bad_variables );
796
+ if (r < 0 )
797
+ return r ;
713
798
714
799
if (!strextend (& s , t ))
715
- return NULL ;
800
+ return - ENOMEM ;
716
801
717
802
word = e -- ;
718
803
i -- ;
@@ -722,74 +807,125 @@ char *replace_env_n(const char *format, size_t n, char **env, ReplaceEnvFlags fl
722
807
}
723
808
724
809
if (state == VARIABLE_RAW ) {
725
- const char * t ;
810
+ char * t ;
726
811
727
812
assert (flags & REPLACE_ENV_ALLOW_BRACELESS );
728
813
729
- t = strv_env_get_n (env , word + 1 , e - word - 1 , flags );
730
- return strjoin (s , t );
731
- } else
732
- return strnappend (s , word , e - word );
814
+ r = strv_env_get_n_validated (env , word + 1 , e - word - 1 , flags , & t , & unset_variables , & bad_variables );
815
+ if (r < 0 )
816
+ return r ;
817
+
818
+ if (!strextend (& s , t ))
819
+ return - ENOMEM ;
820
+
821
+ } else if (!strextendn (& s , word , e - word ))
822
+ return - ENOMEM ;
823
+
824
+ if (ret_unset_variables )
825
+ * ret_unset_variables = TAKE_PTR (unset_variables );
826
+ if (ret_bad_variables )
827
+ * ret_bad_variables = TAKE_PTR (bad_variables );
828
+
829
+ if (ret )
830
+ * ret = TAKE_PTR (s );
831
+
832
+ return 0 ;
733
833
}
734
834
735
- char * * replace_env_argv (char * * argv , char * * env ) {
736
- _cleanup_strv_free_ char * * ret = NULL ;
835
+ int replace_env_argv (
836
+ char * * argv ,
837
+ char * * env ,
838
+ char * * * ret ,
839
+ char * * * ret_unset_variables ,
840
+ char * * * ret_bad_variables ) {
841
+
842
+ _cleanup_strv_free_ char * * n = NULL , * * unset_variables = NULL , * * bad_variables = NULL ;
737
843
size_t k = 0 , l = 0 ;
844
+ int r ;
738
845
739
846
l = strv_length (argv );
740
847
741
- ret = new (char * , l + 1 );
742
- if (!ret )
743
- return NULL ;
848
+ n = new (char * , l + 1 );
849
+ if (!n )
850
+ return - ENOMEM ;
744
851
745
852
STRV_FOREACH (i , argv ) {
853
+ const char * word = * i ;
746
854
747
855
/* If $FOO appears as single word, replace it by the split up variable */
748
- if ((* i )[0 ] == '$' && !IN_SET ((* i )[1 ], '{' , '$' )) {
749
- char * e ;
750
- char * * w ;
856
+ if (word [0 ] == '$' && !IN_SET (word [1 ], '{' , '$' )) {
751
857
_cleanup_strv_free_ char * * m = NULL ;
858
+ const char * name = word + 1 ;
859
+ char * e , * * w ;
752
860
size_t q ;
753
861
754
- e = strv_env_get (env , * i + 1 );
755
- if (e ) {
756
- int r ;
757
-
758
- r = strv_split_full (& m , e , WHITESPACE , EXTRACT_RELAX |EXTRACT_UNQUOTE );
759
- if (r < 0 ) {
760
- ret [k ] = NULL ;
761
- return NULL ;
762
- }
763
- }
862
+ if (env_name_is_valid (name )) {
863
+ e = strv_env_get (env , name );
864
+ if (e )
865
+ r = strv_split_full (& m , e , WHITESPACE , EXTRACT_RELAX |EXTRACT_UNQUOTE );
866
+ else if (ret_unset_variables )
867
+ r = strv_extend (& unset_variables , name );
868
+ else
869
+ r = 0 ;
870
+ } else if (ret_bad_variables )
871
+ r = strv_extend (& bad_variables , name );
872
+ else
873
+ r = 0 ;
874
+ if (r < 0 )
875
+ return r ;
764
876
765
877
q = strv_length (m );
766
878
l = l + q - 1 ;
767
879
768
- w = reallocarray (ret , l + 1 , sizeof (char * ));
769
- if (!w ) {
770
- ret [k ] = NULL ;
771
- return NULL ;
772
- }
880
+ w = reallocarray (n , l + 1 , sizeof (char * ));
881
+ if (!w )
882
+ return - ENOMEM ;
773
883
774
- ret = w ;
884
+ n = w ;
775
885
if (m ) {
776
- memcpy (ret + k , m , q * sizeof (char * ));
886
+ memcpy (n + k , m , ( q + 1 ) * sizeof (char * ));
777
887
m = mfree (m );
778
888
}
779
889
780
890
k += q ;
781
891
continue ;
782
892
}
783
893
894
+ _cleanup_strv_free_ char * * u = NULL , * * b = NULL ;
895
+
784
896
/* If ${FOO} appears as part of a word, replace it by the variable as-is */
785
- ret [k ] = replace_env (* i , env , 0 );
786
- if (!ret [k ])
787
- return NULL ;
788
- k ++ ;
897
+ r = replace_env_full (
898
+ word ,
899
+ /* length= */ SIZE_MAX ,
900
+ env ,
901
+ /* flags= */ 0 ,
902
+ n + k ,
903
+ ret_unset_variables ? & u : NULL ,
904
+ ret_bad_variables ? & b : NULL );
905
+ if (r < 0 )
906
+ return r ;
907
+ n [++ k ] = NULL ;
908
+
909
+ r = strv_extend_strv (& unset_variables , u , /* filter_duplicates= */ true);
910
+ if (r < 0 )
911
+ return r ;
912
+
913
+ r = strv_extend_strv (& bad_variables , b , /*filter_duplicates= */ true);
914
+ if (r < 0 )
915
+ return r ;
789
916
}
790
917
791
- ret [k ] = NULL ;
792
- return TAKE_PTR (ret );
918
+ if (ret_unset_variables ) {
919
+ strv_uniq (strv_sort (unset_variables ));
920
+ * ret_unset_variables = TAKE_PTR (unset_variables );
921
+ }
922
+ if (ret_bad_variables ) {
923
+ strv_uniq (strv_sort (bad_variables ));
924
+ * ret_bad_variables = TAKE_PTR (bad_variables );
925
+ }
926
+
927
+ * ret = TAKE_PTR (n );
928
+ return 0 ;
793
929
}
794
930
795
931
int getenv_bool (const char * p ) {
0 commit comments