snprintf formatting specifier - c

In my code I use snprintf as below and could able to see the following behaviour
char text[30] = {0};
snprintf(text, sizeof(text), "%s", "hello");
printf("Interm... %s\n", text);
snprintf(text, "%20s", text);
printf("At the end ... %s\n", text);
Output
Interm... hello
At the end ...
As you can see if source and destinaton of snprintf are same, it clears the buffer. I want the output to be in the 20s format specifier. I can't do this in first step itself as I need to append multiple string and do the format specifier at the last step.
Is copying to the temporary buffer and from there to the original buffer the only possible solution? Can you please throw some light on this.

From the snprintf man page
C99 and POSIX.1-2001 specify that the results are undefined if a call
to sprintf(), snprintf(), vsprintf(), or vsnprintf() would cause
copying to take place between objects that overlap (e.g., if the
target string array and one of the supplied input arguments refer to
the same buffer).
This means that the line
snprintf(text, "%20s", text)
isn't valid. Your suggestion of using an additional, temporary, buffer is correct.

To get your goal accomplished: str1 followed by str2, all of that padded on the left with spaces, or truncated on the right, to a length of (wid) characters, stored in outbuf (which must be at least wid+1 chars wide) you can use just one sprintf...
size_t len1=strlen(str1), len2=strlen(str2);
if (len2 > wid-len1) { len2 = wid-len1; }
snprinf(outbuf, wid+1, "%*s%.%s", wid-len2, str1, len2, str2);
Note that (wid-len2) is the padded size of str1 on output, and len2 is the truncated size of str2 on output. The wid+1 buffer size in snprintf() is the guard against the pathological case where len1>wid.

Related

strncpy and strcat usage

My homework requires that my name be displayed such that it looks like this: 'Lastname, Firstname'. Last name then [comma space] firstname. While not moving over the rest of the text after that name. This is my code:
char str1[11];
char str2[3];
char str3[16];
strcpy (str1,fn);
strcpy (str2,", ");
strcpy (str3,ln);
strncat (str1, str2, 14);
strncat (str1, str3, 31);
My teacher said that I did what he wanted, but he doesn't like how many lines of code I used and said I am doing extra work than I need.
Variables: ln = last name, fn = first name I made str2 for the ', ' comma space.
What is it that he wants me to do?
Assuming you know length of strings, why not do
char result[50];
sprintf(result,"%s, %s",ln, fn);
char result[50];
strcpy(result, ln);
strcat(result, ", ");
strcat(result, fn);
He's right, you used way too many statements (and wasted too much memory doing it).
This is all you need:
strcpy (str1,ln); // Copy the last name to str1
strcat (str1,", "); // Now append ", " to the last name in str1
strcat (str1,fn); // Lastly, append the first name to str1
You must make sure that str1 is large enough to hold all of that. An array of 13 characters may not be sufficient.
Your strcpy calls are really unnecessary since you already have first name and last name in variables. You can just create a buffer big enough to hold the entire string then use strcat to create the final "lastname, firstname" string.
I'm surprised your instructor accepted your answer. You are accumulating your result in the char array str1, which you've declared has only 11 characters. Unless strlen(ln)+strlen(fn)<=8, you will overflow the space you've allocated for the result str1. In the bad old days, a C programmer would have simply allocated a result array that seemed big enough, and not bothered to check. The standard C library function sprintf in stdio.h is designed for this job:
#include <stdio.h>
...fn, ln defined...
char result[80]; /* 80 characters is probably enough */
sprintf(result, "%s, %s", ln, fn);
A modern C programmer would never assume that ln and fn were short enough not to overflow a result array, no matter how long. The safe, modern alternative would be to replace the sprintf call by snprintf:
snprintf(result, 80, "%s, %s", ln, fn);
You can also use the strcpy/strcat family of functions, which are in string.h. (Was this specified in your assignment?) The safe, modern equivalents are strncpy and strncat, which let you specify a maximum number of characters to copy. A quick and dirty but safe solution using these functions would be:
char result[80];
result[0] = '\0'; /* initialize result to "" */
strncat(result, ln, 40);
strcat(result, ", ") /* you know this adds exactly two characters */
strncat(result, fn, 38);
The strncpy call is dangerous because it may not leave a null-terminated string in result, which will cause a subsequent strcat to fail. This isn't many fewer lines than what you did, but using just these string.h functions, it's tough to do much better and be guaranteed you can't overflow the result buffer no matter what ln and fn are given.

