Contents

The Freeswitch Preprocessor

Freeswitch

Freeswitch is a server for real time communication. Freeswitch can use several protocols for communication. It is written in C.

Configuration

The configuration is written in XML and files can be included. It has a root config file, which include all configuration files. The configuration files are include by a preprocessor.

This is a example configuration for Freeswitch. The root config file includes the file vars.xml and the XML-files in the folder dialplan.

1
2
3
4
5
6
7
8
<?xml version="1.0"?>
<document type="freeswitch/xml">
  <X-PRE-PROCESS cmd="include" data="vars.xml"/>

  <section name="dialplan" description="Regex/XML Dialplan">
    <X-PRE-PROCESS cmd="include" data="dialplan/*.xml"/>
  </section>
</document>

The files can contain comments as well. Check out the next example.

1
2
3
4
5
<?xml version="1.0"?>
<document type="freeswitch/xml">
  <!-- the next line include the file `vars.xml` -->
  <X-PRE-PROCESS cmd="include" data="vars.xml"/>
</document>

The errors

After writting the following configuration file and running Freeswitch strange errors appeard.

freeswitch.xml

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
<?xml version="1.0"?>
<document type="freeswitch/xml">
  <X-PRE-PROCESS cmd="include" data="vars.xml"/>

  <!--<X-PRE-PROCESS cmd="include" data="test.xml"/>-->

  <section name="dialplan" description="Regex/XML Dialplan">
    <X-PRE-PROCESS cmd="include" data="dialplan/*.xml"/>
  </section>
</document>

test.xml

1
<-- fault -->

The file test.xml had several errors in the file, but I don’t wanted to include the test.xml-file. For that reason I didn’t cared about the errors in the test.xml-file.

After running Freeswitch I got the following error message, which I did not expected and didn’t understood.

1
Cannot Initialize [[error near line 206]: unexpected <]

The error message tells me, in line 206 is an error. After opening the file with vim and typing :206 I realized, something is strange. The file has no 206 lines, just a few lines. After a lot of debugging I found out how the preprocessor of Freeswitch works and what I should expected from a preprocessor.

The preprocessor

The preprocessor works like a pretty stupid text parser. The preprocessor don’t understand the XML. If the config file contains the following line: <!--<X-PRE-PROCESS cmd="include" data="test.xml"/>-->, the preprocessor will include the content of the file test.xml. It don’t know about XML and what a comment is. It just read include and it will include the file. After the preprocessor finished the work, there is a big file with all lines in one file. After understanding how the preprocessor works, it was clear that the preprocessor don’t understand XML. It is a lot easier to write a text parser and look for a include-string, than writting a XML-parser and including the files. But a text preprocessor don’t know what a XML-comment is.

C and the preprocessor

Let’s compare the behavior to the C preprocessor. The C preprocessor works in a similar way. Let’s just focus on the simple parts like #include and #define not parts like #if, #ifdef and other.

Here is a little C-Program, which just print Hello world! and returns with 0. The #include tells the preprocessor, please include the headerfile stdio.h. In the most cases is the headerfile delivered with your distribution or developer environment.

1
2
3
4
5
6
7
#include <stdio.h>

int main(void)
{
    printf("Hello, world!\n");
    return 0;
}

The preprocessor just replace the string #include <stdio.h> with the content of the file stdio.h. The syntax looks totaly different from the rest of the code. That is because the preprocessor is just a string replacement-tool with it’s own syntax. For that reason it don’t understand anything of the C language. It can be used for other string replacement use cases as well. The preprocessor can be used for macros as well (#define MAGIC 42). Just ignore the missing #include stdio.h on modern systems it will be included automatically.

1
2
3
4
5
6
7
#define MAGIC 42

int main()
{
        int magic = MAGIC;
        printf("Magic: %d\n", magic);
}

You can run the compiler gcc with the following paramaters to step a specific compilation stage: gcc -E magic.c. Here is the result of the preprocessor:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# 1 "magic.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 31 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 32 "<command-line>" 2
# 1 "magic.c"

int main()
{
 int magic = 42;
 printf("Magic: %d\n", magic);
}

Maybe just ignore the first few lines until the main function, this lines are for the C compiler (more later). There you will see that the line int magic = MAGIC is replaced by int magic = 42.

Let’s write some bad macros and see what happens.

1
2
3
4
5
6
#define MAGIC  no_function("Magic: %d\n", 42)
#define SUPERMAGIC MAGIC
int main()
{
        SUPERMAGIC;
}

The macro SUPERMAGIC is the same macro like MAGIC after the preprocessor finished it’s work. The result looks like the following:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# 1 "magic.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 31 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 32 "<command-line>" 2
# 1 "magic.c"


int main()
{
 no_function("Magic: %d\n", 42);
}

If we try to compile the full program, the linker will tell us, there is no function no_function(). As an easy explanation, the function no_function() just not exists and the program can not be compiled. In this case the whole process can provide better error messages than Freeswitch. The macros are tracked and the error shows the correct line number although there are nested macros.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
gcc magic.c
magic.c: In function ‘main’:
magic.c:1:16: warning: implicit declaration of function ‘no_function’ [-Wimplicit-function-declaration]
    1 | #define MAGIC  no_function("Magic: %d\n", 42)
      |                ^~~~~~~~~~~
magic.c:2:20: note: in expansion of macro ‘MAGIC’
    2 | #define SUPERMAGIC MAGIC
      |                    ^~~~~
magic.c:5:2: note: in expansion of macro ‘SUPERMAGIC’
    5 |  SUPERMAGIC;
      |  ^~~~~~~~~~
/usr/bin/ld: /tmp/cczufmsa.o: in function `main':
magic.c:(.text+0x16): undefined reference to `no_function'
collect2: error: ld returned 1 exit status

This are some easy examples for the C preprocessor and how the C prepprocessor works on some easy tasks. If the complexity increase, it will be harder to provide also as good error messages as the simple examples can do.

Also if you try something like this:

1
2
3
4
5
6
7
8
9
#define MAGIC "42"
#define MAGICFAULT "42" */
int main()
{
        /* MAGICFAULT */
        int magic = MAGIC;
        printf("Magic: %d\n", magic);
        return 0;
}

and the result of the preprocessor.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
# 1 "magic.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 31 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 32 "<command-line>" 2
# 1 "magic.c"


int main()
{

 int magic = "42";
 printf("Magic: %d\n", magic);
 return 0;
}

It is harder to trick the preprocessor and the C compiler than the Freeswitch preprocessor. But if you expect that the result will print Magic: 42 I wish you good luck until you get this line. You will get a lot of numbers and I quess 42 will not be that often. I just recommend to try the last example by yourself.

Conclusion

The preprocessor of C and Freeswitch works in a similar way. They are both simply text replacement tools with there own syntax. But the C process can track the macros and can also provide better error messages. This is done by using the first few lines in the examples. The lines are generated by the preprocessor and contains the necessary information for the compiler. Should Freeswitch replace there preprocessor with the C preprocessor? I don’t think so. It will be a lot work and also will break old configurations. The syntax is not the same. Should Freeswitch provide better error messages when starting? I would say better error messages will improve debugging a lot but on the other side it can be a lot of work to implement. Depending what Freeswitch allready know based on the information provided by the preprocessor it maybe even don’t know the file where the error is, because all files are included in one single file. If you know about this odd feature of the preprocessor it will be pretty easy to create a work around.