Archive

Archive for October, 2013

Logging to debug MySQL

October 30, 2013 No comments

It’s common, when using more or less complex stored procedures, to make mistakes that make us waste much time, and sometimes we would like to see the value of a variable or the result of a query because it could tell us what’s going wrong.

This is a little help, an easy piece of code we can insert into our database and will let is write in a table what’s happening.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
DROP SCHEMA IF EXISTS `PBUtils` ;
CREATE SCHEMA IF NOT EXISTS `PBUtils` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci ;

USE PBUtils;

DROP TABLE IF EXISTS `PBUtils`.`LOG`;
CREATE TABLE IF NOT EXISTS `PBUtils`.`LOG` (
   `Log_id` bigint UNSIGNED NOT NULL AUTO_INCREMENT,
   `Log_date` DATETIME NOT NULL,
   `Log_description` varchar(128) NOT NULL,
   `Log_text` TEXT NULL,
   `Log_number` bigint NULL,
   `Log_float` double NULL,
   PRIMARY KEY(Log_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

DELIMITER $$
DROP PROCEDURE IF EXISTS `PBUtils`.`logText` $$
CREATE PROCEDURE `PBUtils`.`logText`(
    IN  In_description VARCHAR(128),
    IN  In_text TEXT
)
BEGIN
    INSERT INTO PBUtils.LOG (`Log_date`, `Log_description`, `Log_text`) VALUES (NOW(), In_description, In_text);
END $$
DELIMITER ;

DELIMITER $$
DROP PROCEDURE IF EXISTS `PBUtils`.`logNumber` $$
CREATE PROCEDURE `PBUtils`.`logNumber`(
    IN  In_description VARCHAR(128),
    IN  In_number bigint
)
BEGIN
    INSERT INTO PBUtils.LOG (`Log_date`, `Log_description`, `Log_number`) VALUES (NOW(), In_description, In_number);
END $$
DELIMITER ;

DELIMITER $$
DROP PROCEDURE IF EXISTS `PBUtils`.`logFloat` $$
CREATE PROCEDURE `PBUtils`.`logFloat`(
    IN  In_description VARCHAR(128),
    IN  In_float double
)
BEGIN
    INSERT INTO PBUtils.LOG (`Log_date`, `Log_description`, `Log_float`) VALUES (NOW(), In_description, In_float);
END $$
DELIMITER ;

Now, what we have to do is insert our procedure to debug values, anywhere in our code:

1
PBUtils.logText('Text to describe', variable);
1
PBUtils.logNumber('Text to describe', variable);
1
PBUtils.logFloat('Text to describe', variable);

depending on what we want to record, a text, number or float

To see what has been written in our log:

1
SELECT * FROM PBUtils.LOG ORDER BY Log_data DESC

Backup MySQL users

October 28, 2013 No comments

When we are dumping a MySQL database, is normal to ignore “mysql”, “test” and “information_schema” databases. We could have undesirable problems when restoring, for example if we change MySQL version.
But sometimes, when backing up the database we want to save also user table information. If we have several applications using the same database, we should have a user for each application and we don’t want to lose them.

So we could use the query SHOW GRANTS FOR [email protected] in MySQL. We could automate it to give us all user permissions with a little bash script:

1
2
USERINFO="-uroot -ppasswd"
mysql $USERINFO -BNe "select concat('SHOW GRANTS FOR \'',user,'\'@\'',host,'\'; ') from mysql.user where user != 'root'" | mysql $USERINFO -BN | sed 's/$/;/g' > users_backup.sql

In the USERINFO variable, we store login data for MySQL, we can use root user for this.

With the first call, we generate the queries we are going to make to the database to get user privileges generation SQL. We look who are the users, their hosts and we generate for each row the following string:

1
SHOW GRANTS FOR ‘user’@'host’;

In the second call, we make all queries, and it should return for each one of them something like this:

1
2
GRANT USAGE ON *.* TOuser’@'localhost’ IDENTIFIED BY PASSWORD ‘*1C3116A0C895332DE9EE63FFF08863CC9397ED8E’
GRANT ALL PRIVILEGES ON `database`.* TO ‘user’@'
localhost’

If the user has privileges on more tables or databases, they will appear here too.

The third call is to finish each line with ; (semicolon), so MySQL won’t give us problems when restoring them.

To restore the users just do:

1
mysql -uroot -ppaswwd < users_backup.sql

Know PHP methods visibility (public, protected, private)

October 24, 2013 No comments

When coding in Object Oriented PHP, sometimes we would like to know the visibility of certain methods. For example, we are using an instance of an unknown class in compile-time (or coding time) with its name in a variable just like:

1
2
3
4
5
<?php
  $class = (isset($_GET['class']))?$_GET['class']:false;
  if ($class)
    $myObject = new $class;
?>

So, if we are going to call any method, we should know its visibility first. Another example, could be when we are checking the visibility in a base class. A subclass (“Heir”) of “Base” was instanced, and some methods of Base are going to call methods of Heir, so Base must know if they can be called.

Let’s check the visibility with this code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
<?php
function methodType($class, $method)
{
  try
    {
      $method = new ReflectionMethod($class, $method);
     
      if ($method->isPrivate())
    return 'private';
      elseif ($method->isProtected())
    return 'protected';
      elseif ($method->isPublic())
    return 'public';
    }
  catch (Exception $e)
    {
      return 'error';
    }
}

class MyClass
{
  function __construct()
  {
  }

  private function myPrivate()
  {
    echo "shhh... this is private\n";
  }

  public function myPublic()
  {
    echo "Hello everybody!!\n";
  }

  protected function myProtected()
  {
    echo "Protected method!!\n";
  }

}

$myclass = new MyClass();

echo 'myPrivate: '.methodType($myclass, 'myPrivate')."\n";
echo 'myPublic: '.methodType($myclass, 'myPublic')."\n";
echo 'myProtected: '.methodType($myclass, 'myProtected')."\n";
echo 'notExists: '.methodType($myclass, 'notExists')."\n";
?>

Here we have methodType() function, giving the class and method, it will return a string “public”, “private”, “protected” or “error”. This function uses ReflectionMethod class from PHP, it can do lots of things more.4

Well, back to the second example, I’d like to record calls of some methods, so I will create a superclass MyClassBase with a invoker method used to call other methods inside the other class only if they are public:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
<?php
function methodType($class, $method)
{
  try
    {
      $method = new ReflectionMethod($class, $method);
     
      if ($method->isPrivate())
    return 'private';
      elseif ($method->isProtected())
    return 'protected';
      elseif ($method->isPublic())
    return 'public';
    }
  catch (Exception $e)
    {
      return 'error';
    }
}

class MyClassBase
{
  function __construct()
  {
    echo "Class created\n";
  }
 
  public function invoker($method, $arguments=array())
  {
  try
    {
      $method = new ReflectionMethod($this, $method);
     
      if ($method->isPublic())
    {
      echo "I'm invoking method ".$method->name."...\n";
      return $method->invokeArgs($this, $arguments);
    }
      else
    echo "Method ".$method->name." is not public!!\n";
    }
  catch (Exception $e)
    {
      echo "Error invoking ".$method."\n";
    }
  }
}

class MyClass extends MyClassBase
{
  function __construct()
  {
    echo "Creating MyClass...\n";
  }

  private function myPrivate()
  {
    echo "shhh... this is private\n";
  }

  public function myPublic()
  {
    echo "Hello everybody!!\n";
  }

  public function myPublicCalls()
  {
    echo "I'm a public method that calls private methods...\n";
    $this->myPrivate();
  }

  public function myPublicArguments($arg1, $arg2)
  {
    echo "I'm a public method with two arguments: ".$arg1." and ".$arg2."\n";
  }

  protected function myProtected()
  {
    echo "Protected method!!\n";
  }

}

$myclass = new MyClass();

$myclass->invoker('notExists');
$myclass->invoker('myPrivate');
$myclass->invoker('myPublic');
$myclass->invoker('myPublicCalls');
$myclass->invoker('myPublicArguments', array('argument1', 'argument2'));
?>

The answer will be:

Creating MyClass…
Error invoking notExists
Method myPrivate is not public!!
I’m invoking method myPublic…
Hello everybody!!
I’m invoking method myPublicCalls…
I’m a public method that calls private methods…
shhh… this is private
I’m invoking method myPublicArguments…
I’m a public method with two arguments: argument1 and argument2

In fact, when a private or inexistent method is called, invoker() will give an error, and we can capture it easily, avoiding a PHP error to be triggered, or we can throw an exception. And if the method is public, we can run code before and after calling it, so we can log this call.

Another interesting thing is invoking the method with arguments, these arguments must be specified inside an array, so instead of calling:

1
2
3
<?php
myPublicArguments('argument1', 'argument2');
?>

When using the invoker we must do:

1
2
3
<?php
$myclass->invoker('myPublicArguments', array('argument1', 'argument2'));
?>

Get the HOME directory in C

October 23, 2013 No comments

In our programs, it’s usual to know where is the user home directory, to read/store configuration files, to search for something, or to know if the program is installed globally or locally.

There is a short function to get it:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <pwd.h>

char *getHomeDir()
{
  static char *home = NULL;
 
  if (!home)
    {
      home = getenv("HOME") ;
    }
  if (!home)
    {
      struct passwd *pw = getpwuid(getuid());
      if (pw)
          home = pw->pw_dir ;
    }
  return home;
}

int main(int argc, char *argv[])
{
  printf ("HOME: %s\n", getHomeDir());

  return EXIT_SUCCESS;
}

If we look at getHomeDir() function, the directory we’re looking for is stored in a static variable, as we are not freeing this variable, we will get the value of this variable when we ask again (the user isn’t changing his home). We will ask the system just for the first time.

In the other hand, we have two ways of getting the PATH, the first is with the HOME environment variable, most of the times it will be defined and we can get it, but if it’s not there, we can get this information from /etc/passwd. There is much more information that what we want and we can have a look at the manual of getpwuid() to know what we can get (the user password is not included, muahaha, it isn’t stored there anymore, and if it is in an old or embedded system, you will probably see a hash).

Foto: Neetesh Gupta (Flickr) CC-by.

stermp.h, trying to port conio.h to Linux

October 22, 2013 No comments

This time I want to rescue an old project. I started it long ago. These days I’ve been reading some source codes in facebook using conio.h so I hope this could be interesting for anyone.

Of course there are some libraries that allow us to to write strings in colors and get/set position on screen and keys without echoing and pressing Enter, or we can do it without them, using ANSI codes directly but we would have to do a lot of changes in the source code.

I tried to keep the name of the functions the same, we use:

  • clrscr() : To clean screen
  • textbackground(color) : To change background color
  • textcolor(color) : To change text color
  • gotoxy(x,y) : Go to specific position
  • wherex() : To get X position
  • wherey() : To get Y position
  • getch() : To get a key press without ENTER
  • getche() : Like getch but echoing character on screen
  • kbhit() : To know if a key has been pressed without stopping execution. Returns true or false

We also have some additional stuff like:

  • wherexy() : Returns X,Y position in a struct
  • kbhit2() : Gets a key code if pressed without stopping execution
  • kbhit_pre() : Prepares to do lots of kbhits() to increase performance
  • restore_terminal_color() : Restores terminal color
  • screenheight() : Gets screen height.
  • screenwidth() : Gets screen width.

I tried also to keep color names the same. Let’s see an example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include <stdio.h>
#include <time.h>
#include "stermp.h"

void update_time()
{
  struct tm *tm;
  time_t _time;
  char text[50];
  textcolor(YELLOW);
  _time=time(NULL);
  tm = localtime(&_time);
  strftime(text,50,"%d/%m/%Y %H:%M:%S", tm);
  gotoxy(1,1);
  printf("%s    ", text);
}

int main()
{
  int x,y;
  int width, height;
  int key;
  term_init();

  width = screenwidth();
  height = screenheight();
  /* Rellenamos de verde la pantalla */
  textbackground(GREEN);
  clrscr();

  textbackground(BLUE);
  /* Rellenamos de azul la primera fila */
  for (x=0; x<width; x++)
    printf(" ");

  gotoxy(1,height);
  /* Rellenamos de azul la última fila */
  for (x=0; x<width; x++)
    printf(" ");

  gotoxy(2,2);
  while ((key=kbhit2())==0)
      update_time();

  printf("You have pressed: %d\n", key);

  term_defaults();
 
}

We can see I’m calling term_init() and term_defaults() but they are just to restore terminal after the execution ends.
You can download the source code on github. Just include stermp.h in your code and include stermp.h and stermp.c in your project.

Replacing substrings in C++, this time using maps, for multiple replacements

October 18, 2013 No comments

Some days ago, we talked about how to replace substrings inside a string in C++. We finally got a method to just copy and paste into our projects, but when we want to replace multiple substrings we will get some ugly code, and some times it won’t fit.

We will use, one common container in C++ called map, it’s just a collection of associations between two values, we can see it as an array of key and value elements. So we will associate some substrings with another substring (we will associate fromStrs with toStrs. We also will make a replace() function accepting two initial arguments (the big string, and the map), we will look for each one of the keys and replace them like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include <iostream>
#include <string>
#include <map>

using namespace std;

string replace(string source, std::map<string,string>strMap, int offset=0, int times=0)
{
  int total = 0;
  string::size_type pos;

  for (std::map<string, string>::iterator i=strMap.begin(); i!=strMap.end(); ++i)
    {
      string fromStr = i->first;
      string toStr = i->second;
      pos=offset;
      while ( (pos = source.find(fromStr, pos)) < string::npos)
    {
      if ( (times!=0) && (total++>=times) )
        return source;  // Don't work anymore

      source.replace(pos, fromStr.length(), toStr);
      pos+=toStr.size();
    }
    }
  return source;
}

int main()
{
  string original = "I usually write silly things when testing my programs.";

  map<string,string> mapa;
  mapa["usually"] = "always";
  mapa["silly things"] = "lorem ipsum";

  cout << "Original string: "<<original<<endl;

  cout << "Resulting string: "<<replace2(original, mapa)<<endl;

  return 0;
}

In this case, we can add as much elements as we want to the map, and all of them will be searched in the big string. This function is good when we don’t know the fromStr and toStr in compilation time (we can generate them in runtime), we want to fill the map little by little and then do all replacements at once.

But we can have a little problem, when some toStr are contained inside some fromStr and vice versa, this function won’t work as expected. We will have to iterate the map for each one of the substitutions, instead of doing it globally and make single replacements (like we did with the old replace):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#include <iostream>
#include <string>
#include <map>

using namespace std;

string replace2(string source, std::map<string,string>strMap, int offset=0, int times=0)
{
  int total = 0;
  string::size_type pos=offset;
  string::size_type newPos;
  string::size_type lowerPos;

  do
    {
      string rep;
      for (std::map<string, string>::iterator i=strMap.begin(); i!=strMap.end(); ++i)
    {
      string fromStr = i->first;

      newPos = source.find(fromStr, pos);
      if ( (i==strMap.begin()) || (newPos<lowerPos) )
        {
          rep = fromStr;
          lowerPos = newPos;
        }
    }

      pos = lowerPos;
      if (pos == string::npos)
    break;

      string toStr = strMap[rep];

      source.replace(pos, rep.length(), toStr);
      pos+=toStr.size();

    } while ( (times==0) || (++total<times) );

  return source;
}

int main()
{

  string original = "If a black bug bleeds black blood, what color blood does a blue bug bleed?";
  map<string,string> mapa;
  mapa["black"] = "blue";
  mapa["blue"] = "black";

  cout << "Original string: "<<original<<endl;

  cout << "Resulting string: "<<replace2(original, mapa)<<endl;

  return 0;
}

The expected result is:

Original string: If a black bug bleeds black blood, what color blood does a blue bug bleed?
Resulting string: If a blue bug bleeds blue blood, what color blood does a black bug bleed?

A bit tongue twisting but I think you’ve got the idea.

One more interesting thing is the map creation. Lot’s of times we will have a clear idea of what elements go in the map. So we don’t want to spend time adding elements one by one. As I told you some days ago, you can pass a variable number of arguments to a C function, so, why not do it here? Let’s pass the strings as char* and add them to the map:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
map <string, string> strMap(const char* first, const char* second, ...)
{
  va_list args;
  map<string, string> ret;;
  int n=0;
  char *value;
  string _first, _second;

  ret.insert(pair<string, string>(first, second));
  va_start(args, second);

  do
    {
      value = va_arg(args, char*);
      if (value==NULL)
        break;

      if (++n % 2 ==0)
    {
      _second = string(value);
      ret.insert(pair<string, string>(_first, _second));
    }
      else
    _first = string(value);

    } while (1);

  return ret;
}

Now we can create the map by doing:

1
map <string, string> mapa = strMap("black", "blue", "blue", "black", NULL);

Don’t forget the last NULL, because it can cause a disaster in runtime (not always, you may have luck, but so often), due to strMap condition to stop reading arguments, it stops when it sees a NULL there, if you don’t put it, maybe it is there yet, or maybe not.

I hope this code is useful for you.

Restore root password in MySQL server

October 17, 2013 No comments

Sometimes, especially in our test server (or even production, who knows), we can lose our MySQL root password. This days we think about deleting everything, lose everything we’ve been working on, but we must be patient, we can’t destroy everything freely…

So, I leave here some easy steps (not only for you, but for me, I’m so absent-minded). We must have root privileges in the server we have MySQL installed:

1. First, stop MySQL service, depending the distribution, you may use one of these (or not, there can be more ways to do this)

1.1. $ service mysqld stop
1.2. $ /etc/init.d/mysqld stop
1.3. $ /etc/rc.d/mysqld stop

2. As root, we start MySQL service in a special way, ignoring privileges tables:

root # mysqld –skip-grant-tables

(you can use sudo on Ubuntu, for example)

Depending on your MySQL version (or distribution), it may not let you start as root, so we will specify the user, it will use:

root # mysqld -u mysql –skip-grant-tables &

In most systems the user will be mysql.

3. Access as superuser (as we are not checking privileges tables, we will have easy access). It can be done with a normal user. Use mysql database (or schema), that’s where the privileges tables are in.

$ mysql -u root mysql

4. From MySQL client we will update the password:

mysql> UPDATE mysql.user SET Password=PASSWORD(‘new password’) WHERE User=’root’;
mysql> FLUSH PRIVILEGES;

5. Exit mysql client

mysql> quit

6. Close mysqld (as root), it will take some seconds, we must be patient

$ killall mysqld

7. Restart MySQL server as we are used to, the same way we stopped it in step 1:

7.1. $ service mysqld start
7.2. $ /etc/init.d/mysqld start
7.3. $ /etc/rc.d/mysqld start

Let’s carry on working with our new root password

Alternate letters of several strings to create a new one

October 16, 2013 No comments

When hashing a password, it is recommended not only hashing the password. Concatenate the password string with other strings (or salt value), so those passwords won’t be broken using hashes databases. This is another technique to include salt to our passwords, or even a method to generate passwords.

This function will alternate letters of some given strings and put them in a new one. So if we take these two words: “Binary” and “Poetry”, it will generate “BPioneatrryy” (I’m sure you haven’t read that :) )

Lets see the function:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
  function letterAlternate($strs)
  {
    $n_strs = count ($strs);

    if ($n_strs==1)        /* If there is only one string, return it */
      return $strs[0];

    /* Count the characters of every string and store them */
    $chars_str=array();
    for ($i=0; $i<$n_strs; $i++)
      $chars_str[$i]=strlen($strs[$i]);

    $max_str = max ($chars_str); /* The larges string determines when we stop */
    $res='';

    for ($j=0; $j<$max_str; $j++)
      {

    for ($i=0; $i<$n_strs; $i++)
      {
        /* If the string isn't finished, we'll add the character to the string */
        if ($j<$chars_str[$i])
          $res.=$strs[$i][$j];
      }
      }

    return $res;
  }

echo letterAlternate (array("Binary", "Poetry", "Gaspar", "Fernandez", "Programming", "Blog", "Free", "Software"));

We will get: BPGFPBFSioaerlronesrooefatpnggetrraarwyyrnaadmremezing

stermp – conio.h compatible terminal features in Linux (for small projects)

October 15, 2013 No comments

When making CLI programs, sometimes we want to add some colors, or get the position of the cursor, terminal dimension or something like that. If you have been programming for years, you probably would remember conio.h, it was a C library used in the 80′s ? or 90′s to make user friendly terminal environments. And it is still being taught in some courses and even universities (omg). And I decided to create this some years ago, because conio.h was only compatible with MSDOS and Windows.

There are some Linux libraries to do so, or cross-platform, what is even better. But they are not as easy to use as conio.h was. And this was my intention, provide easy of use colors and position for terminal programs.

To use stermp, you just have to add stermp.h / stermp.c files to your project, and compile it, it will add a few (~10Kb) to your project. But it offers you these capabilities:

  • Terminal positioning (go to specific position, and get current terminal position.
  • Gets terminal dimensions (in MSDOS/Win) this is limited to some fixed resolutions, but in *nixes you can have lot of terminal sizes.
  • Text coloring
  • Background coloring
  • Text effects (underline, blink!! (ohh nooo), please don’t abuse)
  • Get keys without waiting for ENTER key
  • Detect whether if a key is pressed or not

To download it, just get stermp.tar (4.7Kb), or go to my github.

Categories: C, Linux Tags:

Variable number of arguments for a function in C

October 13, 2013 No comments

If you have ever program in C, I’m sure you had met printf(), it is, probably, the first thing you used in this language (at least for a Hello World). Well, this function accepts as many arguments as keywords you enter in its first argument, so here we have a variable number of arguments.

Some people think that only printf() and scanf() functions worth it, but there are so many cases where it is needed, for example when creating a function with optional arguments (these functions are common in C++, but C doesn’t allow it so easily), when concatenating some strings, filling data variables, internationalizing, and more.

All we must do is to include stdarg.h wichi provides us the tools to make it happen.

Let’s see this code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>

void putargs(int nargs, ...)
{
  va_list ap;
  int i;
  int num;

  va_start(ap, nargs); // nargs, because is the last known argument
  for (i =0; i<nargs; i++)
    {
      num=va_arg(ap, int); // All arguments are int
      printf("Argument %d = %d\n", i, num);
    }
  va_end(ap);
  printf("\n");
}

int main(int argc, char *argv[])
{
  putargs(5, 6, 77, 445, 34, 23, 12, 43, 32);
  putargs(2, 23, 94);
  putargs(0);

  return EXIT_SUCCESS;
}

We can see here, function putargs() prints out values of possible arguments we’ve passed the function, being the first of them the number of arguments we have, because we have no way to count the number of arguments passed (like printf, we will use as many arguments as %[x] we have in the format string). So we will have to use different techniques to know the total number of arguments, like:

  • Ask the user, like we’ve done here, with an argument telling the function how many arguments to read.
  • Search for a value inside all arguments, we can read arguments until the value I found is 5 (for example). It is used so often when passing pointers to the function, I will read arguments until I get a NUL value.
  • Like printf() use a number of keywords, this will be the number of arguments

We just have to copy this sample code and make some test, have in mind when we start looking for arguments, we must call va_start() with a va_list variable and the last known argument we gave the function, then each time we call va_arg, we will get a different argument from the “unknown argument list”, that is the list of arguments starting by the three dots.

In the next example we will have a variable argument function used to fill a struct with people data. We will have the function new_person where we must say the name, but it will add data to the struct as you are adding arguments when you call the function, it’s fun and easy:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>

#define MAX_STRLEN 64

struct TPersonData
{
  char name[MAX_STRLEN];
  char blog[MAX_STRLEN];
  char facebook[MAX_STRLEN];
  char twitter[MAX_STRLEN];
  char city[MAX_STRLEN];
  char drink[MAX_STRLEN];   /* Don't know anything else I can put for this example :) */
  char phone[MAX_STRLEN];
};

/* Remember always to clear data. Variable initialization is important if we don't
 want to see garbage. */

struct TPersonData cleanPerson()
{
  struct TPersonData p;
  strcpy(p.name, "");
  strcpy(p.blog, "");
  strcpy(p.facebook, "");
  strcpy(p.twitter, "");
  strcpy(p.city, "");
  strcpy(p.drink, "");
  strcpy(p.phone, "");

  return p;
}

struct TPersonData new_person (char *name, ...)
{
  va_list vl;
  char *temp;
  int num = 1;
  struct TPersonData out = cleanPerson();

  strcpy(out.name, name);

  va_start(vl, name);
 
  while ( (temp = va_arg(vl, char*))!=NULL)
    {
      switch (num++)
    {
    case 1:
      strcpy(out.blog, temp);
      break;
    case 2:
      strcpy(out.facebook, temp);
      break;
    case 3:
      strcpy(out.twitter, temp);
      break;
    case 4:
      strcpy(out.city, temp);
      break;
    case 5:
      strcpy(out.drink, temp);
      break;
    case 6:
      strcpy(out.phone, temp);
      break;
    default:
      break;
    }
    }
  va_end(vl);

  return out;
}

void printPerson(struct TPersonData data)
{
  printf ("Name: %s\n", data.name);
  printf ("Blog: %s\n", data.blog);
  printf ("Facebook: %s\n", data.facebook);
  printf ("Twitter: %s\n", data.twitter);
  printf ("City: %s\n", data.city);
  printf ("Drink: %s\n", data.drink);
  printf ("Phone: %s\n", data.phone);
  printf ("\n\n");
}

int main(int argc, char *argv[])
{
  struct TPersonData p = new_person("Gaspar Fernandez", "Binary Prose", NULL);
  printPerson(p);
 
  p = new_person("John Doe", "JD Blog", "http://facebook.com/johndoe", "@johndoe", "New York", NULL);
  printPerson(p);
  return EXIT_SUCCESS;
}

It’s important not to forget the NULL after all arguments are passed, or we will see some unexpected behavior in runtime.

Photo: Robyn Petrovich (Flickr) CC-by

Top