sscanf reading multiple characters

I am trying to read a list of interfaces given in a config file.
char readList[3];
memset(readList,'\0',sizeof(readList));
fgets(linebuf, sizeof(linebuf),fp); //I get the file pointer(fp) without any error
sscanf(linebuf, "List %s, %s, %s \n",
&readList[0],
&readList[1],
&readList[2]);
Suppose the line the config file is something like this
list value1 value2 value3
I am not able to read this. Can someone please tell me the correct syntax for doing this. What am I doing wrong here.
The %s conversion specifier in the format string reads a sequence of non-whitespace characters and stores in buffer pointed to by the corresponding argument passed to sscanf. The buffer must be large enough to store the input string plus the terminating null byte which is added automatically by sscanf else it is undefined behaviour.
char readList[3];
The above statement defines readList to be an array of 3 characters. What you need is an array of characters arrays large enough to store the strings written by sscanf. Also the format string "List %s, %s, %s \n" in your sscanf call means that it must exactly match "List" and two commas ' in that order in the string linebuf else sscanf will fail due to matching failure. Make sure that you string linebuf is formatted accordingly else here is what I suggest. Also, you must guard against sscanf overrunning the buffer it writes into by specifying the maximum field width else it will cause undefined behaviour. The maximum field width should be one less than the buffer size to accommodate the terminating null byte.
// assuming the max length of a string in linebuf is 40
// +1 for the terminating null byte which is added by sscanf
char readList[3][40+1];
fgets(linebuf, sizeof linebuf, fp);
// "%40s" means that sscanf will write at most 40 chars
// in the buffer and then append the null byte at the end.
sscanf(linebuf,
"%40s%40s%40s",
readList[0],
readList[1],
readList[2]);
Your char readlist[3] is an array of three chars. What you need however is an array of three strings, like char readlist[3][MUCH]. Then, reading them like
sscanf(linebuf, "list %s %s %s",
readList[0],
readList[1],
readList[2]);
will perhaps succeed. Note that the alphabetic strings in scanf need to match character-by-character (hence list, not List), and any whitespace in the format string is a signal to skip all whitespace in the string up to the next non-whitespace character. Also note the absence of & in readList arguments since they are already pointers.

Reading a char array using %s i.e string specifier

