]> git.ipfire.org Git - thirdparty/bacula.git/commitdiff
BEE Backport bacula/src/lib/scan.c
authorAlain Spineux <alain@baculasystems.com>
Thu, 23 Apr 2020 14:32:40 +0000 (16:32 +0200)
committerEric Bollengier <eric@baculasystems.com>
Thu, 29 Apr 2021 08:44:17 +0000 (10:44 +0200)
This commit is the result of the squash of the following main commits:

Author: Alain Spineux <alain@baculasystems.com>
Date:   Mon Apr 8 13:57:48 2019 +0200

    new scan_string() to be used where "old" bsscanf() don't work

Author: Eric Bollengier <eric@baculasystems.com>
Date:   Sun Apr 7 11:56:45 2019 +0200

    Make bsscanf compatible with the old bsscanf

Author: Eric Bollengier <eric@baculasystems.com>
Date:   Fri Apr 5 20:38:00 2019 +0200

    Fix bsscanf again with empty strings at the end of line

Author: Alain Spineux <alain@baculasystems.com>
Date:   Fri Apr 5 13:56:08 2019 +0200

    fix bsscanf continue parsing + more unittest

    - error is used for error and not for a "no match"
      - error in the format string like %b where 'b' is not a valid
        format character
    - the return code let you know HOW many "real" matches you got
    - lot of new tests
       - check respect string limit
       - check negative numbers, up to the 63/64 bit limits

Author: Eric Bollengier <eric@baculasystems.com>
Date:   Fri Apr 5 17:48:19 2019 +0200

    Fix issue with scan

Author: Alain Spineux <alain@baculasystems.com>
Date:   Fri Mar 29 14:28:11 2019 +0100

    Fix bsscanf() returns the wrong number of conversion

    - returns the right number of successful conversion
      bsscanf("1", "%d %d",..)==2 must be 1
    - mimic sscanf for empty strings
    - return EOF instead of doing a segfault when the input or fmt string are
      NULL

Author: Alain Spineux <alain@baculasystems.com>
Date:   Fri Mar 29 09:32:09 2019 +0100

    regress: Add unittest scan_test to test bsscanf() in scan.c

    - test bsscanf()

Author: Kern Sibbald <kern@sibbald.com>
Date:   Mon Feb 7 15:01:09 2011 +0100

    Add negative numbers to bsscanf

bacula/src/lib/scan.c

index 4a8d2628972d6b31de653ef0601299664d274906..b09e399f0f7817700c0f30c0790498c6d569d8a0 100644 (file)
@@ -345,6 +345,8 @@ void split_path_and_filename(const char *fname, POOLMEM **path, int *pnl,
  * Note, BIG is the default maximum length when no length
  *   has been specified for %s. If it is not big enough, then
  *   simply add a length such as %10000s.
+ *
+ * Don't use this bsscanf() anymore, use scan_string() instead
  */
 const int BIG = 1000;
 int bsscanf(const char *buf, const char *fmt, ...)
@@ -492,6 +494,183 @@ switch_top:
    return count;
 }
 
