aboutsummaryrefslogblamecommitdiffstats
path: root/src/libmdsserver/macros.h
blob: 45412b99b1d0b9ed32e6b0e75f48f1ff18f459dd (plain) (tree)
1
2
3

                                 
                                                                         

















                                                                        
                   
                       
 

                  
                   


                   
                       
 
  
                    
                   


                      
  

 


                                                  

                                                     
                                            





                                                       








                                                          
                                                           


      












                                                               
                                                     



























                                                               
   








                                                                     

                                                                  
  






                                                                                                

  

   

                                                                         


                                                                                                               
   

                                                                    

 



                                                     

                                                                                                             
   

                                                  





                                                     


                                                                                                             
   

                                                               

 
   







                                                                                                             

                                                        











                                                                                                             

                                                                     


   




                                                                                   







                                                      
 






                                                                                            







                                                      
 







                                          

                               








                                          

                               









                                                                                               

                                      








                                                                                                
                                                       
   

                                                




                                                          




                                                                                                     
   

                                                   




                                   

                                     
                                                                                                  
                                    
   

                                                         




                                   

                                     
                                                                                                  
                                    
   

                                                         

 






                                                                                                
                                                       
   


                                             








                                                                                                
                                                   
   


                                             

 


                                      


                                                        
   

                        




                                                    


                                                                         
   

                                              








                                                        


                                                            

 




                                                                                          
                                                                    
   

                                                     

 
   




                                                 
                                                                                       

                    

                                                                                                      

                                     
      

                                                             
       








                                                  
                                                                                       

                    

                                                                                                      

                                     
      

                                                             
       



      



                                                                         




















                                                                                                               




                                             
                                             


                                                                                              






                                                 


   
                                                                          
    



                                                                                            
   

                                                    
  







                                                                                
  


   





                                                                               

                              
  






                                                               
  


   
                                                                          
    



                                                                                            
   

                                                 
  







                                                                                
  

 
   





                                                                               

                                              
  






                                                               
  


   
                                                                           
    



                                                                                            

                                        
                                                          
  







                                                                                
  


   
                                          
    
                                                                                            





                                                                                            

                                                                                               
  







                                                                                       
  










                                                                                             


                                                                       
  








                                                                                      
  


   

                                                  




                                                                                            
   

                                                                                                 
  







                                                                                                           
  


   


                                                                               
                                                                        

                                                                                       

                                                                
  






                                                                                                      
  


   








                                                                                       

                                   
  






                                                                                                      



   








                                                                                       


                                                               
  







                                                                                           
  



   



                                                               

                                            


   
                                               
   
                             
   










                                                                                           




                                                               

                                                               
   

                                                                  

 
   






                                                          

                           


   



                                                          
                                 


      
   



                                               
                               



      



                                         
                          


      



                                                 

                                          






                                                 

                                        

 
      
/**
 * mds — A micro-display server
 * Copyright © 2014, 2015, 2016, 2017  Mattias Andrée (maandree@kth.se)
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
#ifndef MDS_LIBMDSSERVER_MACROS_H
#define MDS_LIBMDSSERVER_MACROS_H


#include "config.h"
#include "macro-bits.h"

#include <stdio.h>
#include <errno.h>
#include <signal.h>
#include <unistd.h>
#include <time.h>
#include <stddef.h>
#include <sys/socket.h>

/*
#include <pthread.h>
#include <string.h>
#include <sys/types.h>
#include <dirent.h>
#include <stdlib.h>
*/


/* # pragma GCC diagnostic ignored "-Wpedantic" */


/* CLOCK_MONOTONIC_RAW is a Linux-specific bug-fix */
#ifndef CLOCK_MONOTONIC_RAW
# define CLOCK_MONOTONIC_RAW CLOCK_MONOTONIC
#endif

/* Define TEMP_FAILURE_RETRY if not defined, however
 * this version does not return a value, it will hoever
 * clear `errno` if no error occurs. */
#ifndef TEMP_FAILURE_RETRY
# define TEMP_FAILURE_RETRY(expression)\
	do {\
		ssize_t __result;\
		do\
			__result = (ssize_t)(expression);\
		while (__result < 0 && errno == EINTR);\
		if (__result >= 0)\
			errno = 0;\
	} while (0)
