1 // sourcefile.c written by Mitchell Foral. mitchell<att>caladbolg.net.
2 // See COPYING for license information.
15 #include "languages.h"
22 SourceFile *ohcount_sourcefile_new(const char *filepath) {
23 SourceFile *sourcefile = malloc(sizeof(SourceFile));
25 int length = strlen(filepath);
26 sourcefile->filepath = malloc(length + 1);
27 strncpy(sourcefile->filepath, filepath, length);
28 char *p = sourcefile->filepath + length;
31 while (p > sourcefile->filepath && *(p - 1) != '.' &&
32 *(p - 1) != '/' && *(p - 1) != '\\') p--;
33 sourcefile->ext = (*(p - 1) == '.') ? p : sourcefile->filepath + length;
35 while (p > sourcefile->filepath &&
36 *(p - 1) != '/' && *(p - 1) != '\\') p--;
37 sourcefile->filename = p;
39 sourcefile->dirpath = (p - 1) - sourcefile->filepath;
40 if (sourcefile->dirpath < 0) sourcefile->dirpath = 0;
42 sourcefile->diskpath = NULL;
44 sourcefile->contents = NULL;
45 sourcefile->size = -1;
47 sourcefile->language = NULL;
48 sourcefile->language_detected = 0;
50 sourcefile->parsed_language_list = NULL;
52 sourcefile->license_list = NULL;
54 sourcefile->loc_list = NULL;
56 sourcefile->filenames = NULL;
61 void ohcount_sourcefile_set_diskpath(SourceFile *sourcefile,
62 const char *diskpath) {
63 if (sourcefile->diskpath)
64 free(sourcefile->diskpath);
65 int size = strlen(diskpath);
66 sourcefile->diskpath = malloc(size + 1);
67 strncpy(sourcefile->diskpath, diskpath, size);
68 sourcefile->diskpath[size] = '\0';
71 void ohcount_sourcefile_set_contents(SourceFile *sourcefile,
72 const char *contents) {
73 if (sourcefile->contents)
74 free(sourcefile->contents);
75 int size = strlen(contents);
76 sourcefile->contents = malloc(size + 1);
77 strncpy(sourcefile->contents, contents, size);
78 sourcefile->contents[size] = '\0';
79 sourcefile->size = size;
82 char *ohcount_sourcefile_get_contents(SourceFile *sourcefile) {
83 if (sourcefile->contents == NULL) {
84 char *path = sourcefile->filepath;
85 if (sourcefile->diskpath)
86 path = sourcefile->diskpath;
87 FILE *f = fopen(path, "rb");
89 fseek(f, 0, SEEK_END);
92 sourcefile->contents = malloc(size + 1);
93 if (fread(sourcefile->contents, size, 1, f) != 1) {
94 free(sourcefile->contents);
95 sourcefile->contents = NULL;
98 sourcefile->size = size;
99 sourcefile->contents[size] = '\0';
103 sourcefile->contents = NULL;
104 sourcefile->size = 0;
107 return sourcefile->contents;
110 int ohcount_sourcefile_get_contents_size(SourceFile *sourcefile) {
111 if (sourcefile->size < 0)
112 ohcount_sourcefile_get_contents(sourcefile);
113 return sourcefile->size;
116 void ohcount_sourcefile_set_language(SourceFile *sourcefile,
117 const char *language) {
118 struct LanguageMap *rl =
119 ohcount_hash_language_from_name(language, strlen(language));
121 sourcefile->language = rl->name;
122 sourcefile->language_detected = 1;
126 const char *ohcount_sourcefile_get_language(SourceFile *sourcefile) {
127 if (!sourcefile->language_detected) {
128 sourcefile->language = ohcount_detect_language(sourcefile);
129 sourcefile->language_detected = 1;
131 return sourcefile->language;
135 * Callback function for populating a SourceFile's parsed_language_list field.
136 * This callback is passed to ohcount_parse() for parsing lines of code.
137 * @param language The language associated with the incoming line.
138 * @param entity The type of line. ("lcode", "lcomment", or "lblank").
139 * @param start The start position of the entity relative to the start of the
141 * @param end The end position of the entity relative to the start of the buffer
143 * @param userdata Pointer to the SourceFile being parsed.
144 * @see ohcount_sourcefile_parse.
146 void parser_callback(const char *language, const char *entity, int start,
147 int end, void *userdata) {
148 SourceFile *sf = (SourceFile *)userdata;
149 char *buffer = sf->contents; // field is guaranteed to exist
150 int buffer_size = sf->size; // field is guaranteed to exist
151 char *p = buffer + start, *pe = buffer + end;
153 ParsedLanguageList *list = sf->parsed_language_list;
154 ParsedLanguage *lang;
155 if (list->head == NULL) {
159 list->pl = ohcount_parsed_language_new(language, buffer_size);
163 // Has this language been detected before?
164 ParsedLanguageList *iter = list->head;
166 if (strcmp(iter->pl->name, language) == 0)
171 // This language has not been detected before. Create a new entry and add
173 iter = ohcount_parsed_language_list_new();
174 iter->pl = ohcount_parsed_language_new(language, buffer_size);
175 list->tail->next = iter;
181 if (strcmp(entity, "lcode") == 0) {
182 while (*p == ' ' || *p == '\t') p++;
183 ohcount_parsed_language_add_code(lang, p, pe - p);
184 } else if (strcmp(entity, "lcomment") == 0) {
185 while (*p == ' ' || *p == '\t') p++;
186 ohcount_parsed_language_add_comment(lang, p, pe - p);
187 } else if (strcmp(entity, "lblank") == 0) {
188 lang->blanks_count++;
192 void ohcount_sourcefile_parse(SourceFile *sourcefile) {
193 if (sourcefile->parsed_language_list == NULL) {
194 sourcefile->parsed_language_list = ohcount_parsed_language_list_new();
195 ohcount_parse(sourcefile, 1, parser_callback, sourcefile);
197 // Since the SourceFile contents are not 'free'd until the SourceFile itself
198 // is, continually parsing SourceFiles in a SourceFileList will cause an
199 // undesirable build-up of memory until the SourceFileList is 'free'd.
200 // While it is expensive to re-read the contents from the disk, it is
201 // unlikely they will need to be accessed again after parsing.
202 free(sourcefile->contents); // field is guaranteed to exist
203 sourcefile->contents = NULL;
207 ParsedLanguageList *ohcount_sourcefile_get_parsed_language_list(SourceFile
209 ohcount_sourcefile_parse(sourcefile);
210 return sourcefile->parsed_language_list;
213 void ohcount_sourcefile_parse_with_callback(SourceFile *sourcefile,
214 void (*callback)(const char *,
218 ohcount_parse(sourcefile, 1, callback, userdata);
221 void ohcount_sourcefile_parse_entities_with_callback(SourceFile *sourcefile,
227 ohcount_parse(sourcefile, 0, callback, userdata);
230 LicenseList *ohcount_sourcefile_get_license_list(SourceFile *sourcefile) {
231 if (sourcefile->license_list == NULL)
232 sourcefile->license_list = ohcount_detect_license(sourcefile);
233 return sourcefile->license_list;
236 LocList *ohcount_sourcefile_get_loc_list(SourceFile *sourcefile) {
237 if (sourcefile->loc_list == NULL) {
238 LocList *list = ohcount_loc_list_new();
239 ohcount_sourcefile_parse(sourcefile);
240 ParsedLanguageList *iter;
241 iter = ohcount_sourcefile_get_parsed_language_list(sourcefile)->head;
243 Loc *loc = ohcount_loc_new(iter->pl->name, iter->pl->code_count,
244 iter->pl->comments_count,
245 iter->pl->blanks_count, 1);
246 ohcount_loc_list_add_loc(list, loc);
247 ohcount_loc_free(loc);
250 sourcefile->loc_list = list;
252 return sourcefile->loc_list;
255 LocDeltaList *ohcount_sourcefile_diff(SourceFile *from, SourceFile *to) {
256 log_it("Starting ohcount_sourcefile_diff:");
257 log_it(from->filename);
259 log_it(to->filename);
261 LocDeltaList *list = ohcount_loc_delta_list_new();
263 ParsedLanguageList *iter;
264 iter = ohcount_sourcefile_get_parsed_language_list(from)->head;
266 LocDelta *delta = ohcount_sourcefile_calc_loc_delta(from,
269 ohcount_loc_delta_list_add_loc_delta(list, delta);
270 ohcount_loc_delta_free(delta);
273 iter = ohcount_sourcefile_get_parsed_language_list(to)->head;
275 if (!ohcount_loc_delta_list_get_loc_delta(list, iter->pl->name)) {
276 LocDelta *delta = ohcount_sourcefile_calc_loc_delta(from,
279 ohcount_loc_delta_list_add_loc_delta(list, delta);
280 ohcount_loc_delta_free(delta);
288 LocDelta *ohcount_sourcefile_calc_loc_delta(SourceFile *from_file,
289 const char *language,
290 SourceFile *to_file) {
291 LocDelta *delta = ohcount_loc_delta_new(language, 0, 0, 0, 0, 0, 0);
292 ParsedLanguage * const blank = ohcount_parsed_language_new(language, 0);
293 ParsedLanguage *from = blank, *to = blank;
295 ParsedLanguageList *iter;
296 for (iter = ohcount_sourcefile_get_parsed_language_list(from_file)->head;
299 if (strcmp(language, iter->pl->name) == 0) {
305 for (iter = ohcount_sourcefile_get_parsed_language_list(to_file)->head;
308 if (strcmp(language, iter->pl->name) == 0) {
314 ohcount_calc_diff(from->code, to->code, &delta->code_added,
315 &delta->code_removed);
316 ohcount_calc_diff(from->comments, to->comments, &delta->comments_added,
317 &delta->comments_removed);
318 if (from->blanks_count > to->blanks_count)
319 delta->blanks_removed = from->blanks_count - to->blanks_count;
321 delta->blanks_added = to->blanks_count - from->blanks_count;
326 /* NOTE! Does not free sourcefile->filenames.
327 * Calling code is responsible for alloc+free of filenames.
329 void ohcount_sourcefile_free(SourceFile *sourcefile) {
330 free(sourcefile->filepath);
331 if (sourcefile->diskpath)
332 free(sourcefile->diskpath);
333 if (sourcefile->contents)
334 free(sourcefile->contents);
335 if (sourcefile->parsed_language_list)
336 ohcount_parsed_language_list_free(sourcefile->parsed_language_list);
337 if (sourcefile->license_list)
338 ohcount_license_list_free(sourcefile->license_list);
339 if (sourcefile->loc_list)
340 ohcount_loc_list_free(sourcefile->loc_list);
346 SourceFileList *ohcount_sourcefile_list_new() {
347 SourceFileList *list = malloc(sizeof(SourceFileList));
355 void ohcount_sourcefile_list_add_file(SourceFileList *list,
356 const char *filepath) {
357 if (list->head == NULL) { // empty list
360 list->head->sf = ohcount_sourcefile_new(filepath);
361 list->head->next = NULL;
363 SourceFileList *item = ohcount_sourcefile_list_new();
364 item->sf = ohcount_sourcefile_new(filepath);
365 list->tail->next = item;
370 void ohcount_sourcefile_list_add_directory(SourceFileList *list,
371 const char *directory) {
372 char filepath[FILENAME_MAX];
373 strncpy(filepath, directory, strlen(directory));
374 *(filepath + strlen(directory)) = '/';
375 char *f_p = filepath + strlen(directory) + 1;
379 d = opendir(directory);
381 while ((file = readdir(d))) {
383 int length = strlen(file->d_name);
384 strncpy(f_p, (const char *)file->d_name, length);
385 *(f_p + length) = '\0';
387 lstat(filepath, &st);
388 if(S_ISLNK(st.st_mode))
391 if (S_ISDIR(st.st_mode) && *file->d_name != '.') // no hidden dirs
392 ohcount_sourcefile_list_add_directory(list, filepath);
393 else if (S_ISREG(st.st_mode))
394 ohcount_sourcefile_list_add_file(list, filepath);
400 LocList *ohcount_sourcefile_list_analyze_languages(SourceFileList *list) {
401 LocList *loc_list = ohcount_loc_list_new();
402 SourceFileList *iter = list->head;
404 ohcount_loc_list_add_loc_list(loc_list,
405 ohcount_sourcefile_get_loc_list(iter->sf));
411 void ohcount_sourcefile_list_free(SourceFileList *list) {
413 SourceFileList *iter = list->head;
415 SourceFileList *next = iter->next;
416 ohcount_sourcefile_free(iter->sf);