22 |
#include <assert.h> |
#include <assert.h> |
23 |
#include <ctype.h> |
#include <ctype.h> |
24 |
#include <signal.h> |
#include <signal.h> |
25 |
|
#include <stdint.h> |
26 |
#include <string.h> |
#include <string.h> |
27 |
|
|
28 |
#include <dirent.h> |
#include <dirent.h> |
29 |
|
#ifdef HAVE_PTHREAD_H |
30 |
#include <pthread.h> |
#include <pthread.h> |
31 |
|
#endif |
32 |
|
|
33 |
|
#include "encoding.h" |
34 |
|
#include "global.h" |
35 |
#include "http-server.h" |
#include "http-server.h" |
36 |
#include "instrument-js.h" |
#include "instrument-js.h" |
37 |
#include "resource-manager.h" |
#include "resource-manager.h" |
38 |
#include "stream.h" |
#include "stream.h" |
39 |
#include "util.h" |
#include "util.h" |
40 |
|
|
41 |
|
const char * jscoverage_encoding = "ISO-8859-1"; |
42 |
|
bool jscoverage_highlight = true; |
43 |
|
|
44 |
typedef struct SourceCache { |
typedef struct SourceCache { |
45 |
char * url; |
char * url; |
46 |
Stream * source; |
uint16_t * characters; |
47 |
|
size_t num_characters; |
48 |
struct SourceCache * next; |
struct SourceCache * next; |
49 |
} SourceCache; |
} SourceCache; |
50 |
|
|
73 |
static const char ** no_instrument; |
static const char ** no_instrument; |
74 |
static size_t num_no_instrument = 0; |
static size_t num_no_instrument = 0; |
75 |
|
|
76 |
|
#ifdef __MINGW32__ |
77 |
|
CRITICAL_SECTION javascript_mutex; |
78 |
|
CRITICAL_SECTION source_cache_mutex; |
79 |
|
#define LOCK EnterCriticalSection |
80 |
|
#define UNLOCK LeaveCriticalSection |
81 |
|
#else |
82 |
pthread_mutex_t javascript_mutex = PTHREAD_MUTEX_INITIALIZER; |
pthread_mutex_t javascript_mutex = PTHREAD_MUTEX_INITIALIZER; |
83 |
pthread_mutex_t source_cache_mutex = PTHREAD_MUTEX_INITIALIZER; |
pthread_mutex_t source_cache_mutex = PTHREAD_MUTEX_INITIALIZER; |
84 |
|
#define LOCK pthread_mutex_lock |
85 |
static Stream * find_cached_source(const char * url) { |
#define UNLOCK pthread_mutex_unlock |
86 |
Stream * result = NULL; |
#endif |
87 |
pthread_mutex_lock(&source_cache_mutex); |
|
88 |
|
static const SourceCache * find_cached_source(const char * url) { |
89 |
|
SourceCache * result = NULL; |
90 |
|
LOCK(&source_cache_mutex); |
91 |
for (SourceCache * p = source_cache; p != NULL; p = p->next) { |
for (SourceCache * p = source_cache; p != NULL; p = p->next) { |
92 |
if (strcmp(url, p->url) == 0) { |
if (strcmp(url, p->url) == 0) { |
93 |
result = p->source; |
result = p; |
94 |
break; |
break; |
95 |
} |
} |
96 |
} |
} |
97 |
pthread_mutex_unlock(&source_cache_mutex); |
UNLOCK(&source_cache_mutex); |
98 |
return result; |
return result; |
99 |
} |
} |
100 |
|
|
101 |
static void add_cached_source(const char * url, Stream * source) { |
static void add_cached_source(const char * url, uint16_t * characters, size_t num_characters) { |
102 |
SourceCache * new_source_cache = xmalloc(sizeof(SourceCache)); |
SourceCache * new_source_cache = xmalloc(sizeof(SourceCache)); |
103 |
new_source_cache->url = xstrdup(url); |
new_source_cache->url = xstrdup(url); |
104 |
new_source_cache->source = source; |
new_source_cache->characters = characters; |
105 |
pthread_mutex_lock(&source_cache_mutex); |
new_source_cache->num_characters = num_characters; |
106 |
|
LOCK(&source_cache_mutex); |
107 |
new_source_cache->next = source_cache; |
new_source_cache->next = source_cache; |
108 |
source_cache = new_source_cache; |
source_cache = new_source_cache; |
109 |
pthread_mutex_unlock(&source_cache_mutex); |
UNLOCK(&source_cache_mutex); |
110 |
} |
} |
111 |
|
|
112 |
static int get(const char * url, Stream * stream) __attribute__((warn_unused_result)); |
static int get(const char * url, uint16_t ** characters, size_t * num_characters) __attribute__((warn_unused_result)); |
113 |
|
|
114 |
static int get(const char * url, Stream * stream) { |
static int get(const char * url, uint16_t ** characters, size_t * num_characters) { |
115 |
char * host = NULL; |
char * host = NULL; |
116 |
uint16_t port; |
uint16_t port; |
117 |
char * abs_path = NULL; |
char * abs_path = NULL; |
118 |
char * query = NULL; |
char * query = NULL; |
119 |
HTTPConnection * connection = NULL; |
HTTPConnection * connection = NULL; |
120 |
HTTPExchange * exchange = NULL; |
HTTPExchange * exchange = NULL; |
121 |
|
Stream * stream = NULL; |
122 |
|
|
123 |
int result = URL_parse(url, &host, &port, &abs_path, &query); |
int result = URL_parse(url, &host, &port, &abs_path, &query); |
124 |
if (result != 0) { |
if (result != 0) { |
143 |
goto done; |
goto done; |
144 |
} |
} |
145 |
|
|
146 |
|
stream = Stream_new(0); |
147 |
result = HTTPExchange_read_entire_response_entity_body(exchange, stream); |
result = HTTPExchange_read_entire_response_entity_body(exchange, stream); |
148 |
if (result != 0) { |
if (result != 0) { |
149 |
goto done; |
goto done; |
150 |
} |
} |
151 |
|
char * encoding = HTTPMessage_get_charset(HTTPExchange_get_response_message(exchange)); |
152 |
|
if (encoding == NULL) { |
153 |
|
encoding = xstrdup(jscoverage_encoding); |
154 |
|
} |
155 |
|
result = jscoverage_bytes_to_characters(encoding, stream->data, stream->length, characters, num_characters); |
156 |
|
free(encoding); |
157 |
|
if (result != 0) { |
158 |
|
goto done; |
159 |
|
} |
160 |
|
|
161 |
result = 0; |
result = 0; |
162 |
|
|
163 |
done: |
done: |
164 |
|
if (stream != NULL) { |
165 |
|
Stream_delete(stream); |
166 |
|
} |
167 |
if (exchange != NULL) { |
if (exchange != NULL) { |
168 |
HTTPExchange_delete(exchange); |
HTTPExchange_delete(exchange); |
169 |
} |
} |
371 |
return true; |
return true; |
372 |
} |
} |
373 |
|
|
374 |
static int merge(Coverage * coverage, const char * path, size_t size) __attribute__((warn_unused_result)); |
static int merge(Coverage * coverage, FILE * f) __attribute__((warn_unused_result)); |
375 |
|
|
376 |
static int merge(Coverage * coverage, const char * path, size_t size) { |
static int merge(Coverage * coverage, FILE * f) { |
377 |
FILE * f = fopen(path, "r"); |
Stream * stream = Stream_new(0); |
378 |
if (f == NULL) { |
Stream_write_file_contents(stream, f); |
|
return -1; |
|
|
} |
|
|
uint8_t * buffer = xmalloc(size); |
|
|
if (fread(buffer, 1, size, f) != size) { |
|
|
fclose(f); |
|
|
free(buffer); |
|
|
return -1; |
|
|
} |
|
|
fclose(f); |
|
379 |
|
|
380 |
pthread_mutex_lock(&javascript_mutex); |
LOCK(&javascript_mutex); |
381 |
int result = jscoverage_parse_json(coverage, buffer, size); |
int result = jscoverage_parse_json(coverage, stream->data, stream->length); |
382 |
pthread_mutex_unlock(&javascript_mutex); |
UNLOCK(&javascript_mutex); |
383 |
|
|
384 |
free(buffer); |
Stream_delete(stream); |
385 |
return result; |
return result; |
386 |
} |
} |
387 |
|
|
422 |
putc('"', f); |
putc('"', f); |
423 |
} |
} |
424 |
|
|
425 |
|
static void write_source(const char * id, const uint16_t * characters, size_t num_characters, FILE * f) { |
426 |
|
Stream * output = Stream_new(num_characters); |
427 |
|
jscoverage_write_source(id, characters, num_characters, output); |
428 |
|
fwrite(output->data, 1, output->length, f); |
429 |
|
Stream_delete(output); |
430 |
|
} |
431 |
|
|
432 |
static void write_json_for_file(const FileCoverage * file_coverage, int i, void * p) { |
static void write_json_for_file(const FileCoverage * file_coverage, int i, void * p) { |
433 |
FILE * f = p; |
FILE * f = p; |
434 |
|
|
439 |
write_js_quoted_string(f, file_coverage->id, strlen(file_coverage->id)); |
write_js_quoted_string(f, file_coverage->id, strlen(file_coverage->id)); |
440 |
|
|
441 |
fputs(":{\"coverage\":[", f); |
fputs(":{\"coverage\":[", f); |
442 |
for (uint32_t i = 0; i <= file_coverage->num_lines; i++) { |
for (uint32_t i = 0; i < file_coverage->num_coverage_lines; i++) { |
443 |
if (i > 0) { |
if (i > 0) { |
444 |
putc(',', f); |
putc(',', f); |
445 |
} |
} |
446 |
int timesExecuted = file_coverage->lines[i]; |
int timesExecuted = file_coverage->coverage_lines[i]; |
447 |
if (timesExecuted < 0) { |
if (timesExecuted < 0) { |
448 |
fputs("null", f); |
fputs("null", f); |
449 |
} |
} |
452 |
} |
} |
453 |
} |
} |
454 |
fputs("],\"source\":", f); |
fputs("],\"source\":", f); |
455 |
if (file_coverage->source == NULL) { |
if (file_coverage->source_lines == NULL) { |
456 |
if (proxy) { |
if (proxy) { |
457 |
Stream * stream = find_cached_source(file_coverage->id); |
const SourceCache * cached = find_cached_source(file_coverage->id); |
458 |
if (stream == NULL) { |
if (cached == NULL) { |
459 |
stream = Stream_new(0); |
uint16_t * characters; |
460 |
if (get(file_coverage->id, stream) == 0) { |
size_t num_characters; |
461 |
write_js_quoted_string(f, stream->data, stream->length); |
if (get(file_coverage->id, &characters, &num_characters) == 0) { |
462 |
add_cached_source(file_coverage->id, stream); |
write_source(file_coverage->id, characters, num_characters, f); |
463 |
|
add_cached_source(file_coverage->id, characters, num_characters); |
464 |
} |
} |
465 |
else { |
else { |
466 |
fputs("\"\"", f); |
fputs("[]", f); |
467 |
HTTPServer_log_err("Warning: cannot retrieve URL: %s\n", file_coverage->id); |
HTTPServer_log_err("Warning: cannot retrieve URL: %s\n", file_coverage->id); |
|
Stream_delete(stream); |
|
468 |
} |
} |
469 |
} |
} |
470 |
else { |
else { |
471 |
write_js_quoted_string(f, stream->data, stream->length); |
write_source(file_coverage->id, cached->characters, cached->num_characters, f); |
472 |
} |
} |
473 |
} |
} |
474 |
else { |
else { |
475 |
/* check that the path begins with / */ |
/* check that the path begins with / */ |
476 |
if (file_coverage->id[0] == '/') { |
if (file_coverage->id[0] == '/') { |
477 |
char * source_path = make_path(document_root, file_coverage->id + 1); |
char * source_path = make_path(document_root, file_coverage->id + 1); |
478 |
FILE * source_file = fopen(source_path, "r"); |
FILE * source_file = fopen(source_path, "rb"); |
479 |
free(source_path); |
free(source_path); |
480 |
if (source_file == NULL) { |
if (source_file == NULL) { |
481 |
fputs("\"\"", f); |
fputs("[]", f); |
482 |
HTTPServer_log_err("Warning: cannot open file: %s\n", file_coverage->id); |
HTTPServer_log_err("Warning: cannot open file: %s\n", file_coverage->id); |
483 |
} |
} |
484 |
else { |
else { |
485 |
Stream * stream = Stream_new(0); |
Stream * stream = Stream_new(0); |
486 |
Stream_write_file_contents(stream, source_file); |
Stream_write_file_contents(stream, source_file); |
487 |
fclose(source_file); |
fclose(source_file); |
488 |
write_js_quoted_string(f, stream->data, stream->length); |
uint16_t * characters; |
489 |
|
size_t num_characters; |
490 |
|
int result = jscoverage_bytes_to_characters(jscoverage_encoding, stream->data, stream->length, &characters, &num_characters); |
491 |
Stream_delete(stream); |
Stream_delete(stream); |
492 |
|
if (result == JSCOVERAGE_ERROR_ENCODING_NOT_SUPPORTED) { |
493 |
|
fputs("[]", f); |
494 |
|
HTTPServer_log_err("Warning: encoding %s not supported\n", jscoverage_encoding); |
495 |
|
} |
496 |
|
else if (result == JSCOVERAGE_ERROR_INVALID_BYTE_SEQUENCE) { |
497 |
|
fputs("[]", f); |
498 |
|
HTTPServer_log_err("Warning: error decoding %s in file %s\n", jscoverage_encoding, file_coverage->id); |
499 |
|
} |
500 |
|
else { |
501 |
|
write_source(file_coverage->id, characters, num_characters, f); |
502 |
|
free(characters); |
503 |
|
} |
504 |
} |
} |
505 |
} |
} |
506 |
else { |
else { |
507 |
/* path does not begin with / */ |
/* path does not begin with / */ |
508 |
fputs("\"\"", f); |
fputs("[]", f); |
509 |
HTTPServer_log_err("Warning: invalid source path: %s\n", file_coverage->id); |
HTTPServer_log_err("Warning: invalid source path: %s\n", file_coverage->id); |
510 |
} |
} |
511 |
} |
} |
512 |
} |
} |
513 |
else { |
else { |
514 |
write_js_quoted_string(f, file_coverage->source, strlen(file_coverage->source)); |
fputc('[', f); |
515 |
|
for (uint32_t i = 0; i < file_coverage->num_source_lines; i++) { |
516 |
|
if (i > 0) { |
517 |
|
fputc(',', f); |
518 |
|
} |
519 |
|
char * source_line = file_coverage->source_lines[i]; |
520 |
|
write_js_quoted_string(f, source_line, strlen(source_line)); |
521 |
|
} |
522 |
|
fputc(']', f); |
523 |
} |
} |
524 |
fputc('}', f); |
fputc('}', f); |
525 |
} |
} |
564 |
} |
} |
565 |
|
|
566 |
Coverage * coverage = Coverage_new(); |
Coverage * coverage = Coverage_new(); |
567 |
pthread_mutex_lock(&javascript_mutex); |
LOCK(&javascript_mutex); |
568 |
int result = jscoverage_parse_json(coverage, json->data, json->length); |
int result = jscoverage_parse_json(coverage, json->data, json->length); |
569 |
pthread_mutex_unlock(&javascript_mutex); |
UNLOCK(&javascript_mutex); |
570 |
Stream_delete(json); |
Stream_delete(json); |
571 |
|
|
572 |
if (result != 0) { |
if (result != 0) { |
577 |
|
|
578 |
mkdir_if_necessary(report_directory); |
mkdir_if_necessary(report_directory); |
579 |
char * path = make_path(report_directory, "jscoverage.json"); |
char * path = make_path(report_directory, "jscoverage.json"); |
580 |
|
|
581 |
|
/* check if the JSON file exists */ |
582 |
struct stat buf; |
struct stat buf; |
583 |
if (stat(path, &buf) == 0) { |
if (stat(path, &buf) == 0) { |
584 |
/* it exists: merge */ |
/* it exists: merge */ |
585 |
result = merge(coverage, path, buf.st_size); |
FILE * f = fopen(path, "r"); |
586 |
|
if (f == NULL) { |
587 |
|
result = 1; |
588 |
|
} |
589 |
|
else { |
590 |
|
result = merge(coverage, f); |
591 |
|
if (fclose(f) == EOF) { |
592 |
|
result = 1; |
593 |
|
} |
594 |
|
} |
595 |
if (result != 0) { |
if (result != 0) { |
596 |
free(path); |
free(path); |
597 |
Coverage_delete(coverage); |
Coverage_delete(coverage); |
667 |
} |
} |
668 |
} |
} |
669 |
|
|
670 |
static void instrument_js(const char * id, Stream * input_stream, Stream * output_stream) { |
static void instrument_js(const char * id, const uint16_t * characters, size_t num_characters, Stream * output_stream) { |
671 |
pthread_mutex_lock(&javascript_mutex); |
LOCK(&javascript_mutex); |
672 |
jscoverage_instrument_js(id, input_stream, output_stream); |
jscoverage_instrument_js(id, characters, num_characters, output_stream); |
673 |
pthread_mutex_unlock(&javascript_mutex); |
UNLOCK(&javascript_mutex); |
674 |
|
|
675 |
const struct Resource * resource = get_resource("report.js"); |
const struct Resource * resource = get_resource("report.js"); |
676 |
Stream_write(output_stream, resource->data, resource->length); |
Stream_write(output_stream, resource->data, resource->length); |
791 |
} |
} |
792 |
|
|
793 |
const char * request_uri = HTTPExchange_get_request_uri(client_exchange); |
const char * request_uri = HTTPExchange_get_request_uri(client_exchange); |
794 |
|
char * encoding = HTTPMessage_get_charset(HTTPExchange_get_response_message(server_exchange)); |
795 |
|
if (encoding == NULL) { |
796 |
|
encoding = xstrdup(jscoverage_encoding); |
797 |
|
} |
798 |
|
uint16_t * characters; |
799 |
|
size_t num_characters; |
800 |
|
int result = jscoverage_bytes_to_characters(encoding, input_stream->data, input_stream->length, &characters, &num_characters); |
801 |
|
free(encoding); |
802 |
|
Stream_delete(input_stream); |
803 |
|
if (result == JSCOVERAGE_ERROR_ENCODING_NOT_SUPPORTED) { |
804 |
|
free(characters); |
805 |
|
send_response(client_exchange, 502, "Encoding not supported\n"); |
806 |
|
goto done; |
807 |
|
} |
808 |
|
else if (result == JSCOVERAGE_ERROR_INVALID_BYTE_SEQUENCE) { |
809 |
|
free(characters); |
810 |
|
send_response(client_exchange, 502, "Error decoding response\n"); |
811 |
|
goto done; |
812 |
|
} |
813 |
|
|
814 |
Stream * output_stream = Stream_new(0); |
Stream * output_stream = Stream_new(0); |
815 |
instrument_js(request_uri, input_stream, output_stream); |
instrument_js(request_uri, characters, num_characters, output_stream); |
816 |
|
|
817 |
/* send the headers to the client */ |
/* send the headers to the client */ |
818 |
for (const HTTPHeader * h = HTTPExchange_get_response_headers(server_exchange); h != NULL; h = h->next) { |
for (const HTTPHeader * h = HTTPExchange_get_response_headers(server_exchange); h != NULL; h = h->next) { |
829 |
HTTPServer_log_err("Warning: error writing to client\n"); |
HTTPServer_log_err("Warning: error writing to client\n"); |
830 |
} |
} |
831 |
|
|
832 |
/* input_stream goes on the cache */ |
/* characters go on the cache */ |
833 |
/* |
/* |
834 |
Stream_delete(input_stream); |
free(characters); |
835 |
*/ |
*/ |
836 |
Stream_delete(output_stream); |
Stream_delete(output_stream); |
837 |
add_cached_source(request_uri, input_stream); |
add_cached_source(request_uri, characters, num_characters); |
838 |
} |
} |
839 |
else { |
else { |
840 |
/* does not need instrumentation */ |
/* does not need instrumentation */ |
898 |
} |
} |
899 |
|
|
900 |
filesystem_path = make_path(document_root, abs_path + 1); |
filesystem_path = make_path(document_root, abs_path + 1); |
901 |
|
size_t filesystem_path_length = strlen(filesystem_path); |
902 |
|
if (filesystem_path_length > 0 && filesystem_path[filesystem_path_length - 1] == '/') { |
903 |
|
/* stat on Windows doesn't work with trailing slash */ |
904 |
|
filesystem_path[filesystem_path_length - 1] = '\0'; |
905 |
|
} |
906 |
|
|
907 |
struct stat buf; |
struct stat buf; |
908 |
if (stat(filesystem_path, &buf) == -1) { |
if (stat(filesystem_path, &buf) == -1) { |
911 |
} |
} |
912 |
|
|
913 |
if (S_ISDIR(buf.st_mode)) { |
if (S_ISDIR(buf.st_mode)) { |
914 |
if (filesystem_path[strlen(filesystem_path) - 1] != '/') { |
if (abs_path[strlen(abs_path) - 1] != '/') { |
915 |
const char * request_uri = HTTPExchange_get_request_uri(exchange); |
const char * request_uri = HTTPExchange_get_request_uri(exchange); |
916 |
char * uri = xmalloc(strlen(request_uri) + 2); |
char * uri = xmalloc(strlen(request_uri) + 2); |
917 |
strcpy(uri, request_uri); |
strcpy(uri, request_uri); |
946 |
closedir(d); |
closedir(d); |
947 |
} |
} |
948 |
else if (S_ISREG(buf.st_mode)) { |
else if (S_ISREG(buf.st_mode)) { |
949 |
FILE * f = fopen(filesystem_path, "r"); |
FILE * f = fopen(filesystem_path, "rb"); |
950 |
if (f == NULL) { |
if (f == NULL) { |
951 |
send_response(exchange, 404, "Not found\n"); |
send_response(exchange, 404, "Not found\n"); |
952 |
goto done; |
goto done; |
954 |
|
|
955 |
const char * content_type = get_content_type(filesystem_path); |
const char * content_type = get_content_type(filesystem_path); |
956 |
HTTPExchange_set_response_header(exchange, HTTP_CONTENT_TYPE, content_type); |
HTTPExchange_set_response_header(exchange, HTTP_CONTENT_TYPE, content_type); |
957 |
const char * request_uri = HTTPExchange_get_request_uri(exchange); |
if (strcmp(content_type, "text/javascript") == 0 && ! is_no_instrument(abs_path)) { |
|
if (strcmp(content_type, "text/javascript") == 0 && ! is_no_instrument(request_uri)) { |
|
958 |
Stream * input_stream = Stream_new(0); |
Stream * input_stream = Stream_new(0); |
|
Stream * output_stream = Stream_new(0); |
|
|
|
|
959 |
Stream_write_file_contents(input_stream, f); |
Stream_write_file_contents(input_stream, f); |
960 |
|
|
961 |
instrument_js(request_uri, input_stream, output_stream); |
uint16_t * characters; |
962 |
|
size_t num_characters; |
963 |
|
int result = jscoverage_bytes_to_characters(jscoverage_encoding, input_stream->data, input_stream->length, &characters, &num_characters); |
964 |
|
Stream_delete(input_stream); |
965 |
|
|
966 |
|
if (result == JSCOVERAGE_ERROR_ENCODING_NOT_SUPPORTED) { |
967 |
|
free(characters); |
968 |
|
send_response(exchange, 500, "Encoding not supported\n"); |
969 |
|
goto done; |
970 |
|
} |
971 |
|
else if (result == JSCOVERAGE_ERROR_INVALID_BYTE_SEQUENCE) { |
972 |
|
free(characters); |
973 |
|
send_response(exchange, 500, "Error decoding JavaScript file\n"); |
974 |
|
goto done; |
975 |
|
} |
976 |
|
|
977 |
|
Stream * output_stream = Stream_new(0); |
978 |
|
instrument_js(abs_path, characters, num_characters, output_stream); |
979 |
|
free(characters); |
980 |
|
|
981 |
if (HTTPExchange_write_response(exchange, output_stream->data, output_stream->length) != 0) { |
if (HTTPExchange_write_response(exchange, output_stream->data, output_stream->length) != 0) { |
982 |
HTTPServer_log_err("Warning: error writing to client\n"); |
HTTPServer_log_err("Warning: error writing to client\n"); |
983 |
} |
} |
|
|
|
|
Stream_delete(input_stream); |
|
984 |
Stream_delete(output_stream); |
Stream_delete(output_stream); |
985 |
} |
} |
986 |
else { |
else { |
1060 |
document_root = argv[i] + 16; |
document_root = argv[i] + 16; |
1061 |
} |
} |
1062 |
|
|
1063 |
|
else if (strcmp(argv[i], "--encoding") == 0) { |
1064 |
|
i++; |
1065 |
|
if (i == argc) { |
1066 |
|
fatal("--encoding: option requires an argument"); |
1067 |
|
} |
1068 |
|
jscoverage_encoding = argv[i]; |
1069 |
|
} |
1070 |
|
else if (strncmp(argv[i], "--encoding=", 11) == 0) { |
1071 |
|
jscoverage_encoding = argv[i] + 11; |
1072 |
|
} |
1073 |
|
|
1074 |
else if (strcmp(argv[i], "--ip-address") == 0) { |
else if (strcmp(argv[i], "--ip-address") == 0) { |
1075 |
i++; |
i++; |
1076 |
if (i == argc) { |
if (i == argc) { |
1082 |
ip_address = argv[i] + 13; |
ip_address = argv[i] + 13; |
1083 |
} |
} |
1084 |
|
|
1085 |
|
else if (strcmp(argv[i], "--no-highlight") == 0) { |
1086 |
|
jscoverage_highlight = false; |
1087 |
|
} |
1088 |
|
|
1089 |
else if (strcmp(argv[i], "--no-instrument") == 0) { |
else if (strcmp(argv[i], "--no-instrument") == 0) { |
1090 |
i++; |
i++; |
1091 |
if (i == argc) { |
if (i == argc) { |
1138 |
|
|
1139 |
/* is this a shutdown? */ |
/* is this a shutdown? */ |
1140 |
if (shutdown) { |
if (shutdown) { |
1141 |
|
#ifdef __MINGW32__ |
1142 |
|
WSADATA data; |
1143 |
|
if (WSAStartup(MAKEWORD(1, 1), &data) != 0) { |
1144 |
|
fatal("could not start Winsock"); |
1145 |
|
} |
1146 |
|
#endif |
1147 |
|
|
1148 |
/* INADDR_LOOPBACK */ |
/* INADDR_LOOPBACK */ |
1149 |
HTTPConnection * connection = HTTPConnection_new_client("127.0.0.1", numeric_port); |
HTTPConnection * connection = HTTPConnection_new_client("127.0.0.1", numeric_port); |
1150 |
if (connection == NULL) { |
if (connection == NULL) { |
1174 |
|
|
1175 |
jscoverage_init(); |
jscoverage_init(); |
1176 |
|
|
1177 |
|
#ifndef __MINGW32__ |
1178 |
/* handle broken pipe */ |
/* handle broken pipe */ |
1179 |
signal(SIGPIPE, SIG_IGN); |
signal(SIGPIPE, SIG_IGN); |
1180 |
|
#endif |
1181 |
|
|
1182 |
|
#ifdef __MINGW32__ |
1183 |
|
InitializeCriticalSection(&javascript_mutex); |
1184 |
|
InitializeCriticalSection(&source_cache_mutex); |
1185 |
|
#endif |
1186 |
|
|
1187 |
if (verbose) { |
if (verbose) { |
1188 |
printf("Starting HTTP server on %s:%lu\n", ip_address, numeric_port); |
printf("Starting HTTP server on %s:%lu\n", ip_address, numeric_port); |
1189 |
|
fflush(stdout); |
1190 |
} |
} |
1191 |
HTTPServer_run(ip_address, (uint16_t) numeric_port, handler); |
HTTPServer_run(ip_address, (uint16_t) numeric_port, handler); |
1192 |
if (verbose) { |
if (verbose) { |
1193 |
printf("Stopping HTTP server\n"); |
printf("Stopping HTTP server\n"); |
1194 |
|
fflush(stdout); |
1195 |
} |
} |
1196 |
|
|
1197 |
jscoverage_cleanup(); |
jscoverage_cleanup(); |
1198 |
|
|
1199 |
free(no_instrument); |
free(no_instrument); |
1200 |
|
|
1201 |
pthread_mutex_lock(&source_cache_mutex); |
LOCK(&source_cache_mutex); |
1202 |
while (source_cache != NULL) { |
while (source_cache != NULL) { |
1203 |
SourceCache * p = source_cache; |
SourceCache * p = source_cache; |
1204 |
source_cache = source_cache->next; |
source_cache = source_cache->next; |
1205 |
free(p->url); |
free(p->url); |
1206 |
Stream_delete(p->source); |
free(p->characters); |
1207 |
free(p); |
free(p); |
1208 |
} |
} |
1209 |
pthread_mutex_unlock(&source_cache_mutex); |
UNLOCK(&source_cache_mutex); |
1210 |
|
|
1211 |
return 0; |
return 0; |
1212 |
} |
} |