way to send strings to stdout AND socket in 1 line - c

I want to write this to only 1 line:
fprintf(stdout, "RCPT TO: <%s>\r\n", argv[argc-1]);
fprintf(sockfd, "RCPT TO: <%s>\r\n", argv[argc-1]);
so i want to send the same string to stdout and to my open socket. How can I do this?

With
#include <stdarg.h>
int fprintf_both(FILE *a, FILE *b, const char *fmt, ...)
{
FILE *f[2];
const int n = sizeof(f) / sizeof(f[0]);
int i;
int sum = 0;
f[0] = a;
f[1] = b;
for (i = 0; i < n; i++) {
va_list ap;
int bytes;
va_start(ap, fmt);
bytes = vfprintf(f[i], fmt, ap);
va_end(ap);
if (bytes < 0)
return bytes;
else
sum += bytes;
}
return sum;
}
you can
fprintf_both(stdout, sockfd, "RCPT TO: <%s>\r\n", argv[argc-1]);

Not unless you want to write your own function that takes two File* and varargs, and calls fprintf twice.

I guess you want to do this to put it inside something like a while loop condition? You might like the C comma operator, e.g.
while ( f1(), f2() ) { //bla }
The comma causes f1() to be executed, it's return value discarded, followed by f2() and the its return value kept. (i.e. f2() should return an int or bool and f1() doesn't matter)

Related

Using va_args without passing num (like printf)

