/[jscoverage]/trunk/jscoverage-server.c
ViewVC logotype

Diff of /trunk/jscoverage-server.c

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 119 by siliconforks, Sun Jun 1 14:05:47 2008 UTC revision 179 by siliconforks, Sun Sep 21 18:35:21 2008 UTC
# Line 22  Line 22 
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    
# Line 64  Line 73 
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) {
# Line 123  Line 143 
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    }    }
# Line 344  Line 377 
377    Stream * stream = Stream_new(0);    Stream * stream = Stream_new(0);
378    Stream_write_file_contents(stream, f);    Stream_write_file_contents(stream, f);
379    
380    pthread_mutex_lock(&javascript_mutex);    LOCK(&javascript_mutex);
381    int result = jscoverage_parse_json(coverage, stream->data, stream->length);    int result = jscoverage_parse_json(coverage, stream->data, stream->length);
382    pthread_mutex_unlock(&javascript_mutex);    UNLOCK(&javascript_mutex);
383    
384    Stream_delete(stream);    Stream_delete(stream);
385    return result;    return result;
# Line 389  Line 422 
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    
# Line 399  Line 439 
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      }      }
# Line 412  Line 452 
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  }  }
# Line 502  Line 564 
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) {
# Line 515  Line 577 
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      FILE * f = fopen(path, "r");  
581      if (f != NULL) {      /* check if the JSON file exists */
582        struct stat buf;
583        if (stat(path, &buf) == 0) {
584        /* it exists: merge */        /* it exists: merge */
585        result = merge(coverage, f);        FILE * f = fopen(path, "r");
586        if (fclose(f) == EOF) {        if (f == NULL) {
587          result = 1;          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);
# Line 541  Line 611 
611      /* copy other files */      /* copy other files */
612      jscoverage_copy_resources(report_directory);      jscoverage_copy_resources(report_directory);
613      path = make_path(report_directory, "jscoverage.js");      path = make_path(report_directory, "jscoverage.js");
614      f = fopen(path, "ab");      FILE * f = fopen(path, "ab");
615      free(path);      free(path);
616      if (f == NULL) {      if (f == NULL) {
617        send_response(exchange, 500, "Could not write to file: jscoverage.js\n");        send_response(exchange, 500, "Could not write to file: jscoverage.js\n");
# Line 597  Line 667 
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);
# Line 721  Line 791 
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) {
# Line 739  Line 829 
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 */
# Line 808  Line 898 
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) {
# Line 816  Line 911 
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);
# Line 851  Line 946 
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;
# Line 859  Line 954 
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 {
# Line 952  Line 1060 
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) {
# Line 963  Line 1082 
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) {
# Line 1015  Line 1138 
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) {
# Line 1044  Line 1174 
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  }  }

Legend:
Removed from v.119  
changed lines
  Added in v.179

  ViewVC Help
Powered by ViewVC 1.1.24