# define MDS_LIBMDSSERVER_MACROS_DEFINED_TEMP_FAILURE_RETRY
#endif


/* Ensure that all aliases for AF_UNIX are defined */
#if !defined(AF_LOCAL) && !defined(AF_UNIX) && defined(AF_FILE)
# define AF_LOCAL AF_FILE
# define AF_UNIX AF_FILE
#elif !defined(AF_LOCAL) && defined(AF_UNIX)
# define AF_LOCAL AF_UNIX
#elif defined(AF_LOCAL) && !defined(AF_UNIX)
# define AF_UNIX AF_LOCAL
#endif
#if !defined(AF_FILE) && defined(AF_LOCAL)
# define AF_FILE AF_LOCAL
#endif

/* Ensure that all aliases for PF_UNIX are defined */
#if !defined(PF_LOCAL) && !defined(PF_UNIX) && defined(PF_FILE)
# define PF_LOCAL PF_FILE
# define PF_UNIX PF_FILE
#elif !defined(PF_LOCAL) && defined(PF_UNIX)
# define PF_LOCAL PF_UNIX
#elif defined(PF_LOCAL) && !defined(PF_UNIX)
# define PF_UNIX PF_LOCAL
#endif
#if !defined(PF_FILE) && defined(PF_LOCAL)
# define PF_FILE PF_LOCAL
#endif

/* Ensure that all aliases for AF_NETLINK are defined */
#if !defined(AF_NETLINK) && defined(AF_ROUTE)
# define AF_NETLINK AF_ROUTE
#elif defined(AF_NETLINK) && !defined(AF_ROUTE)
# define AF_ROUTE AF_NETLINK
#endif

/* Ensure that all aliases for PF_NETLINK are defined */
#if !defined(PF_NETLINK) && defined(PF_ROUTE)
# define PF_NETLINK PF_ROUTE
#elif defined(PF_NETLINK) && !defined(PF_ROUTE)
# define PF_ROUTE PF_NETLINK
#endif



/**
 * Wrapper around `asprintf` that makes sure that first
 * argument gets set to `NULL` on error and that zero is
 * returned on success rather than the number of printed
 * characters
 * 
 * @param   VAR:char**            The output parameter for the string
 * @param   ...:const char*, ...  The format string and arguments
 * @return  :int                  Zero on success, -1 on error
 */
#define xasprintf(VAR, ...)\
	(asprintf(&(VAR), __VA_ARGS__) < 0 ? (VAR = NULL, -1) : 0)
/*
#define xasprintf(VAR, ...)\
	({\
		int _x_rc = (asprintf(&(VAR), __VA_ARGS__) < 0 ? (VAR = NULL, -1) : 0);\
		fprintf(stderr, "xasprintf(%s, %s)(=%zu) @ %s:%i\n",\
		        #VAR, #__VA_ARGS__, _x_rc ? 0 : (strlen(VAR) + 1), __FILE__, __LINE__);\
		_x_rc;\
	})
*/


/**
 * Wrapper for `snprintf` that allows you to forget about the buffer size
 * 
 * @param   buffer:char[]         The buffer, must be of the type `char[]` and not `char*`
 * @param   ...:const char*, ...  The format string and arguments
 * @return  :int                  The number of bytes written, including the NUL-termination, negative on error
 */
#define xsnprintf(buffer, ...)\
	snprintf(buffer, sizeof(buffer) / sizeof(char), __VA_ARGS__)


/**
 * Wrapper for `fprintf` that prints to `stderr` with
 * the program name prefixed and new line suffixed
 * 
 * @param   format:const char*  The format
 * @return  :int                The number of bytes written, including the NUL-termination, negative on error
 */
#define eprint(format)\
	fprintf(stderr, "%s: " format "\n", *argv)


/**
 * Wrapper for `fprintf` that prints to `stderr` with
 * the program name prefixed and new line suffixed
 * 
 * @param   format:const char*  The format
 * @param   ...                 The arguments
 * @return  :int                The number of bytes written, including the NUL-termination, negative on error
 */
#define eprintf(format, ...)\
	fprintf(stderr, "%s: " format "\n", *argv, __VA_ARGS__)


/**
 * Wrapper for `fprintf` that prints to `stderr` with
 * the prefixed with information telling the user that
 * the output is part of an state and statistics dump
 * and a new line suffixed
 * 
 * @param   format:const char*  The format
 * @return  :int                The number of bytes written, including the NUL-termination, negative on error
 */
