static int ssl_cipher_process_rulestr(const char *rule_str,
                CIPHER_ORDER **head_p, CIPHER_ORDER **tail_p,
                SSL_CIPHER **ca_list)
	{
	unsigned long algorithms, mask, algo_strength, mask_strength;
	const char *l, *start, *buf;
	int j, multi, found, rule, retval, ok, buflen;
	unsigned long cipher_id;
	char ch;

	retval = 1;
	l = rule_str;
	for (;;)
		{
		ch = *l;

		if (ch == '\0')
			break;		/* done */
		if (ch == '-')
			{ rule = CIPHER_DEL; l++; }
		else if (ch == '+')
			{ rule = CIPHER_ORD; l++; }
		else if (ch == '!')
			{ rule = CIPHER_KILL; l++; }
		else if (ch == '@')
			{ rule = CIPHER_SPECIAL; l++; }
		else
			{ rule = CIPHER_ADD; }

		if (ITEM_SEP(ch))
			{
			l++;
			continue;
			}

		algorithms = mask = algo_strength = mask_strength = 0;

		start=l;
		for (;;)
			{
			ch = *l;
			buf = l;
			buflen = 0;
#ifndef CHARSET_EBCDIC
			while (	((ch >= 'A') && (ch <= 'Z')) ||
				((ch >= '0') && (ch <= '9')) ||
				((ch >= 'a') && (ch <= 'z')) ||
				 (ch == '-'))
#else
			while (	isalnum(ch) || (ch == '-'))
#endif
				 {
				 ch = *(++l);
				 buflen++;
				 }

			if (buflen == 0)
				{
				/*
				 * We hit something we cannot deal with,
				 * it is no command or separator nor
				 * alphanumeric, so we call this an error.
				 */
				SSLerr(SSL_F_SSL_CIPHER_PROCESS_RULESTR,
				       SSL_R_INVALID_COMMAND);
				retval = found = 0;
				l++;
				break;
				}

			if (rule == CIPHER_SPECIAL)
				{
				found = 0; /* unused -- avoid compiler warning */
				break;	/* special treatment */
				}

			/* check for multi-part specification */
			if (ch == '+')
				{
				multi=1;
				l++;
				}
			else
				multi=0;

			/*
			 * Now search for the cipher alias in the ca_list. Be careful
			 * with the strncmp, because the "buflen" limitation
			 * will make the rule "ADH:SOME" and the cipher
			 * "ADH-MY-CIPHER" look like a match for buflen=3.
			 * So additionally check whether the cipher name found
			 * has the correct length. We can save a strlen() call:
			 * just checking for the '\0' at the right place is
			 * sufficient, we have to strncmp() anyway. (We cannot
			 * use strcmp(), because buf is not '\0' terminated.)
			 */
			 j = found = 0;
			 cipher_id = 0;
			 while (ca_list[j])
				{
				if (!strncmp(buf, ca_list[j]->name, buflen) &&
				    (ca_list[j]->name[buflen] == '\0'))
					{
					found = 1;
					break;
					}
				else
					j++;
				}
			if (!found)
				break;	/* ignore this entry */

			if (ca_list[j]->valid)
				{
				cipher_id = ca_list[j]->id;
				break;
				}

			/* New algorithms:
			 *  1 - any old restrictions apply outside new mask
			 *  2 - any new restrictions apply outside old mask
			 *  3 - enforce old & new where masks intersect
			 */
			algorithms = (algorithms & ~ca_list[j]->mask) |		/* 1 */
			             (ca_list[j]->algorithms & ~mask) |		/* 2 */
			             (algorithms & ca_list[j]->algorithms);	/* 3 */
			mask |= ca_list[j]->mask;
			algo_strength = (algo_strength & ~ca_list[j]->mask_strength) |
			                (ca_list[j]->algo_strength & ~mask_strength) |
			                (algo_strength & ca_list[j]->algo_strength);
			mask_strength |= ca_list[j]->mask_strength;

			if (!multi) break;
			}

		/*
		 * Ok, we have the rule, now apply it
		 */
		if (rule == CIPHER_SPECIAL)
			{	/* special command */
			ok = 0;
			if ((buflen == 8) &&
				!strncmp(buf, "STRENGTH", 8))
				ok = ssl_cipher_strength_sort(head_p, tail_p);
			else
				SSLerr(SSL_F_SSL_CIPHER_PROCESS_RULESTR,
					SSL_R_INVALID_COMMAND);
			if (ok == 0)
				retval = 0;
			/*
			 * We do not support any "multi" options
			 * together with "@", so throw away the
			 * rest of the command, if any left, until
			 * end or ':' is found.
			 */
			while ((*l != '\0') && ITEM_SEP(*l))
				l++;
			}
		else if (found)
			{
			ssl_cipher_apply_rule(cipher_id, algorithms, mask,
				algo_strength, mask_strength, rule, -1,
				head_p, tail_p);
			}
		else
			{
			while ((*l != '\0') && ITEM_SEP(*l))
				l++;
			}
		if (*l == '\0') break; /* done */
		}

	return(retval);
	}