Reputation: 31
I want to modify a string like this:—
MainString="", ToUpdate="ABC" -> return# "ABC=1"
MainString="ABC=1", ToUpdate="ABC" -> return# "ABC=2"
MainString="ABC=2", ToUpdate="ABC" -> return# "ABC=3"
MainString="ABC=3", ToUpdate="XYZ" -> return# "ABC=3:XYZ=1"
MainString="ABC=3:XYZ=1", ToUpdate="XYZ" -> return# "ABC=3:XYZ=2"
MainString="ABC=3:XYZ=2", ToUpdate="XYZ" -> return# "ABC=3:XYZ=3"
I have the following function:
void UpdateString(char *MainString, char ToUpdate[20])
{
char *pData[50][2];
char *saveptr1=NULL;
int i=0,j=0,nIsPresentFlag=0;
unsigned int CdrCnt=1;
char workbuf1[200];
char workbuf[200];
memset(workbuf,0,200);
memset(workbuf1,0,200);
if(strlen(MainString)>0)
{
strcat(MainString,":");
}
strcpy(workbuf1,MainString);
pData[i][0]=strtok_r(workbuf1,"=",&saveptr1);
pData[i][1]=strtok_r(NULL,":",&saveptr1);
if(pData[i][0]) {i++;pData[i][0]=NULL; pData[i][1]=NULL;}
while((pData[i][0]=strtok_r(NULL,"=",&saveptr1)))
{
pData[i][1]=strtok_r(NULL,":",&saveptr1);
i++;
}
for(j=0;j<i;j++)
if(strncmp(ToUpdate,pData[j][0],strlen(pData[j][0]))==0)
{
CdrCnt=atoi(pData[j][1]);
CdrCnt+=1;
sprintf(pData[j][1],"%d",CdrCnt);
nIsPresentFlag=1;
break;
}
if(nIsPresentFlag==1)
for(j=0;j<i;j++)
sprintf(workbuf,"%s%s=%s:",workbuf,pData[j][0],pData[j][1]);
else
sprintf(workbuf,"%s%s=%d:",MainString,ToUpdate,1);
workbuf[strlen(workbuf)-1]='\0';
memset(MainString,0,200);
strcpy(MainString,workbuf);
}
Curiously, this function is working, but sometimes causing a core dump with segfault.
What is wrong with this code? Any better way I can manage the above task?
==============================================================================
String declaration:
char MainString[200];
The call is like:
UpdateString((char*)&MainString,"ABC");
Upvotes: 1
Views: 122
Reputation: 754420
Given this test code:
static void chkit(char *s, char *u)
{
printf("[%s] && [%s]", s, u);
UpdateString(s, u);
printf(" ==> [%s]\n", s);
}
int main(void)
{
char MainString[200] = "";
chkit(MainString, "ABC");
chkit(MainString, "ABC");
chkit(MainString, "ABC");
chkit(MainString, "XYZ");
chkit(MainString, "XYZ");
chkit(MainString, "XYZ");
chkit(MainString, "DEF");
chkit(MainString, "ABC");
chkit(MainString, "ABC");
chkit(MainString, "ABC");
chkit(MainString, "ABC");
chkit(MainString, "ABC");
chkit(MainString, "ABC");
chkit(MainString, "ABC");
chkit(MainString, "XYZ");
chkit(MainString, "ABC");
chkit(MainString, "GHI");
return 0;
}
The output I get is:
[] && [ABC] ==> [ABC=1]
[ABC=1] && [ABC] ==> [ABC=2]
[ABC=2] && [ABC] ==> [ABC=3]
[ABC=3] && [XYZ] ==> [ABC=3:XYZ=1]
[ABC=3:XYZ=1] && [XYZ] ==> [ABC=3:XYZ=2]
[ABC=3:XYZ=2] && [XYZ] ==> [ABC=3:XYZ=3]
[ABC=3:XYZ=3] && [DEF] ==> [ABC=3:XYZ=3:DEF=1]
[ABC=3:XYZ=3:DEF=1] && [ABC] ==> [ABC=4:XYZ=3:DEF=1]
[ABC=4:XYZ=3:DEF=1] && [ABC] ==> [ABC=5:XYZ=3:DEF=1]
[ABC=5:XYZ=3:DEF=1] && [ABC] ==> [ABC=6:XYZ=3:DEF=1]
[ABC=6:XYZ=3:DEF=1] && [ABC] ==> [ABC=7:XYZ=3:DEF=1]
[ABC=7:XYZ=3:DEF=1] && [ABC] ==> [ABC=8:XYZ=3:DEF=1]
[ABC=8:XYZ=3:DEF=1] && [ABC] ==> [ABC=9:XYZ=3:DEF=1]
[ABC=9:XYZ=3:DEF=1] && [ABC] ==> [ABC=10:=3:DEF=1]
[ABC=10:=3:DEF=1] && [XYZ] ==> [ABC=10:=3:DEF=1:XYZ=1]
[ABC=10:=3:DEF=1:XYZ=1] && [ABC] ==> [ABC=11:3:DEF=1:XYZ=1]
[ABC=11:3:DEF=1:XYZ=1] && [GHI] ==> [ABC=11:3:DEF=1:XYZ=1:GHI=1]
There's clearly a problem when the numbers increase from 1 digit to 2 digits.
In the code:
if (nIsPresentFlag == 1)
for (j = 0; j < i; j++)
sprintf(workbuf, "%s%s=%s:", workbuf, pData[j][0], pData[j][1]);
you are invoking undefined behaviour by writing into workbuf
and passing it as one of the parameters. That is simply dangerous. There's a moderate chance you'll get away with it, but 'get away' is the operative term — there's no guarantee that it will work.
The overwrite problem occurs when you format the new number into insufficient space.
The code below seems to work:
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
static void UpdateString(char *MainString, char ToUpdate[20])
{
char *pData[50][2];
char *saveptr1 = NULL;
int i = 0;
int nIsPresentFlag = 0;
char workbuf1[200];
char workbuf[200];
char extra[16];
if (strlen(MainString) > 0)
strcat(MainString, ":");
strcpy(workbuf1, MainString);
pData[i][0] = strtok_r(workbuf1, "=", &saveptr1);
pData[i][1] = strtok_r(NULL, ":", &saveptr1);
if (pData[i][0])
i++;
while ((pData[i][0] = strtok_r(NULL, "=", &saveptr1)) != 0)
{
pData[i][1] = strtok_r(NULL, ":", &saveptr1);
i++;
}
for (int j = 0; j < i; j++)
{
if (strncmp(ToUpdate, pData[j][0], strlen(pData[j][0])) == 0)
{
unsigned int CdrCnt = atoi(pData[j][1]);
CdrCnt += 1;
pData[j][1] = extra;
sprintf(pData[j][1], "%u", CdrCnt);
nIsPresentFlag = 1;
break;
}
}
if (nIsPresentFlag == 1)
{
char *dst = workbuf;
for (int j = 0; j < i; j++)
{
int n = sprintf(dst, "%s=%s:", pData[j][0], pData[j][1]);
/* Broken if sprintf() returns -1 */
dst += n;
}
}
else
sprintf(workbuf, "%s%s=%d:", MainString, ToUpdate, 1);
workbuf[strlen(workbuf)-1] = '\0';
strcpy(MainString, workbuf);
}
static void chkit(char *s, char *u)
{
printf("[%s] && [%s]", s, u);
UpdateString(s, u);
printf(" ==> [%s]\n", s);
}
int main(void)
{
char MainString[200] = "";
chkit(MainString, "ABC");
chkit(MainString, "ABC");
chkit(MainString, "ABC");
chkit(MainString, "XYZ");
chkit(MainString, "XYZ");
chkit(MainString, "XYZ");
chkit(MainString, "DEF");
chkit(MainString, "ABC");
chkit(MainString, "ABC");
chkit(MainString, "ABC");
chkit(MainString, "ABC");
chkit(MainString, "ABC");
chkit(MainString, "ABC");
chkit(MainString, "ABC");
chkit(MainString, "XYZ");
chkit(MainString, "ABC");
chkit(MainString, "GHI");
chkit(MainString, "PQRSTU");
chkit(MainString, "I");
chkit(MainString, "I");
chkit(MainString, "I");
chkit(MainString, "PQRSTU");
return 0;
}
It leaves out the memset()
operations; a null terminated string can be copied over arbitrary data and as long as you don't go looking beyond the null terminator, you won't have any problems. The variable extra
is used to store the new number; it avoids problems when the number changes from N to N+1 digits. The function sprintf()
returns the number of characters it wrote; that is used to add the data to the work buffer safely.
[] && [ABC] ==> [ABC=1]
[ABC=1] && [ABC] ==> [ABC=2]
[ABC=2] && [ABC] ==> [ABC=3]
[ABC=3] && [XYZ] ==> [ABC=3:XYZ=1]
[ABC=3:XYZ=1] && [XYZ] ==> [ABC=3:XYZ=2]
[ABC=3:XYZ=2] && [XYZ] ==> [ABC=3:XYZ=3]
[ABC=3:XYZ=3] && [DEF] ==> [ABC=3:XYZ=3:DEF=1]
[ABC=3:XYZ=3:DEF=1] && [ABC] ==> [ABC=4:XYZ=3:DEF=1]
[ABC=4:XYZ=3:DEF=1] && [ABC] ==> [ABC=5:XYZ=3:DEF=1]
[ABC=5:XYZ=3:DEF=1] && [ABC] ==> [ABC=6:XYZ=3:DEF=1]
[ABC=6:XYZ=3:DEF=1] && [ABC] ==> [ABC=7:XYZ=3:DEF=1]
[ABC=7:XYZ=3:DEF=1] && [ABC] ==> [ABC=8:XYZ=3:DEF=1]
[ABC=8:XYZ=3:DEF=1] && [ABC] ==> [ABC=9:XYZ=3:DEF=1]
[ABC=9:XYZ=3:DEF=1] && [ABC] ==> [ABC=10:XYZ=3:DEF=1]
[ABC=10:XYZ=3:DEF=1] && [XYZ] ==> [ABC=10:XYZ=4:DEF=1]
[ABC=10:XYZ=4:DEF=1] && [ABC] ==> [ABC=11:XYZ=4:DEF=1]
[ABC=11:XYZ=4:DEF=1] && [GHI] ==> [ABC=11:XYZ=4:DEF=1:GHI=1]
[ABC=11:XYZ=4:DEF=1:GHI=1] && [PQRSTU] ==> [ABC=11:XYZ=4:DEF=1:GHI=1:PQRSTU=1]
[ABC=11:XYZ=4:DEF=1:GHI=1:PQRSTU=1] && [I] ==> [ABC=11:XYZ=4:DEF=1:GHI=1:PQRSTU=1:I=1]
[ABC=11:XYZ=4:DEF=1:GHI=1:PQRSTU=1:I=1] && [I] ==> [ABC=11:XYZ=4:DEF=1:GHI=1:PQRSTU=1:I=2]
[ABC=11:XYZ=4:DEF=1:GHI=1:PQRSTU=1:I=2] && [I] ==> [ABC=11:XYZ=4:DEF=1:GHI=1:PQRSTU=1:I=3]
[ABC=11:XYZ=4:DEF=1:GHI=1:PQRSTU=1:I=3] && [PQRSTU] ==> [ABC=11:XYZ=4:DEF=1:GHI=1:PQRSTU=2:I=3]
I did some basic checking with valgrind
(and added some dynamic memory allocation) and it came up with a clean bill of health.
Note the style of diagnostic printing. It shows the inputs and the output, which is helpful. And it encloses strings in a distinctive marker (here []
) so that stray spaces etc can be spotted more easily.
Upvotes: 5