#define iprint(format)\
	fprintf(stderr, "%s: info: " format "\n", *argv)


/**
 * Wrapper for `fprintf` that prints to `stderr` with
 * the prefixed with information telling the user that
 * the output is part of an state and statistics dump
 * and a new line suffixed
 * 
 * @param   format:const char*  The format
 * @param   ...                 The arguments
 * @return  :int                The number of bytes written, including the NUL-termination, negative on error
 */
#define iprintf(format, ...)\
	fprintf(stderr, "%s: info: " format "\n", *argv, __VA_ARGS__)


/**
 * Wrapper for `pthread_mutex_lock` and `pthread_mutex_unlock`
 * 
 * @param  mutex:pthread_mutex_t  The mutex
 * @param  instructions           The instructions to run while the mutex is locked
 */
#define with_mutex(mutex, instructions)\
	do {\
		errno = pthread_mutex_lock(&mutex);\
		do {\
			instructions;\
		} while (0);\
		errno = pthread_mutex_unlock(&mutex);\
	} while (0)

/**
 * Wrapper for `pthread_mutex_lock` and `pthread_mutex_unlock` with an embedded if-statement
 * 
 * @param  mutex:pthread_mutex_t  The mutex
 * @parma  condition              The condition to test
 * @param  instructions           The instructions to run while the mutex is locked
 */
#define with_mutex_if(mutex, condition, instructions)\
	do {\
		errno = pthread_mutex_lock(&mutex);\
		if (condition) {\
			instructions;\
		}\
		errno = pthread_mutex_unlock(&mutex);\
	} while (0)


/**
 * Return the maximum value of two values
 * 
 * @param   a  One of the values
 * @param   b  The other one of the values
 * @return     The maximum value
 */
#define max(a, b)\
	((a) < (b) ? (b) : (a))


/**
 * Return the minimum value of two values
 * 
 * @param   a  One of the values
 * @param   b  The other one of the values
 * @return     The minimum value
 */
#define min(a, b)\
	((a) < (b) ? (a) : (b))


/**
 * Cast a buffer to another type and get the slot for an element
 * 
 * @param   buffer:char*  The buffer
 * @param   type          The data type of the elements for the data type to cast the buffer to
 * @param   index:size_t  The index of the element to address
 * @return  [type]        A slot that can be set or get
 */
#define buf_cast(buffer, type, index)\
	(((type *)(buffer))[index])


/**
 * Set the value of an element a buffer that is being cast
 * 
 * @param   buffer:char*   The buffer
 * @param   type           The data type of the elements for the data type to cast the buffer to
 * @param   index:size_t   The index of the element to address
 * @param   variable:type  The new value of the element
 * @return  variable:      The new value of the element
 */
#define buf_set(buffer, type, index, variable)\
	(((type *)(buffer))[index] = (variable))


/**
 * Get the value of an element a buffer that is being cast
 * 
 * @param   buffer:const char*  The buffer
 * @param   type                The data type of the elements for the data type to cast the buffer to
 * @param   index:size_t        The index of the element to address
 * @param   variable:type       Slot to set with the value of the element
 * @return  variable:           The value of the element
 */
#define buf_get(buffer, type, index, variable)\
	(variable = ((const type*)(buffer))[index])


/**
 * Increase the pointer of a buffer
 * 
 * @param   buffer:char*  The buffer
 * @param   type          A data type
 * @param   count:size_t  The number elements of the data type `type` to increase the pointer with
 * @return  buffer:       The buffer
 */
#define buf_next(buffer, type, count)\
	(buffer += (count) * sizeof(type) / sizeof(char))


/**
 * Decrease the pointer of a buffer
 * 
 * @param   buffer:char*  The buffer
 * @param   type          A data type
 * @param   count:size_t  The number elements of the data type `type` to decrease the pointer with
 * @return  buffer:       The buffer
 */
#define buf_prev(buffer, type, count)\
	(buffer -= (count) * sizeof(type) / sizeof(char))


/**
 * This macro combines `buf_set` with `buf_next`, it sets
 * element zero and increase the pointer by one element
 * 
 * @param   buffer:char*   The buffer
 * @param   type           The data type of the elements for the data type to cast the buffer to
 * @param   variable:type  The new value of the element
 * @return  variable:      The new value of the element
 */
