text: #include "context_internal.h"
  #include <string.h>
  #include <stdio.h>
  #include <stdlib.h>
  #include <errno.h>
  
  #define COMP_USER  0
  #define COMP_ROLE  1
  #define COMP_TYPE  2
  #define COMP_RANGE 3
  
  typedef struct {
  	char *current_str;	/* This is made up-to-date only when needed */
  	char *(component[4]);
  } context_private_t;
  
  /*
   * Allocate a new context, initialized from str.  There must be 3 or
   * 4 colon-separated components and no whitespace in any component other
   * than the MLS component.
   */
  context_t context_new(const char *str)
  {
  	int i, count;
  	errno = 0;
  	context_private_t *n =
  	    (context_private_t *) malloc(sizeof(context_private_t));
  	context_t result = (context_t) malloc(sizeof(context_s_t));
  	const char *p, *tok;
  
  	if (result)
  		result->ptr = n;
  	else
  		free(n);
  	if (n == 0 || result == 0) {
  		goto err;
  	}
  	n->current_str = n->component[0] = n->component[1] = n->component[2] =
  	    n->component[3] = 0;
  	for (i = count = 0, p = str; *p; p++) {
  		switch (*p) {
  		case ':':
  			count++;
  			break;
  		case '\n':
  		case '\t':
  		case '\r':
  			goto err;	/* sanity check */
  		case ' ':
  			if (count < 3)
  				goto err;	/* sanity check */
  		}
  	}
  	/*
  	 * Could be anywhere from 2 - 5
  	 * e.g user:role:type to user:role:type:sens1:cata-sens2:catb
  	 */
  	if (count < 2 || count > 5) {	/* might not have a range */
  		goto err;
  	}
  
  	n->component[3] = 0;
  	for (i = 0, tok = str; *tok; i++) {
  		if (i < 3)
  			for (p = tok; *p && *p != ':'; p++) {	/* empty */
  		} else {
  			/* MLS range is one component */
  			for (p = tok; *p; p++) {	/* empty */
  			}
  		}
  		n->component[i] = (char *)malloc(p - tok + 1);
  		if (n->component[i] == 0)
  			goto err;
  		strncpy(n->component[i], tok, p - tok);
  		n->component[i][p - tok] = '\0';
  		tok = *p ? p + 1 : p;
  	}
  	return result;
        err:
  	if (errno == 0) errno = EINVAL;
  	context_free(result);
  	return 0;
  }
  
  hidden_def(context_new)
  
  static void conditional_free(char **v)
  {
  	if (*v) {
  		free(*v);
  	}
  	*v = 0;
  }
  
  /*
   * free all storage used by a context.  Safe to call with
   * null pointer. 
   */
  void context_free(context_t context)
  {
  	context_private_t *n;
  	int i;
  	if (context) {
  		n = context->ptr;
  		if (n) {
  			conditional_free(&n->current_str);
  			for (i = 0; i < 4; i++) {
  				conditional_free(&n->component[i]);
  			}
  			free(n);
  		}
  		free(context);
  	}
  }
  
  hidden_def(context_free)
  
  /*
   * Return a pointer to the string value of the context.
   */
  char *context_str(context_t context)
  {
  	context_private_t *n = context->ptr;
  	int i;
  	size_t total = 0;
  	conditional_free(&n->current_str);
  	for (i = 0; i < 4; i++) {
  		if (n->component[i]) {
  			total += strlen(n->component[i]) + 1;
  		}
  	}
  	n->current_str = malloc(total);
  	if (n->current_str != 0) {
  		char *cp = n->current_str;
  
  		strcpy(cp, n->component[0]);
  		cp += strlen(cp);
  		for (i = 1; i < 4; i++) {
  			if (n->component[i]) {
  				*cp++ = ':';
  				strcpy(cp, n->component[i]);
  				cp += strlen(cp);
  			}
  		}
  	}
  	return n->current_str;
  }
  
  hidden_def(context_str)
  
  /* Returns nonzero iff failed */
  static int set_comp(context_private_t * n, int idx, const char *str)
  {
  	char *t = NULL;
  	const char *p;
  	if (str) {
  		t = (char *)malloc(strlen(str) + 1);
  		if (!t) {
  			return 1;
  		}
  		for (p = str; *p; p++) {
  			if (*p == '\t' || *p == '\n' || *p == '\r' ||
  			    ((*p == ':' || *p == ' ') && idx != COMP_RANGE)) {
  				free(t);
  				errno = EINVAL;
  				return 1;
  			}
  		}
  		strcpy(t, str);
  	}
  	conditional_free(&n->component[idx]);
  	n->component[idx] = t;
  	return 0;
  }
  
  #define def_get(name,tag) \
  const char * context_ ## name ## _get(context_t context) \
  { \
          context_private_t *n = context->ptr; \
          return n->component[tag]; \
  } \
  hidden_def(context_ ## name ## _get)
  
  def_get(type, COMP_TYPE)
      def_get(user, COMP_USER)
      def_get(range, COMP_RANGE)
      def_get(role, COMP_ROLE)
  #define def_set(name,tag) \
  int context_ ## name ## _set(context_t context, const char* str) \
  { \
          return set_comp(context->ptr,tag,str);\
  } \
  hidden_def(context_ ## name ## _set)
      def_set(type, COMP_TYPE)
      def_set(role, COMP_ROLE)
      def_set(user, COMP_USER)
      def_set(range, COMP_RANGE)
  