* 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, ...)
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
}
#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