#define buf_set_next(buffer, type, variable)\
	(buf_set(buffer, type, 0, variable),\
	 buf_next(buffer, type, 1))


/**
 * This macro combines `buf_set` with `buf_next`, it sets
 * element zero and increase the pointer by one element
 * 
 * @param   buffer:char*   The buffer
 * @param   type           The data type of the elements for the data type to cast the buffer to
 * @param   variable:type  Slot to set with the value of the element
 * @return  variable:      The value of the element
 */
#define buf_get_next(buffer, type, variable)\
	(buf_get(buffer, type, 0, variable),\
	 buf_next(buffer, type, 1))


/**
 * Check whether two strings are equal
 * 
 * @param   a:const char*  One of the strings
 * @param   b:const char*  The other of the strings
 * @return  :int           Whether the strings are equal
 */
#define strequals(a, b)\
	(!strcmp(a, b))


/**
 * Check whether a string starts with another string
 * 
 * @param   haystack:const char*  The string to inspect
 * @param   needle:const char*    The string `haystack` should start with
 * @return  :int                  Whether `haystack` starts with `needle`
 */
#define startswith(haystack, needle)\
	(strstr(haystack, needle) == haystack)


/**
 * Set effective user and the effective group to the
 * real user and the real group, respectively. If the
 * group cannot be set, the user till not be set either.
 * 
 * @return  :int  Non-zero on error
 */
#define drop_privileges()\
	((getegid() == getgid() ? 0 : setegid(getgid())) ||\
	 (geteuid() == getuid() ? 0 : seteuid(getuid())))


/**
 * Wrapper for `clock_gettime` that gets some kind of
 * monotonic time, the exact clock ID is not specified
 * 
 * @param   time_slot:struct timespec*  Pointer to the variable in which to store the time
 * @return  :int                        Zero on success, -1 on error
 */
#define monotone(time_slot)\
	clock_gettime(CLOCK_MONOTONIC_RAW, time_slot)


/**
 * Wrapper for `close` that will retry if it gets
 * interrupted
 * 
 * @param  fd:int  The file descriptor
 */
#if 1 /* For kernels that ensure that close(2) always closes valid file descriptors. */
# define xclose(fd)\
	close(fd)
#else /* For kernels that ensure that close(2) never closes valid file descriptors on interruption. */
# ifdef MDS_LIBMDSSERVER_MACROS_DEFINED_TEMP_FAILURE_RETRY
#  define xclose(fd)\
	TEMP_FAILURE_RETRY(close(fd))
# else
#  define xclose(fd)\
	(TEMP_FAILURE_RETRY(close(fd)) < 0 ? 0 : (errno = 0))
# endif
#endif


/**
 * Wrapper for `fclose` that will retry if it gets
 * interrupted
 * 
 * @param  f:FILE*  The stream
 */
#if 1 /* For kernels that ensure that close(2) always closes valid file descriptors. */
# define xfclose(f)\
	fclose(f)
#else /* For kernels that ensure that close(2) never closes valid file descriptors on interruption. */
# ifdef MDS_LIBMDSSERVER_MACROS_DEFINED_TEMP_FAILURE_RETRY
#  define xfclose(f)\
	TEMP_FAILURE_RETRY(fclose(f))
# else
#  define xfclose(f)\
	(TEMP_FAILURE_RETRY(fclose(f)) < 0 ? 0 : (errno = 0))
# endif
#endif


/**
 * Close all file descriptors that satisfies a condition
 * 
 * @param  condition  The condition, it should evaluate the variable `fd`
 */
#define close_files(condition)\
	do {\
		DIR *dir = opendir(SELF_FD);\
		struct dirent *file;\
		int dfd, fd;\
		\
		if (!dir) {\
			perror(*argv); /* Well, that is just unfortunate, but we cannot really do anything. */\
		} else {\
			dfd = dirfd(dir);\
			while ((file = readdir(dir))) {\
				if (strcmp(file->d_name, ".") && strcmp(file->d_name, "..")) {\
					fd = atoi(file->d_name);\
					if (fd != dfd)\
						if (condition)\
							xclose(fd);\
				}\
			}\
			closedir(dir);\
		}\
	} while (0)