The following is the most basic example I can come up with to pass variable-length args to a function:
int printme(int num, ...)
{
va_list ap;
va_start(ap, num);
for (int i = 0; i < num; ++i) {
char *arg = va_arg(ap, char *);
printf("%d. %s\n", i + 1, arg);
}
va_end(ap);
return 1;
}
int main(void)
{
printme(2, "X", "YY");
}
However, notice that I am passing in the length as the first argument (or as any argument). Is it possible to use these va_ macros with something like a printf-ish function? For example, could I do something like this (without passing the number of args?
print2("Hello something %s %s", "Arg 1", "Arg 2");
print2("Hello something %s %s %s", "Arg 1", "Arg 2", "Arg 3");
If so, what would the function to receive that look like? And if it's not possible, how does printf implement it then?
If you only want to pass strings, it can be done quite easily by writing a function to parse the first argument like countargs(char*) that I've written here. It returns the number of arguments:
#include <stdarg.h>
#include <stdio.h>
int printme(char* fmt, ...)
{
va_list ap;
int num = countargs(fmt);
va_start(ap, num);
for (int i=0; i < num; ++i) {
char* arg = va_arg(ap, char*);
printf("%d. %s\n", i+1, arg);
}
va_end(ap);
return 1;
}
int countargs(char* fmt)
{
int i, num = 0;
if(strlen(fmt) < 2)
{
return 0;
}
for(i = 0; fmt[i+1] != '\0'; i++)
{
if (fmt[i] == '%' && fmt[i+1] == 's')
{
num++;
}
}
return num;
}
int main(void)
{
printme("%s%s", "Stack", "Overflow");
}
Here's a basic example of detecting the number of arguments from a parsed string. It's not taking into account any special circumstances and doesn't do anything with actually formatting/parsing the strings, but shows a basic working program to show how the count can be parsed from a string:
int printmy(char * format, ...)
{
// num args
int num = 0;
for(char idx=0, c; c=format[idx]; idx++) {
// ignore escape char + double-percent
if (c=='\\' || (c=='%' && format[idx+1]=='%'))
idx++;
else if (c=='%')
num++;
}
// print variable args
va_list ap;
va_start(ap, format); // need to give it the last argument before the "..."
for (int i=0; i < num; ++i) {
char* arg = va_arg(ap, char*);
printf("%d. %s\n", i+1, arg);
}
va_end(ap);
// return num args received
return num;
}
int main(void)
{
int num_args = printmy("Hello something \% \\% %% %s %s %s", "Arg 1", "Arg 2", "Arg 3");
printf("Num Args Parsed: %d\n", num_args);
}
And we get:
1. Arg 1
2. Arg 2
3. Arg 3
4.
Num Args Parsed: 4
You could use macroprocessor to count arguments.
#include <stdarg.h>
#include <stdio.h>
int printme(int num, ...)
{
va_list ap;
va_start(ap, num);
for (int i=0; i < num; ++i) {
char* arg = va_arg(ap, char*);
printf("%d. %s\n", i+1, arg);
}
va_end(ap);
return 1;
}
#define TENTH(p1,p2,p3,p4,p5,p6,p7,p8,p9,p10,...) p10
#define NARGS(...) TENTH(__VA_ARGS__,9,8,7,6,5,4,3,2,1,0)
#define printme(...) printme(NARGS(__VA_ARGS__), __VA_ARGS__)
int main(void)
{
printme("X", "YY");
}
How it works:
Macro TENTH simply returns the tenth argument
Example:
TENTH(a,b,c,d,e,f,g,h,i,j,k)
expands as j.
It can used to obtain a number of argument:
| tenth argument
v
TENTH("X", "XY", 9,8,7,6,5,4,3,2,1,0)
expand as 2 because 2 is the tenth argument of TENTH.
Similarly,
| tenth argument
v
TENTH("A", "B", "C", "D", 9,8,7,6,5,4,3,2,1,0)
expands as 4.
Macro NARGS places its arguments before sequence 9,8,... shifting the sequence to get the number of arguments.
I've used printme to avoid polluting namespace.Whenever a macro is expand is get disabled to avoid infinite recursion. It will not expand any further becaming a call to original printme(). The first argument num is computed with NARGS macro. All remaining arguments are passed after the num one.
There are still some disadvantages:
works up to ten arguments, but this limitation can be easily lifted
requires at least one argument (it's possible to workaround but it is complex)

Why is this program crashing?

This function should return a string that contains string representations of the second and subsequent arguments, each to two decimal places and separated by commas. The first argument is a count of the number of arguments that follow. I did some research and found out about va_list and tried to work with it. Unfortunately the program is crashing and I have no idea why. I'm posting this hoping that someone could maybe spot an obvious mistake or something like that.
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
char *to_string(int count,...)
{
int i,x,k=0;
float tmp;
va_list valist;
va_start(valist, count+1);
char comma=',';
char buffer[count*6];
for(i=0;i<count;i++)
{
tmp=va_arg(valist, double)*100;
x=tmp/100.0;
k+=4;
snprintf(buffer + k, "%C",comma);
k++;
snprintf(buffer + 1, "%.2f", x);
}
va_end(valist);
printf("%s", buffer);
return buffer;
}
int main()
{
to_string(2,3.14876,6.123243);
}
As noted in the comments, you need to read the manual pages for va_start() and snprintf() in particular, and also pay attention to memory management (you cannot safely return a pointer to a local (non-static) variable).
Here's a revamped piece of code that cleans up the issues identified and that doesn't crash (for me on my Mac running macOS Sierra 10.12.1 and GCC 6.2.0).
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
static char *to_string(char *buffer, size_t buflen, int count, ...)
{
va_list valist;
va_start(valist, count);
char *data = buffer;
for (int i = 0; i < count; i++)
{
int x = va_arg(valist, double) * 100;
double tmp = x / 100.0 + 0.005;
int n = snprintf(data, buflen, ",%.2f", tmp);
if (n > (int)(buflen - 1))
{
fprintf(stderr, "%s: buffer not big enough\n", __func__);
break;
}
data += n;
buflen -= n;
}
va_end(valist);
printf("%s: [%s]\n", __func__, buffer);
return buffer;
}
int main(void)
{
char buffer[80];
printf("main: [%s]\n", to_string(buffer, sizeof(buffer), 2, 3.14876, 6.123243));
return 0;
}
Example output:
to_string: [,3.15,6.12]
main: [,3.15,6.12]
Note that there's the leading comma which you probably don't want. That can be fixed like this:
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
static char *to_string(char *buffer, size_t buflen, int count, ...)
{
va_list valist;
va_start(valist, count);
char *data = buffer;
const char *pad = "";
for (int i = 0; i < count; i++)
{
int x = va_arg(valist, double) * 100;
double tmp = x / 100.0 + 0.005;
int n = snprintf(data, buflen, "%s%.2f", pad, tmp);
if (n > (int)(buflen - 1))
{
fprintf(stderr, "%s: buffer not big enough\n", __func__);
break;
}
pad = ",";
data += n;
buflen -= n;
}
va_end(valist);
printf("%s: [%s]\n", __func__, buffer);
return buffer;
}
int main(void)
{
char buffer[80];
printf("main: [%s]\n", to_string(buffer, sizeof(buffer), 2, 3.14876, 6.123243));
printf("main: [%s]\n", to_string(buffer, sizeof(buffer), 20,
6.67, 0.04, 8.81, 8.49, 3.50, 1.20, 4.28, 0.67, 1.93, 5.63,
5.30, 8.43, 1.99, 4.62, 5.54, 7.21, 9.43, 2.02, 4.77, 0.29));
return 0;
}
Example output:
to_string: buffer not big enough
to_string: [3.15,6.12]
main: [3.15,6.12]
to_string: [6.67,0.04,8.82,8.50,3.50,1.20,4.29,0.68,1.93,5.63,5.30,8.44,1.99,4.62,5.54,7.21]
main: [6.67,0.04,8.82,8.50,3.50,1.20,4.29,0.68,1.93,5.63,5.30,8.44,1.99,4.62,5.54,7.21]

create a function with variable pointers parameters in C

how to create a function with variable pointers parameters that
take any number of pointer and print it such as :
print("hello ","world ");
print("i'm ","adil"," blah "," blah");
result like that :
$ ./myprogramme
hello word im adil blah blah
I found stdarg.h but i don't know how to make it ?
In C, you can create a variadic function using stdarg.h. Example from Wikipedia link,
#include <stdarg.h>
double average(int count, ...)
{
va_list ap;
int j;
double sum = 0;
va_start(ap, count); /* Requires the last fixed parameter
(to get the address) */
for (j = 0; j < count; j++) {
sum += va_arg(ap, double); /* Increments ap to the next argument. */
}
va_end(ap);
return sum / count;
}
This is almost what you wanted, but it needed a NULL terminator
#include <stdio.h>
#include <stdarg.h>
void print (char *first, ...)
{
va_list argptr;
char *next;
va_start (argptr, first);
next = first;
while (next) {
printf ("%s", next);
next = va_arg(argptr, char*);
}
va_end (argptr);
}
int main()
{
print("hello ","world ", NULL);
print("i'm ","adil"," blah "," blah", NULL);
return 0;
}
But by adding a different argument, there's an easy answer anyway
printf("%s%s", "hello ","world ");
printf("%s%s%s%s", "i'm ","adil"," blah "," blah");

How to print contents of va_list in ansi c

I am trying to print the contents of a va_list,
I want to pass an array to it
I am getting gibrish in return
int printVA(int num_args,...);
int main(int argc, const char * argv[])
{
int numArgs = 3;
int arr [3];
arr[0]=183;
arr[1]=184;
arr[2]=15;
printVA(numArgs,arr);
return 0;
}
int printVA(int num_args,...){
va_list arg_list;
int my_arg;
va_start(arg_list, num_args);
for(int i = 0; i<num_args;i++){
my_arg = va_arg(arg_list, int);
printf("%d\n", my_arg);
}
va_end(arg_list);
return 1;
}
this is what i get
1606416584
15
1606416584
You are calling it incorrectly, pass the arguments themselves, not an array of them:
printVA(numArgs, arr[0], arr[1], arr[2]);
or simply:
printVA(numArgs, 183, 184, 15);
On the other hand, if you really want to pass the array, va_list is not the right solution.

Concatenation in C

I have a program which does concatenation.
its like char *testConc(int a,..)
Where a indicates number of arguments are being passed for concatenation.
As legth keeps on changing is there is anything like constructor overloading in C
or any simple syntax which implements the functionality
Yes, there are varadic functions
#include <stdio.h>
#include <stdarg.h>
/* print all non-negative args one at a time;
all args are assumed to be of int type */
void printargs(int arg1, ...)
{
va_list ap;
int i;
va_start(ap, arg1);
for (i = arg1; i >= 0; i = va_arg(ap, int))
printf("%d ", i);
va_end(ap);
putchar('\n');
}
int main(void)
{
printargs(5, 2, 14, 84, 97, 15, 24, 48, -1);
printargs(84, 51, -1);
printargs(-1);
printargs(1, -1);
return 0;
}
C does not have function overloading capabilities. The syntax you have is called a variadic function, which can be used to perform what you asked.
The textConc function would look something like this:
char *textConc(int argc, ...)
{
va_list args;
char *str = NULL;
size_t len = 0;
va_start(args, argc);
while (argc--)
{
/* next string */
const char *temp = va_arg(args, const char *);
size_t size = strlen(temp);
/* make room and copy over */
str = realloc(str, len+size+1);
memcpy(str+len, temp, size+1);
/* new length */
len += size;
}
va_end(args);
return str;
}
int main(int argc, char **argv)
{
char *example = textConc(4, "Hello", "All", "good", "morning");
puts(example);
free(example);
return 0;
}
If you use GCC, we can fake overloading completely, using a little help of macros.
Rename textConc to textConcN and use the following macros:
#define ARGCOUNT(...) (sizeof((const char *[]){__VA_ARGS__})/sizeof(const char *))
#define textConc(...) textConcN(ARGCOUNT(__VA_ARGS__), __VA_ARGS__)
int main(int argc, char **argv)
{
/* notice, no more need for the number of arguments */
char *example = textConc("Hello", "All", "good", "morning");
puts(example);
free(example);
return 0;
}
Functions can't be overloaded in C.
You could rewrite your function as char *testConc(const char *s, ...), where you mark the end of the list with NULL:
testConc("foo", "bar", "baz", "quux", (char *)0);
This makes adding changing the number of actual arguments easier. If you have a C99 compiler, you can even write a wrapping macro that adds the NULL for you:
#define TESTCONC(...) testConc(__VA_ARGS__, (char *)0)

Resources