# dklibs 1.26.0

- Overview
- Download
- Download / Installation
- Installation from source on Windows systems
- Usage
- FAQ
- Change log
- Other projects
- Tutorial
- Developer docu
- stc
- tracecc and trana
- tracecc GUI
- tracecc PDF man.
- API: sort and search
- API: Generic I/O
- Preferences
- File search
- Font configuration
- LaTeX encodings
- Security

## Tutorial

- Usage guidelines
- Applications
- The simple program
- Adding application support
- Source
- Application start
- Preferences
- Internationalization
- New code
- String table
- Mechanisms
- Logging
- New code
- Mechanisms
- Retrieving command line arguments
- Code
- Mechanism
- Memory allocation
- Code
- The code explained

### Usage guidelines

- Do not instantiate types from ``dktypes.h'' statically in the application code (except the simple data types).
- Use the ``..._open()/..._new()'' functions instead to allocate the variables dynamically.
  The allocation functions are placed in the same module as the functions accessing these data types so they use the same data type definitions.
- Do not access member variables directly, use the functions provided by the library modules instead.

### Applications

#### The simple program

In this section we will expand the following simple program ``hello01.c'':

    int main(int argc, char *argv[]) {
      printf("Hello world!\n");
      printf("Goodbye world!\n");
      exit(0);
      return 0;
    }

#### Adding application support

##### Source

We change the source to hello02.c:

    static char pkgname[]= { "hello" };
    static char sysconfdir[] = { "/etc" };

    static int success = 0;

    int main(int argc, char *argv[]) {
      dk_app_t *app = NULL;
      app = dkapp_open_ext1(argc, argv, pkgname, sysconfdir, 0, 0);
      if(app) {
        success = 1;
        printf("Hello world!\n");
        printf("Goodbye world!\n");
        dkapp_close(app); app = NULL;
      }
      success = (success ? 0 : 1);
      exit(success);
      return success;
    }

##### Application start

If we run

    ./hello02

we see the expected output

    Hello world!
    Goodbye world!

If we run

    ./hello02 --/log/stdout/level=progress

we see some log messages:

    Application "hello02" started.
    Hello world!
    Goodbye world!
    Application "hello02" finished.

The ``--/...'' arguments are preference overwrites. Here we overwrite the ``/log/stdout/level'' preference which configures logging to standard output.

If we run

    ./hello02 --/log/file/keep=debug

we will find a file hello02.11310.log (the number in the middle can differ) in the home directory:

    Application "hello02" started.
    Application "hello02" finished.

The ``/log/file/keep'' preference controls whether or not a log file is removed when an application exits.

#### Preferences

In your home directory create a subdirectory ``.defaults'' (on Unix/Linux systems) or ``defaults'' (on Windows systems).
In this directory we need some files named ``hello02'', ``hello03'', ``hello04''...
Contents should be as follows:

    /ui/lang		=	en
    /dir/app		=	/home/myname/src/share/DOCU/tutorial
    /log/file/level	=	debug
    /log/file/keep	=	error
    /log/stderr/level	=	progress

For ``/dir/app'' use the directory where the tutorial files are located.
The next preferences control logging: Very detailed messages (debug) are written to the logfile. The logfile is deleted when the application exits unless there was at least one error message or a higher priorized message.
Progress messages and higher priorized messages are shown on the standard error output. Log message priority levels are ``none'', ``panic'', ``fatal'', ``error'', ``warning'', ``info'', ``progress'' and ``debug''.

#### Internationalization

##### New code

We change the source to hello03.c:

    static char pkgname[]= { "hello" };
    static char sysconfdir[] = { "/etc" };

    static int success = 0;

    static dk_key_value_t kv[] = {
      { "hello",	"Hello world!" },
      { "goodbye",	"Goodbye world!" }
    };
    static size_t szkv = sizeof(kv)/sizeof(dk_key_value_t);

    int main(int argc, char *argv[]) {
      dk_app_t *app = NULL;
      char **msg = NULL;
      app = dkapp_open_ext1(argc, argv, pkgname, sysconfdir, 0, 0);
      if(app) {
        msg = dkapp_find_key_value(app, kv, szkv, "h03msg");
        if(msg) {
          success = 1;
          printf("%s\n", msg[0]);
          printf("%s\n", msg[1]);
          dk_delete(msg);
        }
        dkapp_close(app); app = NULL;
      }
      success = (success ? 0 : 1);
      exit(success);
      return success;
    }

##### String table

We create a string table source file h03msg.str:

    "hello"
    en =		"Hello, I am an internationalized program."
    sp =		"Buenos dias."
    de =		"Moin moin."

    "goodbye"
    en =		"Now it's time to say goodbye."
    sp =		"Adios muchachos."
    de =		"Und wech."

Each string translation consists of the string name (key) in double quotes as used in the dk_key_value_t array in the program source and a list of
``language[_region][.encoding]="text"'' pairs containing one text for each language/region/encoding combination.

To convert the string table source file into the binary string tables files read by the ``dkstt.c'' or ``dkapp.c'' module we run

    stc h03msg.str .

Now we see new directories ``en'', ``sp'' and ``de'' containing resources for one language each.

##### Mechanisms

- For each internationalized string we have an entry in a key/value table (dk_key_value_t type). The key is a name for the string (key to find the string in the table). The value is the default string to use when string search failes.
- The dkapp_find_key_value() function returns an array of pointers to strings. The string values in this array are in the same order as they were in the kv table.
- After usage we have to release the memory used by the pointers array, but not the array elements.
- When the application is started it search for an application-specific resources directory and a shared resources directory in the preference. If no directory is found the current working directory is used for both purposes.
- The current language, region and encoding is derived from the ``/ui/lang'' preference or the ``LANG'' environment variable (unless ``/ui/lang/env'' is set to false).

