|  | @@ -177,15 +177,13 @@ extern char *_getcwd(char *buf, size_t size);
 | 
	
		
			
				|  |  |  #define MAX_CONF_FILE_LINE_SIZE (8 * 1024)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  struct tuser_data {
 | 
	
		
			
				|  |  | -	char *first_message;
 | 
	
		
			
				|  |  | +	int _unused;
 | 
	
		
			
				|  |  |  };
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -/* Exit flag for the main loop (read and writen by different threads, thus
 | 
	
		
			
				|  |  | +/* Exit flag for the main loop (read and written by different threads, thus
 | 
	
		
			
				|  |  |   * volatile). */
 | 
	
		
			
				|  |  |  volatile int g_exit_flag = 0; /* 0 = continue running main loop */
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |  static char g_server_base_name[40]; /* Set by init_server_name() */
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  static const char *g_server_name; /* Default from init_server_name,
 | 
	
	
		
			
				|  | @@ -260,7 +258,7 @@ static NO_RETURN void
 | 
	
		
			
				|  |  |  die(const char *fmt, ...)
 | 
	
		
			
				|  |  |  {
 | 
	
		
			
				|  |  |  	va_list ap;
 | 
	
		
			
				|  |  | -	char msg[512] = "";
 | 
	
		
			
				|  |  | +	char msg[1024] = "";
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  	va_start(ap, fmt);
 | 
	
		
			
				|  |  |  	(void)vsnprintf(msg, sizeof(msg) - 1, fmt, ap);
 | 
	
	
		
			
				|  | @@ -268,7 +266,7 @@ die(const char *fmt, ...)
 | 
	
		
			
				|  |  |  	va_end(ap);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  #if defined(_WIN32)
 | 
	
		
			
				|  |  | -	MessageBox(NULL, msg, "Error", MB_OK);
 | 
	
		
			
				|  |  | +	MessageBox(NULL, msg, "Error", MB_ICONERROR | MB_OK);
 | 
	
		
			
				|  |  |  #else
 | 
	
		
			
				|  |  |  	fprintf(stderr, "%s\n", msg);
 | 
	
		
			
				|  |  |  #endif
 | 
	
	
		
			
				|  | @@ -469,135 +467,6 @@ sdup(const char *str)
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -#if 0 /* Unused code from "string duplicate with escape" */
 | 
	
		
			
				|  |  | -static unsigned
 | 
	
		
			
				|  |  | -hex2dec(char x)
 | 
	
		
			
				|  |  | -{
 | 
	
		
			
				|  |  | -    if ((x >= '0') && (x <= '9')) {
 | 
	
		
			
				|  |  | -        return (unsigned)x - (unsigned)'0';
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -    if ((x >= 'A') && (x <= 'F')) {
 | 
	
		
			
				|  |  | -        return (unsigned)x - (unsigned)'A' + 10u;
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -    if ((x >= 'a') && (x <= 'f')) {
 | 
	
		
			
				|  |  | -        return (unsigned)x - (unsigned)'a' + 10u;
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -    return 0;
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -static char *
 | 
	
		
			
				|  |  | -sdupesc(const char *str)
 | 
	
		
			
				|  |  | -{
 | 
	
		
			
				|  |  | -	char *p = sdup(str);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -	if (p) {
 | 
	
		
			
				|  |  | -		char *d = p;
 | 
	
		
			
				|  |  | -		while ((d = strchr(d, '\\')) != NULL) {
 | 
	
		
			
				|  |  | -			switch (d[1]) {
 | 
	
		
			
				|  |  | -			case 'a':
 | 
	
		
			
				|  |  | -				d[0] = '\a';
 | 
	
		
			
				|  |  | -				memmove(d + 1, d + 2, strlen(d + 1));
 | 
	
		
			
				|  |  | -				break;
 | 
	
		
			
				|  |  | -			case 'b':
 | 
	
		
			
				|  |  | -				d[0] = '\b';
 | 
	
		
			
				|  |  | -				memmove(d + 1, d + 2, strlen(d + 1));
 | 
	
		
			
				|  |  | -				break;
 | 
	
		
			
				|  |  | -			case 'e':
 | 
	
		
			
				|  |  | -				d[0] = 27;
 | 
	
		
			
				|  |  | -				memmove(d + 1, d + 2, strlen(d + 1));
 | 
	
		
			
				|  |  | -				break;
 | 
	
		
			
				|  |  | -			case 'f':
 | 
	
		
			
				|  |  | -				d[0] = '\f';
 | 
	
		
			
				|  |  | -				memmove(d + 1, d + 2, strlen(d + 1));
 | 
	
		
			
				|  |  | -				break;
 | 
	
		
			
				|  |  | -			case 'n':
 | 
	
		
			
				|  |  | -				d[0] = '\n';
 | 
	
		
			
				|  |  | -				memmove(d + 1, d + 2, strlen(d + 1));
 | 
	
		
			
				|  |  | -				break;
 | 
	
		
			
				|  |  | -			case 'r':
 | 
	
		
			
				|  |  | -				d[0] = '\r';
 | 
	
		
			
				|  |  | -				memmove(d + 1, d + 2, strlen(d + 1));
 | 
	
		
			
				|  |  | -				break;
 | 
	
		
			
				|  |  | -			case 't':
 | 
	
		
			
				|  |  | -				d[0] = '\t';
 | 
	
		
			
				|  |  | -				memmove(d + 1, d + 2, strlen(d + 1));
 | 
	
		
			
				|  |  | -				break;
 | 
	
		
			
				|  |  | -			case 'u':
 | 
	
		
			
				|  |  | -				if (isxdigit(d[2]) && isxdigit(d[3]) && isxdigit(d[4])
 | 
	
		
			
				|  |  | -				    && isxdigit(d[5])) {
 | 
	
		
			
				|  |  | -					unsigned short u = (unsigned short)(hex2dec(d[2]) * 4096
 | 
	
		
			
				|  |  | -					                                    + hex2dec(d[3]) * 256
 | 
	
		
			
				|  |  | -					                                    + hex2dec(d[4]) * 16
 | 
	
		
			
				|  |  | -					                                    + hex2dec(d[5]));
 | 
	
		
			
				|  |  | -					char mbc[16];
 | 
	
		
			
				|  |  | -					int mbl = wctomb(mbc, (wchar_t)u);
 | 
	
		
			
				|  |  | -					if ((mbl > 0) && (mbl < 6)) {
 | 
	
		
			
				|  |  | -						memcpy(d, mbc, (unsigned)mbl);
 | 
	
		
			
				|  |  | -						memmove(d + mbl, d + 6, strlen(d + 5));
 | 
	
		
			
				|  |  | -						/* Advance mbl characters (+1 is below) */
 | 
	
		
			
				|  |  | -						d += (mbl - 1);
 | 
	
		
			
				|  |  | -					} else {
 | 
	
		
			
				|  |  | -						/* Invalid multi byte character */
 | 
	
		
			
				|  |  | -						/* TODO: define what to do */
 | 
	
		
			
				|  |  | -					}
 | 
	
		
			
				|  |  | -				} else {
 | 
	
		
			
				|  |  | -					/* Invalid esc sequence */
 | 
	
		
			
				|  |  | -					/* TODO: define what to do */
 | 
	
		
			
				|  |  | -				}
 | 
	
		
			
				|  |  | -				break;
 | 
	
		
			
				|  |  | -			case 'v':
 | 
	
		
			
				|  |  | -				d[0] = '\v';
 | 
	
		
			
				|  |  | -				memmove(d + 1, d + 2, strlen(d + 1));
 | 
	
		
			
				|  |  | -				break;
 | 
	
		
			
				|  |  | -			case 'x':
 | 
	
		
			
				|  |  | -				if (isxdigit(d[2]) && isxdigit(d[3])) {
 | 
	
		
			
				|  |  | -					d[0] = (char)((unsigned char)(hex2dec(d[2]) * 16
 | 
	
		
			
				|  |  | -					                              + hex2dec(d[3])));
 | 
	
		
			
				|  |  | -					memmove(d + 1, d + 4, strlen(d + 3));
 | 
	
		
			
				|  |  | -				} else {
 | 
	
		
			
				|  |  | -					/* Invalid esc sequence */
 | 
	
		
			
				|  |  | -					/* TODO: define what to do */
 | 
	
		
			
				|  |  | -				}
 | 
	
		
			
				|  |  | -				break;
 | 
	
		
			
				|  |  | -			case 'z':
 | 
	
		
			
				|  |  | -				d[0] = 0;
 | 
	
		
			
				|  |  | -				memmove(d + 1, d + 2, strlen(d + 1));
 | 
	
		
			
				|  |  | -				break;
 | 
	
		
			
				|  |  | -			case '\\':
 | 
	
		
			
				|  |  | -				d[0] = '\\';
 | 
	
		
			
				|  |  | -				memmove(d + 1, d + 2, strlen(d + 1));
 | 
	
		
			
				|  |  | -				break;
 | 
	
		
			
				|  |  | -			case '\'':
 | 
	
		
			
				|  |  | -				d[0] = '\'';
 | 
	
		
			
				|  |  | -				memmove(d + 1, d + 2, strlen(d + 1));
 | 
	
		
			
				|  |  | -				break;
 | 
	
		
			
				|  |  | -			case '\"':
 | 
	
		
			
				|  |  | -				d[0] = '\"';
 | 
	
		
			
				|  |  | -				memmove(d + 1, d + 2, strlen(d + 1));
 | 
	
		
			
				|  |  | -				break;
 | 
	
		
			
				|  |  | -			case 0:
 | 
	
		
			
				|  |  | -				if (d == p) {
 | 
	
		
			
				|  |  | -					/* Line is only \ */
 | 
	
		
			
				|  |  | -					free(p);
 | 
	
		
			
				|  |  | -					return NULL;
 | 
	
		
			
				|  |  | -				}
 | 
	
		
			
				|  |  | -			/* no break */
 | 
	
		
			
				|  |  | -			default:
 | 
	
		
			
				|  |  | -				/* invalid ESC sequence */
 | 
	
		
			
				|  |  | -				/* TODO: define what to do */
 | 
	
		
			
				|  |  | -				break;
 | 
	
		
			
				|  |  | -			}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -			/* Advance to next character */
 | 
	
		
			
				|  |  | -			d++;
 | 
	
		
			
				|  |  | -		}
 | 
	
		
			
				|  |  | -	}
 | 
	
		
			
				|  |  | -	return p;
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -#endif
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |  static const char *
 | 
	
		
			
				|  |  |  get_option(char **options, const char *option_name)
 | 
	
		
			
				|  |  |  {
 | 
	
	
		
			
				|  | @@ -991,22 +860,6 @@ free_system_info(void)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  static int
 | 
	
		
			
				|  |  | -log_message(const struct mg_connection *conn, const char *message)
 | 
	
		
			
				|  |  | -{
 | 
	
		
			
				|  |  | -	const struct mg_context *ctx = mg_get_context(conn);
 | 
	
		
			
				|  |  | -	struct tuser_data *ud = (struct tuser_data *)mg_get_user_data(ctx);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -	fprintf(stderr, "%s\n", message);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -	if (ud->first_message == NULL) {
 | 
	
		
			
				|  |  | -		ud->first_message = sdup(message);
 | 
	
		
			
				|  |  | -	}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -	return 0;
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -static int
 | 
	
		
			
				|  |  |  is_path_absolute(const char *path)
 | 
	
		
			
				|  |  |  {
 | 
	
		
			
				|  |  |  #if defined(_WIN32)
 | 
	
	
		
			
				|  | @@ -1318,11 +1171,19 @@ sanitize_options(char *options[] /* server options */,
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +/* Forward declaration: */
 | 
	
		
			
				|  |  | +static void show_settings_dialog(void);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  static void
 | 
	
		
			
				|  |  |  start_civetweb(int argc, char *argv[])
 | 
	
		
			
				|  |  |  {
 | 
	
		
			
				|  |  |  	struct mg_callbacks callbacks;
 | 
	
		
			
				|  |  |  	char *options[2 * MAX_OPTIONS + 1];
 | 
	
		
			
				|  |  | +	struct mg_init_data init;
 | 
	
		
			
				|  |  | +	struct mg_error_data error;
 | 
	
		
			
				|  |  | +	char error_text[256];
 | 
	
		
			
				|  |  | +	unsigned error_code;
 | 
	
		
			
				|  |  |  	int i;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  	/* Start option -I:
 | 
	
	
		
			
				|  | @@ -1460,10 +1321,20 @@ start_civetweb(int argc, char *argv[])
 | 
	
		
			
				|  |  |  	/* Initialize user data */
 | 
	
		
			
				|  |  |  	memset(&g_user_data, 0, sizeof(g_user_data));
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -	/* Start Civetweb */
 | 
	
		
			
				|  |  |  	memset(&callbacks, 0, sizeof(callbacks));
 | 
	
		
			
				|  |  | -	callbacks.log_message = &log_message;
 | 
	
		
			
				|  |  | -	g_ctx = mg_start(&callbacks, &g_user_data, (const char **)options);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	memset(&init, 0, sizeof(init));
 | 
	
		
			
				|  |  | +	init.callbacks = &callbacks;
 | 
	
		
			
				|  |  | +	init.configuration_options = options;
 | 
	
		
			
				|  |  | +	init.user_data = &g_user_data;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	memset(&error, 0, sizeof(error));
 | 
	
		
			
				|  |  | +	error.text = error_text;
 | 
	
		
			
				|  |  | +	error.text_buffer_size = sizeof(error_text);
 | 
	
		
			
				|  |  | +	error.code = &error_code;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	/* Start Civetweb */
 | 
	
		
			
				|  |  | +	g_ctx = mg_start2(&init, &error);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  	/* mg_start copies all options to an internal buffer.
 | 
	
		
			
				|  |  |  	 * The options data field here is not required anymore. */
 | 
	
	
		
			
				|  | @@ -1473,11 +1344,31 @@ start_civetweb(int argc, char *argv[])
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  	/* If mg_start fails, it returns NULL */
 | 
	
		
			
				|  |  |  	if (g_ctx == NULL) {
 | 
	
		
			
				|  |  | -		die("Failed to start %s:\n%s",
 | 
	
		
			
				|  |  | +#ifdef _WIN32
 | 
	
		
			
				|  |  | +		/* On Windows: provide option to edit configuration file. */
 | 
	
		
			
				|  |  | +		char errtxt[1024];
 | 
	
		
			
				|  |  | +		int ret;
 | 
	
		
			
				|  |  | +		sprintf(errtxt,
 | 
	
		
			
				|  |  | +		        "Failed to start %s with code %u:\n%s\n\nEdit settings?",
 | 
	
		
			
				|  |  | +		        g_server_name,
 | 
	
		
			
				|  |  | +		        error_code,
 | 
	
		
			
				|  |  | +		        error_text);
 | 
	
		
			
				|  |  | +		ret = MessageBox(NULL, errtxt, "Error", MB_ICONERROR | MB_YESNOCANCEL);
 | 
	
		
			
				|  |  | +		if (ret == IDYES) {
 | 
	
		
			
				|  |  | +			show_settings_dialog();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +			/* Hitting "save" will also restart the server. */
 | 
	
		
			
				|  |  | +			if (g_ctx != NULL) {
 | 
	
		
			
				|  |  | +				return;
 | 
	
		
			
				|  |  | +			}
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +		exit(EXIT_FAILURE);
 | 
	
		
			
				|  |  | +#else
 | 
	
		
			
				|  |  | +		die("Failed to start %s with code %u:\n%s",
 | 
	
		
			
				|  |  |  		    g_server_name,
 | 
	
		
			
				|  |  | -		    ((g_user_data.first_message == NULL) ? "unknown reason"
 | 
	
		
			
				|  |  | -		                                         : g_user_data.first_message));
 | 
	
		
			
				|  |  | -		/* TODO: Edit file g_config_file_name */
 | 
	
		
			
				|  |  | +		    error_code,
 | 
	
		
			
				|  |  | +		    error_text);
 | 
	
		
			
				|  |  | +#endif
 | 
	
		
			
				|  |  |  	}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  #if defined(MG_EXPERIMENTAL_INTERFACES)
 | 
	
	
		
			
				|  | @@ -1517,8 +1408,6 @@ static void
 | 
	
		
			
				|  |  |  stop_civetweb(void)
 | 
	
		
			
				|  |  |  {
 | 
	
		
			
				|  |  |  	mg_stop(g_ctx);
 | 
	
		
			
				|  |  | -	free(g_user_data.first_message);
 | 
	
		
			
				|  |  | -	g_user_data.first_message = NULL;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  
 |