/**
 * Free an array and all elements in an array
 * 
 * @param  array:void**     The array to free
 * @param  elements:size_t  The number of elements, in the array, to free
 * @scope  i:size_t         The variable `i` must be declared as `size_t` and avaiable for use
 */
#define xfree(array, elements)\
	do {\
		for (i = 0; i < (elements); i++)\
			free((array)[i]);\
		free(array);\
		(array) = NULL;\
	} while (0)


/**
 * `malloc` wrapper that returns whether the allocation was not successful
 *  
 * @param   var:type*        The variable to which to assign the allocation
 * @param   elements:size_t  The number of elements to allocate
 * @param   type             The data type of the elements for which to create an allocation
 * @return  :int             Evaluates to true if an only if the allocation failed
 */
#define xmalloc(var, elements, type)\
	(!(var = malloc((elements) * sizeof(type))))
/*
#define xmalloc(var, elements, type)\
	({\
		size_t _x_elements = (elements);\
		size_t _x_size = _x_elements * sizeof(type);\
		fprintf(stderr, "xmalloc(%s, %zu, %s)(=%zu) @ %s:%i\n",\
		        #var, _x_elements, #type, _x_size, __FILE__, __LINE__);\
		(!(var = malloc(_x_size)));\
	})
*/


/**
 * `malloc` wrapper that returns whether the allocation was not successful
 *  
 * @param   var:type*     The variable to which to assign the allocation
 * @param   bytes:size_t  The number of bytes to allocate
 * @return  :int          Evaluates to true if an only if the allocation failed
 */
#define xbmalloc(var, bytes) \
  (!(var = malloc(bytes)))
/*
#define xbmalloc(var, bytes)\
	({\
		size_t _x_bytes = (bytes);\
		fprintf(stderr, "xbmalloc(%s, %zu) @ %s:%i\n",\
		        #var, _x_bytes, __FILE__, __LINE__);\
		(!(var = malloc(_x_bytes)));\
	})
*/


/**
 * `calloc` wrapper that returns whether the allocation was not successful
 *  
 * @param   var:type*        The variable to which to assign the allocation
 * @param   elements:size_t  The number of elements to allocate
 * @param   type             The data type of the elements for which to create an allocation
 * @return  :int             Evaluates to true if an only if the allocation failed
 */
#define xcalloc(var, elements, type)\
	(!(var = calloc(elements, sizeof(type))))
/*
#define xcalloc(var, elements, type)\
	({\
		size_t _x_elements = (elements);\
		size_t _x_size = _x_elements * sizeof(type);\
		fprintf(stderr, "xcalloc(%s, %zu, %s)(=%zu) @ %s:%i\n",\
		        #var, _x_elements, #type, _x_size, __FILE__, __LINE__);\
		(!(var = calloc(_x_elements, sizeof(type))));\
	})
*/


/**
 * `calloc` wrapper that returns whether the allocation was not successful
 *  
 * @param   var:type*     The variable to which to assign the allocation
 * @param   bytes:size_t  The number of bytes to allocate
 * @return  :int          Evaluates to true if an only if the allocation failed
 */
#define xbcalloc(var, bytes)\
	(!(var = calloc(bytes, sizeof(char))))
/*
#define xbcalloc(var, bytes)\
	({\
		size_t _x_bytes = (bytes);\
		fprintf(stderr, "xbcalloc(%s, %zu) @ %s:%i\n",\
			#var, _x_bytes, __FILE__, __LINE__);\
		(!(var = calloc(_x_bytes, sizeof(char))));\
	})
*/


/**
 * `realloc` wrapper that returns whether the allocation was not successful
 *  
 * @param   var:type*        The variable to which to assign the reallocation
 * @param   elements:size_t  The number of elements to allocate
 * @param   type             The data type of the elements for which to create an allocation
 * @return  :int             Evaluates to true if an only if the allocation failed
 */
#define xrealloc(var, elements, type)  \
	(!(var = realloc(var, (elements) * sizeof(type))))
/*
#define xrealloc(var, elements, type)\
	({\
		size_t _x_elements = (elements);\
		size_t _x_size = _x_elements * sizeof(type);\
		fprintf(stderr, "xrealloc(%s, %zu, %s)(=%zu) @ %s:%i\n",\
		        #var, _x_elements, #type, _x_size, __FILE__, __LINE__);\
		(!(var = realloc(var, _x_size)));\
	})
*/