char *ptr=(char*)calloc(n,sizeof(int));
using the above, we can allocate memory for char array. But is reading it character-by-character mandatory? How to read and access it using%s` i.e the string format specifier?
Reading character by character is not mandatory and using exactly %s is susceptible to buffer overruns. Specifying the maximum number of characters to read, one less than the number of bytes in the buffer being populated, prevents the buffer overrun. For example "%10s" reads a maximum of ten characters then assigns the null terminating character so the target buffer requires at least 11 bytes.
However, as the code suggests that n is unknown at compile time using %s with a dynamic width is not possible explicitly. But it would be possible to construct the format specifier (the format specifier is not required to be a string literal):
char fmt[32];
sprintf(fmt, "%%%ds", n - 1); /* If 'n == 10' then 'fmt == %9s' */
if (1 == scanf(fmt, ptr))
{
printf("[%s]\n", ptr);
}
An alternative would be fgets():
if (fgets(ptr, n, stdin))
{
}
but the behaviour is slightly different:
fgets() does use whitespace to terminate input.
fgets() will store the newline character if it encounters it.
Casting the return value of calloc() (or malloc() or realloc()) is unrequired (see Do I cast the result of malloc?) and the posted is confusing as it is allocating space for int[n] but is intended to be character array. Instead:
char* ptr = calloc(n, 1); /* 1 == sizeof(char) */
Also, if a null terminated string is being read into ptr the initialisation provided by calloc() is superfluous so a malloc() only would suffice:
char* ptr = malloc(n, 1);
And remember to free() whatever you malloc()d, calloc()d or realloc()d.
Yes, you can read such array using %s but make sure you have allocated enough memory for what you try to read(don't forget the terminating zero character!).

How do I use strlen() on a formatted string?

I'd like to write a wrapper function for the mvwprint/mvwchgat ncurses functions which prints the message in the specified window and then changes its attributes.
However, mvwchgat needs to know how many characters it should change - and I have no idea how to tell mvwchgat how long the formatted string is, since a strlen() on, for instance, "abc%d" obviously returns 5, because strlen doesn't know what %d stands for ...
In C99 or C11, you can use a line like this:
length = snprintf(NULL, 0, format_string, args);
From the manual of snprintf (emphasis mine):
The functions snprintf() and vsnprintf() do not write more than size bytes (including the terminating null byte ('\0')). If the output was truncated due to this limit then the return value is the number of characters (excluding the terminating null byte) which would have been written to the final string if enough space had been available. Thus, a return value of size or more means that the output was truncated.
Since we are giving snprintf 0 as the size, then the output is always truncated and the output of snprintf would be the number of characters that would have been written, which is basically the length of the string.
In C89, you don't have snprintf. A workaround is to create a temporary file, or if you are in *nix open /dev/null and write something like this:
FILE *throw_away = fopen("/dev/null", "w"); /* On windows should be "NUL" but I haven't tested */
if (throw_away)
{
fprintf(throw_away, "<format goes here>%n", <args go here>, &length);
fclose(throw_away);
} /* else, try opening a temporary file */
You can't know in advance how long your string will be:
printf("abc%d", 0); //4 chars
printf("abc%d", 111111111);//12 chars
All with the same format string.
The only sure way is to sprintf the text in question into a buffer, strlen(buffer) the result and printf("%s", buffer); the result to screen.
This solution avoids double formatting at the cost of allocating long enough buffer.

Using snprintf to avoid buffer overruns

I am using snprintf like this to avoid a buffer overrun:
char err_msg[32] = {0};
snprintf(err_msg, sizeof(err_msg) - 1, "[ ST_ENGINE_FAILED ]");
I added the -1 to reserve space for the null terminator in case the string is more than 32 bytes long.
Am I correct in my thinking?
Platform:
GCC 4.4.1
C99
As others have said, you do not need the -1 in this case. If the array is fixed size, I would use strncpy instead. It was made for copying strings - sprintf was made for doing difficult formatting. However, if the size of the array is unknown or you are trying to determine how much storage is necessary for a formatted string. This is what I really like about the Standard specified version of snprintf:
char* get_error_message(char const *msg) {
size_t needed = snprintf(NULL, 0, "%s: %s (%d)", msg, strerror(errno), errno);
char *buffer = malloc(needed+1);
sprintf(buffer, "%s: %s (%d)", msg, strerror(errno), errno);
return buffer;
}
Combine this feature with va_copy and you can create very safe formatted string operations.
You don't need the -1, as the reference states:
The functions snprintf() and
vsnprintf() do not write more than
size bytes (including the trailing
'\0').
Note the "including the trailing '\0'" part
No need for -1. C99 snprintf always zero-terminates. Size argument specifies the size of output buffer including zero terminator. The code, thus, becomes
char err_msg[32];
int ret = snprintf(err_msg, sizeof err_msg, "[ ST_ENGINE_FAILED ]");
ret contains actual number of characters printed (excluding zero terminator).
However, do not confuse with Microsoft's _snprintf (pre-C99), which does not null-terminate, and, for that matter, has completely different behaviour (e.g. returning -1 instead of would-be printed length in case if buffer is not big enough). If using _snprintf, you should be using the same code as in your question.
According to snprintf(3):
The functions snprintf() and vsnprintf() do not write more than size bytes (including the trailing '\0').
For the example given, you should be doing this instead:
char err_msg[32];
strncpy(err_msg, "[ ST_ENGINE_FAILED ]", sizeof(err_msg));
err_msg[sizeof(err_msg) - 1] = '\0';
or even better:
char err_msg[32] = "[ ST_ENGINE_FAILED ]";
sizeof will return the number of bytes the datatype will use in memory, not the length of the string. E.g. sizeof(int) returns '4' bytes on a 32-bit system (well, depending on the implementation I guess). Since you use a constant in your array, you can happily pass that to the printf.

Resources