+/*
+ * scan_string() is more strict than the standard sscanf() function.
+ * The format sting must match exactly ! If so the function
+ * return then number or argument (%) parsed, else it return EOF
+ * One space in the format string match one or more spaces in the input string
+ * see the unittest below to see how it works.
+ *
+ */
+int scan_string(const char *buf, const char *fmt, ...)
+{
+   va_list ap;
+   int count = 0;
+   void *vp;
+   char *cp;
+   int l = 0;
+   int max_len = BIG;
+   uint64_t value;
+   bool error = false;
+   bool negative;
+
+   if ((buf==NULL || fmt==NULL)) {
+      return EOF; // original sscanf segfault
+   }
+
+   if (*buf=='\0' && *fmt!='\0') {
+      return EOF; // mimic sscanf
+   }
+
+   va_start(ap, fmt);
+   while (*fmt && !error) {
+//    Dmsg1(000, "fmt=%c\n", *fmt);
+      if (*fmt == '%') {
+         fmt++;
+//       Dmsg1(000, "Got %% nxt=%c\n", *fmt);
+switch_top:
+         switch (*fmt++) {
+         case 'u':
+            value = 0;
+            if (!B_ISDIGIT(*buf)) {
+               error = true;
+               break;
+            }
+            while (B_ISDIGIT(*buf)) {
+               value = B_TIMES10(value) + *buf++ - '0';
+            }
+            vp = (void *)va_arg(ap, void *);
+//          Dmsg2(000, "val=%lld at 0x%lx\n", value, (long unsigned)vp);
+            if (l == 0) {
+               *((int *)vp) = (int)value;
+            } else if (l == 1) {
+               *((uint32_t *)vp) = (uint32_t)value;
+//             Dmsg0(000, "Store 32 bit int\n");
+            } else {
+               *((uint64_t *)vp) = (uint64_t)value;
+//             Dmsg0(000, "Store 64 bit int\n");
+            }
+            count++;
+            l = 0;
+            break;
+         case 'd':
+            value = 0;
+            if (*buf == '-') {
+               negative = true;
+               buf++;
+            } else {
+               negative = false;
+            }
+            if (!B_ISDIGIT(*buf)) {
+               error = true;
+               break;
+            }
+            while (B_ISDIGIT(*buf)) {
+               value = B_TIMES10(value) + *buf++ - '0';
+            }
+            if (negative) {
+               value = -value;
+            }
+            vp = (void *)va_arg(ap, void *);
+//          Dmsg2(000, "val=%lld at 0x%lx\n", value, (long unsigned)vp);
+            if (l == 0) {
+               *((int *)vp) = (int)value;
+            } else if (l == 1) {
+               *((int32_t *)vp) = (int32_t)value;
+//             Dmsg0(000, "Store 32 bit int\n");
+            } else {
+               *((int64_t *)vp) = (int64_t)value;
+//             Dmsg0(000, "Store 64 bit int\n");
+            }
+            count++;
+            l = 0;
+            break;
+         case 'l':
+//          Dmsg0(000, "got l\n");
+            l = 1;
+            if (*fmt == 'l') {
+               l++;
+               fmt++;
+            }
+            if (*fmt == 'd' || *fmt == 'u') {
+               goto switch_top;
+            }
+//          Dmsg1(000, "fmt=%c !=d,u\n", *fmt);
+            error = true;
+            break;
+         case 'q':
+            l = 2;
+            if (*fmt == 'd' || *fmt == 'u') {
+               goto switch_top;
+            }
+//          Dmsg1(000, "fmt=%c !=d,u\n", *fmt);
+            error = true;
+            break;
+         case 's':
+//          Dmsg1(000, "Store string max_len=%d\n", max_len);
+            cp = (char *)va_arg(ap, char *);
+            while (*buf && !B_ISSPACE(*buf) && max_len-- > 0) {
+               *cp++ = *buf++;
+            }
+            *cp = 0;
+            count++;
+            max_len = BIG;
+            break;
+         case 'c':
+            cp = (char *)va_arg(ap, char *);
+            if (*buf == '\0') {
+               error = true;
+               break;
+            }
+            *cp = *buf++;
+            count++;
+            break;
+         case '%':
+            if (*buf++ != '%') {
+               error = true;
+            }
+            break;
+         default:
+            fmt--;
+            max_len = 0;
+            while (B_ISDIGIT(*fmt)) {
+               max_len = B_TIMES10(max_len) + *fmt++ - '0';
+            }
+//          Dmsg1(000, "Default max_len=%d\n", max_len);
+            if (*fmt == 's') {
+               goto switch_top;
+            }
+//          Dmsg1(000, "Default c=%c\n", *fmt);
+            error = true;
+            break;                    /* error: unknown format */
+         }
+         continue;
+
+      /* White space eats ONE or more whitespace */
+      } else if (B_ISSPACE(*fmt)) {
+         /* Ignore extra white space in the format string */
+         while (B_ISSPACE(*fmt)) fmt++;
+         if (!B_ISSPACE(*buf)) {
+            error = true;
+         }
+         while (B_ISSPACE(*buf)) {
+            buf++;
+         }
+      /* Plain text must match */
+      } else if (*buf++ != *fmt++) {
+//       Dmsg2(000, "Mismatch buf=%c fmt=%c\n", *--buf, *--fmt);
+         error = true;
+         break;
+      }
+   }
+   va_end(ap);
+// Dmsg2(000, "Error=%d count=%d\n", error, count);
+   if (error) {
+      count = -1;
+   }
+   return count;
+}
+
 /*
  * Return next name from a comma separated list.  Note, this
  *   routine is destructive because it stored 0 at the end
@@ -545,76 +724,270 @@ char *next_name(char **s)
 }
 
 #ifdef TEST_PROGRAM
+#include "unittests.h"
+
+#undef sscanf
+
 int main(int argc, char *argv[])
 {
-   char buf[100];
-   uint32_t val32;
-   uint64_t val64;
-   uint32_t FirstIndex, LastIndex, StartFile, EndFile, StartBlock, EndBlock;
-   char Job[200];
-   int cnt;
-   char *helloreq= "Hello *UserAgent* calling\n";
-   char *hello = "Hello %127s calling\n";
-   char *catreq =
-"CatReq Job=NightlySave.2004-06-11_19.11.32 CreateJobMedia FirstIndex=1 LastIndex=114 StartFile=0 EndFile=0 StartBlock=208 EndBlock=2903248";
-static char Create_job_media[] = "CatReq Job=%127s CreateJobMedia "
-  "FirstIndex=%u LastIndex=%u StartFile=%u EndFile=%u "
-  "StartBlock=%u EndBlock=%u\n";
-static char OK_media[] = "1000 OK VolName=%127s VolJobs=%u VolFiles=%u"
-   " VolBlocks=%u VolBytes=%" lld " VolMounts=%u VolErrors=%u VolWrites=%u"
-   " MaxVolBytes=%" lld " VolCapacityBytes=%" lld " VolStatus=%20s"
-   " Slot=%d MaxVolJobs=%u MaxVolFiles=%u InChanger=%d"
-   " VolReadTime=%" lld " VolWriteTime=%" lld;
-   char *media =
-"1000 OK VolName=TestVolume001 VolJobs=0 VolFiles=0 VolBlocks=0 VolBytes=1 VolMounts=0 VolErrors=0 VolWrites=0 MaxVolBytes=0 VolCapacityBytes=0 VolStatus=Append Slot=0 MaxVolJobs=0 MaxVolFiles=0 InChanger=1 VolReadTime=0 VolWriteTime=0";
-struct VOLUME_CAT_INFO {
-   /* Media info for the current Volume */
-   uint32_t VolCatJobs;               /* number of jobs on this Volume */
-   uint32_t VolCatFiles;              /* Number of files */
-   uint32_t VolCatBlocks;             /* Number of blocks */
-   uint64_t VolCatBytes;              /* Number of bytes written */
-   uint32_t VolCatMounts;             /* Number of mounts this volume */
-   uint32_t VolCatErrors;             /* Number of errors this volume */
-   uint32_t VolCatWrites;             /* Number of writes this volume */
-   uint32_t VolCatReads;              /* Number of reads this volume */
-   uint64_t VolCatRBytes;             /* Number of bytes read */
-   uint32_t VolCatRecycles;           /* Number of recycles this volume */
-   int32_t  Slot;                     /* Slot in changer */
-   bool     InChanger;                /* Set if vol in current magazine */
-   uint32_t VolCatMaxJobs;            /* Maximum Jobs to write to volume */
-   uint32_t VolCatMaxFiles;           /* Maximum files to write to volume */
-   uint64_t VolCatMaxBytes;           /* Max bytes to write to volume */
-   uint64_t VolCatCapacityBytes;      /* capacity estimate */
-   uint64_t VolReadTime;              /* time spent reading */
-   uint64_t VolWriteTime;             /* time spent writing this Volume */
-   char VolCatStatus[20];             /* Volume status */
-   char VolCatName[MAX_NAME_LENGTH];  /* Desired volume to mount */
-};
-   struct VOLUME_CAT_INFO vol;
-
-#ifdef xxx
-   bsscanf("Hello_world 123 1234", "%120s %ld %lld", buf, &val32, &val64);
-   printf("%s %d %lld\n", buf, val32, val64);
-
-   *Job=0;
-   cnt = bsscanf(catreq, Create_job_media, &Job,
-      &FirstIndex, &LastIndex, &StartFile, &EndFile,
-      &StartBlock, &EndBlock);
-   printf("cnt=%d Job=%s\n", cnt, Job);
-   cnt = bsscanf(helloreq, hello, &Job);
-   printf("cnt=%d Agent=%s\n", cnt, Job);
-#endif
-   cnt = bsscanf(media, OK_media,
-               vol.VolCatName,
-               &vol.VolCatJobs, &vol.VolCatFiles,
-               &vol.VolCatBlocks, &vol.VolCatBytes,
-               &vol.VolCatMounts, &vol.VolCatErrors,
-               &vol.VolCatWrites, &vol.VolCatMaxBytes,
-               &vol.VolCatCapacityBytes, vol.VolCatStatus,
-               &vol.Slot, &vol.VolCatMaxJobs, &vol.VolCatMaxFiles,
-               &vol.InChanger, &vol.VolReadTime, &vol.VolWriteTime);
-   printf("cnt=%d Vol=%s\n", cnt, vol.VolCatName);
+   Unittests test1("bsscanf_test");
+   {
+      int cnt;
+      char buf[512];
+      int32_t a = -99;
+
+      cnt=bsscanf("CatReq JobId=5 CreateFileMedia\n", "CatReq JobId=%ld CreateFileMedia\n", &a);
+      ok(cnt == 1 && a==5, "[CatReq JobId=5 CreateFileMedia] => [CatReq JobId=%ld CreateFileMedia]");
+
+      cnt=bsscanf("CatReq JobId=5 CreateJobMedia\n", "CatReq JobId=%ld CreateFileMedia\n", &a);
+      ok(cnt == -1, "[CatReq JobId=5 CreateJobMedia] => [CatReq JobId=%ld CreateFileMedia]");
+
+      uint32_t FirstIndex, LastIndex, StartFile, EndFile, StartBlock, EndBlock;
+      const char *catreq = "CatReq Job=NightlySave.2004-06-11_19.11.32 CreateJobMedia FirstIndex=1 LastIndex=114 StartFile=0 EndFile=0 StartBlock=208 EndBlock=2903248";
+      const char *Create_job_media = "CatReq Job=%127s CreateJobMedia "
+     "FirstIndex=%u LastIndex=%u StartFile=%u EndFile=%u "
+     "StartBlock=%u EndBlock=%u\n";
+      memset(buf, '\0', sizeof(buf));
+      FirstIndex=LastIndex=StartFile=EndFile=StartBlock=EndBlock=231170;
+      cnt = bsscanf(catreq, Create_job_media, &buf,
+         &FirstIndex, &LastIndex, &StartFile, &EndFile,
+         &StartBlock, &EndBlock);
+      ok(cnt==7 && strcmp(buf, "NightlySave.2004-06-11_19.11.32")==0 &&
+            FirstIndex==1 && LastIndex==114 && StartFile==0 &&
+            EndFile==0 && StartBlock==208 && EndBlock==2903248, "CatReq Job");
+
+   }
+
+   Unittests test2("scan_string_test");
+   {
+      int a,b,c,d,e,f;
+      int u;
+      int32_t cnt;
+      int64_t g;
+      char buf[512];
+
+      strcpy(buf, "xxx");
+      cnt=scan_string("string=", "string=%256s", buf);
+      ok(cnt==1 && *buf == 0, "[string=] => [string=%256s]");
+      //Dmsg2(0, "cnt=%d buf=%s\n", cnt, buf);
+
+      a = b = c = d = e =f = g = -99;
+      cnt=scan_string("1 2 223 6 0\n", "%d %d %d %d %d %d %lld\n",
+                  &a,&b,&c,&d,&e,&f,&g);
+      ok(cnt == -1, "[1 2 223 6 0] => [%d %d %d %d %d %d %lld]");
+
+      a = b = c = d = e =f = g = -99;
+      cnt=scan_string("1 2 -223 -0 0\n", "%d %d %d %d %lld\n", &a,&b,&c,&d,&g);
+      ok(cnt == 5 && a==1 && b==2 && c==-223 && d==0 && g==0,
+            "[1 2 -223 -0 0] => [%u %u %u %u %u %u %lld]");
+
+      u = 99;
+      cnt=scan_string("-223\n", "%u\n", &u);
+      ok(cnt == -1 && u == 99, "[-223] => [%u]");
+
+//      printf("%d %d %d %d %d %lld\n", cnt, a, b, c, d, g);
+
+      Pmsg0(0, "Test scan_string bug ignore final 0\n");
+      {
+         int a, b, cnt;
+         a=b=-99;
+         cnt=scan_string("123", "%d %d", &a, &b); /* Format error */
+         ok(cnt==-1, "Check count [%d %d] => [123]");
+         ok(a==123, "Check first value [%d %d] => [123]");
+         ok(b==-99, "Check second value [%d %d] => [123] ");
+      }
+
+      memset(buf, '\0', sizeof(buf)); a=b=-99;
+      cnt=scan_string("Hello  world   123 456   end", "Hello %100s %d %d end", buf, &a, &b);
+      ok(cnt==3 && strcmp(buf, "world")==0 && a==123 && b==456, "multi space");
+
+      Pmsg0(0, "Test scan_string basic tests\n");
+      {
+         char buf[100];
+         int a, b, c, cnt;
+         uint32_t val32;
+         uint64_t val64;
+         int32_t sval32;
+         int64_t sval64;
+
+         memset(buf, '\0', sizeof(buf)); a=b=-99;
+         cnt=scan_string("Hello world 123 456 end", "Hello %100s %d %d end", buf, &a, &b);
+         ok(cnt==3 && strcmp(buf, "world")==0 && a==123 && b==456, "test1");
+
+         memset(buf, '\0', sizeof(buf)); val64=val32=99;
+         cnt=scan_string("world 123 72057594037927953", "%120s %ld %lld", buf, &val32, &val64);
+         ok(cnt==3 && strcmp(buf, "world")==0 && val32==123 && val64==72057594037927953, "test2");
+
+
+         memset(buf, '\0', sizeof(buf)); a=b=-99;
+         cnt=scan_string("Hello world 123", "Hello %100s %d %d end", buf, &a, &b);
+         //ok(cnt==2 && strcmp(buf, "world")==0 && a==123 && b==-99, "one missing"); // scanf compatible
+         ok(cnt==-1 && strcmp(buf, "world")==0 && a==123 && b==-99, "one missing"); // scanf compatible
+
+         a=b=-99;
+         cnt=scan_string("Hello % -123", "Hello %% %d", &a);
+         ok(cnt==1 && a==-123, "match %%");
+
+         a=b=-99;
+         cnt=scan_string("", "Hello %d %d end", &a, &b);
+         ok(cnt==-1 && a==-99 && b==-99, "empty string 1");
+
+         a=b=-99;
+         cnt=scan_string("", "%d", &a);
+         ok(cnt==-1 && a==-99, "empty string 2");
+
+         a=b=-99;
+         cnt=scan_string("", "", &a);
+         ok(cnt==0 && a==-99, "empty string and format string");
+
+         a=b=-99;
+         cnt=scan_string("Hello", "", &a);
+         ok(cnt==0 && a==-99, "empty string and format string");
+
+         // Original sscanf segfault if any of the format or the input string are NULL
+         // scan_string return EOF instead
+
+   #if 1
+         /* Float scan is not supported yet by scan_string */
+         a=b=-99;
+         cnt=scan_string("3.14 17", "%d %d", &a, &b);
+         ok(cnt==-1 && a==3 && b==-99, "parse float instead of int");
+   #endif
+
+         a=b=c=-99;
+         cnt=scan_string("Hello 123 c=2", "Hello %d %d c=%d", &a, &b, &c);
+         ok(cnt==-1 && a==123 && b==-99, "bug continue parsing after a 'no match'");
+         //      printf("%d %d %d %d\n", cnt, a, b, c);
+
+         a=b=c=-99;
+         cnt=scan_string("Hello -123 456 end", "Hello %d %d", &a, &b);
+         ok(cnt==2 && a==-123 && b==456, "negative number");
+
+   #if 1
+         a=b=c=-99;
+         cnt=scan_string("Hello -123 - 456", "Hello %d %d", &a, &b, &c);
+         ok(cnt==-1 && a==-123 && b==-99 && c==-99, "incomplete negative number");
+   #endif
+
+         a=b=c=-99;
+         cnt=scan_string("Hello -123 - 456", "Hello %d %d", &a, &b);
+         ok(cnt==-1 && a==-123, "incomplete negative number");
+
+         // 2^63==9223372036854775808
+         val32=val64=sval32=sval64=99;
+         cnt=scan_string("9223372036854775809", "%lld", &val64);
+         ok(cnt==1 && val64==9223372036854775809UL, "long >63bits");
+
+         val32=val64=sval32=sval64=99;
+         cnt=scan_string("-9223372036854775807", "%lld", &sval64);
+         ok(cnt==1 && sval64==-9223372036854775807L, "unsigned long <63bits");
+
+         a=b=c=-99; memset(buf, 'A', sizeof(buf)); buf[sizeof(buf)-1]='\0';
+         cnt=scan_string("Hello world", "Hello %2s", buf);
+         ok(cnt==1 && strcmp(buf, "wo")==0 && buf[3]=='A', "string limitation");// This match sscanf
+
+   #if 0
+         /* The format is not correct */
+         a=b=c=-99; memset(buf, 'A', sizeof(buf)); buf[sizeof(buf)-1]='\0';
+         cnt=scan_string("Hello world 123", "Hello %2s %d", buf, &a);
+         ok(cnt==1 && a==-99 && strcmp(buf, "wo")==0 && buf[3]=='A', "string limitation & stop matching"); // This match sscanf
+   #endif
+         a=b=c=-99; memset(buf, 'A', sizeof(buf)); buf[sizeof(buf)-1]='\0';
+         cnt=scan_string("Hello world 123", "Hello %2s %d", buf, &a);
+         ok(cnt==-1 && a==-99 && strcmp(buf, "wo")==0 && buf[3]=='A', "string limitation & stop matching");
+
+         a=b=c=-99; memset(buf, 'A', sizeof(buf)); buf[sizeof(buf)-1]='\0';
+         cnt=scan_string("Hello world", "Hello %2srld", buf);
+         ok(cnt==1 && strcmp(buf, "wo")==0 && buf[3]=='A', "string limitation weird 1");// This match sscanf
+
+         a=b=c=-99; memset(buf, 'A', sizeof(buf)); buf[sizeof(buf)-1]='\0';
+         cnt=scan_string("Hello world 123", "Hello %2srld %d", buf, &a);
+         ok(cnt==2 && a==123 && strcmp(buf, "wo")==0 && buf[3]=='A', "string limitation weird 2");// This match sscanf
+
+         {
+            Pmsg0(0, "Test FD-SD hello\n");
+
+            char job_name[500];
+            int fd_version, sd_version, tlspsk;
+            const char *s="Hello Bacula SD: Start Job backupXXX 14 tlspsk=100";
+            cnt=-99, fd_version=-99, sd_version=-99, tlspsk=-99;
+            cnt=scan_string(s, "Hello Bacula SD: Start Job %127s %d %d tlspsk=%d", job_name, &fd_version, &sd_version, &tlspsk);
+   #if 0
+            /* The format is incorrect, it should return -1 */
+            ok(cnt==2 && strcmp(job_name, "backupXXX")==0 && fd_version==14 && sd_version==-99 && tlspsk==-99, "SD-FD hello fmt1");
+   #endif
+            ok(cnt==-1 && strcmp(job_name, "backupXXX")==0 && fd_version==14, "SD-FD hello fmt1");
+            // printf("%d %d %d %d %s\n", cnt, fd_version, sd_version, tlspsk, job_name);
+
+            cnt=-99, fd_version=-99, sd_version=-99, tlspsk=-99;
+            cnt=scan_string(s, "Hello Bacula SD: Start Job %127s %d %d", job_name, &fd_version, &sd_version);
+   #if 0
+            /* The format is incorrect, it should return -1 */
+            ok(cnt==2 && strcmp(job_name, "backupXXX")==0 && fd_version==14 && sd_version==-99 && tlspsk==-99, "SD-FD hello fmt2");
+   #endif
+            ok(cnt==-1 && strcmp(job_name, "backupXXX")==0 && fd_version==14, "SD-FD hello fmt2");
+
+            cnt=-99, fd_version=-99, sd_version=-99, tlspsk=-99;
+            cnt=scan_string(s, "Hello Bacula SD: Start Job %127s %d tlspsk=%d", job_name, &fd_version, &tlspsk);
+            ok(cnt==3 && strcmp(job_name, "backupXXX")==0 && fd_version==14 && sd_version==-99 && tlspsk==100, "SD-FD hello fmt3");
+
+            cnt=-99, fd_version=-99, sd_version=-99, tlspsk=-99;
+            cnt=scan_string(s, "Hello Bacula SD: Start Job %127s %d", job_name, &fd_version);
+            ok(cnt==2 && strcmp(job_name, "backupXXX")==0 && fd_version==14 && sd_version==-99 && tlspsk==-99, "SD-FD hello fmt4");
+
+            cnt=-99, fd_version=-99, sd_version=-99, tlspsk=-99;
+            cnt=scan_string(s, "Hello FD: Bacula Storage calling Start Job %127s %d", job_name, &sd_version);
+            ok(cnt==-1 && fd_version==-99 && sd_version==-99 && tlspsk==-99, "SD-FD hello fmt5");
+         }
+
+         Pmsg0(0, "Test bscanf vs sscanf behavior, all are expected to return an error\n");
+         memset(buf, '\0', sizeof(buf)); a=b=-99;
+         cnt=scan_string("DO YOU HAVE FIRE", "NO MATCH"); // => cnt==0
+         ok(cnt==-1, "when literal dont match");
+
+         // BUT !!!
+         cnt=scan_string("Literal", "Literal");
+         ok(cnt==0, "literal (strcmp like)");
+
+         /* scan_string parameters order: string_to_test, pattern */
+         Pmsg0(0, "Test scan_string test starting with literal\n");
+         cnt=scan_string("YES", "NO");
+         ok(cnt==-1, "wrong literal YES NO");
+
+         cnt=scan_string("Hello World", "Hello Format");
+         ok(cnt==-1, "different end");
+
+         cnt=scan_string("Hello", "HelloFormat");
+         ok(cnt==-1, "end missing in format (format incomplete)");
+
+         cnt=scan_string("Hello", "Hello Format");
+         ok(cnt==-1, "end missing in format with space  (format incomplete)");
+
+         cnt=scan_string("Hello World", "Hello");
+         ok(cnt==0, "format shorter than string");
+
+         cnt=scan_string("Hello World", "Hello %d", &a);
+   #if 0
+         /* the format is not valid */
+         ok(cnt==0, "format shorter with try to match");
+   #endif
+         ok(cnt==-1, "format shorter with try to match");
+
+         a = -99;
+         cnt=scan_string("3000 OK Hello 30005", "3000 OK Hello %d", &a);
+         ok(cnt==1 && a == 30005, "[3000 OK Hello 30005] => [3000 OK Hello %d]");
+         Dmsg2(0, "cnt=%d a=%d\n", cnt, a);
+
+         a = -99; strcpy(buf, "xxx");
+         cnt=scan_string("string=", "string=%256s", buf);
+         ok(cnt==1 && *buf == 0, "[string=] => [string=%256s]");
+         Dmsg2(0, "cnt=%d buf=%s\n", cnt, buf);
+
+      }
+   }
+
 
+   return report();
 }
 
 #endif