You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
ravi/vscode-debugger/src/protocol.c

718 lines
26 KiB

#include "protocol.h"
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
/* Parse a VSCode JSON message type
*/
static int vscode_message_type(json_value *js, FILE *log) {
if (js->type != json_object) {
fprintf(log, "Bad JSON message - expected object\n");
return VSCODE_UNKNOWN;
}
for (int i = 0; i < js->u.object.length; i++) {
json_object_entry *value = &js->u.object.values[i];
if (strncmp(value->name, "type", value->name_length) == 0) {
if (value->value->type != json_string) {
fprintf(log, "Bad JSON message - type is not a string\n");
return VSCODE_UNKNOWN;
}
if (strncmp(value->value->u.string.ptr, "request",
value->value->u.string.length) == 0) {
return VSCODE_REQUEST;
}
else if (strncmp(value->value->u.string.ptr, "response",
value->value->u.string.length) == 0) {
return VSCODE_RESPONSE;
}
else if (strncmp(value->value->u.string.ptr, "event",
value->value->u.string.length) == 0) {
return VSCODE_EVENT;
}
else {
fprintf(log, "Bad JSON message - unknown type\n");
return VSCODE_UNKNOWN;
}
}
}
fprintf(log, "Bad JSON message - unknown type\n");
return VSCODE_UNKNOWN;
}
static const char *get_string_value(json_value *js, const char *key,
FILE *log) {
if (js->type != json_object) { return NULL; }
for (int i = 0; i < js->u.object.length; i++) {
json_object_entry *value = &js->u.object.values[i];
if (strncmp(value->name, key, value->name_length) == 0) {
if (value->value->type == json_string) {
return value->value->u.string.ptr;
}
else {
return NULL;
}
}
}
return NULL;
}
static int get_int_value(json_value *js, const char *key, FILE *log,
int *found) {
*found = 0;
if (js->type != json_object) { return 0; }
for (int i = 0; i < js->u.object.length; i++) {
json_object_entry *value = &js->u.object.values[i];
if (strncmp(value->name, key, value->name_length) == 0) {
if (value->value->type == json_integer) {
*found = 1;
return value->value->u.integer;
}
else {
return 0;
}
}
}
return 0;
}
static int get_boolean_value(json_value *js, const char *key, FILE *log,
int *found) {
*found = 0;
if (js->type != json_object) { return 0; }
for (int i = 0; i < js->u.object.length; i++) {
json_object_entry *value = &js->u.object.values[i];
if (strncmp(value->name, key, value->name_length) == 0) {
if (value->value->type == json_boolean) {
*found = 1;
return value->value->u.boolean;
}
else {
return 0;
}
}
}
return 0;
}
static json_value *get_object_value(json_value *js, const char *key,
FILE *log) {
if (js->type != json_object) { return NULL; }
for (int i = 0; i < js->u.object.length; i++) {
json_object_entry *value = &js->u.object.values[i];
if (strncmp(value->name, key, value->name_length) == 0) {
if (value->value->type == json_object) { return value->value; }
else {
return NULL;
}
}
}
return NULL;
}
static json_value *get_array_value(json_value *js, const char *key,
FILE *log) {
if (js->type != json_object) { return NULL; }
for (int i = 0; i < js->u.object.length; i++) {
json_object_entry *value = &js->u.object.values[i];
if (strncmp(value->name, key, value->name_length) == 0) {
if (value->value->type == json_array) { return value->value; }
else {
return NULL;
}
}
}
return NULL;
}
static void dump_keys(json_value *js, FILE *log) {
if (js->type != json_object) return;
for (int i = 0; i < js->u.object.length; i++) {
json_object_entry *value = &js->u.object.values[i];
fprintf(log, "key '%s'\n", value->name);
}
}
typedef struct {
const char *cmdname;
int code;
} CommandMapping;
static CommandMapping commands[] = {
{"initialize", VSCODE_INITIALIZE_REQUEST},
{"configurationDone", VSCODE_CONFIGURATION_DONE_REQUEST},
{"launch", VSCODE_LAUNCH_REQUEST},
{"attach", VSCODE_ATTACH_REQUEST},
{"disconnect", VSCODE_DISCONNECT_REQUEST},
{"setBreakpoints", VSCODE_SET_BREAKPOINTS_REQUEST},
{"setFunctionBreakpoints", VSCODE_SET_FUNCTION_BREAKPOINTS_REQUEST},
{"setExceptionBreakpoints", VSCODE_SET_EXCEPTION_BREAKPOINTS_REQUEST},
{"continue", VSCODE_CONTINUE_REQUEST},
{"next", VSCODE_NEXT_REQUEST},
{"stepIn", VSCODE_STEPIN_REQUEST},
{"stepOut", VSCODE_STEPOUT_REQUEST},
{"pause", VSCODE_PAUSE_REQUEST},
{"stackTrace", VSCODE_STACK_TRACE_REQUEST},
{"scopes", VSCODE_SCOPES_REQUEST},
{"variables", VSCODE_VARIABLES_REQUEST},
{"source", VSCODE_SOURCE_REQUEST},
{"threads", VSCODE_THREAD_REQUEST},
{"evaluate", VSCODE_EVALUATE_REQUEST},
{NULL, VSCODE_UNKNOWN_REQUEST}};
static int get_cmdtype(const char *cmd) {
for (int i = 0; commands[i].cmdname != NULL; i++) {
if (strcmp(cmd, commands[i].cmdname) == 0) return commands[i].code;
}
return VSCODE_UNKNOWN_REQUEST;
}
static int vscode_parse_initialize_request(json_value *js, ProtocolMessage *msg,
FILE *log) {
// {"type":"request","seq":1,"command":"initialize","arguments":{"adapterID":"lua","pathFormat":"path","linesStartAt1":true,"columnsStartAt1":true}}
json_value *args = get_object_value(js, "arguments", log);
if (args == NULL) return VSCODE_UNKNOWN_REQUEST;
const char *adapterID = get_string_value(args, "adapterID", log);
if (adapterID == NULL || strcmp(adapterID, "lua") != 0) {
fprintf(log, "Unknown adapterID '%s' in initialize command\n",
adapterID != NULL ? adapterID : "null");
return VSCODE_UNKNOWN_REQUEST;
}
strncpy(msg->u.Request.u.InitializeRequest.adapterID, adapterID,
sizeof msg->u.Request.u.InitializeRequest.adapterID);
const char *pathFormat = get_string_value(args, "pathFormat", log);
if (pathFormat != NULL && strcmp(pathFormat, "path") != 0) {
fprintf(log, "Unsupported pathFormat '%s' in initialize command\n",
pathFormat != NULL ? pathFormat : "null");
return VSCODE_UNKNOWN_REQUEST;
}
if (pathFormat)
strncpy(msg->u.Request.u.InitializeRequest.pathFormat, pathFormat,
sizeof msg->u.Request.u.InitializeRequest.pathFormat);
int found = 0;
msg->u.Request.u.InitializeRequest.columnsStartAt1 =
get_boolean_value(args, "columnsStartAt1", log, &found);
msg->u.Request.u.InitializeRequest.linesStartAt1 =
get_boolean_value(args, "linesStartAt1", log, &found);
msg->u.Request.request_type = VSCODE_INITIALIZE_REQUEST;
return VSCODE_INITIALIZE_REQUEST;
}
/* Parse VSCode request message which has an integer parameter
*/
static int vscode_parse_intarg_request(json_value *js, ProtocolMessage *msg,
int msgtype, const char *key,
FILE *log) {
json_value *args = get_object_value(js, "arguments", log);
if (args == NULL) return VSCODE_UNKNOWN_REQUEST;
int found = 0;
msg->u.Request.u.CommonIntRequest.integer =
get_int_value(args, key, log, &found);
msg->u.Request.request_type = msgtype;
return msgtype;
}
/* Parse launch request
*/
static int vscode_parse_launch_request(json_value *js, ProtocolMessage *msg,
int msgtype, FILE *log) {
json_value *args = get_object_value(js, "arguments", log);
if (args == NULL) return VSCODE_UNKNOWN_REQUEST;
int found = 0;
msg->u.Request.u.LaunchRequest.noDebug =
get_boolean_value(args, "noDebug", log, &found);
msg->u.Request.u.LaunchRequest.stopOnEntry =
get_boolean_value(args, "stopOnEntry", log, &found);
const char *prog = get_string_value(args, "program", log);
if (prog == NULL) return VSCODE_UNKNOWN_REQUEST;
strncpy(msg->u.Request.u.LaunchRequest.program, prog,
sizeof msg->u.Request.u.LaunchRequest.program);
for (char *cp = msg->u.Request.u.LaunchRequest.program; *cp; cp++) {
/* replace back slashes with front slash as the VSCode front end doesn't
* like
* back slashes even though it sends them!
*/
if (*cp == '\\') *cp = '/';
}
fprintf(log, "LAUNCH %s\n", prog);
msg->u.Request.request_type = msgtype;
return msgtype;
}
static int vscode_parse_set_breakpoints_request(json_value *js, ProtocolMessage *msg,
int msgtype, FILE *log) {
int found = 0;
json_value *args = get_object_value(js, "arguments", log);
if (args == NULL) return VSCODE_UNKNOWN_REQUEST;
json_value *source = get_object_value(args, "source", log);
if (source == NULL) return VSCODE_UNKNOWN_REQUEST;
const char *prog = get_string_value(source, "path", log);
if (prog == NULL) return VSCODE_UNKNOWN_REQUEST;
strncpy(msg->u.Request.u.SetBreakpointsRequest.source.path, prog,
sizeof msg->u.Request.u.SetBreakpointsRequest.source.path);
for (char *cp = msg->u.Request.u.SetBreakpointsRequest.source.path; *cp; cp++) {
if (*cp == '\\')
*cp = '/';
}
json_value *breakpoints = get_array_value(args, "breakpoints", log);
if (breakpoints == NULL || breakpoints->type != json_array)
return VSCODE_UNKNOWN_REQUEST;
for (int i = 0; i < breakpoints->u.array.length && i < MAX_BREAKPOINTS; i++) {
json_value *element = breakpoints->u.array.values[i];
if (element->type != json_object)
return VSCODE_UNKNOWN_REQUEST;
int line = get_int_value(element, "line", log, &found);
fprintf(log, "Set breakpoint line = %d\n", line);
msg->u.Request.u.SetBreakpointsRequest.breakpoints[i].line = found ? line: -1;
}
msg->u.Request.request_type = msgtype;
return msgtype;
}
static int vscode_parse_stack_trace_request(json_value *js,
ProtocolMessage *msg, int msgtype,
FILE *log) {
json_value *args = get_object_value(js, "arguments", log);
if (args == NULL) return VSCODE_UNKNOWN_REQUEST;
int found = 0;
msg->u.Request.u.StackTraceRequest.threadId =
get_int_value(args, "threadId", log, &found);
msg->u.Request.u.StackTraceRequest.levels =
get_int_value(args, "levels", log, &found);
msg->u.Request.u.StackTraceRequest.startFrame =
get_int_value(args, "startFrame", log, &found);
msg->u.Request.request_type = msgtype;
return msgtype;
}
/* Parse a VSCode request */
static int vscode_parse_request(json_value *js, ProtocolMessage *msg,
FILE *log) {
const char *cmd = get_string_value(js, "command", log);
int found = 0;
if (cmd == NULL) return VSCODE_UNKNOWN_REQUEST;
msg->type = VSCODE_REQUEST;
msg->seq = get_int_value(js, "seq", log, &found);
strncpy(msg->u.Request.command, cmd, sizeof msg->u.Request.command);
fprintf(log, "\nRequest --> '%s'\n", cmd);
int cmdtype = get_cmdtype(msg->u.Request.command);
switch (cmdtype) {
case VSCODE_INITIALIZE_REQUEST:
return vscode_parse_initialize_request(js, msg, log);
case VSCODE_DISCONNECT_REQUEST:
case VSCODE_ATTACH_REQUEST:
case VSCODE_CONFIGURATION_DONE_REQUEST:
case VSCODE_THREAD_REQUEST: msg->u.Request.request_type = cmdtype; break;
case VSCODE_CONTINUE_REQUEST:
case VSCODE_NEXT_REQUEST:
case VSCODE_STEPIN_REQUEST:
case VSCODE_STEPOUT_REQUEST:
case VSCODE_PAUSE_REQUEST:
return vscode_parse_intarg_request(js, msg, cmdtype, "threadId", log);
case VSCODE_SCOPES_REQUEST:
return vscode_parse_intarg_request(js, msg, cmdtype, "frameId", log);
case VSCODE_VARIABLES_REQUEST:
return vscode_parse_intarg_request(js, msg, cmdtype, "variablesReference",
log);
case VSCODE_LAUNCH_REQUEST:
return vscode_parse_launch_request(js, msg, cmdtype, log);
case VSCODE_STACK_TRACE_REQUEST:
return vscode_parse_stack_trace_request(js, msg, cmdtype, log);
case VSCODE_SET_EXCEPTION_BREAKPOINTS_REQUEST:
msg->u.Request.request_type = cmdtype;
break;
case VSCODE_SET_BREAKPOINTS_REQUEST:
return vscode_parse_set_breakpoints_request(js, msg, cmdtype, log);
case VSCODE_UNKNOWN_REQUEST: break;
default: msg->u.Request.request_type = cmdtype;
}
return cmdtype;
}
int vscode_parse_message(char *buf, size_t len, ProtocolMessage *msg,
FILE *log) {
memset(msg, 0, sizeof(ProtocolMessage));
json_value *js = json_parse(buf, len);
if (js == NULL) return VSCODE_UNKNOWN;
int msgtype = vscode_message_type(js, log);
if (msgtype == VSCODE_REQUEST) msgtype = vscode_parse_request(js, msg, log);
json_value_free(js);
return msgtype;
}
static int seq = 1;
void vscode_make_error_response(ProtocolMessage *req, ProtocolMessage *res,
int restype, const char *errormsg) {
memset(res, 0, sizeof(ProtocolMessage));
res->seq = seq++;
res->type = VSCODE_RESPONSE;
strncpy(res->u.Response.command, req->u.Request.command,
sizeof res->u.Response.command);
res->u.Response.request_seq = req->seq;
res->u.Response.response_type = restype;
strncpy(res->u.Response.message, errormsg, sizeof res->u.Response.message);
res->u.Response.success = 0;
}
void vscode_make_success_response(ProtocolMessage *req, ProtocolMessage *res,
int restype) {
memset(res, 0, sizeof(ProtocolMessage));
res->seq = seq++;
res->type = VSCODE_RESPONSE;
strncpy(res->u.Response.command, req->u.Request.command,
sizeof res->u.Response.command);
res->u.Response.request_seq = req->seq;
res->u.Response.response_type = restype;
res->u.Response.success = 1;
}
void vscode_make_initialized_event(ProtocolMessage *res) {
memset(res, 0, sizeof(ProtocolMessage));
res->seq = seq++;
res->type = VSCODE_EVENT;
strncpy(res->u.Event.event, "initialized", sizeof res->u.Event.event);
res->u.Event.event_type = VSCODE_INITIALIZED_EVENT;
}
void vscode_make_output_event(ProtocolMessage *res, const char *msg) {
memset(res, 0, sizeof(ProtocolMessage));
res->seq = seq++;
res->type = VSCODE_EVENT;
strncpy(res->u.Event.event, "output", sizeof res->u.Event.event);
strncpy(res->u.Event.u.OutputEvent.output, msg,
sizeof res->u.Event.u.OutputEvent.output);
res->u.Event.event_type = VSCODE_OUTPUT_EVENT;
}
/*
* Build a StoppedEvent {event, reason, threadId}
*/
void vscode_make_stopped_event(ProtocolMessage *res, const char *reason) {
memset(res, 0, sizeof(ProtocolMessage));
res->seq = seq++;
res->type = VSCODE_EVENT;
res->u.Event.event_type = VSCODE_STOPPED_EVENT;
strncpy(res->u.Event.event, "stopped", sizeof res->u.Event.event);
res->u.Event.u.StoppedEvent.threadId = 1; /* dummy thread id - always 1 */
strncpy(res->u.Event.u.StoppedEvent.reason, reason,
sizeof res->u.Event.u.StoppedEvent.reason);
}
/*
* Build a TerminatedEvent
*/
void vscode_make_terminated_event(ProtocolMessage *res) {
memset(res, 0, sizeof(ProtocolMessage));
res->seq = seq++;
res->type = VSCODE_EVENT;
res->u.Event.event_type = VSCODE_TERMINATED_EVENT;
strncpy(res->u.Event.event, "terminated", sizeof res->u.Event.event);
res->u.Event.u.TerminatedEvent.restart = 0;
}
/*
* Build a ThreadEvent {event, reason, threadid}
*/
void vscode_make_thread_event(ProtocolMessage *res, bool started) {
memset(res, 0, sizeof(ProtocolMessage));
res->seq = seq++;
res->type = VSCODE_EVENT;
res->u.Event.event_type = VSCODE_THREAD_EVENT;
strncpy(res->u.Event.event, "thread", sizeof res->u.Event.event);
res->u.Event.u.ThreadEvent.threadId = 1; /* dummy thread id - always 1 */
strncpy(res->u.Event.u.ThreadEvent.reason, started ? "started" : "exited",
sizeof res->u.Event.u.ThreadEvent.reason);
}
void vscode_serialize_response(char *buf, size_t buflen, ProtocolMessage *res) {
char temp[1024 * 8] = {0};
char *cp = temp;
buf[0] = 0;
if (res->type != VSCODE_RESPONSE) return;
snprintf(cp, sizeof temp - strlen(temp),
"{\"type\":\"response\",\"seq\":%d,\"command\":\"%s\",\"request_"
"seq\":%d,\"success\":%s",
res->seq, res->u.Response.command, res->u.Response.request_seq,
res->u.Response.success ? "true" : "false");
cp = temp + strlen(temp);
if (res->u.Response.message[0]) {
snprintf(cp, sizeof temp - strlen(temp), ",\"message\":\"%s\"",
res->u.Response.message);
cp = temp + strlen(temp);
}
if (res->u.Response.response_type == VSCODE_INITIALIZE_RESPONSE &&
res->u.Response.success) {
snprintf(
cp, sizeof temp - strlen(temp),
",\"body\":{\"supportsConfigurationDoneRequest\":%s,"
"\"supportsFunctionBreakpoints\":%s,\"supportsConditionalBreakpoints\":"
"%s,\"supportsEvaluateForHovers\":%s}",
res->u.Response.u.InitializeResponse.body
.supportsConfigurationDoneRequest
? "true"
: "false",
res->u.Response.u.InitializeResponse.body.supportsFunctionBreakpoints
? "true"
: "false",
res->u.Response.u.InitializeResponse.body.supportsConditionalBreakpoints
? "true"
: "false",
res->u.Response.u.InitializeResponse.body.supportsEvaluateForHovers
? "true"
: "false");
cp = temp + strlen(temp);
}
else if (res->u.Response.response_type == VSCODE_THREAD_RESPONSE &&
res->u.Response.success) {
snprintf(cp, sizeof temp - strlen(temp),
",\"body\":{\"threads\":[{\"name\":\"%s\",\"id\":%d}]}",
res->u.Response.u.ThreadResponse.threads[0].name,
res->u.Response.u.ThreadResponse.threads[0].id);
cp = temp + strlen(temp);
}
else if (res->u.Response.response_type == VSCODE_STACK_TRACE_RESPONSE &&
res->u.Response.success) {
snprintf(cp, sizeof temp - strlen(temp),
",\"body\":{\"totalFrames\":%d,\"stackFrames\":[",
res->u.Response.u.StackTraceResponse.totalFrames);
cp = temp + strlen(temp);
int d = 0;
for (; d < res->u.Response.u.StackTraceResponse.totalFrames; d++) {
if (d) {
snprintf(cp, sizeof temp - strlen(temp), ",");
cp = temp + strlen(temp);
}
snprintf(cp, sizeof temp - strlen(temp),
"{\"id\":%d,\"name\":\"%s\",\"column\":1,\"line\":%d,\"source\":"
"{\"name\":\"%s\",\"path\":\"%s\",\"sourceReference\":0}}",
d, res->u.Response.u.StackTraceResponse.stackFrames[d].name,
res->u.Response.u.StackTraceResponse.stackFrames[d].line,
res->u.Response.u.StackTraceResponse.stackFrames[d].source.name,
res->u.Response.u.StackTraceResponse.stackFrames[d].source.path);
cp = temp + strlen(temp);
}
snprintf(cp, sizeof temp - strlen(temp), "]}");
cp = temp + strlen(temp);
}
else if (res->u.Response.response_type == VSCODE_SCOPES_RESPONSE &&
res->u.Response.success) {
snprintf(cp, sizeof temp - strlen(temp), ",\"body\":{\"scopes\":[");
cp = temp + strlen(temp);
for (int d = 0; d < MAX_SCOPES; d++) {
if (!res->u.Response.u.ScopesResponse.scopes[d].name[0]) break;
if (d) {
snprintf(cp, sizeof temp - strlen(temp), ",");
cp = temp + strlen(temp);
}
snprintf(cp, sizeof temp - strlen(temp),
"{\"name\":\"%s\",\"variablesReference\":%d,\"expensive\":%s}",
res->u.Response.u.ScopesResponse.scopes[d].name,
res->u.Response.u.ScopesResponse.scopes[d].variablesReference,
res->u.Response.u.ScopesResponse.scopes[d].expensive ? "true"
: "false");
cp = temp + strlen(temp);
}
snprintf(cp, sizeof temp - strlen(temp), "]}");
cp = temp + strlen(temp);
}
else if (res->u.Response.response_type == VSCODE_VARIABLES_RESPONSE &&
res->u.Response.success) {
snprintf(cp, sizeof temp - strlen(temp), ",\"body\":{\"variables\":[");
cp = temp + strlen(temp);
for (int d = 0; d < MAX_VARIABLES; d++) {
if (!res->u.Response.u.VariablesResponse.variables[d].name[0]) break;
if (d) {
snprintf(cp, sizeof temp - strlen(temp), ",");
cp = temp + strlen(temp);
}
snprintf(cp, sizeof temp - strlen(temp),
"{\"name\":\"%s\",\"variablesReference\":0,\"value\":\"%s\"}",
res->u.Response.u.VariablesResponse.variables[d].name,
res->u.Response.u.VariablesResponse.variables[d].value);
cp = temp + strlen(temp);
}
snprintf(cp, sizeof temp - strlen(temp), "]}");
cp = temp + strlen(temp);
}
else if (res->u.Response.response_type == VSCODE_SET_BREAKPOINTS_RESPONSE &&
res->u.Response.success) {
snprintf(cp, sizeof temp - strlen(temp), ",\"body\":{\"breakpoints\":[");
cp = temp + strlen(temp);
for (int d = 0; d < MAX_BREAKPOINTS; d++) {
if (!res->u.Response.u.SetBreakpointsResponse.breakpoints[d].source.path[0]) break;
if (d) {
snprintf(cp, sizeof temp - strlen(temp), ",");
cp = temp + strlen(temp);
}
snprintf(cp, sizeof temp - strlen(temp),
"{\"verified\":%s,\"source\":{\"path\":\"%s\"},\"line\":%d}",
res->u.Response.u.SetBreakpointsResponse.breakpoints[d].verified ? "true" : "false",
res->u.Response.u.SetBreakpointsResponse.breakpoints[d].source.path,
res->u.Response.u.SetBreakpointsResponse.breakpoints[d].line);
cp = temp + strlen(temp);
}
snprintf(cp, sizeof temp - strlen(temp), "]}");
cp = temp + strlen(temp);
}
snprintf(cp, sizeof temp - strlen(temp), "}");
cp = temp + strlen(temp);
snprintf(buf, buflen, "Content-Length: %d\r\n\r\n%s", (int)strlen(temp),
temp);
// printf(buf);
}
/*
* Create a serialized form of the event in VSCode
* wire protocol format (see protocol.h)
*/
void vscode_serialize_event(char *buf, size_t buflen, ProtocolMessage *res) {
char temp[1024] = {0};
char *cp = temp;
buf[0] = 0;
if (res->type != VSCODE_EVENT) return;
snprintf(cp, sizeof temp - strlen(temp),
"{\"type\":\"event\",\"seq\":%d,\"event\":\"%s\"", res->seq,
res->u.Event.event);
cp = temp + strlen(temp);
if (res->u.Event.event_type == VSCODE_OUTPUT_EVENT) {
snprintf(cp, sizeof temp - strlen(temp), ",\"body\":{\"output\":\"%s\"}",
res->u.Event.u.OutputEvent.output);
cp = temp + strlen(temp);
}
else if (res->u.Event.event_type == VSCODE_THREAD_EVENT) {
snprintf(cp, sizeof temp - strlen(temp),
",\"body\":{\"reason\":\"%s\",\"threadId\":%d}",
res->u.Event.u.ThreadEvent.reason,
res->u.Event.u.ThreadEvent.threadId);
cp = temp + strlen(temp);
}
else if (res->u.Event.event_type == VSCODE_STOPPED_EVENT) {
snprintf(cp, sizeof temp - strlen(temp),
",\"body\":{\"reason\":\"%s\",\"threadId\":%d}",
res->u.Event.u.StoppedEvent.reason,
res->u.Event.u.StoppedEvent.threadId);
cp = temp + strlen(temp);
}
else if (res->u.Event.event_type == VSCODE_TERMINATED_EVENT) {
snprintf(cp, sizeof temp - strlen(temp), ",\"body\":{\"restart\":%s}",
res->u.Event.u.TerminatedEvent.restart ? "true" : "false");
cp = temp + strlen(temp);
}
snprintf(cp, sizeof temp - strlen(temp), "}");
cp = temp + strlen(temp);
snprintf(buf, buflen, "Content-Length: %d\r\n\r\n%s", (int)strlen(temp),
temp);
}
void vscode_send(ProtocolMessage *msg, FILE *out, FILE *log) {
char buf[4096];
if (msg->type == VSCODE_EVENT)
vscode_serialize_event(buf, sizeof buf, msg);
else if (msg->type == VSCODE_RESPONSE)
vscode_serialize_response(buf, sizeof buf, msg);
else
return;
fprintf(log, "%s\n", buf);
fprintf(out, buf);
}
/*
* Send VSCode a StoppedEvent
* The event indicates that the execution of the debuggee has stopped due to some
* condition.
* This can be caused by a break point previously set, a stepping action has
* completed, by executing a debugger statement etc.
*/
void vscode_send_stopped_event(ProtocolMessage *res, const char *msg, FILE *out,
FILE *log) {
vscode_make_stopped_event(res, msg);
vscode_send(res, out, log);
}
/*
* Send VSCode a ThreadEvent
*/
void vscode_send_thread_event(ProtocolMessage *res, bool started, FILE *out,
FILE *log) {
vscode_make_thread_event(res, started);
vscode_send(res, out, log);
}
/*
* Send VSCode a TerminatedEvent
*/
void vscode_send_terminated_event(ProtocolMessage *res, FILE *out, FILE *log) {
vscode_make_terminated_event(res);
vscode_send(res, out, log);
}
void vscode_send_output_event(ProtocolMessage *res, const char *msg, FILE *out,
FILE *log) {
vscode_make_output_event(res, msg);
vscode_send(res, out, log);
}
void vscode_send_error_response(ProtocolMessage *req, ProtocolMessage *res,
int responseType, const char *msg, FILE *out,
FILE *log) {
vscode_make_error_response(req, res, responseType, msg);
vscode_send(res, out, log);
}
void vscode_send_success_response(ProtocolMessage *req, ProtocolMessage *res,
int responseType, FILE *out, FILE *log) {
vscode_make_success_response(req, res, responseType);
vscode_send(res, out, log);
}
/*
* Get command from VSCode
* VSCode commands begin with the sequence:
* 'Content-Length: nnn\r\n\r\n'
* This is followed by nnn bytes which has a JSON
* format request message
* We currently don't bother checking the
* \r\n\r\n sequence for incoming messages
*/
int vscode_get_request(FILE *in, ProtocolMessage *req, FILE *log) {
char buf[4096] = {0};
if (fgets(buf, sizeof buf, in) != NULL) {
buf[sizeof buf - 1] = 0; /* NULL terminate - just in case */
const char *bufp = strstr(buf, "Content-Length: ");
if (bufp != NULL) {
bufp += 16;
/* get the message length */
int len = atoi(bufp);
if (len >= sizeof buf) {
/* FIXME */
fprintf(log,
"FATAL ERROR - Content-Length = %d is greater than bufsize\n",
len);
exit(1);
}
buf[0] = 0;
/* skip blank line - actually \r\n */
if (fgets(buf, sizeof buf, stdin) == NULL) {
fprintf(log, "FATAL ERROR - cannot read %d bytes\n", len);
exit(1);
}
/* Now read exact number of characters */
buf[0] = 0;
if (fread(buf, len, 1, stdin) != 1) {
fprintf(log, "FATAL ERROR - cannot read %d bytes\n", len);
exit(1);
}
buf[len] = 0;
fprintf(log, "Content-Length: %d\r\n\r\n%s", len, buf);
fflush(log);
}
return vscode_parse_message(buf, sizeof buf, req, log);
}
else {
return VSCODE_EOF;
}
}