#### Logging

##### New code

We change the source to hello04.c.

    static char pkgname[]= { "hello" };
    static char sysconfdir[] = { "/etc" };

    static int success = 0;

    static dk_key_value_t kv[] = {
      { "/msg/pan",	"Test panic message!" },
      { "/msg/fat",	"Test message for fatal error!" },
      { "/msg/err", "Test error message!" },
      { "/msg/wrn", "Test warning message!" },
      { "/msg/inf", "Test information message." },
      { "/msg/prg", "Test progress message." },
      { "/msg/dbg", "Test debug message." }
    };
    static size_t szkv = sizeof(kv)/sizeof(dk_key_value_t);

    static char *an_array_of_strings[] = {
      "Jim ",
      "and Joe",
      " are drinking beer.",
    };

    int main(int argc, char *argv[]) {
      dk_app_t *app = NULL;
      char **msg = NULL;
      app = dkapp_open_ext1(argc, argv, pkgname, sysconfdir, 0, 0);
      if(app) {
        msg = dkapp_find_key_value(app, kv, szkv, "h04msg");
        if(msg) {
          success = 1;
          dkapp_log_msg(app, DK_LOG_LEVEL_PANIC,    &(msg[0]), 1);
          dkapp_log_msg(app, DK_LOG_LEVEL_FATAL,    &(msg[1]), 1);
          dkapp_log_msg(app, DK_LOG_LEVEL_ERROR,    &(msg[2]), 1);
          dkapp_log_msg(app, DK_LOG_LEVEL_WARNING,  &(msg[3]), 1);
          dkapp_log_msg(app, DK_LOG_LEVEL_INFO,     &(msg[4]), 1);
          dkapp_log_msg(app, DK_LOG_LEVEL_PROGRESS, &(msg[5]), 1);
          dkapp_log_msg(app, DK_LOG_LEVEL_DEBUG,    &(msg[6]), 1);
          dkapp_log_msg(app, DK_LOG_LEVEL_INFO,     an_array_of_strings, 3);
          dk_delete(msg);
        }
        dkapp_close(app); app = NULL;
      }
      success = (success ? 0 : 1);
      exit(success);
      return success;
    }

##### Mechanisms

- The dkapp_log_msg() function needs a message priority (DK_LOG_LEVEL_PANIC, DK_LOG_LEVEL_FATAL, DK_LOG_LEVEL_ERROR, DK_LOG_LEVEL_WARNING, DK_LOG_LEVEL_INFO, DK_LOG_LEVEL_PROGRESS or DK_LOG_LEVEL_DEBUG), defined in ``dklogc.h''.
- Additional parameters are a pointer to an array of strings and the number of strings in the array. These strings are shown as message text, spaces are inserted between the strings as separators.

#### Retrieving command line arguments

##### Code

    static char pkgname[]= { "hello" };
    static char sysconfdir[] = { "/etc" };

    static int success = 0;

    int main(int argc, char *argv[]) {
      dk_app_t *app = NULL;
      int i, my_argc;
      char **my_argv, **lfdptr;
      app = dkapp_open_ext1(argc, argv, pkgname, sysconfdir, 0, 0);
      if(app) {
        success = 1;
        my_argc = dkapp_get_argc(app);
        my_argv = dkapp_get_argv(app);
        lfdptr = my_argv;
        i = 0;
        while(i < my_argc) {
          if(*lfdptr) {
    	printf("%5d \"%s\"\n", i, *lfdptr);
          }
          i++; lfdptr++;
        }
        dkapp_close(app); app = NULL;
      }
      success = (success ? 0 : 1);
      exit(success);
      return success;
    }

##### Mechanism

- When the application is opened it builds an array of ``real'' command line arguments (preference overwrites are removed from the original command line arguments list).
- The dkapp_get_argc() function returns the number of command line arguments.
- The dkapp_get_argv() function returns a pointer to an array of strings.

### Memory allocation

#### Code

hello06.c:

    static char pkgname[]= { "hello" };
    static char sysconfdir[] = { "/etc" };
    static char space[] = { " " };

    static int success = 0;

    int main(int argc, char *argv[]) {
      dk_app_t *app = NULL;
      int i, my_argc;
      char *cmdline, **my_argv, **lfdptr;
      size_t lgt;
      app = dkapp_open_ext1(argc, argv, pkgname, sysconfdir, 0, 0);
      if(app) {
        success = 1;
        my_argc = dkapp_get_argc(app);
        my_argv = dkapp_get_argv(app);
        lfdptr = my_argv;
        i = 0; lgt = 0;
        while(i < my_argc) {
          if(*lfdptr) {
            lgt += strlen(*lfdptr);
          }
          i++; lfdptr++;
        }
        lgt += my_argc;
        cmdline = dk_new(char,lgt);
        if(cmdline) {
          lfdptr = my_argv; i = 0;
          while(i < my_argc) {
            if(*lfdptr) {
    	  if(i > 0) {
    	    strcat(cmdline, space);
    	    strcat(cmdline, *lfdptr);
    	  } else {
    	    strcpy(cmdline, *lfdptr);
    	  }
    	  i++; lfdptr++;
    	}
          }
          printf("Command line: %s\n", cmdline);
          dk_delete(cmdline);
        }
        dkapp_close(app); app = NULL;
      }
      success = (success ? 0 : 1);
      exit(success);
      return success;
    }

#### The code explained

- In the first while loop we traverse the array of command line arguments and build the summary of all string lengths.
  Arguments 1...my_argc need a leading space, an additional byte is needed for the finalizing 0x00 byte of the string.
- The dk_new() macro from ``dkmem.h'' is used to allocate memory dynamically. The data type and the number of elements must be passed to the macro.
- The dk_delete() macro is used to release the memory.