Compare commits
1 Commits
reimplemen
...
master
Author | SHA1 | Date | |
---|---|---|---|
8eacef2857 |
28
Makefile
28
Makefile
@ -1,39 +1,19 @@
|
|||||||
OBJ:= main.o fs.o strhash.o hashtable.o
|
OBJ:= main.o fs.o
|
||||||
CC ?= clang
|
|
||||||
|
|
||||||
# OS dependend targets
|
|
||||||
OBJ += fs_linux.o
|
|
||||||
OUTPUT:= texdepends
|
OUTPUT:= texdepends
|
||||||
|
|
||||||
.PHONY: test debug clean
|
.PHONY: test debug clean
|
||||||
|
|
||||||
all: debug
|
all: debug
|
||||||
test: ctests debug
|
test: debug
|
||||||
./$(OUTPUT) test.tex
|
./$(OUTPUT) test.tex
|
||||||
|
|
||||||
ctests: test_path test_hashtable
|
debug: CXXFLAGS:= -g -std=c++11
|
||||||
$(foreach exe,$^,./$(exe);)
|
|
||||||
|
|
||||||
.INTERMEDIATE: test_path tests.o test_path.o
|
|
||||||
|
|
||||||
test_path: test_path.o fs.o fs_linux.o tests.o
|
|
||||||
$(CC) $^ -o test_path
|
|
||||||
test_path.c: fs.o tests.o
|
|
||||||
|
|
||||||
test_hashtable: test_hashtable.o strhash.o tests.o hashtable.o
|
|
||||||
$(CC) $^ -o test_hashtable
|
|
||||||
|
|
||||||
test_hashtable.c: hashtable.h
|
|
||||||
|
|
||||||
debug: CXXFLAGS += -g -std=c++11
|
|
||||||
debug: CFLAGS += -g
|
|
||||||
debug: $(OUTPUT)
|
debug: $(OUTPUT)
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
$(RM) $(OBJ) $(OUTPUT)
|
$(RM) $(OBJ) $(OUTPUT)
|
||||||
|
|
||||||
fs.c: fs_linux.o
|
main.o: fs.o
|
||||||
main.c: fs.o strhash.o hashtable.o
|
|
||||||
|
|
||||||
$(OUTPUT): main.o
|
$(OUTPUT): main.o
|
||||||
$(CXX) $(OBJ) -o $(OUTPUT)
|
$(CXX) $(OBJ) -o $(OUTPUT)
|
||||||
|
52
README.md
Normal file
52
README.md
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
## Description
|
||||||
|
texdepends is a small tool that scans given latex files for the following macros:
|
||||||
|
- \input{}
|
||||||
|
- \include{}
|
||||||
|
- \lstinputlisting{}
|
||||||
|
and lists all the files included using these macros in a file with either a given name
|
||||||
|
or with the name of the processed file and a .d extension, using makefile dependency syntax.
|
||||||
|
|
||||||
|
This means for a file that uses e.g. `\input{Intro.tex}` somewhere (the file is named `document.tex`},
|
||||||
|
a call to `texdepends document.tex` will generate a file called `document.d` with the following content:
|
||||||
|
```makefile
|
||||||
|
document.tex: Intro.tex\
|
||||||
|
```
|
||||||
|
|
||||||
|
This file can be used to trigger a rebuild using make of the document if there were changes in files affecting it.
|
||||||
|
|
||||||
|
## Command line options
|
||||||
|
|
||||||
|
There are two possible switches:
|
||||||
|
|
||||||
|
| Name | Description |
|
||||||
|
| --:|:-- |
|
||||||
|
| --target | |
|
||||||
|
| -t | Can be used to set the string output before the colon char |
|
||||||
|
| | |
|
||||||
|
| --output: | |
|
||||||
|
| -o: | modifies the behaviour so all file dependencies of all input files will be written to the given file, instead of creating one file per input. |
|
||||||
|
|
||||||
|
|
||||||
|
**Note: The --output switch should be used in combination with --target, because otherwise no target will be present in the output file!**
|
||||||
|
|
||||||
|
## Building
|
||||||
|
The Program is currently only written for Linux, i will see if a windows port is feasible (should be straight forward).
|
||||||
|
|
||||||
|
### Linux
|
||||||
|
Under Linux, a call to `make` should build produce a binary called `texdepends`.
|
||||||
|
A simple test can be performed using `make test`
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- The program will follow all found *.tex links and parse them too, if possible. Files included in included files will therefore also show up as dependencies. It will only parse a file once
|
||||||
|
- This is no latex parser, it has two major drawbacks:
|
||||||
|
- No Parsing of `\if`-directives, which means, that files included via `\if` will always show up
|
||||||
|
- No parsing of arguments to macros. The Only macro-supsitution happens inside `\include` or `\input` arguments. The only macro definitions supsituted are those made via `\def`. Therefore the following include will not show up in the output because it happend in a macro-argument.
|
||||||
|
|
||||||
|
```
|
||||||
|
\frame{
|
||||||
|
\include{Intro.tex}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
40
fs.c
40
fs.c
@ -1,40 +0,0 @@
|
|||||||
/*
|
|
||||||
* fs.c
|
|
||||||
*
|
|
||||||
* Created on: 20.10.2017
|
|
||||||
* Author: julian
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "fs.h"
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
|
|
||||||
void path_join(struct path *p1, struct path *p2) {
|
|
||||||
size_t other = p2->len;
|
|
||||||
|
|
||||||
while(other) {
|
|
||||||
p1->entries = realloc(p1->entries, sizeof(const char*) * (p1->len+1));
|
|
||||||
if (p1->entries == NULL) {
|
|
||||||
// OUT OF MEM, oper incomplete
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
p1->entries[p1->len] = strdup(p2->entries[p2->len - other]);
|
|
||||||
|
|
||||||
other--;
|
|
||||||
p1->len++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void free_path(struct path * p) {
|
|
||||||
while(p->len) {
|
|
||||||
free(p->entries[p->len-1]);
|
|
||||||
p->len--;
|
|
||||||
}
|
|
||||||
|
|
||||||
// free the old array
|
|
||||||
free(p->entries);
|
|
||||||
}
|
|
||||||
|
|
30
fs.h
30
fs.h
@ -1,30 +0,0 @@
|
|||||||
/*
|
|
||||||
* fs.h
|
|
||||||
*
|
|
||||||
* Created on: 20.10.2017
|
|
||||||
* Author: julian
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef FS_H_
|
|
||||||
#define FS_H_
|
|
||||||
|
|
||||||
#include <stddef.h> // size_t
|
|
||||||
|
|
||||||
struct path {
|
|
||||||
char ** entries;
|
|
||||||
size_t len;
|
|
||||||
int absolute:1;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
extern void path_join(struct path *p1, struct path *p2);
|
|
||||||
extern void free_path(struct path *p);
|
|
||||||
extern void create_path(struct path * p, const char * fsPath);
|
|
||||||
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
// behaves like fopen, but uses the path struct as path
|
|
||||||
extern FILE * path_fopen(struct path * p, const char * flags);
|
|
||||||
|
|
||||||
#endif /* FS_H_ */
|
|
44
fs_linux.c
44
fs_linux.c
@ -1,44 +0,0 @@
|
|||||||
/*
|
|
||||||
* fs_linux.c
|
|
||||||
*
|
|
||||||
* Created on: 20.10.2017
|
|
||||||
* Author: julian
|
|
||||||
*/
|
|
||||||
#include "fs.h"
|
|
||||||
#include <string.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
void create_path(struct path * p, const char * fsPath) {
|
|
||||||
free_path(p);
|
|
||||||
|
|
||||||
const char *start = fsPath, *current = fsPath;
|
|
||||||
|
|
||||||
if (*start == '/') {
|
|
||||||
// absolute path
|
|
||||||
p->absolute = 1;
|
|
||||||
current = ++start;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (;*current != '\0'; ++current) {
|
|
||||||
if (*current == '/') {
|
|
||||||
p->entries = realloc(p->entries, sizeof(p->entries[0]) * (p->len+1));
|
|
||||||
p->entries[p->len] = strndup(start, current-start);
|
|
||||||
|
|
||||||
// skip delimiter
|
|
||||||
start = current+1;
|
|
||||||
p->len++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (start != current) {
|
|
||||||
p->entries = realloc(p->entries, sizeof(p->entries[0]) *(p->len+1));
|
|
||||||
p->entries[p->len] = strdup(start);
|
|
||||||
p->len++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
FILE * path_fopen(struct path * p, const char * flags) {
|
|
||||||
// reconstruct system path
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
}
|
|
185
hashtable.c
185
hashtable.c
@ -1,185 +0,0 @@
|
|||||||
/*
|
|
||||||
* hashtable.c
|
|
||||||
*
|
|
||||||
* Created on: 24.10.2017
|
|
||||||
* Author: julian
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "strhash.h"
|
|
||||||
#include "hashtable.h"
|
|
||||||
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include <errno.h>
|
|
||||||
#include <assert.h>
|
|
||||||
|
|
||||||
int key_compare(entry_key_t one, entry_key_t two) {
|
|
||||||
return strcmp(one, two) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
entry_hash_t key_hash(entry_key_t key) {
|
|
||||||
return strhash_str(key) + 1; // make hash always nonzero
|
|
||||||
}
|
|
||||||
|
|
||||||
void hashtable_init(struct hashtable * table) {
|
|
||||||
memset(table, 0, sizeof(table));
|
|
||||||
}
|
|
||||||
|
|
||||||
hashtable_iterator_t hashtable_end(struct hashtable * table) {
|
|
||||||
return table->data + table->len;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
hashtable_iterator_t hashtable_next(struct hashtable * table, hashtable_iterator_t current) {
|
|
||||||
if (table == NULL) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (current == NULL) {
|
|
||||||
current = table->data;
|
|
||||||
} else {
|
|
||||||
// we want the NEXT pointer :)
|
|
||||||
current++;
|
|
||||||
}
|
|
||||||
|
|
||||||
while(current != hashtable_end(table)) {
|
|
||||||
if (current->hash != 0)
|
|
||||||
break;
|
|
||||||
|
|
||||||
current++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return current;
|
|
||||||
}
|
|
||||||
|
|
||||||
hashtable_iterator_t hashtable_get_hash(struct hashtable * table, entry_hash_t hash) {
|
|
||||||
if (!table->len) {
|
|
||||||
return hashtable_end(table);
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t index = hash % table->len;
|
|
||||||
|
|
||||||
if (table->data[index].hash == 0) {
|
|
||||||
return hashtable_end(table);
|
|
||||||
}
|
|
||||||
|
|
||||||
return table->data + index;
|
|
||||||
}
|
|
||||||
|
|
||||||
hashtable_iterator_t hashtable_get(struct hashtable * table, entry_key_t key) {
|
|
||||||
entry_hash_t hash = key_hash(key);
|
|
||||||
return hashtable_get_hash(table, hash);
|
|
||||||
}
|
|
||||||
|
|
||||||
void hashtable_clear(struct hashtable * table) {
|
|
||||||
if (table->dealloc_data) {
|
|
||||||
hashtable_iterator_t it = hashtable_next(table, NULL);
|
|
||||||
for(;it != hashtable_end(table); it = hashtable_next(table, it)){
|
|
||||||
table->dealloc_data(it->data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
free(table->data);
|
|
||||||
table->data = NULL;
|
|
||||||
table->count = table->len = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int hashtable_resize(struct hashtable * table, size_t newsize) {
|
|
||||||
if (table == NULL) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (newsize == 0) {
|
|
||||||
// will fail, correct to 1
|
|
||||||
newsize = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct hashtable temp = *table;
|
|
||||||
table->data = calloc(newsize, sizeof(struct entry));
|
|
||||||
|
|
||||||
if (table->data == NULL) {
|
|
||||||
table->data = temp.data;
|
|
||||||
|
|
||||||
errno = ENOMEM;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// try to reinsert old data
|
|
||||||
table->len = newsize;
|
|
||||||
|
|
||||||
for (size_t i = 0; i < temp.len; i++) {
|
|
||||||
if (temp.data[i].hash && hashtable_add(table, temp.data[i]) == -1) {
|
|
||||||
// abort mission, restore old table
|
|
||||||
free(table->data);
|
|
||||||
*table = temp;
|
|
||||||
|
|
||||||
return -2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// delete old table
|
|
||||||
free(temp.data);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int hashtable_add(struct hashtable * table, struct entry entry) {
|
|
||||||
if (table == NULL) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (table->len == 0) {
|
|
||||||
// initial alloc
|
|
||||||
int err = hashtable_resize(table, 1);
|
|
||||||
|
|
||||||
if (err < 0) {
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
return hashtable_add(table, entry);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// try to insert into table
|
|
||||||
size_t index = entry.hash % table->len;
|
|
||||||
|
|
||||||
if (table->data->hash && !key_compare(table->data[index].key, entry.key)) {
|
|
||||||
// key collision
|
|
||||||
// make table bigger
|
|
||||||
if (hashtable_resize(table, table->len*2) != 0) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return hashtable_add(table, entry);
|
|
||||||
}
|
|
||||||
|
|
||||||
// insert new entry
|
|
||||||
table->data[index] = entry;
|
|
||||||
table->count++;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct entry hashtable_make_entry(entry_key_t key, void * data) {
|
|
||||||
return (struct entry){ .data = data,
|
|
||||||
.key = key,
|
|
||||||
.hash = key_hash(key),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
int hashtable_remove(struct hashtable * table, hashtable_iterator_t it) {
|
|
||||||
if (!it || it->hash == 0) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (table->dealloc_data) {
|
|
||||||
table->dealloc_data(it->data);
|
|
||||||
}
|
|
||||||
|
|
||||||
it->data = NULL;
|
|
||||||
it->hash = 0;
|
|
||||||
table->count--;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
116
hashtable.h
116
hashtable.h
@ -1,116 +0,0 @@
|
|||||||
/*
|
|
||||||
* hashtable.h
|
|
||||||
*
|
|
||||||
* Created on: 21.10.2017
|
|
||||||
* Author: julian
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef HASHTABLE_H_
|
|
||||||
#define HASHTABLE_H_
|
|
||||||
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include "strhash.h"
|
|
||||||
#include <errno.h>
|
|
||||||
#include <assert.h>
|
|
||||||
|
|
||||||
|
|
||||||
typedef strhash_t entry_hash_t;
|
|
||||||
typedef const char * entry_key_t;
|
|
||||||
typedef struct entry * hashtable_iterator_t;
|
|
||||||
|
|
||||||
// data deallocator
|
|
||||||
typedef void(*hashtable_dealloc)(void*);
|
|
||||||
|
|
||||||
extern int key_compare(entry_key_t one, entry_key_t two);
|
|
||||||
extern entry_hash_t key_hash(entry_key_t key);
|
|
||||||
|
|
||||||
struct entry {
|
|
||||||
entry_hash_t hash;
|
|
||||||
entry_key_t key;
|
|
||||||
void * data;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct hashtable {
|
|
||||||
struct entry * data;
|
|
||||||
size_t len, count;
|
|
||||||
|
|
||||||
hashtable_dealloc dealloc_data;
|
|
||||||
};
|
|
||||||
|
|
||||||
extern void hashtable_init(struct hashtable * table);
|
|
||||||
|
|
||||||
extern hashtable_iterator_t hashtable_end(struct hashtable * table);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Iterate of the hashmap.
|
|
||||||
*
|
|
||||||
* pass the return value to current to iterate over the entire map
|
|
||||||
* pass NULL to get the beginning
|
|
||||||
*
|
|
||||||
* returns the next set entry in the table from current
|
|
||||||
* returns hashtable_end() when there is nons
|
|
||||||
* example loop:
|
|
||||||
* hashmap_iterator_t current = hashtable_next(table, NULL);
|
|
||||||
* for(;current != hashtable_end(table); current = hashtable_next(table, current)) {}
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
hashtable_iterator_t hashtable_next(struct hashtable * table, hashtable_iterator_t current);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* returns the entry identified by the given hash
|
|
||||||
* returns hashtable_end() if no entry matched
|
|
||||||
*/
|
|
||||||
hashtable_iterator_t hashtable_get_hash(struct hashtable * table, entry_hash_t hash);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* returns the entry identified by the given key
|
|
||||||
* returns hashtable_end() if no entry matched
|
|
||||||
*/
|
|
||||||
hashtable_iterator_t hashtable_get(struct hashtable * table, entry_key_t key);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* delete all entries of the given table
|
|
||||||
*
|
|
||||||
* calls dealloc of the table for every data element, if it was set.
|
|
||||||
*/
|
|
||||||
void hashtable_clear(struct hashtable * table);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* insert a new element in the table
|
|
||||||
*/
|
|
||||||
int hashtable_add(struct hashtable * table, struct entry entry);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* resize the hashtable to a new physical size
|
|
||||||
* can also try to make the hashtable smaller
|
|
||||||
*
|
|
||||||
* returns 0 on success
|
|
||||||
* returns -1 on error (sets errno)
|
|
||||||
* returns -2 if hashtable does not fit in new size.
|
|
||||||
*/
|
|
||||||
int hashtable_resize(struct hashtable * table, size_t newsize);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* add new element in hashtable
|
|
||||||
*
|
|
||||||
* returns 0 on success
|
|
||||||
* returns -1 on error
|
|
||||||
*/
|
|
||||||
int hashtable_add(struct hashtable * table, struct entry entry);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* make a new entry that can be added out of key and data
|
|
||||||
*/
|
|
||||||
struct entry hashtable_make_entry(entry_key_t key, void * data);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* remove one entry using the key
|
|
||||||
* @return -1 on error
|
|
||||||
* @return 1 on success
|
|
||||||
* @return 0 on not found
|
|
||||||
*/
|
|
||||||
int hashtable_remove(struct hashtable * table, hashtable_iterator_t it);
|
|
||||||
|
|
||||||
#endif /* HASHTABLE_H_ */
|
|
296
main.c
296
main.c
@ -1,296 +0,0 @@
|
|||||||
#include <string.h>
|
|
||||||
#include "fs.h"
|
|
||||||
#include <errno.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include "hashtable.h"
|
|
||||||
|
|
||||||
#define _(x) x
|
|
||||||
|
|
||||||
struct macro {
|
|
||||||
char * name;
|
|
||||||
};
|
|
||||||
|
|
||||||
void *copy_macro(void * macro) {
|
|
||||||
struct macro * temp = (struct macro*)(macro),
|
|
||||||
* new = malloc(sizeof(struct macro));
|
|
||||||
|
|
||||||
new->name = strdup(temp->name);
|
|
||||||
return (void*)(new);
|
|
||||||
}
|
|
||||||
|
|
||||||
void free_macro(void * macro) {
|
|
||||||
struct macro * temp = (struct macro*)(macro);
|
|
||||||
|
|
||||||
free(temp->name);
|
|
||||||
free(temp);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
typedef struct hashtable macrotable_t;
|
|
||||||
|
|
||||||
// init default macros empty
|
|
||||||
macrotable_t default_macros = {};
|
|
||||||
|
|
||||||
void build_default_macros() {
|
|
||||||
// default macros currently implemented:
|
|
||||||
// if
|
|
||||||
// lstinputlisting
|
|
||||||
// include
|
|
||||||
// input
|
|
||||||
// includegraphics
|
|
||||||
// graphicspath
|
|
||||||
hashtable_clear(&default_macros);
|
|
||||||
}
|
|
||||||
|
|
||||||
void macrotable_init(macrotable_t * table) {
|
|
||||||
hashtable_init(table);
|
|
||||||
table->dealloc_data = free_macro;
|
|
||||||
}
|
|
||||||
|
|
||||||
void macrotable_copy(macrotable_t * to, macrotable_t * from) {
|
|
||||||
if (!to || !from) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
hashtable_iterator_t it = hashtable_next(from, NULL);
|
|
||||||
struct entry newEntry;
|
|
||||||
|
|
||||||
for(; it != hashtable_end(from); it = hashtable_next(from, it)) {
|
|
||||||
// insert it into to
|
|
||||||
newEntry = *it;
|
|
||||||
newEntry.data = copy_macro(newEntry.data);
|
|
||||||
|
|
||||||
hashtable_add(to, newEntry);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct filelist {
|
|
||||||
struct filelist * next;
|
|
||||||
char * path;
|
|
||||||
struct hashtable * macros;
|
|
||||||
};
|
|
||||||
|
|
||||||
void free_filelist(struct filelist * list) {
|
|
||||||
if (list == NULL) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct filelist * next = list->next;
|
|
||||||
|
|
||||||
while(list->next) {
|
|
||||||
next = list->next;
|
|
||||||
free(next->path);
|
|
||||||
free(next);
|
|
||||||
|
|
||||||
list->next = list->next->next;
|
|
||||||
}
|
|
||||||
|
|
||||||
free(list->path);
|
|
||||||
free(list);
|
|
||||||
}
|
|
||||||
|
|
||||||
int filelist_push(struct filelist * pos, const char * path) {
|
|
||||||
struct filelist * new = malloc(sizeof(struct filelist));
|
|
||||||
if (!new) {
|
|
||||||
errno = ENOMEM;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
new->next = pos->next;
|
|
||||||
new->path = strdup(path);
|
|
||||||
|
|
||||||
pos->next = new;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int good(FILE * file) {
|
|
||||||
return !feof(file);
|
|
||||||
}
|
|
||||||
|
|
||||||
volatile int line = 0, pos = 0;
|
|
||||||
FILE * fopen_count(const char * path, const char * flags) {
|
|
||||||
line = pos = 0;
|
|
||||||
return fopen(path, flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
char fgetc_count(FILE *file) {
|
|
||||||
char temp = fgetc(file);
|
|
||||||
if (temp == '\n') {
|
|
||||||
line++;
|
|
||||||
pos = 0;
|
|
||||||
} else {
|
|
||||||
pos++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return temp;
|
|
||||||
}
|
|
||||||
|
|
||||||
int add_char(char ** str, char c) {
|
|
||||||
if (str == NULL) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (*str != NULL) {
|
|
||||||
int len = strlen(*str);
|
|
||||||
|
|
||||||
char *temp = realloc(*str, len + 2);
|
|
||||||
if (temp == NULL) {
|
|
||||||
errno = ENOMEM;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
*str = temp;
|
|
||||||
|
|
||||||
(*str)[len] = c;
|
|
||||||
(*str)[len+1] = 0;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
*str = malloc(2);
|
|
||||||
if (*str == NULL) {
|
|
||||||
errno = ENOMEM;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
(*str)[0] = c;
|
|
||||||
(*str)[1] = '\0';
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int macro_limiter(char c) {
|
|
||||||
switch(c) {
|
|
||||||
case '\\':
|
|
||||||
// next macro
|
|
||||||
return 1;
|
|
||||||
case '{':
|
|
||||||
// arguments
|
|
||||||
return 2;
|
|
||||||
case '[':
|
|
||||||
// optional arguments
|
|
||||||
return 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void eval_macro(struct filelist * last, FILE * file) {
|
|
||||||
char * name = NULL;
|
|
||||||
strhash_t hash = 0;
|
|
||||||
hashtable_iterator_t macro = hashtable_end(last->macros);
|
|
||||||
|
|
||||||
char current = 0;
|
|
||||||
|
|
||||||
// read macro name
|
|
||||||
while(good(file) && !macro_limiter(current) && macro == hashtable_end(last->macros)) {
|
|
||||||
current = fgetc_count(file);
|
|
||||||
|
|
||||||
hash = strhash_add(hash, current);
|
|
||||||
add_char(&name, current);
|
|
||||||
|
|
||||||
// try to find macro in table
|
|
||||||
macro = hashtable_get_hash(last->macros, hash);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (macro == hashtable_end(last->macros)) {
|
|
||||||
// no macro with that name found
|
|
||||||
printf("%d:%d:macro not found: %s\n", line, pos, name);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!good(file)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void eval_file(struct filelist * last) {
|
|
||||||
// open file
|
|
||||||
FILE * file = fopen_count(last->path, "r");
|
|
||||||
|
|
||||||
if (file == NULL) {
|
|
||||||
fprintf(stderr, _("could not open file \"%s\":%s\n"), last->path, strerror(errno));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("processing file: %s\n", last->path);
|
|
||||||
|
|
||||||
char current = 1;
|
|
||||||
while(good(file)) {
|
|
||||||
current = fgetc_count(file);
|
|
||||||
|
|
||||||
switch(current) {
|
|
||||||
case '%':
|
|
||||||
// comment, skip line
|
|
||||||
do {
|
|
||||||
current = fgetc_count(file);
|
|
||||||
} while(good(file) && current != '\n');
|
|
||||||
|
|
||||||
break;
|
|
||||||
case '\\':
|
|
||||||
// macro
|
|
||||||
eval_macro(last, file);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
printf(_("%d:%d:unrecognized char! %c\n"), line, pos, current);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fclose(file);
|
|
||||||
}
|
|
||||||
|
|
||||||
#include <getopt.h>
|
|
||||||
|
|
||||||
struct option long_opts[] = {
|
|
||||||
{"output", required_argument, NULL, 'o'},
|
|
||||||
{"target", required_argument, NULL, 't'},
|
|
||||||
{}, // terminator
|
|
||||||
};
|
|
||||||
|
|
||||||
void help(int argc, char ** args) {
|
|
||||||
printf("%s [OPTIONS] file1 file2...\n"
|
|
||||||
"--output\n"
|
|
||||||
"-o\tby default the program will create a depfile for every input\n"
|
|
||||||
"\tnaming it like the tex file with a .d extension. By giving the -o Option\n"
|
|
||||||
"\tthe output will instead be put in this file exculsivly\n"
|
|
||||||
"--target\n"
|
|
||||||
"-t\tOverride the target name\n", args[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char ** args) {
|
|
||||||
int long_index = 0;
|
|
||||||
int opt = 0;
|
|
||||||
|
|
||||||
const char *outfile, *target;
|
|
||||||
|
|
||||||
while((opt = getopt_long(argc, args, "o:t:", long_opts, &long_index)) != -1) {
|
|
||||||
switch(opt) {
|
|
||||||
case 'o':
|
|
||||||
outfile = optarg;
|
|
||||||
break;
|
|
||||||
case 't':
|
|
||||||
target = optarg;
|
|
||||||
break;
|
|
||||||
case '?':
|
|
||||||
help(argc, args);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
char * current_file = NULL;
|
|
||||||
|
|
||||||
// process all files
|
|
||||||
for(;optind < argc; ++optind) {
|
|
||||||
current_file = args[optind];
|
|
||||||
|
|
||||||
struct filelist list;
|
|
||||||
list.next = NULL;
|
|
||||||
list.path = current_file;
|
|
||||||
list.macros = malloc(sizeof(macrotable_t));
|
|
||||||
|
|
||||||
macrotable_init(list.macros);
|
|
||||||
macrotable_copy(list.macros, &default_macros);
|
|
||||||
|
|
||||||
eval_file(&list);
|
|
||||||
free_filelist(list.next);
|
|
||||||
}
|
|
||||||
}
|
|
@ -44,6 +44,9 @@ protected:
|
|||||||
std::set<Path::Path> includes;
|
std::set<Path::Path> includes;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
|
||||||
template <typename iterator>
|
template <typename iterator>
|
||||||
std::string readTill(iterator &start, const iterator &end, std::function<bool(const iterator&)> limiter) {
|
std::string readTill(iterator &start, const iterator &end, std::function<bool(const iterator&)> limiter) {
|
||||||
iterator current = start;
|
iterator current = start;
|
||||||
@ -134,7 +137,7 @@ InputExtractor::List InputExtractor::Include(Path::Path path) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
cout << endl;
|
cout << endl;
|
||||||
|
|
||||||
// add to include list
|
// add to include list
|
||||||
includes.insert(path);
|
includes.insert(path);
|
||||||
|
|
28
strhash.c
28
strhash.c
@ -1,28 +0,0 @@
|
|||||||
/*
|
|
||||||
* strhash.c
|
|
||||||
*
|
|
||||||
* Created on: 21.10.2017
|
|
||||||
* Author: julian
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "strhash.h"
|
|
||||||
|
|
||||||
// use rule:
|
|
||||||
// hash(i) = hash(i - 1) * 33 ^ str[i]
|
|
||||||
// see: http://www.cse.yorku.ca/~oz/hash.html
|
|
||||||
strhash_t strhash_add(strhash_t hash, char c) {
|
|
||||||
return hash * 33 ^ c;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
strhash_t strhash_str(const char * str) {
|
|
||||||
strhash_t hash = 0;
|
|
||||||
while(*str) {
|
|
||||||
hash = strhash_add(hash, *str);
|
|
||||||
str++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return hash;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
23
strhash.h
23
strhash.h
@ -1,23 +0,0 @@
|
|||||||
/*
|
|
||||||
* strhash.h
|
|
||||||
*
|
|
||||||
* Created on: 21.10.2017
|
|
||||||
* Author: julian
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef STRHASH_H_
|
|
||||||
#define STRHASH_H_
|
|
||||||
|
|
||||||
#include <stddef.h>
|
|
||||||
|
|
||||||
typedef size_t strhash_t;
|
|
||||||
|
|
||||||
// "rehash" string with new char added to end
|
|
||||||
strhash_t strhash_add(strhash_t hash, char c);
|
|
||||||
|
|
||||||
/// hash a given string
|
|
||||||
strhash_t strhash_str(const char * str);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#endif /* STRHASH_H_ */
|
|
150
test_hashtable.c
150
test_hashtable.c
@ -1,150 +0,0 @@
|
|||||||
/*
|
|
||||||
* test_hashtable.c
|
|
||||||
*
|
|
||||||
* Created on: 21.10.2017
|
|
||||||
* Author: julian
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "tests.h"
|
|
||||||
#include "hashtable.h"
|
|
||||||
|
|
||||||
struct hashtable table = {};
|
|
||||||
struct entry nEntry;
|
|
||||||
|
|
||||||
void testresize() {
|
|
||||||
init("resize");
|
|
||||||
int err = hashtable_resize(&table, 20);
|
|
||||||
if (err != 0) {
|
|
||||||
fail("resize return code was error %d", err);
|
|
||||||
}
|
|
||||||
if (table.len != 20) {
|
|
||||||
fail("table has wrong size %d", table.len);
|
|
||||||
}
|
|
||||||
|
|
||||||
pass();
|
|
||||||
}
|
|
||||||
|
|
||||||
void testadd() {
|
|
||||||
init("add");
|
|
||||||
nEntry = hashtable_make_entry("hi", "20");
|
|
||||||
size_t count = table.count;
|
|
||||||
|
|
||||||
int err = hashtable_add(&table, nEntry);
|
|
||||||
|
|
||||||
if (err != 0) {
|
|
||||||
fail("add gave error %d", err);
|
|
||||||
}
|
|
||||||
if (table.count != count +1) {
|
|
||||||
fail("table has wrong count (has %d, needs %d)", table.count, count+1);
|
|
||||||
}
|
|
||||||
|
|
||||||
pass();
|
|
||||||
}
|
|
||||||
|
|
||||||
volatile int number_copied = 0;
|
|
||||||
|
|
||||||
void* test_copy(void * data) {
|
|
||||||
number_copied++;
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
void testget() {
|
|
||||||
init("get");
|
|
||||||
|
|
||||||
hashtable_clear(&table);
|
|
||||||
|
|
||||||
if (hashtable_get(&table, "hi") != hashtable_end(&table)) {
|
|
||||||
fail("found entry in empty table");
|
|
||||||
}
|
|
||||||
|
|
||||||
hashtable_add(&table, nEntry);
|
|
||||||
hashtable_iterator_t elem = hashtable_get(&table, "hi");
|
|
||||||
|
|
||||||
if (elem == hashtable_end(&table)) {
|
|
||||||
fail("element was not found");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (elem->data != nEntry.data) {
|
|
||||||
fail("returned wrong element");
|
|
||||||
}
|
|
||||||
|
|
||||||
pass();
|
|
||||||
}
|
|
||||||
|
|
||||||
void testiterate() {
|
|
||||||
init("iterate");
|
|
||||||
|
|
||||||
hashtable_iterator_t it = hashtable_next(&table, NULL);
|
|
||||||
|
|
||||||
if (it == hashtable_end(&table)) {
|
|
||||||
fail("table seems empty?");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strcmp(it->key, "hi")) {
|
|
||||||
fail("wrong entry (smh)");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
pass();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
volatile int dealloc_called = 0;
|
|
||||||
|
|
||||||
void test_dealloc(void* data) {
|
|
||||||
dealloc_called++;
|
|
||||||
}
|
|
||||||
|
|
||||||
void testremove() {
|
|
||||||
init("remove");
|
|
||||||
dealloc_called = 0;
|
|
||||||
table.dealloc_data = test_dealloc;
|
|
||||||
|
|
||||||
if (hashtable_add(&table, hashtable_make_entry("woop", NULL)) < 0) {
|
|
||||||
fail("could not add");
|
|
||||||
}
|
|
||||||
|
|
||||||
hashtable_iterator_t it = hashtable_get(&table, "woop");
|
|
||||||
int ret = hashtable_remove(&table, it);
|
|
||||||
|
|
||||||
if (ret < 0) {
|
|
||||||
fail("negative return code");
|
|
||||||
}
|
|
||||||
if (!ret) {
|
|
||||||
fail("could not remove");
|
|
||||||
}
|
|
||||||
if (!dealloc_called) {
|
|
||||||
fail("deallocator not called");
|
|
||||||
}
|
|
||||||
|
|
||||||
pass();
|
|
||||||
}
|
|
||||||
void testclear() {
|
|
||||||
init("clear");
|
|
||||||
|
|
||||||
size_t count = table.count;
|
|
||||||
table.dealloc_data = test_dealloc;
|
|
||||||
|
|
||||||
hashtable_clear(&table);
|
|
||||||
if (table.count != 0 || table.len != 0 || table.data != NULL) {
|
|
||||||
fail("memory was not freed");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dealloc_called != count) {
|
|
||||||
fail("dealloc was not called for all the data");
|
|
||||||
}
|
|
||||||
|
|
||||||
pass();
|
|
||||||
}
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
init("hashtable");
|
|
||||||
testresize();
|
|
||||||
testadd();
|
|
||||||
testget();
|
|
||||||
testiterate();
|
|
||||||
testclear();
|
|
||||||
pass();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
74
test_path.c
74
test_path.c
@ -1,74 +0,0 @@
|
|||||||
/*
|
|
||||||
* test_path.c
|
|
||||||
*
|
|
||||||
* Created on: 20.10.2017
|
|
||||||
* Author: julian
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#include "tests.h"
|
|
||||||
#include "fs.h"
|
|
||||||
|
|
||||||
#include <string.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
const char * reason_invalid_entry = "invalid entry";
|
|
||||||
|
|
||||||
void test1() {
|
|
||||||
init("a/b/c");
|
|
||||||
|
|
||||||
struct path p = {};
|
|
||||||
create_path(&p, "a/b/c");
|
|
||||||
if (p.len < 3) {
|
|
||||||
fail("%d, not enough path splits", p.len);
|
|
||||||
}
|
|
||||||
if (p.absolute) {
|
|
||||||
fail("path should be relative");
|
|
||||||
}
|
|
||||||
if (strcmp(p.entries[0], "a") != 0) {
|
|
||||||
fail(reason_invalid_entry);
|
|
||||||
}
|
|
||||||
if (strcmp(p.entries[1], "b") != 0) {
|
|
||||||
fail(reason_invalid_entry);
|
|
||||||
}
|
|
||||||
if (strcmp(p.entries[2], "c") != 0) {
|
|
||||||
fail(reason_invalid_entry);
|
|
||||||
}
|
|
||||||
|
|
||||||
free_path(&p);
|
|
||||||
pass();
|
|
||||||
}
|
|
||||||
|
|
||||||
void test2() {
|
|
||||||
init("/a/b/c");
|
|
||||||
|
|
||||||
struct path p = {};
|
|
||||||
create_path(&p, "/a/b/c");
|
|
||||||
if (p.len != 3) {
|
|
||||||
fail("%d, not enough path splits", p.len);
|
|
||||||
}
|
|
||||||
if (!p.absolute) {
|
|
||||||
fail("path should be absolute");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strcmp(p.entries[0], "a") != 0) {
|
|
||||||
fail(reason_invalid_entry);
|
|
||||||
}
|
|
||||||
if (strcmp(p.entries[1], "b") != 0) {
|
|
||||||
fail(reason_invalid_entry);
|
|
||||||
}
|
|
||||||
if (strcmp(p.entries[2], "c") != 0) {
|
|
||||||
fail(reason_invalid_entry);
|
|
||||||
}
|
|
||||||
|
|
||||||
free_path(&p);
|
|
||||||
pass();
|
|
||||||
}
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
init("test_path");
|
|
||||||
test1();
|
|
||||||
test2();
|
|
||||||
pass();
|
|
||||||
}
|
|
||||||
|
|
72
tests.c
72
tests.c
@ -1,72 +0,0 @@
|
|||||||
/*
|
|
||||||
* tests.c
|
|
||||||
*
|
|
||||||
* Created on: 20.10.2017
|
|
||||||
* Author: julian
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "tests.h"
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
#include <signal.h>
|
|
||||||
#include <stdarg.h>
|
|
||||||
|
|
||||||
const char * teststack[20];
|
|
||||||
int current = 0;
|
|
||||||
|
|
||||||
void segfault(int i) {
|
|
||||||
fail("segfault");
|
|
||||||
exit(2);
|
|
||||||
}
|
|
||||||
|
|
||||||
void push(const char * name) {
|
|
||||||
if (current < 20) {
|
|
||||||
teststack[current] = name;
|
|
||||||
current++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void pop() {
|
|
||||||
if (current != 0)
|
|
||||||
current--;
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_name() {
|
|
||||||
int i = 0;
|
|
||||||
for (; i < current-1;i++) {
|
|
||||||
printf("%s@", teststack[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i < current)
|
|
||||||
printf("%s", teststack[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
void init(const char * name) {
|
|
||||||
push(name);
|
|
||||||
test_name();
|
|
||||||
puts(": START");
|
|
||||||
|
|
||||||
// install signal handlers
|
|
||||||
signal(SIGSEGV, segfault);
|
|
||||||
}
|
|
||||||
|
|
||||||
void fail(const char * reason, ...) {
|
|
||||||
va_list list;
|
|
||||||
va_start(list, reason);
|
|
||||||
|
|
||||||
test_name();
|
|
||||||
fputs(": FAIL (", stdout);
|
|
||||||
vprintf(reason, list);
|
|
||||||
va_end(list);
|
|
||||||
|
|
||||||
puts(")");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
void pass() {
|
|
||||||
test_name();
|
|
||||||
puts(": PASS");
|
|
||||||
pop();
|
|
||||||
// exit(0);
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user