예제 #1
0
파일: custom.c 프로젝트: wangdkchina/xcb
/* FIXME */
void sall_command(client c) {
	dstr res = get_indices();
	dstr *fields = NULL;
	int nfield = 0, i;

	RTRIM(res);
	fields = dstr_split_len(res, dstr_length(res), ",", 1, &nfield);
	for (i = 1; i < nfield; ++i) {
		dstr pkey = dstr_new(fields[i]);
		dstr skey = dstr_new(pkey);
		dlist_t dlist;
		struct kvd *kvd;

		table_rwlock_wrlock(subscribers);
		if ((dlist = table_get_value(subscribers, pkey)) == NULL) {
			if (NEW(kvd)) {
				kvd->key = skey;
				kvd->u.dlist = dlist_new(NULL, NULL);
				dlist_insert_tail(kvd->u.dlist, c);
				dlist = dlist_new(cmpkvd, kdfree);
				dlist_insert_sort(dlist, kvd);
				table_insert(subscribers, pkey, dlist);
			} else {
				add_reply_error(c, "error allocating memory for kvd\r\n");
				dstr_free(skey);
				dstr_free(pkey);
			}
		} else {
			if (NEW(kvd)) {
				dlist_node_t node;

				kvd->key     = skey;
				kvd->u.dlist = dlist_new(NULL, NULL);
				if ((node = dlist_find(dlist, kvd)) == NULL) {
					dlist_insert_tail(kvd->u.dlist, c);
					dlist_insert_sort(dlist, kvd);
				} else {
					kdfree(kvd);
					kvd = (struct kvd *)dlist_node_value(node);
					if (dlist_find(kvd->u.dlist, c) == NULL)
						dlist_insert_tail(kvd->u.dlist, c);
				}
			} else {
				add_reply_error(c, "error allocating memory for kvd\r\n");
				dstr_free(skey);
			}
			dstr_free(pkey);
		}
		table_rwlock_unlock(subscribers);
	}
	dstr_free(res);
//--------------------------------------------------------------------------------------------------------------------
//	FIXME
//--------------------------------------------------------------------------------------------------------------------
}
예제 #2
0
void radix_block_insert(char *key, const char *sep, struct node *p)
{
    dstr *fields = NULL;
    int count = 0, i = 0;
    fields = dstr_split_len(key, strlen(key), sep, 1, &count);
    for (i = 0; i < count; i++)
    {
        int j = 0, flag = 0;
        for (j = 0; j < p->child_num; j++)
        {
            if (!strcmp(p->child[j]->key, fields[i]))
            {
                flag = 1;
                if (i == count - 1)
                {
                    p->child[j]->is_real = 1;
                }
                p = p->child[j];
                break;
            }
        }
        if (0 == flag)
        {
            int k;
            struct node *q = (struct node*)malloc(sizeof(struct node));
            q = p;
            for (k = i; k < count; k++)
            {

                struct node *n = (struct node*)malloc(sizeof(struct node));
                strcpy(n->key,fields[k]);
                n->parent = q;
                if (k == count - 1)
                {
                    n->is_real = 1;
                }

                q->child = realloc(q->child, sizeof(struct node));
                q->child[q->child_num] = n;
                q->child_num++;

                q = n;
            }
            break;
        }
    }
}
예제 #3
0
파일: md_xspeed.c 프로젝트: ifzz/xcb
static void on_user_login(struct DFITCUserLoginInfoRtnField *userlogin,
	struct DFITCErrorRtnField *rspinfo) {
	if (rspinfo && rspinfo->nErrorID != 0)
		xcb_log(XCB_LOG_ERROR, "Error occurred: errorid=%d", rspinfo->nErrorID);
	else if (contracts) {
		dstr *fields = NULL;
		int nfield = 0, i;

		fields = dstr_split_len(contracts, strlen(contracts), ",", 1, &nfield);
		for (i = 0; i < nfield; ++i)
			if (xspeed_mdapi_subscribe_market_data(mdapi, &fields[i], 1, 0) == 0)
				xcb_log(XCB_LOG_INFO, "Subscribe contract '%s' succeeded", fields[i]);
			else
				xcb_log(XCB_LOG_INFO, "Subscribe contract '%s' failed", fields[i]);
		dstr_free_tokens(fields, nfield);
	}
}
예제 #4
0
파일: custom.c 프로젝트: wangdkchina/xcb
/* FIXME */
void uall_command(client c) {
	dstr res = get_indices();
	dstr *fields = NULL;
	int nfield = 0, i;

	RTRIM(res);
	fields = dstr_split_len(res, dstr_length(res), ",", 1, &nfield);
	for (i = 1; i < nfield; ++i) {
		dstr pkey = dstr_new(fields[i]);
		dstr skey = dstr_new(pkey);
		dlist_t dlist;

		table_rwlock_wrlock(subscribers);
		if ((dlist = table_get_value(subscribers, pkey))) {
			struct kvd *kvd;

			if (NEW(kvd)) {
				dlist_node_t node, node2;

				kvd->key = skey;
				if ((node = dlist_find(dlist, kvd))) {
					FREE(kvd);
					kvd = (struct kvd *)dlist_node_value(node);
					if ((node2 = dlist_find(kvd->u.dlist, c)))
						dlist_remove(kvd->u.dlist, node2);
					if (dlist_length(kvd->u.dlist) == 0) {
						dlist_remove(dlist, node);
						kdfree(kvd);
					}
					if (dlist_length(dlist) == 0) {
						table_remove(subscribers, pkey);
						dlist_free(&dlist);
					}
				} else
					FREE(kvd);
			} else
				add_reply_error(c, "error allocating memory for kvd");
		}
		table_rwlock_unlock(subscribers);
		dstr_free(skey);
		dstr_free(pkey);
	}
	add_reply_string(c, "\r\n", 2);
	dstr_free(res);
}
예제 #5
0
파일: md_femas.c 프로젝트: herberteuler/xcb
static void on_user_login(struct CUstpFtdcRspUserLoginField *userlogin,
	struct CUstpFtdcRspInfoField *rspinfo, int rid, int islast) {
	/* FIXME */
	if (rspinfo && rspinfo->ErrorID != 0) {
		xcb_log(XCB_LOG_ERROR, "Error occurred: errorid=%d", rspinfo->ErrorID);
	} else if (islast && contracts) {
		dstr *fields = NULL;
		int nfield = 0, i;

		fields = dstr_split_len(contracts, strlen(contracts), ",", 1, &nfield);
		for (i = 0; i < nfield; ++i)
			if (femas_mdapi_subscribe_market_data(mdapi, &fields[i], 1) == 0)
				xcb_log(XCB_LOG_INFO, "Subscribe contract '%s' succeeded", fields[i]);
			else
				xcb_log(XCB_LOG_INFO, "Subscribe contract '%s' failed", fields[i]);
		dstr_free_tokens(fields, nfield);
	}
}
예제 #6
0
파일: md_xspeed2.c 프로젝트: wang-shun/xcb
static void on_user_login(struct ErrorRtnField *rspinfo) {
	/* FIXME */
	if (rspinfo && rspinfo->ErrorID != 0) {
		xcb_log(XCB_LOG_ERROR, "Error occurred: errorid=%d", rspinfo->ErrorID);
		return;
	}
	if (contracts) {
		dstr *fields = NULL;
		int nfield = 0, i;

		fields = dstr_split_len(contracts, strlen(contracts), ",", 1, &nfield);
		for (i = 0; i < nfield; ++i)
			if ((xspeed_l2api_subscribe_market_data(l2api, &fields[i], 1)) == 0)
				xcb_log(XCB_LOG_INFO, "Subscribe contract '%s' succeeded", fields[i]);
			else
				xcb_log(XCB_LOG_INFO, "Subscribe contract '%s' failed", fields[i]);
		dstr_free_tokens(fields, nfield);
	} else {
		if ((xspeed_l2api_subscribe_all(l2api)) == 0)
			xcb_log(XCB_LOG_INFO, "Subscribe all succeeded");
		else
			xcb_log(XCB_LOG_INFO, "Subscribe all failed");
	}
}
예제 #7
0
파일: xcb-client.c 프로젝트: ifzz/xcb
static void *read_thread(void *data) {
	xcb_context_t c = (xcb_context_t)data;
	struct pollfd rfd[1];

	rfd[0].fd     = c->fd;
	rfd[0].events = POLLIN;
	for (;;) {
		int nread;

		if (poll(rfd, 1, -1) == -1) {
			xcb_set_error_from_errno(c, XCB_ERR_IO, "poll(2)");
			break;
		}
		if ((nread = read(c->fd, c->inbuf + c->inpos, sizeof c->inbuf - c->inpos)) == -1) {
			if (errno == EAGAIN || errno == EINTR)
				nread = 0;
			else {
				xcb_set_error(c, XCB_ERR_IO, NULL);
				break;
			}
		} else if (nread == 0) {
			xcb_set_error(c, XCB_ERR_EOF, "Server closed the connection");
			break;
		}
		if (nread)
			c->inpos += nread;
		else
			continue;
		if (c->inpos >= sizeof c->inbuf) {
			xcb_set_error(c, XCB_ERR_IO, "Client reached max input buffer length");
			break;
		}
		c->inbuf[c->inpos] = '\0';
		while (c->inpos > 0) {
			char *newline;
			size_t len;
			dstr *fields = NULL;
			int nfield = 0;

			if ((newline = strstr(c->inbuf, "\r\n")) == NULL) {
				if (c->inpos == sizeof c->inbuf - 1) {
					xcb_set_error(c, XCB_ERR_PROTOCOL, "Too big reply");
					goto end;
				}
				break;
			}
			len = newline - c->inbuf;
			fields = dstr_split_len(c->inbuf, len, ",", 1, &nfield);
			if (nfield > 1) {
				/* FIXME */
				if (!strcasecmp(fields[1], "indices")) {
					if (c->xcb_callback_indices)
						c->xcb_callback_indices(c->inbuf);
				} else if (!strcasecmp(fields[1], "timestamp")) {
					if (c->xcb_callback_index)
						c->xcb_callback_index(c->inbuf);
				} else if (!strcmp(fields[1], "0") || !strcmp(fields[1], "1")) {
					if (c->xcb_callback_query)
						c->xcb_callback_query(c->inbuf);
				} else if (c->xcb_callback_subscribe)
					c->xcb_callback_subscribe(c->inbuf);
			}
			dstr_free_tokens(fields, nfield);
			memmove(c->inbuf, c->inbuf + len + 2, sizeof c->inbuf - len - 2);
			c->inpos -= len + 2;
		}
	}

end:
	rthread = (pthread_t)-1;
	return NULL;
}
예제 #8
0
static int mm_impvbaw_exec(void *data, void *data2) {
	RAII_VAR(struct msg *, msg, (struct msg *)data, msg_decr);
	dstr *fields = NULL;
	int nfield = 0;
	time_t t;
	dstr spotname;
	double vol;
	NOT_USED(data2);

	fields = dstr_split_len(msg->data, strlen(msg->data), ",", 1, &nfield);
	/* FIXME */
	if (nfield != 19) {
		xcb_log(XCB_LOG_WARNING, "Message '%s' garbled", msg->data);
		goto end;
	}
	t        = (time_t)atoi(fields[1]);
	spotname = dstr_new(fields[11]);
	vol      = atof(fields[5]);
	if (!isnan(vol)) {
		struct mm *mm;
		int flag = 0;

		table_lock(contracts);
		if ((mm = table_get_value(contracts, spotname)) == NULL) {
			if (NEW(mm)) {
				mm->min = mm->max = vol;
				flag = 1;
				table_insert(contracts, spotname, mm);
			} else
				xcb_log(XCB_LOG_WARNING, "Error allocating memory for mm");
		} else {
			if (mm->min > vol) {
				mm->min = vol;
				flag = 1;
			} else if (mm->max < vol) {
				mm->max = vol;
				flag = 1;
			}
			dstr_free(spotname);
		}
		if (mm && flag) {
			struct tm lt;
			char datestr[64], res[512];

			localtime_r(&t, &lt);
			lt.tm_hour = lt.tm_min = lt.tm_sec = 0;
			strftime(datestr, sizeof datestr, "%F %T", &lt);
			snprintf(res, sizeof res, "MM_IMPVBAW,%s.000,%s|%f,%f",
				datestr,
				fields[11],
				mm->min,
				mm->max);
			out2rmp(res);
		}
		table_unlock(contracts);
	}

end:
	dstr_free_tokens(fields, nfield);
	return 0;
}
예제 #9
0
static int vxo_exec(void *data, void *data2) {
	RAII_VAR(struct msg *, msg, (struct msg *)data, msg_decr);
	struct msgs *out = (struct msgs *)data2;
	dstr *fields = NULL;
	int nfield = 0;
	double vol, vol2, vol3, spot, strike, r, expiry;
	int sec, msec;
	dstr spotname;
	char *type;
	struct pd *pd;
	struct scp *scp;

	fields = dstr_split_len(msg->data, strlen(msg->data), ",", 1, &nfield);
	/* FIXME */
	if (nfield != 19) {
		xcb_log(XCB_LOG_WARNING, "Message '%s' garbled", msg->data);
		goto end;
	}
	vol      = !strcasecmp(fields[5], "nan") ? NAN : atof(fields[5]);
	vol2     = !strcasecmp(fields[7], "nan") ? NAN : atof(fields[7]);
	vol3     = !strcasecmp(fields[9], "nan") ? NAN : atof(fields[9]);
	if (isnan(vol) && isnan(vol2) && isnan(vol3))
		goto end;
	sec      = atoi(fields[1]);
	msec     = atoi(fields[2]);
	spot     = atof(fields[10]);
	spotname = dstr_new(fields[11]);
	type     = fields[12];
	strike   = atof(fields[13]);
	r        = atof(fields[14]);
	expiry   = atof(fields[15]);
	table_lock(spots);
	if ((pd = table_get_value(spots, spotname)) == NULL) {
		/* can't happen */
		if (NEW(scp) == NULL) {
			xcb_log(XCB_LOG_WARNING, "Error allocating memory for scp");
			table_unlock(spots);
			goto end;
		}
		scp->strike = strike;
		if (!strcasecmp(type, "C")) {
			scp->cvol  = vol;
			scp->pvol  = NAN;
			scp->cvol2 = vol2;
			scp->pvol2 = NAN;
			scp->cvol3 = vol3;
			scp->pvol3 = NAN;
		} else {
			scp->cvol  = NAN;
			scp->pvol  = vol;
			scp->cvol2 = NAN;
			scp->pvol2 = vol2;
			scp->cvol3 = NAN;
			scp->pvol3 = vol3;
		}
		if (NEW(pd) == NULL) {
			xcb_log(XCB_LOG_WARNING, "Error allocating memory for pd");
			FREE(scp);
			table_unlock(spots);
			goto end;
		}
		pd->prevxo = pd->prevxo2 = pd->prevxo3 = NAN;
		pd->sep    = strchr(fields[3], '-') ? "-" : "";
		pd->dlist  = dlist_new(NULL, scpfree);
		dlist_insert_tail(pd->dlist, scp);
		table_insert(spots, spotname, pd);
	} else {
		dlist_iter_t iter = dlist_iter_new(pd->dlist, DLIST_START_HEAD);
		dlist_node_t node;

		while ((node = dlist_next(iter))) {
			scp = (struct scp *)dlist_node_value(node);
			if (scp->strike > strike || fabs(scp->strike - strike) <= 0.000001)
				break;
		}
		if (node && fabs(scp->strike - strike) <= 0.000001) {
			if (!strcasecmp(type, "C")) {
				scp->cvol  = vol;
				scp->cvol2 = vol2;
				scp->cvol3 = vol3;
			} else {
				scp->pvol  = vol;
				scp->pvol2 = vol2;
				scp->pvol3 = vol3;
			}
		} else {
			/* can't happen */
			if (NEW(scp) == NULL) {
				xcb_log(XCB_LOG_WARNING, "Error allocating memory for scp");
				table_unlock(spots);
				goto end;
			}
			scp->strike = strike;
			if (!strcasecmp(type, "C")) {
				scp->cvol  = vol;
				scp->pvol  = NAN;
				scp->cvol2 = vol2;
				scp->pvol2 = NAN;
				scp->cvol3 = vol3;
				scp->pvol3 = NAN;
			} else {
				scp->cvol  = NAN;
				scp->pvol  = vol;
				scp->cvol2 = NAN;
				scp->pvol2 = vol2;
				scp->cvol3 = NAN;
				scp->pvol3 = vol3;
			}
			if (node == NULL)
				dlist_insert_tail(pd->dlist, scp);
			else
				dlist_insert(pd->dlist, node, scp, 0);
		}
		dlist_iter_rewind_head(iter, pd->dlist);
		while ((node = dlist_next(iter))) {
			scp = (struct scp *)dlist_node_value(node);
			if (scp->strike > spot || fabs(scp->strike - spot) <= 0.000001)
				break;
		}
		dlist_iter_free(&iter);
		if (node && dlist_node_prev(node)) {
			struct scp *scp1 = (struct scp *)dlist_node_value(node);
			struct scp *scp2 = (struct scp *)dlist_node_value(dlist_node_prev(node));
			double vxo = NAN, vxo2 = NAN, vxo3 = NAN;
			int flag1, flag2, flag3;

			if (!isnan(scp1->cvol) && !isnan(scp1->pvol) && !isnan(scp2->cvol) && !isnan(scp2->pvol))
				vxo  = ((scp1->cvol + scp1->pvol) / 2) *
					(spot - scp2->strike) / (scp1->strike - scp2->strike) +
					((scp2->cvol + scp2->pvol) / 2) *
					(scp1->strike - spot) / (scp1->strike - scp2->strike);
			if (!isnan(scp1->cvol2) && !isnan(scp1->pvol2) && !isnan(scp2->cvol2) && !isnan(scp2->pvol2))
				vxo2 = ((scp1->cvol2 + scp1->pvol2) / 2) *
					(spot - scp2->strike) / (scp1->strike - scp2->strike) +
					((scp2->cvol2 + scp2->pvol2) / 2) *
					(scp1->strike - spot) / (scp1->strike - scp2->strike);
			if (!isnan(scp1->cvol3) && !isnan(scp1->pvol3) && !isnan(scp2->cvol3) && !isnan(scp2->pvol3))
				vxo3 = ((scp1->cvol3 + scp1->pvol3) / 2) *
					(spot - scp2->strike) / (scp1->strike - scp2->strike) +
					((scp2->cvol3 + scp2->pvol3) / 2) *
					(scp1->strike - spot) / (scp1->strike - scp2->strike);
			if ((flag1 = (isnan(pd->prevxo) && !isnan(vxo)) || (!isnan(pd->prevxo) && isnan(vxo)) ||
				(!isnan(pd->prevxo) && !isnan(vxo) && fabs(pd->prevxo - vxo) > 0.000001) ? 1 : 0))
				pd->prevxo  = vxo;
			if ((flag2 = (isnan(pd->prevxo2) && !isnan(vxo2)) || (!isnan(pd->prevxo2) && isnan(vxo2)) ||
				(!isnan(pd->prevxo2) && !isnan(vxo2) && fabs(pd->prevxo2 - vxo2) > 0.000001) ? 1 : 0))
				pd->prevxo2 = vxo2;
			if ((flag3 = (isnan(pd->prevxo3) && !isnan(vxo3)) || (!isnan(pd->prevxo3) && isnan(vxo3)) ||
				(!isnan(pd->prevxo3) && !isnan(vxo3) && fabs(pd->prevxo3 - vxo3) > 0.000001) ? 1 : 0))
				pd->prevxo3 = vxo3;
			if (flag1 || flag2 || flag3) {
				char *res;

				if ((res = ALLOC(4096))) {
					time_t t = (time_t)sec;
					struct tm lt;
					char datestr[64];
					int off;

					strftime(datestr, sizeof datestr, "%F %T", localtime_r(&t, &lt));
					snprintf(res, 4096, "VXO,%s.%03d,%s|%f,%f,%f",
						datestr,
						msec,
						spotname,
						vxo,
						vxo2,
						vxo3);
					out2rmp(res);
					off = snprintf(res, 4096, "VXO,%d,%d,%s,",
						sec,
						msec,
						spotname);
					iter = dlist_iter_new(pd->dlist, DLIST_START_HEAD);
					while ((node = dlist_next(iter))) {
						scp = (struct scp *)dlist_node_value(node);
						off += snprintf(res + off, 4096 - off, "%.f,%f,%f,%f,",
							scp->strike,
							vxo,
							vxo2,
							vxo3);
					}
					dlist_iter_free(&iter);
					snprintf(res + off, 4096 - off, "%.2f,%f,%f,%s",
						spot,
						r,
						expiry,
						pd->sep);
					if (out2msgs(res, out) == -1)
						FREE(res);
				}
			}
		}
		dstr_free(spotname);
	}
	table_unlock(spots);

end:
	dstr_free_tokens(fields, nfield);
	return 0;
}
예제 #10
0
static int greeks2_exec(void *data, void *data2) {
	RAII_VAR(struct msg *, msg, (struct msg *)data, msg_decr);
	dstr *fields = NULL;
	int nfield = 0;
	time_t t;
	char *contract, *type;
	double spot, strike, r, vol, vol2, vol3, expiry;
	int steps;
	NOT_USED(data2);

	fields = dstr_split_len(msg->data, strlen(msg->data), ",", 1, &nfield);
	/* FIXME */
	if (nfield != 19) {
		xcb_log(XCB_LOG_WARNING, "Message '%s' garbled", msg->data);
		goto end;
	}
	t        = (time_t)atoi(fields[1]);
	contract = fields[3];
	type     = fields[12];
	spot     = atof(fields[10]);
	strike   = atof(fields[13]);
	r        = atof(fields[14]);
	vol      = atof(fields[5]);
	vol2     = atof(fields[7]);
	vol3     = atof(fields[9]);
	expiry   = atof(fields[15]);
	steps    = atoi(fields[18]);
	if (!isnan(vol) || !isnan(vol2) || !isnan(vol3)) {
		double delta, gamma, theta, vega, rho;
		double delta2, gamma2, theta2, vega2, rho2;
		double delta3, gamma3, theta3, vega3, rho3;
		struct tm lt;
		char datestr[64], res[512];

		if (isnan(vol))
			delta = gamma = theta = vega = rho = NAN;
		else {
			if (!strcasecmp(type, "C"))
				bi_amer_call_greeks(spot, strike, r, r, vol, expiry, steps,
					&delta, &gamma, &theta, &vega, &rho);
			else
				bi_amer_put_greeks (spot, strike, r, r, vol, expiry, steps,
					&delta, &gamma, &theta, &vega, &rho);
		}
		if (isnan(vol2))
			delta2 = gamma2 = theta2 = vega2 = rho2 = NAN;
		else if (fabs(vol2 - vol) <= 0.000001) {
			delta2 = delta;
			gamma2 = gamma;
			theta2 = theta;
			vega2  = vega;
			rho2   = rho;
		} else {
			if (!strcasecmp(type, "C"))
				bi_amer_call_greeks(spot, strike, r, r, vol2, expiry, steps,
					&delta2, &gamma2, &theta2, &vega2, &rho2);
			else
				bi_amer_put_greeks (spot, strike, r, r, vol2, expiry, steps,
					&delta2, &gamma2, &theta2, &vega2, &rho2);
		}
		if (isnan(vol3))
			delta3 = gamma3 = theta3 = vega3 = rho3 = NAN;
		else if (fabs(vol3 - vol) <= 0.000001) {
			delta3 = delta;
			gamma3 = gamma;
			theta3 = theta;
			vega3  = vega;
			rho3   = rho;
		} else {
			if (!strcasecmp(type, "C"))
				bi_amer_call_greeks(spot, strike, r, r, vol3, expiry, steps,
					&delta3, &gamma3, &theta3, &vega3, &rho3);
			else
				bi_amer_put_greeks (spot, strike, r, r, vol3, expiry, steps,
					&delta3, &gamma3, &theta3, &vega3, &rho3);
		}
		strftime(datestr, sizeof datestr, "%F %T", localtime_r(&t, &lt));
		snprintf(res, sizeof res, "GREEKS2,%s.%03d,%s|%.2f,%f,%f,%f,%f,%f,%.2f,%f,%f,%f,%f,%f,%.2f,%f,%f,%f,%f,%f",
			datestr,
			atoi(fields[2]),
			contract,
			atof(fields[4]),
			delta,
			gamma,
			theta,
			vega,
			rho,
			atof(fields[6]),
			delta2,
			gamma2,
			theta2,
			vega2,
			rho2,
			atof(fields[8]),
			delta3,
			gamma3,
			theta3,
			vega3,
			rho3);
		out2rmp(res);
	}

end:
	dstr_free_tokens(fields, nfield);
	return 0;
}
예제 #11
0
파일: custom.c 프로젝트: herberteuler/xcb
/* FIXME */
void s_command(client c) {
	dstr pkey, skey;
	int i;
	dlist_t dlist;
	struct kvd *kvd;

	if (dstr_length(c->argv[0]) == 1) {
		add_reply_error(c, "index can't be empty\r\n");
		return;
	}
	pkey = dstr_new(c->argv[0] + 1);
	skey = dstr_new(pkey);
	if (c->argc > 1)
		for (i = 1; i < c->argc; ++i) {
			skey = dstr_cat(skey, ",");
			skey = dstr_cat(skey, c->argv[i]);
		}
	table_lock(cache);
	if ((dlist = table_get_value(cache, pkey))) {
		dlist_iter_t iter = dlist_iter_new(dlist, DLIST_START_HEAD);
		dlist_node_t node;

		/* FIXME: still has room to improve */
		while ((node = dlist_next(iter))) {
			kvd = (struct kvd *)dlist_node_value(node);
			if (dstr_length(kvd->key) >= dstr_length(skey) &&
				!memcmp(kvd->key, skey, dstr_length(skey))) {
				dstr *fields = NULL;
				int nfield = 0;
				dstr res, contracts = NULL;

				fields = dstr_split_len(kvd->key, dstr_length(kvd->key), ",", 1, &nfield);
				res = dstr_new(fields[0]);
				if (nfield > 1) {
					contracts = dstr_new(fields[1]);
					for (i = 2; i < nfield; ++i) {
						contracts = dstr_cat(contracts, ",");
						contracts = dstr_cat(contracts, fields[i]);
					}
				}
				dstr_free_tokens(fields, nfield);
				fields = NULL, nfield = 0;
				fields = dstr_split_len(kvd->u.value, dstr_length(kvd->u.value),
					",", 1, &nfield);
				res = dstr_cat(res, ",");
				res = dstr_cat(res, fields[0]);
				if (contracts) {
					res = dstr_cat(res, ",");
					res = dstr_cat(res, contracts);
				}
				for (i = 1; i < nfield; ++i) {
					res = dstr_cat(res, ",");
					res = dstr_cat(res, fields[i]);
				}
				res = dstr_cat(res, "\r\n");
				pthread_spin_lock(&c->lock);
				if (!(c->flags & CLIENT_CLOSE_ASAP)) {
					if (net_try_write(c->fd, res, dstr_length(res),
						10, NET_NONBLOCK) == -1) {
						xcb_log(XCB_LOG_WARNING, "Writing to client '%p': %s",
							c, strerror(errno));
						if (++c->eagcount >= 3) {
							client_free_async(c);
							pthread_spin_unlock(&c->lock);
							dstr_free(contracts);
							dstr_free(res);
							dstr_free_tokens(fields, nfield);
							table_unlock(cache);
							dstr_free(skey);
							dstr_free(pkey);
							return;
						}
					} else if (c->eagcount)
						c->eagcount = 0;
				}
				pthread_spin_unlock(&c->lock);
				dstr_free(contracts);
				dstr_free(res);
				dstr_free_tokens(fields, nfield);
			}
		}
		dlist_iter_free(&iter);
	}
	table_unlock(cache);
	table_rwlock_wrlock(subscribers);
	if ((dlist = table_get_value(subscribers, pkey)) == NULL) {
		if (NEW(kvd)) {
			kvd->key     = skey;
			kvd->u.dlist = dlist_new(NULL, NULL);
			dlist_insert_tail(kvd->u.dlist, c);
			dlist = dlist_new(cmpkvd, kdfree);
			dlist_insert_sort(dlist, kvd);
			table_insert(subscribers, pkey, dlist);
		} else {
			add_reply_error(c, "error allocating memory for kvd\r\n");
			dstr_free(skey);
			dstr_free(pkey);
		}
	} else {
		if (NEW(kvd)) {
			dlist_node_t node;

			kvd->key     = skey;
			kvd->u.dlist = dlist_new(NULL, NULL);
			if ((node = dlist_find(dlist, kvd)) == NULL) {
				dlist_insert_tail(kvd->u.dlist, c);
				dlist_insert_sort(dlist, kvd);
			} else {
				kdfree(kvd);
				kvd = (struct kvd *)dlist_node_value(node);
				if (dlist_find(kvd->u.dlist, c) == NULL)
					dlist_insert_tail(kvd->u.dlist, c);
			}
		} else {
			add_reply_error(c, "error allocating memory for kvd\r\n");
			dstr_free(skey);
		}
		dstr_free(pkey);
	}
	table_rwlock_unlock(subscribers);
}