I'm trying to write a TCP server which a client can use to browse the server's directories. In addition to that I want to send the size of the directory if that is a regular file. The size of the file is saved into a size_t variable under a "stat" struct.
I'm doing this here:
char *fullName /* The path to the file *.
/**
* Some code here
*/
struct stat buffer;
lstat(fullName, &buffer)
So now buffer.st_size contains the file's size. Now I want to write() it to the listening socket but apparently I have to convert it to a string somehow. I know this can be done somehow with the bitwise right-shift (>>) operator but it seems too painful to me. Can you help me out here (even if there's no way other that bitwise operators)?
By the way this is not for school or smth...
PS: I'm running this on Linux.
You can use the members the sprintf()-family of functions to convert "something" to a "string".
#define _POSIX_C_SOURCE 200112L
#include <stdio.h>
#include <unistd.h>
#include <string.h>
int main(void)
{
size_t s = 123456789;
char str[256] = ""; /* In fact not necessary as snprintf() adds the
0-terminator. */
snprintf(str, sizeof str, "%zu", s);
fputs(stdout, "The size is '");
fflush(stdout);
write(fileno(stdout), str, strlen(str));
fputs(stdout, "'.\n");
return 0;
}
Prints out:
The size is '123456789'.
You know that size_t is unsigned, but the length is unknown. In C99, we have the z modifier for that, making the complete specifier %zu:
size_t s = 123456789;
char str[256];
snprintf(str, sizeof str, "%zu", s);
char * is not necessarily a string, you can send what you want.
Just manage that the other computer use the same protocol.
so you can do that :
write(socket, &(buffer.st_size), sizeof(size_t));
this is maybe to fast, you may have to take account of the endianness etc.
Related
I'm just starting to write in the C language, I ran into a small problem related more to algorithms than to the features of the language.
In my program, the task is to insert the file size after the _ symbol, if there is one in the file name.
I don't quite understand how this can be implemented, maybe someone will tell you and there is a ready-made algorithm that copes with this, insert a number into a string (array of characters)
Here is an example of my code, with explanations of where and what is being done:
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <string.h>
int main(){
DIR * p;
p = opendir("."); // Open catalog
if(p!=NULL){ // check on error
struct dirent * dir;
while((errno=0, dir=readdir(p))){ // Reading catalog
struct stat infoAboutFile;
int res = stat(dir->d_name, &infoAboutFile);
if(res==0){ // check on error
if(S_ISREG(infoAboutFile.st_mode)){ // Check on regular files
char str[256];
strcpy(str,dir->d_name);
int size = infoAboutFile.st_size; //Size file
printf("\n");
}
}else{
perror("Errors in Stat");
}
}
if(errno!=0){ // check on error
perror("Errors in Readdir");
}
int res = closedir(p);
if(res==-1){ // check on errorr
perror("Errors in Closedir");
}
}else{
perror("Errors in Opendir");
}
return 0;
}
The simplest way is to use sprintf:
sprintf( str, "%s_%d", dir->d_name, size );
You will need to make sure str is wide enough for the final string.
EDIT
William Pursell points out in the comments that there's also snprintf, where you specify the maximum number of characters to write to the target buffer such that you avoid an accidental buffer overflow:
snprintf( str, sizeof str, "%s_%d", dir->d_name, size );
Personally, I prefer making sure the target buffer is large enough for the final string, rather than risk truncating the string. The first thing we need to do is compute how large the buffer will need to be:
size_t str_len = strlen( dir->d_name ) // length of d_name
+ 10 // number of decimal digits in a 32-bit int
+ 1; // for the '_' character
If your compiler supports variable-length arrays, then we just declare an array with size str_len:
char str[ str_len + 1 ]; // +1 for the string terminator
Otherwise, we need to allocate that buffer dynamically:
char *str = malloc( str_len + 1 ); // +1 for the string terminator
Then you can use regular sprintf:
sprintf( str, "%s_%d", dir->d_name, size );
If you use malloc, then you will have to remember to deallocate str with free when you're done with it.
I'm making a webserver in C, and I want to allocate just one chunk of memory for everything (strings and arrays).
My allocation strategy starts with this. and bp is the buffer pointer for searches:
char *bp, *buf=malloc(1048576); // allocate 1MB
First 64KB will be the max space for the full HTTP request unprocessed (because I'm not dealing with uploaded file requests). The remainder of the 1MB that's allocated will contain each header that hopefully will be easily be accessible.
Now if I programmed the extraction code this way, I'd have no problem:
char *httpreq=buf+65536;
int linesize=8192; //size of each line
int httprn=0; // Http request header number. increments for each header found.
char *crlf;
while((crlf=strstr(bp,"\r\n"))){ //loop until no more enters are found
memmove(httpreq+(httprn*linesize),bp,crlf-bp);
bp+=2; //move pointer to skip CRLF.
httprn++;
}
But I'd rather program the code this way:
int linesize=8192; //size of each line
char *httpreq[linesize]=buf+65536;
int httprn=0;
while((crlf=strstr(bp,"\r\n"))){
memmove(httpreq[httprn++],bp+=2,crlf-bp); //skip CRLF
}
However the C compiler tells me that I have an invalid initializer and its referring to this particular line:
char *httpreq[linesize]=buf+65536;
is there any way I can use this kind of syntax:
httpreq[n]
instead of this:
httpreq+(linesize*n)
to read the HTTP header n without having to use local static memory?
This:
char httpreq[n][n];
would use static memory, but I'd rather use extended memory for string allocation.
Any ideas?
Yes, but you need to properly construct the pointers. Here is example of what you want to achieve:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define LINESIZE 8192
int main() {
char *buf = (char *) malloc(1048576);
char (*ht)[LINESIZE];
ht = (char (*)[])( buf + 65536);
printf("bf %p\n", buf);
printf("ht[0] %p\n", ht[0] );
printf("ht[1] %p\n", ht[1] );
sprintf(ht[0],"%s\n", "This is the first line");
sprintf(ht[1],"%s\n", "This is the second line");
printf("%s", ht[0]);
printf("%s", ht[1]);
}
so, the char (*ht)[LINESIZE] tells the compiler that ht is an array of char *, each one LINESIZE long.
The (char (*)[])(buff + 65536) is casting the calculation of the offset in the type of ht.
I've resumed C coding for fun after a several year absence.
I've given myself an exercise to safely copy text from standard input to strings using fgets(), and copy to a string just big enough, i.e. only with enough capacity to hold the no. of chars I've actually typed, ultimately to make lists, stacks etc. from scratch, in other words, playing with pointers.
The only way I've managed this smells of kludge to me, as I'm defining the destination string variable for strcpy() late in the control flow. Is there a more elegant/dynamic way to do this?
#inlcude <stdio.h>
#include <string.h>
#define MAXLENGTH 20
int main(int argc, char *argv[]) {
char message[MAXLENGTH];
printf("Enter a string: \n");
fgets(message, MAXLENGTH, stdin);
/* various tests here, omitted for brevity */
char destinationString[strlen(message)];
/*
* Just testing to prove that
* the strlen() of the destination
* string is LESS than MAXLENGTH
*/
printf("Here's the strlen() of destinationString: %lu\n", strlen(destinationString));
printf("Here's the sizeof() destinationString: %lu,\n" sizeof(destinationString));
printf("Here's the contents of the copy: %s", destinationString);
return 0;
}
You can certainly do this dynamically by using malloc.
Consider something like this:
int main(int argc, char *argv[]) {
char *destinationString;
/* ... */
/* Don't forget to allocate one extra byte for the termination character */
destinationString = malloc(strlen(message) + 1);
if (!destinationString)
return -1;
strcpy(destinationString, message);
/* Note: Normally, you should probably use strncpy to avoid overflow
but here, we're sure that there's enough space so strcpy is acceptable */
/* ... */
free(destinationString); /* When you're done using it */
/* ... */
}
I also pointed this out in the comments but to re-iterate, you actually need to allocate strlen(message) + 1 bytes in your destination string buffer or else it will overflow. The extra character is to store the null termination character at the end of C strings.
Code has a number of choices. Here are 2:
malloc() and later free() right sized memory similarly answered by #tangrs. Note that sizeof() destinationString will be the size of a pointer.
size_t size = strlen(message) + 1;
char *destinationString = malloc(size);
memcpy(destinationString, message, size);
Use variable length array, VLA, available in C99 and optionally in C11.
VLA approach with code clean-up
#include <string.h>
#define MAXLENGTH 20
int main(int argc, char *argv[]) {
char message[MAXLENGTH];
printf("Enter a string: \n");
if (fgets(message, sizeof message, stdin) == NULL) {
return -1;
}
// Use type `size_t`
size_t size = strlen(message) + 1;
char destinationString[size];
memcpy(destinationString, message, size);
// Notice "%zu"
// `sizeof destinationString` is the size of an array
printf("Here's the strlen() of destinationString: %zu\n", strlen(destinationString));
printf("Here's the sizeof() destinationString: %zu,\n" sizeof destinationString);
printf("Here's the contents of the copy: \"%s\"", destinationString);
return 0;
}
Input "Hello!" Enter
Here's the strlen() of destinationString: 8
Here's the sizeof() destinationString: 9,
Here's the contents of the copy: "Hello!
"
On my system the inputs ended with a "\r\n". To rid the buffer of those potential pesky characters, use:
fgets(message, sizeof message, stdin);
buffer[strcspn(message, "\r\n")] = '\0';
size_t size = strlen(message) + 1;
...
The following simple code is supposed to read one wide char from stdin and echo it back to stdout, except that it dies of SIGSEGV on the iconv() call. The question is – what's wrong with the code?
#include <unistd.h> /* STDIN_FILENO */
#include <locale.h> /* LC_ALL, setlocale() */
#include <langinfo.h> /* nl_langinfo(), CODESET */
#include <wchar.h> /* wchar_t, putwchar() */
#include <iconv.h> /* iconv_t, iconv_open(), iconv(), iconv_close() */
#include <stdlib.h> /* malloc(), EXIT_SUCCESS */
int main(void) {
setlocale(LC_ALL, ""); // We initialize the locale
iconv_t converter = iconv_open("WCHAR_T", nl_langinfo(CODESET)); // We initialize a converter
wchar_t out; // We allocate memory for one wide char on stack
wchar_t* pOut = &out;
size_t outLeft = sizeof(wchar_t);
while(outLeft > 0) { // Until we've read one wide char...
char in; // We allocate memory for one byte on stack
char* pIn=∈
size_t inLeft = 1;
if(read(STDIN_FILENO, pIn, 1) == 0) break; // We read one byte from stdin to the buffer
iconv(&converter, &pIn, &inLeft, (char**)&pOut, &outLeft); // We feed the byte to the converter
}
iconv_close(converter); // We deinitialize a converter
putwchar(out); // We echo the wide char back to stdout
return EXIT_SUCCESS;
}
UPDATE: After the following update based on #gsg's answer:
iconv(converter, &pIn, &inLeft, &pOut, &outLeft);
the code doesn't throw SIGSEGV anymore, but out == L'\n' for any non-ASCII input.
The signature of iconv is
size_t iconv(iconv_t cd,
char **inbuf, size_t *inbytesleft,
char **outbuf, size_t *outbytesleft);
But you call it with a first argument of pointer to iconv_t:
iconv(&converter, &pIn, &inLeft, (char**)&pOut, &outLeft);
Which should be
iconv(converter, &pIn, &inLeft, (char**)&pOut, &outLeft);
An interesting question is why a warning is not generated. For that, let's look at the definition in iconv.h:
/* Identifier for conversion method from one codeset to another. */
typedef void *iconv_t;
That's an... unfortunate choice.
I would program this a bit differently:
#define _XOPEN_SOURCE 500
#include <stdio.h>
#include <unistd.h>
#include <locale.h>
#include <langinfo.h>
#include <wchar.h>
#include <iconv.h>
#include <stdlib.h>
#include <err.h>
int main(void)
{
iconv_t converter;
char input[8]; /* enough space for a multibyte char */
wchar_t output[8];
char *pinput = input;
char *poutput = (char *)&output[0];
ssize_t bytes_read;
size_t error;
size_t input_bytes_left, output_bytes_left;
setlocale(LC_ALL, "");
converter = iconv_open("WCHAR_T", nl_langinfo(CODESET));
if (converter == (iconv_t)-1)
err(2, "failed to alloc conv_t");
bytes_read = read(STDIN_FILENO, input, sizeof input);
if (bytes_read <= 0)
err(2, "bad read");
input_bytes_left = bytes_read;
output_bytes_left = sizeof output;
error = iconv(converter,
&pinput, &input_bytes_left,
&poutput, &output_bytes_left);
if (error == (size_t)-1)
err(2, "failed conversion");
printf("%lc\n", output[0]);
iconv_close(converter);
return EXIT_SUCCESS;
}
I am by no means an expert, but here's an example that follows what you seem to be trying to do:
http://www.gnu.org/software/libc/manual/html_node/iconv-Examples.html
From the website:
The example also shows the problem of using wide character strings
with iconv. As explained in the description of the iconv function
above, the function always takes a pointer to a char array and the
available space is measured in bytes. In the example, the output
buffer is a wide character buffer; therefore, we use a local variable
wrptr of type char *, which is used in the iconv calls.
This looks rather innocent but can lead to problems on platforms that
have tight restriction on alignment. Therefore the caller of iconv has
to make sure that the pointers passed are suitable for access of
characters from the appropriate character set. Since, in the above
case, the input parameter to the function is a wchar_t pointer, this
is the case (unless the user violates alignment when computing the
parameter). But in other situations, especially when writing generic
functions where one does not know what type of character set one uses
and, therefore, treats text as a sequence of bytes, it might become
tricky.
Essentially, there are issues with alignment with iconv. In fact, there have been a few bugs listed regarding this very issue:
http://lists.debian.org/debian-glibc/2007/02/msg00043.html
Hope that this at least gets you started. I'd try using a char* instead of a wchar_t* for pOut, as shown in the example.
if I want to construct a const char * out of several primitive type arguments, is there a way to build the string using a similar to the printf?
You're probably looking for snprintf.
int snprintf(char *str, size_t size, const char *format, ...);
A simple example:
char buffer[100];
int value = 42;
int nchars = snprintf(buffer, 100, "The answer is %d", value);
printf("%s\n", buffer);
/* outputs: The answer is 42 */
GNU has an example too.
Just to add, you don't actually need to use snprintf - you can use the plain old sprintf (without the size argument) but then it is more difficult to ensure only n characters are written to the buffer. GNU also has a nice function, asprintf which will allocate the buffer for you.
You can use sprintf, which is exactly like printf except the first parameter is a buffer where the string will be placed.
Example:
char buffer[256];
sprintf(buffer, "Hello, %s!\n", "Beta");