// how to use global debug variable and wrap it in accessor methods

// 1. create a file called debug.c

// scope of dbg_val is ONLY in this file, so no other code can mistakenly
// mess with it.

// use "static" meaning only code in THIS file has access to this variable
static unsigned int dbg_val = 0;

// an accessor function to write the debug level
// can call this from main() after doing getopt(3)
void set_dbg_val(int d)
{
  dbg_val = d;
}

// accessor fxn to read the debug value
// can call this anywhere you need to know what's the current debug lebel
int get_dbg_val(void)
{
  return dbg_val;
}

// 2. add to your "global" header file
// you have to #include this code in every other C file, else it won't
// recognize these two functions or know their prototype.

// this says that the functions' code itself is in some other .c file (or a
// library you'll be linking with)
extern void set_dbg_val(int);
extern int get_dbg_val(void);

// attempts to define the function code itself, or tell the compiler to
// ignore prototype definitions.  It sometimes works... but "extern" is more
// proper.
//void set_dbg_val(int);
//int get_dbg_val(void);
// even worse is this: tells compiler to totally ignore the fxn prototye
// can cause stack mem over/underflow and corruptions.
//set_dbg_val();
//get_dbg_val();

//////////////////////////////////////////////////////////////////////

// DEBUG FUNCTIONS

// defines a bitmap: each macro below is one bit (in hex)
// can use Octal as well if wanted
#define DBG_NONE 0x00 // no debug
#define DBG_ENTEXIT 0x01 // entry/exit to fxn
#define DBG_LIB 0x02 // before/after call to lib fxn
#define DBG_SYSCALL 0x04 // before/after call to syscall
// something else can define 0x08
#define DBG_ARGS 0x10 // print args before calling fxn
#define DBG_RET	0x20 // print retval before return from fxn

// if each "feature" is its own bit, you can compose them using logical ORs
#define DBG_ALL (DBG_ENTEXIT|DB_LIB|DB_SYSCALL) // etc. add others

// flags are set by parsing cmd line args
// better if not a global, maybe use a util fxn to get_dbg_flags and
// set_dbg_flags (accessor functions)
static u_int dbg_flags = DBG_NONE; // no debug

// useful tracing macro that prints filename, fxn name, and line no.
#define MYDBG  fprintf(stderr, "MYDBG:%s:%s:%d:\n",__FILE__,__func__,__LINE__)

// can make this a function, preferred inline function
#define DBG_PRINT_ENTEXIT(x,y) \
  if (dbg_flags & DBG_ENTEXIT) { \
    if (dbg_flags & ~DBG_ARGS) { \
      fprintf(stderr, "entered function %s\n", __func__); \
      else \
	fprintf(stderr, "entered function %s (str=\"%s\", num=%d)\n", \
		__func__, \
		((x) != NULL ? (x) : "null"),	\
		(y));				\
    } \
  }

int foo(char *str, u_int num)
{
  in err = 0;

  // easier to create a macro for printing entry/exit for all functions
  DBG_PRINT_ENTEXIT(str, num);

  // body of code
  MYDBG;

  // use dbg_flags var w/ AND, OR, and combo of flags as needed
  fprintf(stderr, "exiting function %s\n", __func__); // no err printed
  return err;
}

// how to "debug" system calls and library calls, like read(2)
int myread(int fd, char *buf, int len)
{
  int ret;
  MYDBG; // or any macro you want before calling actual read(2)
  ret = read(fd, buf, len);
  MYDBG; // or any macro you want after calling actual read(2)
  return ret;
}

main()
{
  int retval;

  // some code

  fprintf(stderr, "about to call function foo\n"); // no args printed
  retval = foo("name", 17);
  fprintf(stderr, "return from fxn foo\n"); // no retval

  // some other code

}

// how to debug rare conditions, eg. partial read/write
// solution: inject an error
main()
{
  int ret;

  while (some condition) {
    ret = read(fd, buf, 100);
    // debug code, you can enable it, say, after having read 10000 bytes
    // or after 5 successful reads.  Can also use same method to inject
    // partial read errors.
    if (DEBUG_INJECT_READ_GOT_FATAL_ERROR_IS_ON) {
      ret = -1;
      errno = EIO;
    }
    if (ret == 100) // all's well: easy to test
      encrypt(...);
    if (ret < 0)
      handle_error; // real error
    if (ret == 0)
      handle_eof; // EOF: easy to test
    // if gets here, we have a partial read
    // code to handle partial read

  }
}