/**
 * `xrealloc` that stores the old variable
 *  
 * @param   old:type*        The variable to which to store with the old variable that needs
 *                           to be `free`:ed on failure, and set to `NULL` on success.
 * @param   var:type*        The variable to which to assign the reallocation
 * @param   elements:size_t  The number of elements to allocate
 * @param   type             The data type of the elements for which to create an allocation
 * @return  :int             Evaluates to true if an only if the allocation failed
 */
#define xxrealloc(old, var, elements, type)\
	(old = var, ((!(var = realloc(var, (elements) * sizeof(type)))) ? 1 : (old = NULL, 0)))
/*
#define xxrealloc(old, var, elements, type)\
	({\
		size_t _x_elements = (elements);\
		size_t _x_size = _x_elements * sizeof(type);\
		fprintf(stderr, "xxrealloc(%s, %s, %zu, %s)(=%zu) @ %s:%i\n",\
			#old, #var, _x_elements, #type, _x_size, __FILE__, __LINE__);\
		(old = var, ((!(var = realloc(var, _x_size))) ? 1 : (old = NULL, 0)));\
	})
*/


/**
 * `xrealloc` that restores the variable on failure
 *  
 * @param   tmp:type*        The variable to which to store with the old variable temporarily
 * @param   var:type*        The variable to which to assign the reallocation
 * @param   elements:size_t  The number of elements to allocate
 * @param   type             The data type of the elements for which to create an allocation
 * @return  :int             Evaluates to true if an only if the allocation failed
 */
#define yrealloc(tmp, var, elements, type)\
	((tmp = var, !(var = realloc(var, (elements) * sizeof(type))))\
	 ? (var = tmp, tmp = NULL, 1) : (tmp = NULL, 0))
/*
#define yrealloc(tmp, var, elements, type)\
	({\
		size_t _x_elements = (elements);\
		size_t _x_size = _x_elements * sizeof(type);\
		fprintf(stderr, "yrealloc(%s, %s, %zu, %s)(=%zu) @ %s:%i\n",\
		        #tmp, #var, _x_elements, #type, _x_size, __FILE__, __LINE__);\
		((tmp = var, !(var = realloc(var, _x_size)))\
		? (var = tmp, tmp = NULL, 1) : (tmp = NULL, 0));\
	})
*/


/**
 * Double to the size of an allocation on the heap
 * 
 * @param   old:type*        Variable in which to store the old value temporarily
 * @param   var:type*        The variable to which to assign the reallocation
 * @param   elements:size_t  The number of elements to allocate
 * @param   type             The data type of the elements for which to create an allocation
 * @return  :int             Evaluates to true if an only if the allocation failed
 */
#define growalloc(old, var, elements, type) \
	(old = var, xrealloc(var, (elements) <<= 1, type) ? (var = old, (elements) >>= 1, 1) : 0)
/*
#define growalloc(old, var, elements, type)\
	({\
		size_t _x_elements_ = (elements);\
		size_t _x_size_ = _x_elements_ * sizeof(type);\
		fprintf(stderr, "growalloc(%s, %s, %zu, %s)(=%zu)\n-->  ",\
		        #old, #var, _x_elements_, #type, _x_size_, __FILE__, __LINE__);\
		(old = var, xrealloc(var, (elements) <<= 1, type) ? (var = old, (elements) >>= 1, 1) : 0);\
	})
*/


/**
 * `strdup` wrapper that returns whether the allocation was not successful
 *  
 * @param   var:char*             The variable to which to assign the duplicate
 * @param   original:const char*  The string to duplicate, may be `NULL`
 * @return  :int                  Evaluates to true if an only if the allocation failed
 */
#define xstrdup(var, original)\
	(original ? !(var = strdup(original)) : (var = NULL, 0))
/*
#define xstrdup(var, original)\
	({\
		size_t _x_size = original ? strlen(original) : 0;\
		fprintf(stderr, "xstrdup(%s, %s(“%s”=%zu))(=%zu) @ %s:%i\n",\
		        #var, #original, original, _x_size, _x_size + !!_x_size, __FILE__, __LINE__);\
		(original ? ((var = strdup(original)) == NULL) : (var = NULL, 0));\
	})
*/


