@@ -1263,6 +1263,7 @@ static bool ReplacePatterns(EvalContext *ctx, Item *file_start, Item *file_end,
12631263 assert (a != NULL );
12641264 assert (pp != NULL );
12651265 assert (edcontext != NULL );
1266+ bool allow_non_convergent = PromiseGetConstraintAsBoolean (ctx , "allow_non_convergent" , pp );
12661267
12671268 char line_buff [CF_EXPANDSIZE ];
12681269 char after [CF_BUFSIZE ];
@@ -1318,9 +1319,14 @@ static bool ReplacePatterns(EvalContext *ctx, Item *file_start, Item *file_end,
13181319 // TODO: gripe if that truncated !
13191320
13201321 // Substitute into line_buff:
1321- snprintf (line_buff + start_off , sizeof (line_buff ) - start_off ,
1322+ int needed = snprintf (line_buff + start_off , sizeof (line_buff ) - start_off ,
13221323 "%s%s" , BufferData (replace ), after );
1323- // TODO: gripe if that truncated or failed !
1324+ if (needed < 0 || needed >= sizeof (line_buff ) - start_off ) {
1325+ RecordInterruption (ctx , pp , a , "Replacement string is too large. '%s' in '%s'" ,
1326+ a -> column .column_separator , edcontext -> filename );
1327+ * result = PromiseResultUpdate (* result , PROMISE_RESULT_INTERRUPTED );
1328+ break ;
1329+ }
13241330 notfound = false;
13251331 replaced = true;
13261332
@@ -1330,17 +1336,32 @@ static bool ReplacePatterns(EvalContext *ctx, Item *file_start, Item *file_end,
13301336 break ;
13311337 }
13321338 }
1333-
1339+ char line_buff_cp [ CF_EXPANDSIZE ];
13341340 if (NotAnchored (pp -> promiser ) && BlockTextMatch (ctx , pp -> promiser , line_buff , & start_off , & end_off ))
13351341 {
1336- RecordInterruption (ctx , pp , a ,
1337- "Promised replacement '%s' on line '%s' for pattern '%s'"
1338- " is not convergent while editing '%s'"
1339- " (regular expression matches the replacement string)" ,
1340- line_buff , ip -> name , pp -> promiser , edcontext -> filename );
1341- * result = PromiseResultUpdate (* result , PROMISE_RESULT_INTERRUPTED );
1342- PromiseRef (LOG_LEVEL_ERR , pp );
1343- break ;
1342+ strlcpy (line_buff_cp , line_buff , sizeof (line_buff_cp ));
1343+ strlcpy (after , line_buff_cp + end_off , sizeof (after ));
1344+ int needed = snprintf (line_buff_cp + start_off , sizeof (line_buff_cp ) - start_off ,
1345+ "%s%s" , BufferData (replace ), after );
1346+
1347+ if (needed < 0 || needed >= sizeof (line_buff_cp ) - start_off ) {
1348+ RecordInterruption (ctx , pp , a , "Replacement string is too large. '%s' in '%s'" ,
1349+ a -> column .column_separator , edcontext -> filename );
1350+ * result = PromiseResultUpdate (* result , PROMISE_RESULT_INTERRUPTED );
1351+ break ;
1352+ }
1353+
1354+ if (!allow_non_convergent || (strlen (line_buff ) != strlen (line_buff_cp )))
1355+ {
1356+ RecordInterruption (ctx , pp , a ,
1357+ "Promised replacement '%s' on line '%s' for pattern '%s'"
1358+ " is not convergent while editing '%s'"
1359+ " (regular expression matches the replacement string)" ,
1360+ line_buff , ip -> name , pp -> promiser , edcontext -> filename );
1361+ * result = PromiseResultUpdate (* result , PROMISE_RESULT_INTERRUPTED );
1362+ PromiseRef (LOG_LEVEL_ERR , pp );
1363+ break ;
1364+ }
13441365 }
13451366
13461367 if (!MakingChanges (ctx , pp , a , result , "replace pattern '%s' in '%s'" , pp -> promiser ,
@@ -1366,8 +1387,21 @@ static bool ReplacePatterns(EvalContext *ctx, Item *file_start, Item *file_end,
13661387 break ;
13671388 }
13681389
1369- if (BlockTextMatch (ctx , pp -> promiser , ip -> name , & start_off , & end_off ))
1390+ if (BlockTextMatch (ctx , pp -> promiser , ip -> name , & start_off , & end_off ) && (!allow_non_convergent
1391+ || (strlen (line_buff ) != strlen (line_buff_cp ))))
13701392 {
1393+ strlcpy (line_buff_cp , line_buff , sizeof (line_buff_cp ));
1394+ strlcpy (after , line_buff_cp + end_off , sizeof (after ));
1395+ int needed = snprintf (line_buff_cp + start_off , sizeof (line_buff_cp ) - start_off ,
1396+ "%s%s" , BufferData (replace ), after );
1397+
1398+ if (needed >= sizeof (line_buff_cp ) - start_off ) {
1399+ RecordInterruption (ctx , pp , a , "Buffer overflow: replacement string is too large. '%s' in '%s'" ,
1400+ a -> column .column_separator , edcontext -> filename );
1401+ * result = PromiseResultUpdate (* result , PROMISE_RESULT_INTERRUPTED );
1402+ break ;
1403+ }
1404+
13711405 RecordInterruption (ctx , pp , a ,
13721406 "Promised replacement '%s' for pattern '%s' is not properly convergent while editing '%s'"
13731407 " (pattern still matches the end-state replacement string '%s', consider use"
0 commit comments