|  | @@ -7,21 +7,34 @@
 | 
	
		
			
				|  |  |  #define MG_MATCH_CONTEXT_MAX_MATCHES (32)
 | 
	
		
			
				|  |  |  #endif
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +struct mg_match_element {
 | 
	
		
			
				|  |  | +	const char *str;
 | 
	
		
			
				|  |  | +	size_t len;
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  struct mg_match_context {
 | 
	
		
			
				|  |  |  	int case_sensitive;
 | 
	
		
			
				|  |  |  	size_t num_matches;
 | 
	
		
			
				|  |  | -	const char *match_str[MG_MATCH_CONTEXT_MAX_MATCHES];
 | 
	
		
			
				|  |  | -	size_t match_len[MG_MATCH_CONTEXT_MAX_MATCHES];
 | 
	
		
			
				|  |  | +	struct mg_match_element match[MG_MATCH_CONTEXT_MAX_MATCHES];
 | 
	
		
			
				|  |  |  };
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +/* Initialize structure with 0 matches */
 | 
	
		
			
				|  |  | +static void
 | 
	
		
			
				|  |  | +match_context_reset(struct mg_match_context *mcx)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +	mcx->num_matches = 0;
 | 
	
		
			
				|  |  | +	memset(mcx->match, 0, sizeof(mcx->match));
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  /* Add a new match to the list of matches */
 | 
	
		
			
				|  |  |  static void
 | 
	
		
			
				|  |  |  match_context_push(const char *str, size_t len, struct mg_match_context *mcx)
 | 
	
		
			
				|  |  |  {
 | 
	
		
			
				|  |  |  	if (mcx->num_matches < MG_MATCH_CONTEXT_MAX_MATCHES) {
 | 
	
		
			
				|  |  | -		mcx->match_str[mcx->num_matches] = str;
 | 
	
		
			
				|  |  | -		mcx->match_len[mcx->num_matches] = len;
 | 
	
		
			
				|  |  | +		mcx->match[mcx->num_matches].str = str;
 | 
	
		
			
				|  |  | +		mcx->match[mcx->num_matches].len = len;
 | 
	
		
			
				|  |  |  		mcx->num_matches++;
 | 
	
		
			
				|  |  |  	}
 | 
	
		
			
				|  |  |  }
 | 
	
	
		
			
				|  | @@ -136,6 +149,11 @@ mg_match_alternatives(const char *pat,
 | 
	
		
			
				|  |  |                        struct mg_match_context *mcx)
 | 
	
		
			
				|  |  |  {
 | 
	
		
			
				|  |  |  	const char *match_alternative = (const char *)memchr(pat, '|', pat_len);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	if (mcx != NULL) {
 | 
	
		
			
				|  |  | +		match_context_reset(mcx);
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  	while (match_alternative != NULL) {
 | 
	
		
			
				|  |  |  		/* Split at | for alternative match */
 | 
	
		
			
				|  |  |  		size_t left_size = (size_t)(match_alternative - pat);
 | 
	
	
		
			
				|  | @@ -147,6 +165,11 @@ mg_match_alternatives(const char *pat,
 | 
	
		
			
				|  |  |  			return ret;
 | 
	
		
			
				|  |  |  		}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +		/* Reset possible incomplete match data */
 | 
	
		
			
				|  |  | +		if (mcx != NULL) {
 | 
	
		
			
				|  |  | +			match_context_reset(mcx);
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  		/* If no match: try right side */
 | 
	
		
			
				|  |  |  		pat += left_size + 1;
 | 
	
		
			
				|  |  |  		pat_len -= left_size + 1;
 | 
	
	
		
			
				|  | @@ -158,6 +181,20 @@ mg_match_alternatives(const char *pat,
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +static int
 | 
	
		
			
				|  |  | +match_compare(const void *p1, const void *p2, void *user)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +	(void)user;
 | 
	
		
			
				|  |  | +	const struct mg_match_element *e1 = (const struct mg_match_element *)p1;
 | 
	
		
			
				|  |  | +	const struct mg_match_element *e2 = (const struct mg_match_element *)p2;
 | 
	
		
			
				|  |  | +	if (e1->str > e2->str)
 | 
	
		
			
				|  |  | +		return +1;
 | 
	
		
			
				|  |  | +	if (e1->str < e2->str)
 | 
	
		
			
				|  |  | +		return -1;
 | 
	
		
			
				|  |  | +	return 0;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  /* Export as public API? */
 | 
	
		
			
				|  |  |  static ptrdiff_t
 | 
	
		
			
				|  |  |  mg_match(const char *pat,
 | 
	
	
		
			
				|  | @@ -165,20 +202,46 @@ mg_match(const char *pat,
 | 
	
		
			
				|  |  |           const char *str,
 | 
	
		
			
				|  |  |           struct mg_match_context *mcx)
 | 
	
		
			
				|  |  |  {
 | 
	
		
			
				|  |  | -	ptrdiff_t ret;
 | 
	
		
			
				|  |  | -	if (mcx != NULL) {
 | 
	
		
			
				|  |  | -		mcx->num_matches = 0;
 | 
	
		
			
				|  |  | -		memset((void *)(mcx->match_str), 0, sizeof(mcx->match_str));
 | 
	
		
			
				|  |  | -		memset(mcx->match_len, 0, sizeof(mcx->match_len));
 | 
	
		
			
				|  |  | -	}
 | 
	
		
			
				|  |  | -	ret = mg_match_alternatives(pat, pat_len, str, mcx);
 | 
	
		
			
				|  |  | +	ptrdiff_t ret = mg_match_alternatives(pat, pat_len, str, mcx);
 | 
	
		
			
				|  |  |  	if (mcx != NULL) {
 | 
	
		
			
				|  |  |  		if (ret < 0) {
 | 
	
		
			
				|  |  | -			mcx->num_matches = 0;
 | 
	
		
			
				|  |  | -			memset((void *)(mcx->match_str), 0, sizeof(mcx->match_str));
 | 
	
		
			
				|  |  | -			memset(mcx->match_len, 0, sizeof(mcx->match_len));
 | 
	
		
			
				|  |  | +			/* Remove possible incomplete data */
 | 
	
		
			
				|  |  | +			match_context_reset(mcx);
 | 
	
		
			
				|  |  |  		} else {
 | 
	
		
			
				|  |  | -			/* TODO: Join matches */
 | 
	
		
			
				|  |  | +			/* Join "?*" to one pattern. */
 | 
	
		
			
				|  |  | +			size_t i, j;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +			/* Use difference of two array elements instead of sizeof, since
 | 
	
		
			
				|  |  | +			 * there may be some additional padding bytes. */
 | 
	
		
			
				|  |  | +			size_t elmsize =
 | 
	
		
			
				|  |  | +			    (size_t)(&mcx->match[1]) - (size_t)(&mcx->match[0]);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +			/* First sort the matches by address ("str" begin to end) */
 | 
	
		
			
				|  |  | +			mg_sort(mcx->match, mcx->num_matches, elmsize, match_compare, NULL);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +			/* Join consecutive matches */
 | 
	
		
			
				|  |  | +			i = 1;
 | 
	
		
			
				|  |  | +			while (i < mcx->num_matches) {
 | 
	
		
			
				|  |  | +				if ((mcx->match[i - 1].str + mcx->match[i - 1].len)
 | 
	
		
			
				|  |  | +				    == mcx->match[i].str) {
 | 
	
		
			
				|  |  | +					/* Two matches are consecutive. Join length. */
 | 
	
		
			
				|  |  | +					mcx->match[i - 1].len += mcx->match[i].len;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +					/* Shift all list elements. */
 | 
	
		
			
				|  |  | +					for (j = i + 1; j < mcx->num_matches; j++) {
 | 
	
		
			
				|  |  | +						mcx->match[j - 1].len = mcx->match[j].len;
 | 
	
		
			
				|  |  | +						mcx->match[j - 1].str = mcx->match[j].str;
 | 
	
		
			
				|  |  | +					}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +					/* Remove/blank last list element. */
 | 
	
		
			
				|  |  | +					mcx->num_matches--;
 | 
	
		
			
				|  |  | +					mcx->match[mcx->num_matches].str = NULL;
 | 
	
		
			
				|  |  | +					mcx->match[mcx->num_matches].len = 0;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +				} else {
 | 
	
		
			
				|  |  | +					i++;
 | 
	
		
			
				|  |  | +				}
 | 
	
		
			
				|  |  | +			}
 | 
	
		
			
				|  |  |  		}
 | 
	
		
			
				|  |  |  	}
 | 
	
		
			
				|  |  |  	return ret;
 |