1 // sourcefile.c written by Mitchell Foral. mitchell<att>caladbolg.net.
2 // See COPYING for license information.
16 SourceFile *ohcount_sourcefile_new(const char *filepath) {
17 SourceFile *sourcefile = malloc(sizeof(SourceFile));
19 int length = strlen(filepath);
20 sourcefile->filepath = malloc(length + 1);
21 strncpy(sourcefile->filepath, filepath, length);
22 char *p = sourcefile->filepath + length;
25 while (p > sourcefile->filepath && *(p - 1) != '.' &&
26 *(p - 1) != '/' && *(p - 1) != '\\') p--;
29 while (p > sourcefile->filepath &&
30 *(p - 1) != '/' && *(p - 1) != '\\') p--;
31 sourcefile->filename = p;
33 sourcefile->dirpath = (p - 1) - sourcefile->filepath;
34 if (sourcefile->dirpath < 0) sourcefile->dirpath = 0;
36 sourcefile->diskpath = NULL;
38 sourcefile->contents = NULL;
39 sourcefile->size = -1;
41 sourcefile->language = NULL;
42 sourcefile->language_detected = 0;
44 sourcefile->parsed_language_list = NULL;
46 sourcefile->license_list = NULL;
48 sourcefile->loc_list = NULL;
50 sourcefile->filenames = NULL;
55 void ohcount_sourcefile_set_diskpath(SourceFile *sourcefile,
56 const char *diskpath) {
57 if (sourcefile->diskpath)
58 free(sourcefile->diskpath);
59 int size = strlen(diskpath);
60 sourcefile->diskpath = malloc(size + 1);
61 strncpy(sourcefile->diskpath, diskpath, size);
62 sourcefile->diskpath[size] = '\0';
65 void ohcount_sourcefile_set_contents(SourceFile *sourcefile,
66 const char *contents) {
67 if (sourcefile->contents)
68 free(sourcefile->contents);
69 int size = strlen(contents);
70 sourcefile->contents = malloc(size + 1);
71 strncpy(sourcefile->contents, contents, size);
72 sourcefile->contents[size] = '\0';
73 sourcefile->size = size;
76 char *ohcount_sourcefile_get_contents(SourceFile *sourcefile) {
77 if (sourcefile->contents == NULL) {
78 FILE *f = fopen(sourcefile->filepath, "r");
80 fseek(f, 0, SEEK_END);
83 sourcefile->contents = malloc(size + 1);
84 fread(sourcefile->contents, 1, size, f);
85 sourcefile->contents[size] = '\0';
86 sourcefile->size = size;
89 sourcefile->contents = NULL;
93 return sourcefile->contents;
96 int ohcount_sourcefile_get_contents_size(SourceFile *sourcefile) {
97 if (sourcefile->size < 0)
98 ohcount_sourcefile_get_contents(sourcefile);
99 return sourcefile->size;
102 void ohcount_sourcefile_set_language(SourceFile *sourcefile,
103 const char *language) {
104 sourcefile->language = language;
105 sourcefile->language_detected = 1;
108 const char *ohcount_sourcefile_get_language(SourceFile *sourcefile) {
109 if (!sourcefile->language_detected) {
110 sourcefile->language = ohcount_detect_language(sourcefile);
111 sourcefile->language_detected = 1;
113 return sourcefile->language;
117 * Callback function for populating a SourceFile's parsed_language_list field.
118 * This callback is passed to ohcount_parse() for parsing lines of code.
119 * @param language The language associated with the incoming line.
120 * @param entity The type of line. ("lcode", "lcomment", or "lblank").
121 * @param start The start position of the entity relative to the start of the
123 * @param end The end position of the entity relative to the start of the buffer
125 * @param userdata Pointer to the SourceFile being parsed.
126 * @see ohcount_sourcefile_parse.
128 void parser_callback(const char *language, const char *entity, int start,
129 int end, void *userdata) {
130 SourceFile *sf = (SourceFile *)userdata;
131 char *buffer = sf->contents; // field is guaranteed to exist
132 int buffer_size = sf->size; // field is guaranteed to exist
133 char *p = buffer + start, *pe = buffer + end;
135 ParsedLanguageList *list = sf->parsed_language_list;
136 ParsedLanguage *lang;
137 if (list->head == NULL) {
141 list->pl = ohcount_parsed_language_new(language, buffer_size);
145 // Has this language been detected before?
146 ParsedLanguageList *iter = list->head;
148 if (strcmp(iter->pl->language, language) == 0)
153 // This language has not been detected before. Create a new entry and add
155 iter = ohcount_parsed_language_list_new();
156 iter->pl = ohcount_parsed_language_new(language, buffer_size);
157 list->tail->next = iter;
163 if (strcmp(entity, "lcode") == 0) {
164 while (*p == ' ' || *p == '\t') p++;
165 ohcount_parsed_language_add_code(lang, p, pe - p);
166 } else if (strcmp(entity, "lcomment") == 0) {
167 while (*p == ' ' || *p == '\t') p++;
168 ohcount_parsed_language_add_comment(lang, p, pe - p);
169 } else if (strcmp(entity, "lblank") == 0) {
170 lang->blanks_count++;
174 void ohcount_sourcefile_parse(SourceFile *sourcefile) {
175 if (sourcefile->parsed_language_list == NULL) {
176 sourcefile->parsed_language_list = ohcount_parsed_language_list_new();
177 ohcount_parse(sourcefile, 1, parser_callback, sourcefile);
181 ParsedLanguageList *ohcount_sourcefile_get_parsed_language_list(SourceFile
183 ohcount_sourcefile_parse(sourcefile);
184 return sourcefile->parsed_language_list;
187 void ohcount_sourcefile_parse_with_callback(SourceFile *sourcefile,
188 void (*callback)(const char *,
192 ohcount_parse(sourcefile, 1, callback, userdata);
195 void ohcount_sourcefile_parse_entities_with_callback(SourceFile *sourcefile,
201 ohcount_parse(sourcefile, 0, callback, userdata);
204 LicenseList *ohcount_sourcefile_get_license_list(SourceFile *sourcefile) {
205 if (sourcefile->license_list == NULL)
206 sourcefile->license_list = ohcount_detect_license(sourcefile);
207 return sourcefile->license_list;
210 LocList *ohcount_sourcefile_get_loc_list(SourceFile *sourcefile) {
211 if (sourcefile->loc_list == NULL) {
212 LocList *list = ohcount_loc_list_new();
213 ohcount_sourcefile_parse(sourcefile);
214 ParsedLanguageList *iter;
215 iter = ohcount_sourcefile_get_parsed_language_list(sourcefile)->head;
217 Loc *loc = ohcount_loc_new(iter->pl->language, iter->pl->code_count,
218 iter->pl->comments_count,
219 iter->pl->blanks_count, 1);
220 ohcount_loc_list_add_loc(list, loc);
221 ohcount_loc_free(loc);
224 sourcefile->loc_list = list;
226 return sourcefile->loc_list;
229 LocDeltaList *ohcount_sourcefile_diff(SourceFile *from, SourceFile *to) {
230 LocDeltaList *list = ohcount_loc_delta_list_new();
232 ParsedLanguageList *iter;
233 iter = ohcount_sourcefile_get_parsed_language_list(from)->head;
235 LocDelta *delta = ohcount_sourcefile_calc_loc_delta(from,
238 ohcount_loc_delta_list_add_loc_delta(list, delta);
239 ohcount_loc_delta_free(delta);
242 iter = ohcount_sourcefile_get_parsed_language_list(to)->head;
244 if (!ohcount_loc_delta_list_get_loc_delta(list, iter->pl->language)) {
245 LocDelta *delta = ohcount_sourcefile_calc_loc_delta(from,
248 ohcount_loc_delta_list_add_loc_delta(list, delta);
249 ohcount_loc_delta_free(delta);
257 LocDelta *ohcount_sourcefile_calc_loc_delta(SourceFile *from,
258 const char *language,
260 LocDelta *delta = ohcount_loc_delta_new(language, 0, 0, 0, 0, 0, 0);
262 char *from_code = "", *to_code = "";
263 char *from_comments = "", *to_comments = "";
264 int from_blanks_count = 0, to_blanks_count = 0;
266 ParsedLanguageList *iter;
267 iter = ohcount_sourcefile_get_parsed_language_list(from)->head;
269 if (strcmp(language, iter->pl->language) == 0) {
270 from_code = iter->pl->code;
271 from_comments = iter->pl->comments;
272 from_blanks_count = iter->pl->blanks_count;
277 iter = ohcount_sourcefile_get_parsed_language_list(to)->head;
279 if (strcmp(language, iter->pl->language) == 0) {
280 to_code = iter->pl->code;
281 to_comments = iter->pl->comments;
282 to_blanks_count = iter->pl->blanks_count;
288 ohcount_calc_diff(from_code, to_code, &delta->code_added,
289 &delta->code_removed);
290 ohcount_calc_diff(from_comments, to_comments, &delta->comments_added,
291 &delta->comments_removed);
292 if (from_blanks_count > to_blanks_count)
293 delta->blanks_removed = from_blanks_count - to_blanks_count;
295 delta->blanks_added = to_blanks_count - from_blanks_count;
300 void ohcount_sourcefile_set_filenames(SourceFile *sourcefile,
302 if (sourcefile->filenames) {
304 while (sourcefile->filenames[i])
305 free(sourcefile->filenames[i++]);
306 free(sourcefile->filenames);
309 if (filenames != NULL) {
311 while (filenames[length] != NULL) length++;
312 char **fnames = calloc(length + 1, sizeof(char *));
315 for (i = 0; i < length; i++) {
316 int len = strlen(filenames[i]);
317 char *fname = malloc(len + 1);
318 strncpy(fname, filenames[i], len);
322 sourcefile->filenames = fnames;
323 } else sourcefile->filenames = NULL;
326 char **ohcount_sourcefile_get_filenames(SourceFile *sourcefile) {
327 if (sourcefile->filenames == NULL) {
328 char dirpath[FILENAME_MAX];
329 strncpy(dirpath, sourcefile->filepath, sourcefile->dirpath);
330 dirpath[sourcefile->dirpath] = '\0';
332 DIR *d = opendir((const char *)dirpath);
335 while ((file = readdir(d))) length++;
338 char **filenames = calloc(length + 1, sizeof(char *));
340 d = opendir((const char *)dirpath);
341 while ((file = readdir(d))) {
342 int len = strlen(file->d_name);
343 char *filename = malloc(len + 1);
344 strncpy(filename, file->d_name, len);
345 filename[len] = '\0';
346 filenames[i++] = filename;
349 sourcefile->filenames = filenames;
352 return sourcefile->filenames;
355 void ohcount_sourcefile_free(SourceFile *sourcefile) {
356 free(sourcefile->filepath);
357 if (sourcefile->diskpath)
358 free(sourcefile->diskpath);
359 if (sourcefile->contents)
360 free(sourcefile->contents);
361 if (sourcefile->parsed_language_list)
362 ohcount_parsed_language_list_free(sourcefile->parsed_language_list);
363 if (sourcefile->license_list)
364 ohcount_license_list_free(sourcefile->license_list);
365 if (sourcefile->loc_list)
366 ohcount_loc_list_free(sourcefile->loc_list);
367 if (sourcefile->filenames) {
369 while (sourcefile->filenames[i])
370 free(sourcefile->filenames[i++]);
371 free(sourcefile->filenames);
378 SourceFileList *ohcount_sourcefile_list_new() {
379 SourceFileList *list = malloc(sizeof(SourceFileList));
387 void ohcount_sourcefile_list_add_file(SourceFileList *list,
388 const char *filepath) {
389 if (list->head == NULL) { // empty list
392 list->head->sf = ohcount_sourcefile_new(filepath);
393 list->head->next = NULL;
395 SourceFileList *item = ohcount_sourcefile_list_new();
396 item->sf = ohcount_sourcefile_new(filepath);
397 list->tail->next = item;
402 void ohcount_sourcefile_list_add_directory(SourceFileList *list,
403 const char *directory) {
404 char filepath[FILENAME_MAX];
405 strncpy(filepath, directory, strlen(directory));
406 *(filepath + strlen(directory)) = '/';
407 char *f_p = filepath + strlen(directory) + 1;
410 DIR *d = opendir(directory);
412 while ((file = readdir(d))) {
413 int length = strlen(file->d_name);
414 strncpy(f_p, (const char *)file->d_name, length);
415 *(f_p + length) = '\0';
417 if (file->d_type == DT_DIR && *file->d_name != '.') // no hidden dirs
418 ohcount_sourcefile_list_add_directory(list, filepath);
419 else if (file->d_type == DT_REG)
420 ohcount_sourcefile_list_add_file(list, filepath);
426 LocList *ohcount_sourcefile_list_analyze_languages(SourceFileList *list) {
427 LocList *loc_list = ohcount_loc_list_new();
428 SourceFileList *iter = list->head;
430 ohcount_loc_list_add_loc_list(loc_list,
431 ohcount_sourcefile_get_loc_list(iter->sf));
437 void ohcount_sourcefile_list_free(SourceFileList *list) {
439 SourceFileList *iter = list->head;
441 SourceFileList *next = iter->next;
442 ohcount_sourcefile_free(iter->sf);