@@ -391,6 +391,8 @@ static bool core_backup_add_entry(core_backup_list_t *backup_list,
391391{
392392 char * backup_filename = NULL ;
393393 core_backup_list_entry_t * entry = NULL ;
394+ const char * p = NULL ;
395+ char * endptr = NULL ;
394396 unsigned long crc = 0 ;
395397 unsigned backup_mode = 0 ;
396398
@@ -401,9 +403,11 @@ static bool core_backup_add_entry(core_backup_list_t *backup_list,
401403 return false;
402404
403405 backup_filename = strdup (path_basename (backup_path ));
404-
405406 if (string_is_empty (backup_filename ))
407+ {
408+ free (backup_filename );
406409 return false;
410+ }
407411
408412 /* Ensure base backup filename matches core */
409413 if (!string_starts_with (backup_filename , core_filename ))
@@ -420,11 +424,93 @@ static bool core_backup_add_entry(core_backup_list_t *backup_list,
420424 * - timestamp: YYYYMMDDTHHMMSS */
421425 entry = & backup_list -> entries [backup_list -> size ];
422426
423- if (sscanf (backup_filename + strlen (core_filename ),
424- ".%04u%02u%02uT%02u%02u%02u.%08lx.%u" ,
425- & entry -> date .year , & entry -> date .month , & entry -> date .day ,
426- & entry -> date .hour , & entry -> date .minute , & entry -> date .second ,
427- & crc , & backup_mode ) != 8 )
427+ p = backup_filename + strlen (core_filename );
428+
429+ /* Expect '.' separator before timestamp */
430+ if (* p != '.' )
431+ {
432+ free (backup_filename );
433+ return false;
434+ }
435+ p ++ ;
436+
437+ /* Timestamp must be exactly 15 characters: YYYYMMDDTHHMMSS */
438+ if (strlen (p ) < 15 || p [8 ] != 'T' )
439+ {
440+ free (backup_filename );
441+ return false;
442+ }
443+
444+ /* Parse date/time components */
445+ {
446+ char buf [5 ];
447+
448+ /* Year (4 digits) */
449+ memcpy (buf , p , 4 );
450+ buf [4 ] = '\0' ;
451+ entry -> date .year = (unsigned )strtoul (buf , & endptr , 10 );
452+ if (* endptr != '\0' ) { free (backup_filename ); return false; }
453+ p += 4 ;
454+
455+ /* Month (2 digits) */
456+ memcpy (buf , p , 2 );
457+ buf [2 ] = '\0' ;
458+ entry -> date .month = (unsigned )strtoul (buf , & endptr , 10 );
459+ if (* endptr != '\0' ) { free (backup_filename ); return false; }
460+ p += 2 ;
461+
462+ /* Day (2 digits) */
463+ memcpy (buf , p , 2 );
464+ buf [2 ] = '\0' ;
465+ entry -> date .day = (unsigned )strtoul (buf , & endptr , 10 );
466+ if (* endptr != '\0' ) { free (backup_filename ); return false; }
467+ p += 2 ;
468+
469+ /* Skip 'T' separator */
470+ p ++ ;
471+
472+ /* Hour (2 digits) */
473+ memcpy (buf , p , 2 );
474+ buf [2 ] = '\0' ;
475+ entry -> date .hour = (unsigned )strtoul (buf , & endptr , 10 );
476+ if (* endptr != '\0' ) { free (backup_filename ); return false; }
477+ p += 2 ;
478+
479+ /* Minute (2 digits) */
480+ memcpy (buf , p , 2 );
481+ buf [2 ] = '\0' ;
482+ entry -> date .minute = (unsigned )strtoul (buf , & endptr , 10 );
483+ if (* endptr != '\0' ) { free (backup_filename ); return false; }
484+ p += 2 ;
485+
486+ /* Second (2 digits) */
487+ memcpy (buf , p , 2 );
488+ buf [2 ] = '\0' ;
489+ entry -> date .second = (unsigned )strtoul (buf , & endptr , 10 );
490+ if (* endptr != '\0' ) { free (backup_filename ); return false; }
491+ p += 2 ;
492+ }
493+
494+ /* Expect '.' separator before CRC */
495+ if (* p != '.' )
496+ {
497+ free (backup_filename );
498+ return false;
499+ }
500+ p ++ ;
501+
502+ /* Parse 8-character hex CRC */
503+ crc = strtoul (p , & endptr , 16 );
504+ if (endptr != p + 8 || * endptr != '.' )
505+ {
506+ free (backup_filename );
507+ return false;
508+ }
509+ p = endptr + 1 ;
510+
511+ /* Parse backup mode */
512+ backup_mode = (unsigned )strtoul (p , & endptr , 10 );
513+ if (endptr == p || * endptr != '\0' )
428514 {
429515 free (backup_filename );
430516 return false;
@@ -438,7 +524,6 @@ static bool core_backup_add_entry(core_backup_list_t *backup_list,
438524 backup_list -> size ++ ;
439525
440526 free (backup_filename );
441-
442527 return true;
443528}
444529
0 commit comments