/**
 * `strdup` wrapper that returns whether the allocation was not successful
 *  
 * This macro was added because GCC 6.1.1 warns of `original` is known to
 * be nonnull, when using `xstrdup`.
 * 
 * @param   var:char*             The variable to which to assign the duplicate
 * @param   original:const char*  The string to duplicate, must not be `NULL`
 * @return  :int                  Evaluates to true if an only if the allocation failed
 */
#define xstrdup_nn(var, original)\
	(!(var = strdup(original)))
/*
#define xstrdup_nn(var, original)\
	({\
		size_t _x_size = strlen(original);\
		fprintf(stderr, "xstrdup_nn(%s, %s(“%s”=%zu))(=%zu) @ %s:%i\n",\
		        #var, #original, original, _x_size, _x_size + !!_x_size, __FILE__, __LINE__);\
		!(var = strdup(original));\
	})
*/


/**
 * `malloc` and `memcpy` wrapper that creates a duplicate of a pointer and
 * returns whether the allocation was not successful
 *  
 * @param   var:void*             The variable to which to assign the duplicate
 * @param   original:const void*  The buffer to duplicate
 * @param   elements:size_t       The number of elements to duplicate
 * @param   type                  The data type of the elements to duplicate
 * @return  :int                  Evaluates to true if an only if the allocation failed
 */
#define xmemdup(var, original, elements, type)\
	(!(var = malloc((elements) * sizeof(type))) ? 1 :\
	 (memcpy(var, original, (elements) * sizeof(type)), 0))
/*
#define xmemdup(var, original, elements, type)\
	({\
		size_t _x_elements = (elements);\
		size_t _x_size = _x_elements * sizeof(type);\
		fprintf(stderr, "xmemdup(%s, %s, %zu, %s)(=%zu) @ %s:%i\n",\
		        #var, #original, _x_elements, #type, _x_size, __FILE__, __LINE__);\
		!(var = malloc(_x_size)) ? 1 : (memcpy(var, original, _x_size), 0);\
	})
*/



/**
 * Call `perror` if `errno` is non-zero and set `errno` to zero
 * 
 * @param  str:const char*  The argument passed to `perror`
 */
#define xperror(str)\
	(errno ? perror(str), errno = 0 : 0)


/**
 * Go to the label `fail` if a condition is met
 * 
 * @param  ...  The condition
 */
#define fail_if(...)\
	do {\
		int _fail_if_saved_errno;\
		if (__VA_ARGS__) {\
			_fail_if_saved_errno = errno;\
			if (errno != EMSGSIZE && errno != ECONNRESET && errno != EINTR)\
				fprintf(stderr, "failure at %s:%i\n", __FILE__, __LINE__);\
			errno = _fail_if_saved_errno;\
			goto fail;\
		}\
	} while (0)


/**
 * Run a set of instructions and return 1 if a condition is met
 * 
 * @param  condition     The condition
 * @param  instructions  The instruction (semicolon-terminated)
 */
#define exit_if(condition, instructions)\
	do { if (condition) { instructions return 1; } } while (0)


/**
 * The way to get a pointer to the end of a string
 * 
 * `strchr(str, '\0')` is faster than `str + strlen(str)`,
 * at least in the GNU C Library. If this is not true for
 * the compiler you are using, you may want to edit this
 * macro.
 */
#define STREND(str)\
	(strchr(str, '\0'))


/**
 * The system is running out of memory.
 * Quick, free up all your unused memory or kill yourself!
 */
#ifndef SIGDANGER
# define SIGDANGER (SIGRTMIN + 1)
#endif


/**
 * The user want the server to dump information
 * about the server's state or statistics
 */
#ifndef SIGINFO
# define SIGINFO (SIGRTMIN + 2)
#endif


/**
 * The user wants the program to re-exec.
 * into an updated binary
 */
#ifndef SIGUPDATE
# define SIGUPDATE SIGUSR1
#endif


/**
 * Normal signal handlers should place this macro
 * at the top of the function
 */
#define SIGHANDLER_START\
	int sighandler_saved_errno = errno


/**
 * Normal signal handlers should place this macro
 * at the bottom of the function, or just before
 * any `return`
 */
#define SIGHANDLER_END\
	(errno = sighandler_saved_errno)


#endif