@@ -2294,8 +2294,11 @@ struct config_store_data {
22942294 int do_not_match ;
22952295 regex_t * value_regex ;
22962296 int multi_replace ;
2297- size_t * seen ;
2298- unsigned int seen_nr , seen_alloc ;
2297+ struct {
2298+ size_t begin , end ;
2299+ enum config_event_t type ;
2300+ } * parsed ;
2301+ unsigned int parsed_nr , parsed_alloc , * seen , seen_nr , seen_alloc ;
22992302 unsigned int key_seen :1 , section_seen :1 , is_keys_section :1 ;
23002303};
23012304
@@ -2313,10 +2316,31 @@ static int matches(const char *key, const char *value,
23132316 (value && !regexec (store -> value_regex , value , 0 , NULL , 0 ));
23142317}
23152318
2319+ static int store_aux_event (enum config_event_t type ,
2320+ size_t begin , size_t end , void * data )
2321+ {
2322+ struct config_store_data * store = data ;
2323+
2324+ ALLOC_GROW (store -> parsed , store -> parsed_nr + 1 , store -> parsed_alloc );
2325+ store -> parsed [store -> parsed_nr ].begin = begin ;
2326+ store -> parsed [store -> parsed_nr ].end = end ;
2327+ store -> parsed [store -> parsed_nr ].type = type ;
2328+ store -> parsed_nr ++ ;
2329+
2330+ if (type == CONFIG_EVENT_SECTION ) {
2331+ if (cf -> var .len < 2 || cf -> var .buf [cf -> var .len - 1 ] != '.' )
2332+ BUG ("Invalid section name '%s'" , cf -> var .buf );
2333+
2334+ /* Is this the section we were looking for? */
2335+ store -> is_keys_section = cf -> var .len - 1 == store -> baselen &&
2336+ !strncasecmp (cf -> var .buf , store -> key , store -> baselen );
2337+ }
2338+
2339+ return 0 ;
2340+ }
2341+
23162342static int store_aux (const char * key , const char * value , void * cb )
23172343{
2318- const char * ep ;
2319- size_t section_len ;
23202344 struct config_store_data * store = cb ;
23212345
23222346 if (store -> key_seen ) {
@@ -2328,55 +2352,21 @@ static int store_aux(const char *key, const char *value, void *cb)
23282352 ALLOC_GROW (store -> seen , store -> seen_nr + 1 ,
23292353 store -> seen_alloc );
23302354
2331- store -> seen [store -> seen_nr ] = cf -> do_ftell ( cf ) ;
2355+ store -> seen [store -> seen_nr ] = store -> parsed_nr ;
23322356 store -> seen_nr ++ ;
23332357 }
2334- return 0 ;
23352358 } else if (store -> is_keys_section ) {
23362359 /*
2337- * What we are looking for is in store->key (both
2338- * section and var), and its section part is baselen
2339- * long. We found key (again, both section and var).
2340- * We would want to know if this key is in the same
2341- * section as what we are looking for. We already
2342- * know we are in the same section as what should
2343- * hold store->key.
2360+ * Do not increment matches yet: this may not be a match, but we
2361+ * are in the desired section.
23442362 */
2345- ep = strrchr (key , '.' );
2346- section_len = ep - key ;
2347-
2348- if ((section_len != store -> baselen ) ||
2349- memcmp (key , store -> key , section_len + 1 )) {
2350- store -> is_keys_section = 0 ;
2351- return 0 ;
2352- }
2353- /*
2354- * Do not increment matches: this is no match, but we
2355- * just made sure we are in the desired section.
2356- */
2357- ALLOC_GROW (store -> seen , store -> seen_nr + 1 ,
2358- store -> seen_alloc );
2359- store -> seen [store -> seen_nr ] = cf -> do_ftell (cf );
2360- }
2361-
2362- if (matches (key , value , store )) {
2363- ALLOC_GROW (store -> seen , store -> seen_nr + 1 ,
2364- store -> seen_alloc );
2365- store -> seen [store -> seen_nr ] = cf -> do_ftell (cf );
2366- store -> seen_nr ++ ;
2367- store -> key_seen = 1 ;
2363+ ALLOC_GROW (store -> seen , store -> seen_nr + 1 , store -> seen_alloc );
2364+ store -> seen [store -> seen_nr ] = store -> parsed_nr ;
23682365 store -> section_seen = 1 ;
2369- store -> is_keys_section = 1 ;
2370- } else {
2371- if (strrchr (key , '.' ) - key == store -> baselen &&
2372- !strncmp (key , store -> key , store -> baselen )) {
2373- store -> section_seen = 1 ;
2374- store -> is_keys_section = 1 ;
2375- ALLOC_GROW (store -> seen ,
2376- store -> seen_nr + 1 ,
2377- store -> seen_alloc );
2378- store -> seen [store -> seen_nr ] =
2379- cf -> do_ftell (cf );
2366+
2367+ if (matches (key , value , store )) {
2368+ store -> seen_nr ++ ;
2369+ store -> key_seen = 1 ;
23802370 }
23812371 }
23822372
@@ -2477,32 +2467,6 @@ static ssize_t write_pair(int fd, const char *key, const char *value,
24772467 return ret ;
24782468}
24792469
2480- static ssize_t find_beginning_of_line (const char * contents , size_t size ,
2481- size_t offset_ , int * found_bracket )
2482- {
2483- size_t equal_offset = size , bracket_offset = size ;
2484- ssize_t offset ;
2485-
2486- contline :
2487- for (offset = offset_ - 2 ; offset > 0
2488- && contents [offset ] != '\n' ; offset -- )
2489- switch (contents [offset ]) {
2490- case '=' : equal_offset = offset ; break ;
2491- case ']' : bracket_offset = offset ; break ;
2492- }
2493- if (offset > 0 && contents [offset - 1 ] == '\\' ) {
2494- offset_ = offset ;
2495- goto contline ;
2496- }
2497- if (bracket_offset < equal_offset ) {
2498- * found_bracket = 1 ;
2499- offset = bracket_offset + 1 ;
2500- } else
2501- offset ++ ;
2502-
2503- return offset ;
2504- }
2505-
25062470int git_config_set_in_file_gently (const char * config_filename ,
25072471 const char * key , const char * value )
25082472{
@@ -2613,6 +2577,7 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,
26132577 struct stat st ;
26142578 size_t copy_begin , copy_end ;
26152579 int i , new_line = 0 ;
2580+ struct config_options opts ;
26162581
26172582 if (value_regex == NULL )
26182583 store .value_regex = NULL ;
@@ -2635,17 +2600,24 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,
26352600 }
26362601 }
26372602
2638- ALLOC_GROW (store .seen , 1 , store .seen_alloc );
2639- store .seen [0 ] = 0 ;
2640- store .seen_nr = 0 ;
2603+ ALLOC_GROW (store .parsed , 1 , store .parsed_alloc );
2604+ store .parsed [0 ].end = 0 ;
2605+
2606+ memset (& opts , 0 , sizeof (opts ));
2607+ opts .event_fn = store_aux_event ;
2608+ opts .event_fn_data = & store ;
26412609
26422610 /*
2643- * After this, store.offset will contain the *end* offset
2644- * of the last match, or remain at 0 if no match was found.
2611+ * After this, store.parsed will contain offsets of all the
2612+ * parsed elements, and store.seen will contain a list of
2613+ * matches, as indices into store.parsed.
2614+ *
26452615 * As a side effect, we make sure to transform only a valid
26462616 * existing config file.
26472617 */
2648- if (git_config_from_file (store_aux , config_filename , & store )) {
2618+ if (git_config_from_file_with_options (store_aux ,
2619+ config_filename ,
2620+ & store , & opts )) {
26492621 error ("invalid config file %s" , config_filename );
26502622 free (store .key );
26512623 if (store .value_regex != NULL &&
@@ -2697,19 +2669,39 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,
26972669 goto out_free ;
26982670 }
26992671
2700- if (store .seen_nr == 0 )
2672+ if (store .seen_nr == 0 ) {
2673+ if (!store .seen_alloc ) {
2674+ /* Did not see key nor section */
2675+ ALLOC_GROW (store .seen , 1 , store .seen_alloc );
2676+ store .seen [0 ] = store .parsed_nr
2677+ - !!store .parsed_nr ;
2678+ }
27012679 store .seen_nr = 1 ;
2680+ }
27022681
27032682 for (i = 0 , copy_begin = 0 ; i < store .seen_nr ; i ++ ) {
2683+ size_t replace_end ;
2684+ int j = store .seen [i ];
2685+
27042686 new_line = 0 ;
2705- if (store .seen [i ] == 0 ) {
2706- store .seen [i ] = copy_end = contents_sz ;
2707- } else if (!store .key_seen ) {
2708- copy_end = store .seen [i ];
2709- } else
2710- copy_end = find_beginning_of_line (
2711- contents , contents_sz ,
2712- store .seen [i ], & new_line );
2687+ if (!store .key_seen ) {
2688+ replace_end = copy_end = store .parsed [j ].end ;
2689+ } else {
2690+ replace_end = store .parsed [j ].end ;
2691+ copy_end = store .parsed [j ].begin ;
2692+ /*
2693+ * Swallow preceding white-space on the same
2694+ * line.
2695+ */
2696+ while (copy_end > 0 ) {
2697+ char c = contents [copy_end - 1 ];
2698+
2699+ if (isspace (c ) && c != '\n' )
2700+ copy_end -- ;
2701+ else
2702+ break ;
2703+ }
2704+ }
27132705
27142706 if (copy_end > 0 && contents [copy_end - 1 ] != '\n' )
27152707 new_line = 1 ;
@@ -2723,7 +2715,7 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,
27232715 write_str_in_full (fd , "\n" ) < 0 )
27242716 goto write_err_out ;
27252717 }
2726- copy_begin = store . seen [ i ] ;
2718+ copy_begin = replace_end ;
27272719 }
27282720
27292721 /* write the pair (value == NULL means unset) */
0 commit comments