├── .gitattributes ├── lib ├── k08_linux.a ├── k08_macos.a ├── k08_wasm.a ├── k08_win.a └── Makefile ├── .vscode ├── visualize-c-memory.so ├── settings.json ├── setup-debugger.sh ├── run.sh ├── launch.json ├── tasks.json └── visualize-c-memory.py ├── programs ├── ip_projects │ ├── ipli │ │ ├── iphw2021_4.pdf │ │ ├── interpreter.h │ │ ├── programs │ │ │ ├── countdivs.ipl │ │ │ ├── primes.ipl │ │ │ ├── humble.ipl │ │ │ ├── factorize.ipl │ │ │ ├── selectsort.ipl │ │ │ ├── matrmult.ipl │ │ │ └── nqueens.ipl │ │ ├── Makefile │ │ ├── ipli.c │ │ ├── parser.h │ │ ├── parser.c │ │ └── interpreter.c │ └── mapcol │ │ ├── mapcol.c │ │ ├── Balkans.txt │ │ ├── Makefile │ │ ├── coloring.h │ │ ├── genmap.c │ │ └── coloring.c ├── visualize │ ├── Makefile │ └── examples.c ├── minmax │ ├── minmax.c │ └── Makefile ├── cat │ ├── Makefile │ ├── cat.c │ ├── input-file │ ├── io.h │ ├── io_test.c │ └── io.c └── iron_bank │ ├── Makefile │ ├── customer.h │ ├── customer_test.c │ ├── iron_bank.c │ ├── customer.c │ └── customers.csv ├── .github └── workflows │ └── run-tests.yml ├── include ├── stats.h ├── common_types.h ├── ADTPriorityQueue.h ├── ADTStack.h ├── ADTQueue.h ├── ADTList.h ├── ADTSet.h ├── ADTMap.h ├── ADTVector.h └── acutest.h ├── .gitignore ├── modules ├── stats.c └── stats_alt.c ├── README.md ├── tests ├── stats_test.c └── Makefile └── Makefile /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf -------------------------------------------------------------------------------- /lib/k08_linux.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chatziko-k08/sample-project/HEAD/lib/k08_linux.a -------------------------------------------------------------------------------- /lib/k08_macos.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chatziko-k08/sample-project/HEAD/lib/k08_macos.a -------------------------------------------------------------------------------- /lib/k08_wasm.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chatziko-k08/sample-project/HEAD/lib/k08_wasm.a -------------------------------------------------------------------------------- /lib/k08_win.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chatziko-k08/sample-project/HEAD/lib/k08_win.a -------------------------------------------------------------------------------- /.vscode/visualize-c-memory.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chatziko-k08/sample-project/HEAD/.vscode/visualize-c-memory.so -------------------------------------------------------------------------------- /programs/ip_projects/ipli/iphw2021_4.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chatziko-k08/sample-project/HEAD/programs/ip_projects/ipli/iphw2021_4.pdf -------------------------------------------------------------------------------- /programs/ip_projects/ipli/interpreter.h: -------------------------------------------------------------------------------- 1 | 2 | 3 | #include 4 | #include 5 | 6 | #include "parser.h" 7 | 8 | 9 | void interpreter_run(Runtime runtime); -------------------------------------------------------------------------------- /programs/ip_projects/mapcol/mapcol.c: -------------------------------------------------------------------------------- 1 | 2 | #include "coloring.h" 3 | 4 | int main(void) { 5 | Coloring coloring = read_input(); 6 | 7 | tzini(coloring); 8 | 9 | print_coloring(coloring); 10 | } 11 | -------------------------------------------------------------------------------- /.github/workflows/run-tests.yml: -------------------------------------------------------------------------------- 1 | name: run-tests 2 | on: [push, pull_request] 3 | 4 | jobs: 5 | build-deploy: 6 | runs-on: ${{ matrix.os }} 7 | strategy: 8 | matrix: 9 | os: [ubuntu-latest, macos-latest, macos-13, windows-latest] 10 | steps: 11 | - uses: actions/checkout@v4 12 | 13 | - name: make run 14 | run: make run 15 | -------------------------------------------------------------------------------- /programs/visualize/Makefile: -------------------------------------------------------------------------------- 1 | # options 2 | CC = gcc 3 | CFLAGS = -Wall -g 4 | 5 | # Αρχεία .o 6 | OBJS = examples.o 7 | 8 | # Το εκτελέσιμο πρόγραμμα 9 | EXEC = examples 10 | 11 | # Παράμετροι για δοκιμαστική εκτέλεση 12 | ARGS = 13 | 14 | $(EXEC): $(OBJS) 15 | $(CC) $(OBJS) -o $(EXEC) $(LDFLAGS) 16 | 17 | clean: 18 | rm -f $(OBJS) $(EXEC) 19 | 20 | run: $(EXEC) 21 | ./$(EXEC) $(ARGS) 22 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "c_project": { 3 | // Όνομα του εκτελέσιμου προγράμματος (μόνο για μεταγλώττιση πολλών αρχείων & make) 4 | "program": "minmax", 5 | 6 | // Directory στο οποίο βρίσκεται το πρόγραμμα (μόνο για μεταγλώτισση με make) 7 | "dir": "programs/minmax", 8 | 9 | // Ορίσματα του προγράμματος. 10 | // Υποστηρίζονται ανακατευθύνσεις εισόδου (< input-file) και εξόδου (> output-file). 11 | // 12 | "args": "1 -2 3 52", 13 | }, 14 | } 15 | -------------------------------------------------------------------------------- /include/stats.h: -------------------------------------------------------------------------------- 1 | // stats.h 2 | // Απλά στατιστικά στοιχεία για πίνακες ακεραίων 3 | 4 | #pragma once // #include το πολύ μία φοράν 5 | 6 | #include // σταθερές INT_MIN, INT_MAX 7 | 8 | // Επιστρέφει το μικρότερο στοιχείο του array με μέγεθος size (ΙΝΤ_ΜΑΧ αν size == 0) 9 | 10 | int stats_find_min(int array[], int size); 11 | 12 | // Επιστρέφει το μεγαλύτερο στοιχείο του array με μέγεθος size (ΙΝΤ_MIN αν size == 0) 13 | 14 | int stats_find_max(int array[], int size); -------------------------------------------------------------------------------- /lib/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile για τη βιβλιοθήκη k08.a 2 | 3 | # Βρίσκουμε ποιο λειτουργικό σύστημα χρησιμοποιείται 4 | ifdef WASM 5 | SUFFIX = wasm 6 | else ifeq ($(OS),Windows_NT) 7 | SUFFIX = win 8 | else ifeq ($(shell uname -s),Linux) 9 | SUFFIX = linux 10 | else 11 | SUFFIX = macos 12 | endif 13 | 14 | all: k08.a 15 | 16 | # "Παράγουμε" το k08.a απλά αντιγράφοντας το αρχείο που αντιστοιχεί στο λειτουργικό σύστημα που χρησιμοποιούμε 17 | k08.a: 18 | cp k08_$(SUFFIX).a k08.a 19 | 20 | clean: 21 | $(RM) k08.a -------------------------------------------------------------------------------- /programs/minmax/minmax.c: -------------------------------------------------------------------------------- 1 | // minmax 2 | // Τυπώνει τον μικρότερο και το μεγαλύτερο αριθμό από αυτούς που δίνονται ως ορίσματα. 3 | 4 | #include 5 | #include 6 | 7 | #include "stats.h" 8 | 9 | int main(int argc, char* argv[]) { 10 | // Δημιουργία array με τις παραμέτρους του προγράμματος, αφού μετατραπούν σε int 11 | int size = argc-1; 12 | int array[size]; 13 | 14 | for(int i = 0; i < size; i++) 15 | array[i] = atoi(argv[i+1]); 16 | 17 | printf("min: %d\n", stats_find_min(array, size)); 18 | printf("max: %d\n", stats_find_max(array, size)); 19 | } -------------------------------------------------------------------------------- /programs/minmax/Makefile: -------------------------------------------------------------------------------- 1 | # paths 2 | MODULES = ../../modules 3 | INCLUDE = ../../include 4 | 5 | # compiler 6 | CC = gcc 7 | 8 | # Compile options. Το -I λέει στον compiler να αναζητήσει εκεί include files 9 | CFLAGS = -Wall -Werror -g -I$(INCLUDE) 10 | 11 | # Αρχεία .o 12 | OBJS = minmax.o $(MODULES)/stats.o 13 | 14 | # Το εκτελέσιμο πρόγραμμα 15 | EXEC = minmax 16 | 17 | # Παράμετροι για δοκιμαστική εκτέλεση 18 | ARGS = 1 -2 3 52 19 | 20 | $(EXEC): $(OBJS) 21 | $(CC) $(OBJS) -o $(EXEC) 22 | 23 | clean: 24 | rm -f $(OBJS) $(EXEC) 25 | 26 | run: $(EXEC) 27 | ./$(EXEC) $(ARGS) -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Το git από default αποθηκεύει _όλα_ τα αρχεία, _εκτός_ όσων υπάρχουν στο .gitignore (blacklist). 2 | # Συχνά όμως είναι πιο βολικό να κάνουμε το αντίστροφο, δηλαδή να μην αποθηκεύουμε _κανένα_ αρχείο 3 | # πάρα _μόνο_ αυτά που δηλώνονται στο .gitignore (whitelist). Αυτό επιτυγχάνεται αγνοώντας τα πάντα (*) 4 | # και χρησιμοποιώντας το "!" που σημαίνει "μην αγνοήσεις το συγκεκριμένο pattern". 5 | 6 | # Αγνοούμε όλα τα αρχεία (όχι τα directories) 7 | * 8 | !*/ 9 | 10 | # Εκτός από τα παρακάτω 11 | !*.c 12 | !*.h 13 | !*.mk 14 | !Makefile 15 | !.gitignore 16 | !README.md 17 | !.vscode/*.json -------------------------------------------------------------------------------- /modules/stats.c: -------------------------------------------------------------------------------- 1 | // stats.c - Υλοποίηση του stats module 2 | 3 | #include "stats.h" 4 | 5 | int stats_find_min(int array[], int size) { 6 | int min = INT_MAX; // "default" τιμή, μεγαλύτερη από όλες 7 | 8 | for(int i = 0; i < size; i++) 9 | if(array[i] < min) 10 | min = array[i]; // βρέθηκε νέο ελάχιστο 11 | 12 | return min; 13 | } 14 | 15 | int stats_find_max(int array[], int size) { 16 | int max = INT_MIN; // "default" τιμή, μικρότερη από όλες 17 | 18 | for(int i = 0; i < size; i++) 19 | if(array[i] > max) 20 | max = array[i]; // βρέθηκε νέο μέγιστο 21 | 22 | return max; 23 | } -------------------------------------------------------------------------------- /programs/ip_projects/ipli/programs/countdivs.ipl: -------------------------------------------------------------------------------- 1 | # Filename: countdivs.ipl 2 | # 3 | # Find the number of divisors of all numbers in a given interval 4 | 5 | argument 1 minnumb 6 | argument 2 maxnumb 7 | number = minnumb 8 | while number <= maxnumb 9 | count = 2 10 | divisor = 2 11 | divisor2 = divisor * divisor 12 | while divisor2 < number 13 | remainder = number % divisor 14 | if remainder == 0 15 | count = count + 2 16 | divisor = divisor + 1 17 | divisor2 = divisor * divisor 18 | quotient = number / divisor 19 | if quotient == divisor 20 | count = count + 1 21 | write number 22 | writeln count 23 | number = number + 1 24 | -------------------------------------------------------------------------------- /programs/ip_projects/mapcol/Balkans.txt: -------------------------------------------------------------------------------- 1 | nocolor Albania Greece Montenegro North_Macedonia Serbia 2 | nocolor Bosnia_and_Herzegovina Croatia Montenegro Serbia 3 | nocolor Bulgaria Greece North_Macedonia Romania Serbia Turkey 4 | nocolor Croatia Bosnia_and_Herzegovina Montenegro Serbia Slovenia 5 | nocolor Greece Albania Bulgaria North_Macedonia Turkey 6 | nocolor Montenegro Albania Bosnia_and_Herzegovina Croatia Serbia 7 | nocolor North_Macedonia Albania Bulgaria Greece Serbia 8 | nocolor Romania Bulgaria Serbia 9 | nocolor Serbia Albania Bosnia_and_Herzegovina Bulgaria Croatia Montenegro North_Macedonia Romania 10 | nocolor Slovenia Croatia 11 | nocolor Turkey Bulgaria Greece 12 | -------------------------------------------------------------------------------- /programs/ip_projects/ipli/programs/primes.ipl: -------------------------------------------------------------------------------- 1 | # Filename: primes.ipl 2 | # 3 | # Find all prime numbers less than or equal to a given number 4 | 5 | read maxnumb 6 | #maxnumb = 20 7 | number = 1 8 | while number <= maxnumb 9 | if number == 1 10 | is_prime = 0 11 | else 12 | is_prime = 1 13 | divisor = 2 14 | if number < 4 15 | stop = 1 16 | else 17 | stop = 0 18 | while stop == 0 19 | remainder = number % divisor 20 | if remainder == 0 21 | is_prime = 0 22 | divisor = divisor + 1 23 | if is_prime == 0 24 | stop = 1 25 | divisor2 = divisor * divisor 26 | if divisor2 > number 27 | stop = 1 28 | if is_prime != 0 29 | writeln number 30 | number = number + 1 31 | -------------------------------------------------------------------------------- /programs/ip_projects/ipli/Makefile: -------------------------------------------------------------------------------- 1 | # paths 2 | LIB = ../../../lib 3 | INCLUDE = ../../../include 4 | 5 | # compiler 6 | CC = gcc 7 | 8 | # Compile options. Το -I λέει στον compiler να αναζητήσει εκεί include files 9 | override CFLAGS += -Wall -Werror -O3 -I$(INCLUDE) 10 | 11 | # Αρχεία .o 12 | OBJS = ipli.o parser.o interpreter.o $(LIB)/k08.a 13 | 14 | # Το εκτελέσιμο πρόγραμμα 15 | EXEC = ipli 16 | 17 | # Παράμετροι για δοκιμαστική εκτέλεση 18 | ARGS = programs/nqueens.ipl 19 | 20 | $(EXEC): $(OBJS) 21 | $(CC) $(OBJS) -o $(EXEC) 22 | 23 | # Για να φτιάξουμε το k08.a, τρέχουμε το make στο lib directory. 24 | $(LIB)/k08.a: 25 | $(MAKE) -C $(LIB) k08.a 26 | 27 | clean: 28 | rm -f $(OBJS) $(EXEC) 29 | 30 | run: $(EXEC) 31 | ./$(EXEC) $(ARGS) -------------------------------------------------------------------------------- /include/common_types.h: -------------------------------------------------------------------------------- 1 | #pragma once // #include το πολύ μία φορά 2 | 3 | // Τύποι που χρησιμοποιούνται σε πολλά modules 4 | 5 | // Χρήση του τύπου "bool" για μεταβλητές που παίρνουν μόνο τιμές true / false 6 | #include 7 | 8 | // Pointer προς ένα αντικείμενο οποιουδήποτε τύπου. Απλά είναι πιο ευανάγνωστο από το "void*" που μοιάζει με το "void" 9 | typedef void* Pointer; 10 | 11 | // unsigned int, για συντομία 12 | typedef unsigned int uint; 13 | 14 | // Δείκτης σε συνάρτηση που συγκρίνει 2 στοιχεία a και b και επιστρέφει: 15 | // < 0 αν a < b 16 | // 0 αν a και b είναι ισοδύναμα (_όχι_ αναγναστικά ίσα) 17 | // > 0 αν a > b 18 | typedef int (*CompareFunc)(Pointer a, Pointer b); 19 | 20 | // Δείκτης σε συνάρτηση που καταστρέφει ένα στοιχείο value 21 | typedef void (*DestroyFunc)(Pointer value); -------------------------------------------------------------------------------- /programs/ip_projects/mapcol/Makefile: -------------------------------------------------------------------------------- 1 | # paths 2 | LIB = ../../../lib 3 | INCLUDE = ../../../include 4 | 5 | # compiler 6 | CC = gcc 7 | 8 | # Compile options. Το -I λέει στον compiler να αναζητήσει εκεί include files 9 | CFLAGS = -Wall -Werror -g -I$(INCLUDE) 10 | LDFLAGS = -lm 11 | 12 | # Αρχεία .o 13 | OBJS = mapcol.o coloring.o $(LIB)/k08.a 14 | 15 | # Το εκτελέσιμο πρόγραμμα 16 | EXEC = mapcol 17 | 18 | # Ορίσματα για make run 19 | ARGS = < Balkans.txt 20 | 21 | 22 | $(EXEC): $(OBJS) 23 | $(CC) $(OBJS) -o $(EXEC) $(LDFLAGS) 24 | 25 | # Για να φτιάξουμε το k08.a, τρέχουμε το make στο lib directory. 26 | $(LIB)/k08.a: 27 | $(MAKE) -C $(LIB) k08.a 28 | 29 | clean: 30 | rm -f $(OBJS) $(EXEC) 31 | 32 | run: $(EXEC) 33 | ./$(EXEC) $(ARGS) 34 | 35 | valgrind: $(EXEC) 36 | valgrind ./$(EXEC) $(ARGS) 37 | -------------------------------------------------------------------------------- /.vscode/setup-debugger.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # We start debugging via bash which then execs the debugged process (see launch.json). 4 | # This is convenient for setting arguments and redirections, but has 2 issues: 5 | # 6 | # 1. macOS/arm does not allow us to debug /bin/bash 7 | # 2. We get a debugger warning that /bin/bash is older than the source files. 8 | # 9 | # To workaround these issues we run this script before debugging which creates 10 | # a .vscode/bash symlink. This way: 11 | # 1. On macOS we can use homebrew's bash instead of the system bash 12 | # (under /opt/homebrew/bin for arm, /usr/local/bin for intel). 13 | # 2. The symlink has newer timestamp so we avoid the warning. 14 | 15 | for path in /opt/homebrew/bin /usr/local/bin /bin 16 | do 17 | if [ -x $path/bash ] 18 | then 19 | ln -sf $path/bash ./bash 20 | exit 0 21 | fi 22 | done 23 | 24 | echo Bash not found 25 | exit 1 26 | 27 | -------------------------------------------------------------------------------- /programs/ip_projects/ipli/programs/humble.ipl: -------------------------------------------------------------------------------- 1 | # Filename: humble.ipl 2 | # 3 | # Find the N-th humble numberm for a given N 4 | 5 | read N 6 | i = 0 # Number of so far found humble numbers 7 | number = 1 # Number to be checked 8 | while i < N 9 | temp = number # Keep current number safe 10 | remainder = temp % 2 11 | while remainder == 0 12 | temp = temp / 2 13 | remainder = temp % 2 14 | remainder = temp % 3 15 | while remainder == 0 16 | temp = temp / 3 17 | remainder = temp % 3 18 | remainder = temp % 5 19 | while remainder == 0 20 | temp = temp / 5 21 | remainder = temp % 5 22 | remainder = temp % 7 23 | while remainder == 0 24 | temp = temp / 7 25 | remainder = temp % 7 26 | if temp == 1 # Found new humble 27 | i = i + 1 28 | number = number + 1 # Prepare next number 29 | number = number - 1 # Cancel last increment 30 | writeln number 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## sample-project 2 | 3 | Παράδειγμα δομής ενός C project. Επιδεικνύει τη χρήση modules, Makefiles και tests. 4 | 5 | ### Παράδειγμα χρήσης 6 | 7 | ```bash 8 | cd programs/minmax 9 | 10 | make minmax 11 | ./minmax 3 -1 12 412 12 | 13 | make clean 14 | ``` 15 | 16 | ### Χρήση από το VS Code 17 | 18 | - `Ctrl-Shift-B` : compile & execute 19 | - `F5` : debug 20 | - Ρυθμίσεις στο `.vscode/settings.json` 21 | 22 | ### Project structure 23 | 24 | - `include` 25 | 26 | Κοινόχρηστα include files (μπορούν να χρησιμοποιηθούν από οποιοδήποτε πρόγραμμα ή module). 27 | 28 | - `lib` 29 | 30 | Κοινόχρηστες βιβλιοθήκες. Εκτελώντας `make` σε αυτό το directory παράγεται η βιβλιοθήκη 31 | `k08.a` η οποία περιέχει υλοποιήσεις από όλα τα ADTs. 32 | 33 | - `programs` 34 | 35 | Εκτελέσιμα προγράμματα. 36 | 37 | - `modules` 38 | 39 | Υλοποιήσεις των κοινόχρηστων modules. 40 | 41 | - `tests` 42 | 43 | Tests για κοινόχρηστα modules. -------------------------------------------------------------------------------- /programs/cat/Makefile: -------------------------------------------------------------------------------- 1 | # paths 2 | LIB = ../../lib 3 | INCLUDE = ../../include 4 | 5 | # compiler 6 | CC = gcc 7 | 8 | # Compile options. Το -I λέει στον compiler να αναζητήσει εκεί include files 9 | CFLAGS = -Wall -Werror -g -I$(INCLUDE) 10 | LDFLAGS = -lm 11 | 12 | # Αρχεία .o 13 | OBJS = cat.o io.o $(LIB)/k08.a 14 | TEST_OBJS = io_test.o io.o $(LIB)/k08.a 15 | 16 | # Το εκτελέσιμο πρόγραμμα 17 | EXEC = cat 18 | TEST = io_test 19 | 20 | # Παράμετροι για δοκιμαστική εκτέλεση 21 | ARGS = input-file 22 | 23 | all: $(EXEC) $(TEST) 24 | 25 | $(EXEC): $(OBJS) 26 | $(CC) $(OBJS) -o $(EXEC) $(LDFLAGS) 27 | 28 | $(TEST): $(TEST_OBJS) 29 | $(CC) $(TEST_OBJS) -o $(TEST) $(LDFLAGS) 30 | 31 | # Για να φτιάξουμε το k08.a, τρέχουμε το make στο lib directory. 32 | $(LIB)/k08.a: 33 | $(MAKE) -C $(LIB) k08.a 34 | 35 | clean: 36 | rm -f $(OBJS) $(TEST_OBJS) $(EXEC) $(TEST) 37 | 38 | run: $(EXEC) $(TEST) 39 | ./$(EXEC) $(ARGS) 40 | ./$(TEST) 41 | -------------------------------------------------------------------------------- /programs/iron_bank/Makefile: -------------------------------------------------------------------------------- 1 | # paths 2 | LIB = ../../lib 3 | INCLUDE = ../../include 4 | 5 | # compiler 6 | CC = gcc 7 | 8 | # Compile options. Το -I λέει στον compiler να αναζητήσει εκεί include files 9 | CFLAGS = -Wall -Werror -g -I$(INCLUDE) 10 | 11 | # Αρχεία .o 12 | OBJS = iron_bank.o customer.o $(LIB)/k08.a 13 | TEST_OBJS = customer_test.o customer.o $(LIB)/k08.a 14 | 15 | # Το εκτελέσιμο πρόγραμμα 16 | EXEC = iron_bank 17 | TEST = customer_test 18 | 19 | # Παράμετροι για δοκιμαστική εκτέλεση 20 | ARGS = 'Jon Snow' 21 | 22 | all: $(EXEC) $(TEST) 23 | 24 | $(EXEC): $(OBJS) 25 | $(CC) $(OBJS) -o $(EXEC) 26 | 27 | $(TEST): $(TEST_OBJS) 28 | $(CC) $(TEST_OBJS) -o $(TEST) $(LDFLAGS) 29 | 30 | # Για να φτιάξουμε το k08.a, τρέχουμε το make στο lib directory. 31 | $(LIB)/k08.a: 32 | $(MAKE) -C $(LIB) k08.a 33 | 34 | clean: 35 | rm -f $(OBJS) $(TEST_OBJS) $(EXEC) $(TEST) 36 | 37 | run: $(EXEC) $(TEST) 38 | ./$(EXEC) $(ARGS) 39 | ./$(TEST) -------------------------------------------------------------------------------- /programs/ip_projects/ipli/programs/factorize.ipl: -------------------------------------------------------------------------------- 1 | # Filename: factorize.ipl 2 | # 3 | # Generate random numbers y, z and find the prime 4 | # factors of the number x produced by the formula 5 | # x = ((y mod 32768) + 1) * ((z mod 32768) + ) + 1 6 | 7 | # generate random values 8 | random y 9 | y = y % 32768 10 | y = y + 1 11 | random z 12 | z = z % 32768 13 | z = 1 + z 14 | x = y * z 15 | x = x + 1 16 | writeln x # print the number to be factorized 17 | remainder = x % 2 18 | while remainder == 0 19 | writeln 2 # print 2 as a factor 20 | x = x / 2 21 | remainder = x % 2 22 | factor = 3 # check if 3, 5, 7, ... are factors 23 | factor2 = factor * factor 24 | while factor2 <= x # stop at the square root 25 | remainder = x % factor 26 | while remainder == 0 27 | writeln factor # print factor found 28 | x = x / factor 29 | remainder = x % factor 30 | factor = factor + 2 31 | factor2 = factor * factor 32 | if x != 1 33 | writeln x # print last factor 34 | -------------------------------------------------------------------------------- /programs/cat/cat.c: -------------------------------------------------------------------------------- 1 | // Υλοποίηση της εντολής cat του unix. 2 | // cat [FILE1] [FILE2] ... 3 | // 4 | // Διαβάζει και τυπώνει τα περιεχόμενα του κάθε αρχείου που δίνεται ως είσοδος 5 | // (με τη διαφορά ότι διαβάζει ολόκληρο το αρχείο πριν αρχίσει να γράφει). 6 | // Αν δε δοθεί αρχείο, ή όταν το αρχείο είναι ίσο με "-", διαβάζει από το stdin. 7 | 8 | #include 9 | 10 | #include "io.h" 11 | 12 | 13 | void process_file(char* filename) { 14 | // Διαβάζουμε το αρχείο ή την είσοδο. Το io.h module κάνει όλη τη δουλειά. 15 | Vector vec = strcmp(filename, "-") == 0 16 | ? io_read_stream_as_vector(stdin) 17 | : io_read_file_as_vector(filename); 18 | 19 | if(vec == NULL) { 20 | fprintf(stderr, "cat: %s: cannot read file\n", filename); 21 | return; 22 | } 23 | 24 | io_write_vector_to_stream(stdout, vec); 25 | vector_destroy(vec); 26 | } 27 | 28 | int main(int argc, char* argv[]) { 29 | for(int i = 1; i < argc; i++) 30 | process_file(argv[i]); 31 | 32 | // χωρίς κανένα αρχείο διαβάζουμε από την είσοδο 33 | if(argc == 1) 34 | process_file("-"); 35 | } 36 | -------------------------------------------------------------------------------- /programs/ip_projects/ipli/programs/selectsort.ipl: -------------------------------------------------------------------------------- 1 | # Filename: selectsort.ipl 2 | # 3 | # Sort in ascending order an array with random 4 | # elements, using the selection sort algorithm 5 | 6 | # initialize 7 | argument size args 8 | if args == 0 9 | n = 1000 10 | else 11 | argument 1 n 12 | new a[n] 13 | m = n * 9 14 | m = m / 10 15 | l = n / 10 16 | while i != n 17 | random a[i] 18 | a[i] = a[i] % m 19 | a[i] = a[i] + l 20 | i = i + 1 21 | # print 22 | i = 0 23 | while i != n 24 | j = i + 1 25 | m = j % 20 26 | if m == 0 27 | writeln a[i] 28 | else 29 | if j == n 30 | writeln a[i] 31 | else 32 | write a[i] 33 | i = j 34 | # sort (using selectsort) 35 | i = 0 36 | while i != n 37 | min = i 38 | j = i + 1 39 | while j != n 40 | if a[j] < a[min] 41 | min = j 42 | j = j + 1 43 | t = a[i] 44 | a[i] = a[min] 45 | a[min] = t 46 | i = i + 1 47 | # print 48 | writeln n 49 | i = 0 50 | while i != n 51 | j = i + 1 52 | m = j % 20 53 | if m == 0 54 | writeln a[i] 55 | else 56 | if j == n 57 | writeln a[i] 58 | else 59 | write a[i] 60 | i = j 61 | free a 62 | -------------------------------------------------------------------------------- /programs/iron_bank/customer.h: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////// 2 | // 3 | // customer 4 | // 5 | // Module που χειρίζεται εγγραφές πελατών της Iron Bank 6 | // 7 | //////////////////////////////////////////////////////////////////////// 8 | 9 | #pragma once // #include το πολύ μία φορά 10 | 11 | 12 | typedef char* String; 13 | 14 | // Στοιχεία πελάτη 15 | 16 | typedef struct { 17 | int balance; // Το balance σε gold dragons. Αρνητικό για τους οφειλέτες. 18 | String name; // Όνομα 19 | } Customer; 20 | 21 | 22 | // Διαβάζει τους πελάτες από το αρχείο filename και τους επιστρέφει 23 | // σε έναν πίνακα ταξινομημένους κατά balance. Στο ret_size επιστρέφεται 24 | // ο αριθμός των πελατών που διαβάστηκαν. 25 | 26 | Customer* customer_read_from_file(String filename, int* ret_size); 27 | 28 | 29 | // Αναζητεί τον πελάτη με όνομα name στον πίνακα customers μεγέθους size. 30 | // Επιστρέφει τον πελάτη αν βρεθεί, αλλιώς NULL. 31 | 32 | Customer* customer_find(Customer* customers, int size, String name); 33 | 34 | 35 | // Εκτυπώνει τα στοιχεία του πελάτη customer 36 | 37 | void customer_print(Customer* customer); 38 | -------------------------------------------------------------------------------- /modules/stats_alt.c: -------------------------------------------------------------------------------- 1 | // stats_alt.c - Εναλλακτική υλοποίηση του stats module 2 | 3 | #include 4 | 5 | #include "stats.h" 6 | 7 | // Επιστρέφει true αν value <= array[i] για κάθε i 8 | 9 | bool smaller_than_all(int value, int array[], int size) { 10 | for(int i = 0; i < size; i++) 11 | if(value > array[i]) 12 | return false; 13 | return true; 14 | } 15 | 16 | // Επιστρέφει true αν value >= array[i] για κάθε i 17 | 18 | bool larger_than_all(int value, int array[], int size) { 19 | for(int i = 0; i < size; i++) 20 | if(value < array[i]) 21 | return false; 22 | return true; 23 | } 24 | 25 | int stats_find_min(int array[], int size) { 26 | for(int i = 0; i < size; i++) 27 | if(smaller_than_all(array[i], array, size)) 28 | return array[i]; 29 | 30 | return INT_MAX; // εδώ φτάνουμε μόνο σε περίπτωση κενού array 31 | } 32 | 33 | int stats_find_max(int array[], int size) { 34 | for(int i = 0; i < size; i++) 35 | if(larger_than_all(array[i], array, size)) 36 | return array[i]; 37 | 38 | return INT_MIN; // εδώ φτάνουμε μόνο σε περίπτωση κενού array 39 | } -------------------------------------------------------------------------------- /programs/cat/input-file: -------------------------------------------------------------------------------- 1 | Going To California 2 | Robert Plant / Jimmy Page 3 | Album: Led Zeppelin IV 4 | 5 | Spent my days with a woman unkind, Smoked my stuff and drank all my wine. 6 | Made up my mind to make a new start, Going To California with an aching in my heart. 7 | Someone told me there's a girl out there with love in her eyes and flowers in her hair. 8 | Took my chances on a big jet plane, never let them tell you that they're all the same. 9 | The sea was red and the sky was grey, wondered how tomorrow could ever follow today. 10 | The mountains and the canyons started to tremble and shake 11 | as the children of the sun began to awake. 12 | 13 | Seems that the wrath of the Gods 14 | Got a punch on the nose and it started to flow; 15 | I think I might be sinking. 16 | Throw me a line if I reach it in time 17 | I'll meet you up there where the path 18 | Runs straight and high. 19 | 20 | To find a queen without a king, 21 | They say she plays guitar and cries and sings... la la la 22 | Ride a white mare in the footsteps of dawn 23 | Tryin' to find a woman who's never, never, never been born. 24 | Standing on a hill in my mountain of dreams, 25 | Telling myself it's not as hard, hard, hard as it seems. 26 | -------------------------------------------------------------------------------- /programs/iron_bank/customer_test.c: -------------------------------------------------------------------------------- 1 | // 2 | // Unit tests για το customer.h module 3 | // 4 | 5 | #include 6 | 7 | #include "acutest.h" // Απλή βιβλιοθήκη για unit testing 8 | 9 | #include "customer.h" 10 | 11 | 12 | void test_customer_read_from_file(void) { 13 | int size; 14 | Customer* customers = customer_read_from_file("customers.csv", &size); 15 | 16 | TEST_ASSERT(customers != NULL); 17 | TEST_ASSERT(size == 162); 18 | 19 | // έλεγχος ότι ο πίνακας είναι ταξινομημένος 20 | for (int i = 1; i < size; i++) 21 | TEST_ASSERT(customers[i-1].balance <= customers[i].balance); 22 | 23 | } 24 | 25 | void test_customer_find(void) { 26 | int size; 27 | Customer* customers = customer_read_from_file("customers.csv", &size); 28 | 29 | Customer* customer = customer_find(customers, size, "Jon Snow"); 30 | 31 | TEST_ASSERT(customer != NULL); 32 | TEST_ASSERT(strcmp(customer->name, "Jon Snow") == 0); 33 | TEST_ASSERT(customer->balance == -687); 34 | } 35 | 36 | // Λίστα με όλα τα tests προς εκτέλεση 37 | TEST_LIST = { 38 | { "test_customer_read_from_file", test_customer_read_from_file }, 39 | { "test_customer_find", test_customer_find }, 40 | { NULL, NULL } // τερματίζουμε τη λίστα με NULL 41 | }; 42 | -------------------------------------------------------------------------------- /programs/cat/io.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "ADTVector.h" 6 | 7 | // Module που διευκολύνει το input/output ολόκληρων αρχείων. Ενα τέτοιο module θα μπορούσε να είναι 8 | // κοινόχρηστο ώστε να χρησιμοποιείται από διαφορετικά προγράμματα ή άλλα modules. 9 | 10 | // Διαβάζει τα περιεχόμενα του stream και τα επιστρέφει ως ένα Vector που περιέχει 11 | // ένα στοιχείο για κάθε γραμμή του αρχείου (χωρίς την αλλαγή γραμμής "\n") 12 | 13 | Vector io_read_stream_as_vector(FILE* stream); 14 | 15 | // Διαβάζει τα περιεχόμενα του αρχείου filename και τα επιστρέφει ως ένα Vector που περιέχει 16 | // ένα στοιχείο για κάθε γραμμή του αρχείου (χωρίς την αλλαγή γραμμής "\n") 17 | 18 | Vector io_read_file_as_vector(char* filename); 19 | 20 | // Γράφει τα περιεχόμενα του string Vector vec στο stream, με αλλαγή γραμμής μετά από 21 | // κάθε στοιχείο. Επιστρέφει τον αριθμό των χαρακτήρων που γράφτηκαν. 22 | 23 | int io_write_vector_to_stream(FILE* stream, Vector vec); 24 | 25 | // Γράφει τα περιεχόμενα του string Vector vec στο αρχείο filename, με αλλαγή γραμμής 26 | // μετά από κάθε στοιχείο. Επιστρέφει τον αριθμό των χαρακτήρων που γράφτηκαν. 27 | 28 | int io_write_vector_to_file(char* filename, Vector vec); 29 | -------------------------------------------------------------------------------- /.vscode/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Helper script for executing programs in tasks/debugger with flexibility for 4 | # passing arguments, input/output redirection, etc. 5 | 6 | if [ -z "$CODE_DEBUG" ] 7 | then 8 | echo " * Executing: $CODE_RUN" 9 | echo 10 | 11 | # If the child process launched by eval $CODE_RUN below segfaults, bash by 12 | # default prints the current script's (run.sh) full path and line number, 13 | # before the "Segmentation fault" message. We can make the error message cleaner 14 | # by using the "trap" below. 15 | # 16 | # NOTE1: the trap handler is not really executed since the segfault happens 17 | # in a child process. However the presence of the trap somehow causes bash 18 | # not to print the script name, so the message becomes cleaner. We still 19 | # echo a relevant message, just in case the trap does run on some system. 20 | # 21 | # NOTE2: SIGABRT does the same for asserts 22 | # 23 | trap 'echo "Segmentation fault"' SIGSEGV SIGABRT 24 | 25 | # we use "eval" to allow for input/output redirections 26 | eval $CODE_RUN 27 | 28 | else 29 | echo " * Executing: $CODE_DEBUG" 30 | echo 31 | 32 | # When debugging we exec in the same process being debugged 33 | eval "exec $CODE_DEBUG" 34 | fi 35 | 36 | -------------------------------------------------------------------------------- /tests/stats_test.c: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////// 2 | // 3 | // Unit tests για το stats module 4 | // Οποιαδήποτε υλοποίηση οφείλει να περνάει όλα τα tests. 5 | // 6 | ////////////////////////////////////////////////////////////////// 7 | 8 | #include "acutest.h" // Απλή βιβλιοθήκη για unit testing 9 | 10 | #include "stats.h" 11 | 12 | 13 | void test_find_min(void) { 14 | int array[] = { 3, 1, -1, 50 }; 15 | 16 | TEST_ASSERT(stats_find_min(array, 4) == -1); 17 | TEST_ASSERT(stats_find_min(array, 3) == -1); 18 | TEST_ASSERT(stats_find_min(array, 2) == 1); 19 | TEST_ASSERT(stats_find_min(array, 1) == 3); 20 | TEST_ASSERT(stats_find_min(array, 0) == INT_MAX); 21 | } 22 | 23 | void test_find_max(void) { 24 | int array[] = { 3, 1, -1, 50 }; 25 | 26 | TEST_ASSERT(stats_find_max(array, 4) == 50); 27 | TEST_ASSERT(stats_find_max(array, 3) == 3); 28 | TEST_ASSERT(stats_find_max(array, 2) == 3); 29 | TEST_ASSERT(stats_find_max(array, 1) == 3); 30 | TEST_ASSERT(stats_find_max(array, 0) == INT_MIN); 31 | } 32 | 33 | // Λίστα με όλα τα tests προς εκτέλεση 34 | TEST_LIST = { 35 | { "find_min", test_find_min }, 36 | { "find_max", test_find_max }, 37 | { NULL, NULL } // τερματίζουμε τη λίστα με NULL 38 | }; -------------------------------------------------------------------------------- /programs/cat/io_test.c: -------------------------------------------------------------------------------- 1 | // 2 | // Unit tests για το io.h module 3 | // 4 | 5 | #include 6 | #include 7 | 8 | #include "acutest.h" // Απλή βιβλιοθήκη για unit testing 9 | 10 | #include "io.h" 11 | 12 | 13 | void test_io_write_vector_to_file(void) { 14 | Vector vec = vector_create(0, NULL); 15 | vector_insert_last(vec, "foo"); 16 | vector_insert_last(vec, "bar"); 17 | 18 | // δημιουργία ενός προσωρινού αρχείου 19 | TEST_ASSERT(io_write_vector_to_file("io_tests_temp", vec) > 0); 20 | 21 | vector_destroy(vec); 22 | } 23 | 24 | void test_io_read_file_as_vector(void) { 25 | Vector vec = io_read_file_as_vector("io_tests_temp"); 26 | TEST_ASSERT(vec != NULL); 27 | 28 | TEST_ASSERT(strcmp(vector_get_at(vec, 0), "foo") == 0); 29 | TEST_ASSERT(strcmp(vector_get_at(vec, 1), "bar") == 0); 30 | 31 | // διαγραφή του προσωρινού αρχείου 32 | remove("io_tests_temp"); 33 | 34 | TEST_ASSERT(io_read_file_as_vector("a_file_that_doesnt_exist") == NULL); 35 | 36 | vector_destroy(vec); 37 | } 38 | 39 | 40 | // Λίστα με όλα τα tests προς εκτέλεση 41 | TEST_LIST = { 42 | { "io_write_vector_to_file", test_io_write_vector_to_file }, 43 | { "io_read_file_as_vector", test_io_read_file_as_vector }, 44 | { NULL, NULL } // τερματίζουμε τη λίστα με NULL 45 | }; 46 | -------------------------------------------------------------------------------- /programs/ip_projects/mapcol/coloring.h: -------------------------------------------------------------------------------- 1 | 2 | #include "ADTSet.h" 3 | #include "ADTList.h" 4 | #include "ADTMap.h" 5 | 6 | typedef char* String; 7 | 8 | // Πληροφορίες για μία χώρα 9 | 10 | struct country { 11 | String color; // χρώμα που έχει ανατεθεί (NULL αν είναι αχρωμάτιστη) 12 | Set avail_colors; // διαθέσιμα χρώματα 13 | List neighbors; // γειτονες 14 | }; 15 | typedef struct country* Country; 16 | 17 | // Ενα Coloring είναι ένα Map: <όνομα χώρας> (String) => "πληροφορίες για τη χώρα" (Country) 18 | // πχ "Greece" => <πληροφορίες για την ελλάδα> 19 | // Κάνουμε typedef απλά για ευκολία, ώστε όταν γράφουμε "Coloring" να καταλαβαίνουμε τι τύπου Map εννοούμε. 20 | 21 | typedef Map Coloring; 22 | 23 | 24 | // Συνάρτηση που παίρνει έναν μερικό χρωματισμό και τον ολοκληρώνει! 25 | // - Αν υπάρχει λύση, την αποθηκεύει στο coloring και επιστρέφει true 26 | // - Αν ΔΕΝ υπάρχει λύση, επιστρέφει false, και αφηνει το coloring στην __ίδια κατάσταση__ που ήταν πριν! 27 | 28 | bool tzini(Coloring coloring); 29 | 30 | // Διαβάζει και επιστρέφει ένα χρωματισμό από την είσοδο 31 | 32 | Coloring read_input(); 33 | 34 | // Τυπώνει ένα χρωματισμό 35 | 36 | void print_coloring(Coloring coloring); 37 | 38 | // Ελέγχει αν ο χρωματισμός είναι έγκυρος 39 | 40 | bool test_coloring(Coloring coloring); 41 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Το Makefile αυτό βρίσκεται στο root ολόκληρου του project και χρησιμεύει για 2 | # να κάνουμε εύκολα compile πολλά τμήματα του project μαζί. Το Makefile αυτό 3 | # καλεί το make στα διάφορα directories ως 4 | # $(MAKE) -C 5 | # το οποίο είναι ισοδύναμο με το να τρέξουμε make μέσα στο directory 6 | 7 | # Ολα τα directories μέσα στο programs τα οποία περιέχουν Makefile 8 | PROGRAMS = $(patsubst programs/%/Makefile, %, $(wildcard programs/*/Makefile)) 9 | 10 | # Compile: όλα, προγράμματα, tests 11 | all: programs tests 12 | 13 | # Η παρακάτω γραμμή δημιουργεί ένα target programs- για οποιοδήποτε . Η μεταβλητή $* περιέχει το "foo" 14 | programs-%: 15 | $(MAKE) -C programs/$* 16 | 17 | programs: $(addprefix programs-, $(PROGRAMS)) # depend στο programs- για κάθε στοιχείο του PROGRAMS 18 | 19 | tests: 20 | $(MAKE) -C tests all 21 | 22 | # Εκτέλεση: όλα, προγράμματα, tests 23 | run: run-tests run-programs 24 | 25 | run-programs-%: 26 | $(MAKE) -C programs/$* run 27 | 28 | run-programs: $(addprefix run-programs-, $(PROGRAMS)) 29 | 30 | run-tests: 31 | $(MAKE) -C tests run 32 | 33 | # Εκκαθάριση 34 | clean-programs-%: 35 | $(MAKE) -C programs/$* clean 36 | 37 | clean: $(addprefix clean-programs-, $(PROGRAMS)) 38 | $(MAKE) -C tests clean 39 | $(MAKE) -C lib clean -------------------------------------------------------------------------------- /programs/ip_projects/ipli/ipli.c: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////// 2 | // 3 | // ipli 4 | // 5 | // Υλοποίηση ενός interpreter για τη γλώσσα IPL 6 | // Πληροφορίες στο iphw2021_4.pdf 7 | // 8 | /////////////////////////////////////////////////////////////// 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "parser.h" 16 | #include "interpreter.h" 17 | 18 | int main(int argc, char* argv[]) { 19 | srand(time(NULL)); 20 | 21 | int first_arg = 1; 22 | bool verbose = false; 23 | if(strcmp(argv[first_arg], "-v") == 0) { 24 | verbose = true; 25 | first_arg++; 26 | } 27 | 28 | // read source 29 | String filename = argv[first_arg]; 30 | FILE* file = fopen(filename, "r"); 31 | if(!file) { 32 | fprintf(stderr, "invalid file\n"); 33 | return -1; 34 | } 35 | Vector source = vector_create(0, NULL); 36 | 37 | int N = 100; 38 | char line[N]; 39 | 40 | while(fgets(line, N, file) != NULL) 41 | vector_insert_last(source, strdup(line)); 42 | fclose(file); 43 | 44 | // collect args 45 | Vector args = vector_create(0, NULL); 46 | for(int i = first_arg + 1; i < argc; i++) 47 | vector_insert_last(args, argv[i]); 48 | 49 | // create program and run 50 | Runtime runtime = parser_create_runtime(source, args, verbose); 51 | interpreter_run(runtime); 52 | } -------------------------------------------------------------------------------- /programs/ip_projects/ipli/programs/matrmult.ipl: -------------------------------------------------------------------------------- 1 | # Filename: matrmult.ipl 2 | # 3 | # Compute the product of an N x L matrix with an L x M matrix 4 | 5 | argument 1 N 6 | argument 2 L 7 | argument 3 M 8 | NL = N * L 9 | LM = L * M 10 | NM = N * M 11 | new a[NL] 12 | new b[LM] 13 | new c[NM] 14 | # random elements on first array 15 | while i < NL 16 | random ele 17 | ele = ele % 100 18 | a[i] = ele 19 | i = i + 1 20 | # print element 21 | m = i % L 22 | if m != 0 23 | write ele 24 | else 25 | writeln ele 26 | # random elements on second array 27 | while j < LM 28 | random ele 29 | ele = ele % 100 30 | b[j] = ele 31 | j = j + 1 32 | # print element 33 | mod = j % M 34 | if mod != 0 35 | write ele 36 | else 37 | writeln ele 38 | # compute and print multiplication 39 | i = 0 40 | while i < N 41 | j = 0 42 | while j < M 43 | k = 0 44 | # index for third (result) array 45 | z = i * M 46 | z = z + j 47 | # loop to compute sum 48 | while k < L 49 | # index for first array 50 | x = i * L 51 | x = x + k 52 | # index for second array 53 | y = k * M 54 | y = y + j 55 | # multiply and add 56 | mul = a[x] * b[y] 57 | c[z] = c[z] + mul 58 | k = k + 1 59 | j = j + 1 60 | # print element 61 | mod = j % M 62 | if mod != 0 63 | write c[z] 64 | else 65 | writeln c[z] 66 | i = i + 1 67 | free a 68 | free b 69 | free c 70 | -------------------------------------------------------------------------------- /programs/ip_projects/ipli/programs/nqueens.ipl: -------------------------------------------------------------------------------- 1 | # Filename: nqueens.ipl 2 | # 3 | # Find one solution of the N-queens problem 4 | 5 | argument size args 6 | if args > 0 7 | argument 1 n 8 | else 9 | n = 8 10 | new q[n] 11 | # starting conditions 12 | i = 0 - 1 13 | ok_so_far = 1 14 | while 0 == 0 15 | # go for next variable 16 | if ok_so_far != 0 17 | i = i + 1 18 | # check if finished 19 | if i == n 20 | break 21 | # try first value 22 | q[i] = 0 23 | # try next value in same variable 24 | else 25 | q[i] = q[i] + 1 26 | # exhausted values 27 | if q[i] == n 28 | # check if failed 29 | if i == 0 30 | break 31 | # backtrack to previous variable 32 | i = i - 1 33 | continue 34 | ok_so_far = 0 35 | # check previous queens 36 | j = 0 37 | while j != i 38 | # check same column 39 | if q[i] == q[j] 40 | continue 2 41 | # check same forward diagonal 42 | diag_i = q[i] - i 43 | diag_j = q[j] - j 44 | if diag_i == diag_j 45 | continue 2 46 | # check same backward diagonal 47 | diag_i = q[i] + i 48 | diag_j = q[j] + j 49 | if diag_i == diag_j 50 | continue 2 51 | j = j + 1 52 | # no collisions! 53 | ok_so_far = 1 54 | # if solved print the solution 55 | if ok_so_far != 0 56 | n1 = n - 1 57 | i = 0 58 | while i != n1 59 | x = q[i] + 1 60 | write x 61 | i = i + 1 62 | x = q[i] + 1 63 | writeln x 64 | free q 65 | -------------------------------------------------------------------------------- /tests/Makefile: -------------------------------------------------------------------------------- 1 | # paths 2 | MODULES = ../modules 3 | INCLUDE = ../include 4 | 5 | # compiler 6 | CC = gcc 7 | 8 | # Compile options. Το -I λέει στον compiler να αναζητήσει εκεί include files 9 | CFLAGS = -Wall -Werror -g -I$(INCLUDE) 10 | 11 | # Για test coverage 12 | ifeq ($(MAKECMDGOALS), coverage) 13 | CFLAGS += --coverage 14 | LDFLAGS += --coverage 15 | endif 16 | 17 | # Αρχεία .o 18 | OBJS1 = stats_test.o $(MODULES)/stats.o 19 | OBJS2 = stats_test.o $(MODULES)/stats_alt.o 20 | 21 | # Το εκτελέσιμο πρόγραμμα 22 | EXEC1 = stats_test 23 | EXEC2 = stats_alt_test 24 | 25 | # Παράμετροι για δοκιμαστική εκτέλεση 26 | ARGS1 = 27 | ARGS2 = 28 | 29 | all: $(EXEC1) $(EXEC2) 30 | 31 | $(EXEC1): $(OBJS1) 32 | $(CC) $(OBJS1) -o $(EXEC1) $(LDFLAGS) 33 | 34 | $(EXEC2): $(OBJS2) 35 | $(CC) $(OBJS2) -o $(EXEC2) $(LDFLAGS) 36 | 37 | clean: 38 | rm -f $(OBJS1) $(OBJS2) $(EXEC1) $(EXEC2) 39 | rm -f ../**/*.gcda ../**/*.gcno 40 | rm -rf coverage 41 | 42 | coverage: clean all run 43 | @mkdir -p coverage 44 | lcov --capture --directory=.. --output-file coverage/lcov.info 45 | cd coverage && genhtml lcov.info 46 | @echo "To see the report open the file below in your brower:" 47 | @echo "$$PWD/coverage/index.html" 48 | 49 | valgrind: all 50 | valgrind ./$(EXEC1) -E $(ARGS1) 51 | valgrind ./$(EXEC2) -E $(ARGS2) 52 | 53 | run: all 54 | ./$(EXEC1) $(ARGS1) 55 | ./$(EXEC2) $(ARGS2) -------------------------------------------------------------------------------- /programs/iron_bank/iron_bank.c: -------------------------------------------------------------------------------- 1 | // iron_bank 2 | // Διαχείριση πελατών της Iron Bank of Braavos 3 | // 4 | // Το πρόγραμμα αυτό τυπώνει τους μεγαλύτερους οφειλέτες και πιστωτές 5 | // της τράπεζας, καθώς και το υπόλοιπο των πελατών που περνιούνται σαν 6 | // όρισμα. 7 | // 8 | // 9 | // ΠΡΟΣΟΧΗ 10 | // 11 | // Το πρόγραμμα πιθανότατα έχει κάποιο bug. Οι χρήστες αναφέρουν ότι για 12 | // κάποιους πελάτες δουλεύει, αλλά για τον "Tyrion Lannister" έχει πρόβλημα. 13 | 14 | #include 15 | 16 | #include "customer.h" 17 | 18 | 19 | void print_top_borrowers(Customer* customers, int size, int n) { 20 | printf("\nTop borrowers\n--------------\n"); 21 | 22 | for (int i = 0; i < n && i < size; i++) 23 | customer_print(&customers[i]); 24 | } 25 | 26 | void print_top_lenders(Customer* customers, int size, int n) { 27 | printf("\nTop lenders\n--------------\n"); 28 | 29 | for (int i = size-1; i > size-1-n && i > 0; i--) 30 | customer_print(&customers[i]); 31 | } 32 | 33 | int main(int argc, char* argv[]) { 34 | // Διαβάζουμε τη βάση δεδομένων των πελατών 35 | int size; 36 | Customer* customers = customer_read_from_file("customers.csv", &size); 37 | 38 | // Εκτύπωση των μεγαλύτερων 39 | print_top_borrowers(customers, size, 5); 40 | print_top_lenders(customers, size, 5); 41 | 42 | // Αν δοθούν ορίσματα γίνεται αναζήτηση χρηστών με τα ονόματα αυτά 43 | printf("\nSearch results\n--------------\n"); 44 | 45 | for (int i = 1; i < argc; i++) { 46 | Customer* customer = customer_find(customers, size, argv[i]); 47 | if (customer) 48 | customer_print(customer); 49 | else 50 | printf("%s not found\n", argv[i]); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /programs/cat/io.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "io.h" 5 | 6 | 7 | Vector io_read_stream_as_vector(FILE* stream) { 8 | // TODO: θεωρούμε ότι κάθε γραμμή έχει max 500 χαρακτήρες. Πώς μπορούμε να αφαιρέσουμε αυτόν τον περιορισμό; 9 | int max_len = 500; 10 | char line[max_len]; 11 | 12 | Vector vec = vector_create(0, free); // αυτόματο free για κάθε στοιχείο που αφαιρείται 13 | 14 | while(fgets(line, max_len, stream)) { 15 | // αφαίρεση του newline, αν υπάρχει 16 | char* newline = strchr(line, '\n'); 17 | if(newline != NULL) 18 | *newline = '\0'; 19 | 20 | // προσθήκη στο vector 21 | vector_insert_last(vec, strdup(line)); 22 | } 23 | 24 | return vec; 25 | } 26 | 27 | Vector io_read_file_as_vector(char* filename) { 28 | // απλά ανοίγουμε το αρχείο και καλούμε την io_read_stream_as_vector 29 | FILE* file = fopen(filename, "r"); 30 | if(file == NULL) 31 | return NULL; 32 | 33 | Vector vec = io_read_stream_as_vector(file); 34 | fclose(file); 35 | 36 | return vec; 37 | } 38 | 39 | int io_write_vector_to_stream(FILE* stream, Vector vec) { 40 | int written = 0; 41 | int size = vector_size(vec); 42 | 43 | // διασχίζουμε το vector και τυπώνουμε κάθε γραμμή 44 | for(int i = 0; i < size; i++) { 45 | char* line = vector_get_at(vec, i); 46 | written += fprintf(stream, "%s\n", line); 47 | } 48 | 49 | return written; 50 | } 51 | 52 | int io_write_vector_to_file(char* filename, Vector vec) { 53 | // απλά ανοίγουμε το αρχείο και καλούμε την io_write_vector_to_stream 54 | FILE* file = fopen(filename, "w"); 55 | if(file == NULL) 56 | return 0; 57 | 58 | int written = io_write_vector_to_stream(file, vec); 59 | fclose(file); 60 | 61 | return written; 62 | } 63 | -------------------------------------------------------------------------------- /include/ADTPriorityQueue.h: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////// 2 | // 3 | // ADT Priority Queue 4 | // 5 | // Abstract ουρά προτεραιότητας. Σε κάθε remove αφαιρείται το 6 | // μεγαλύτερο στοιχείο (με βάση τη συνάρτηση compare). 7 | // 8 | /////////////////////////////////////////////////////////////////// 9 | 10 | #pragma once // #include το πολύ μία φορά 11 | 12 | #include "common_types.h" 13 | #include "ADTVector.h" 14 | 15 | 16 | // Μία ουρά προτεραιότητας αναπαριστάται από τον τύπο PriorityQueue 17 | 18 | typedef struct priority_queue* PriorityQueue; 19 | 20 | 21 | // Δημιουργεί και επιστρέφει μια νέα ουρά προτεραιότητας, της οποίας τα στοιχεία συγκρίνονται με βάση τη συνάρτηση compare. 22 | // Αν destroy_value != NULL, τότε καλείται destroy_value(value) κάθε φορά που αφαιρείται ένα στοιχείο. 23 | // Αν values != NULL, τότε η ουρά αρχικοποιείται με τα στοιχεία του Vector values. 24 | 25 | PriorityQueue pqueue_create(CompareFunc compare, DestroyFunc destroy_value, Vector values); 26 | 27 | // Επιστρέφει τον αριθμό στοιχείων που περιέχει η ουρά pqueue 28 | 29 | int pqueue_size(PriorityQueue pqueue); 30 | 31 | // Επιστρέφει το μεγαλύτερο στοιχείο της ουράς (μη ορισμένο αποτέλεσμα αν η ουρά είναι κενή) 32 | 33 | Pointer pqueue_max(PriorityQueue pqueue); 34 | 35 | // Προσθέτει την τιμή value στην ουρά pqueue. 36 | 37 | void pqueue_insert(PriorityQueue pqueue, Pointer value); 38 | 39 | // Αφαιρεί την μεγαλύτερη τιμή της ουράς (μη ορισμένο αποτέλεσμα αν η ουρά είναι κενή) 40 | 41 | void pqueue_remove_max(PriorityQueue pqueue); 42 | 43 | // Αλλάζει τη συνάρτηση που καλείται σε κάθε αφαίρεση/αντικατάσταση στοιχείου σε 44 | // destroy_value. Επιστρέφει την προηγούμενη τιμή της συνάρτησης. 45 | 46 | DestroyFunc pqueue_set_destroy_value(PriorityQueue pqueue, DestroyFunc destroy_value); 47 | 48 | // Ελευθερώνει όλη τη μνήμη που δεσμεύει η ουρά pqueue. 49 | // Οποιαδήποτε λειτουργία πάνω στη ουρά μετά το destroy είναι μη ορισμένη. 50 | 51 | void pqueue_destroy(PriorityQueue pqueue); 52 | -------------------------------------------------------------------------------- /include/ADTStack.h: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////// 2 | // 3 | // ADT Stack 4 | // 5 | // Abstract στοίβα. Λειτουργία "First In Last Out", δηλαδή σε κάθε 6 | // remove αφαιρείται το τελευταίο στοιχείο που έγινε insert. 7 | // 8 | /////////////////////////////////////////////////////////////////// 9 | 10 | #pragma once // #include το πολύ μία φορά 11 | 12 | #include "common_types.h" 13 | 14 | 15 | // Μία στοίβα αναπαριστάται από τον τύπο Stack. Ο χρήστης δε χρειάζεται να γνωρίζει το περιεχόμενο 16 | // του τύπου αυτού, απλά χρησιμοποιεί τις συναρτήσεις stack_ που δέχονται και επιστρέφουν Stack. 17 | // 18 | // Ο τύπος Stack ορίζεται ως pointer στο "struct stack" του οποίου το περιεχόμενο είναι άγνωστο 19 | // (incomplete struct), και εξαρτάται από την υλοποίηση του ADT Stack. 20 | // 21 | typedef struct stack* Stack; 22 | 23 | 24 | // Δημιουργεί και επιστρέφει μια νέα στοίβα. 25 | // Αν destroy_value != NULL, τότε καλείται destroy_value(value) κάθε φορά που αφαιρείται ένα στοιχείο. 26 | 27 | Stack stack_create(DestroyFunc destroy_value); 28 | 29 | // Επιστρέφει τον αριθμό στοιχείων που περιέχει η στοίβα stack 30 | 31 | int stack_size(Stack stack); 32 | 33 | // Επιστρέφει το στοιχείο στην κορυφή της στοίβας (μη ορισμένο αποτέλεσμα αν η στοίβα είναι κενή) 34 | 35 | Pointer stack_top(Stack stack); 36 | 37 | // Προσθέτει την τιμή value στην κορυφή της στοίβας stack. 38 | 39 | void stack_insert_top(Stack stack, Pointer value); 40 | 41 | // Αφαιρεί την τιμή στην κορυφή της στοίβας (μη ορισμένο αποτέλεσμα αν η στοίβα είναι κενή) 42 | 43 | void stack_remove_top(Stack stack); 44 | 45 | // Αλλάζει τη συνάρτηση που καλείται σε κάθε αφαίρεση/αντικατάσταση στοιχείου σε 46 | // destroy_value. Επιστρέφει την προηγούμενη τιμή της συνάρτησης. 47 | 48 | DestroyFunc stack_set_destroy_value(Stack stack, DestroyFunc destroy_value); 49 | 50 | // Ελευθερώνει όλη τη μνήμη που δεσμεύει η στοιβα stack. 51 | // Οποιαδήποτε λειτουργία πάνω στη στοίβα μετά το destroy είναι μη ορισμένη. 52 | 53 | void stack_destroy(Stack stack); 54 | -------------------------------------------------------------------------------- /include/ADTQueue.h: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////// 2 | // 3 | // ADT Queue 4 | // 5 | // Abstract ουρά. Λειτουργία "First In First Out", δηλαδή σε κάθε 6 | // remove αφαιρείται το παλιότερο στοιχείο που έγινε insert. 7 | // 8 | /////////////////////////////////////////////////////////////////// 9 | 10 | #pragma once // #include το πολύ μία φορά 11 | 12 | #include "common_types.h" 13 | 14 | 15 | // Μία ουρά αναπαριστάται από τον τύπο Queue. Ο χρήστης δε χρειάζεται να γνωρίζει το περιεχόμενο 16 | // του τύπου αυτού, απλά χρησιμοποιεί τις συναρτήσεις queue_ που δέχονται και επιστρέφουν Queue. 17 | // 18 | // Ο τύπος Queue ορίζεται ως pointer στο "struct queue" του οποίου το περιεχόμενο είναι άγνωστο 19 | // (incomplete struct), και εξαρτάται από την υλοποίηση του ADT Queue. 20 | // 21 | typedef struct queue* Queue; 22 | 23 | 24 | // Δημιουργεί και επιστρέφει μια νέα ουρά. 25 | // Αν destroy_value != NULL, τότε καλείται destroy_value(value) κάθε φορά που αφαιρείται ένα στοιχείο. 26 | 27 | Queue queue_create(DestroyFunc destroy_value); 28 | 29 | // Επιστρέφει τον αριθμό στοιχείων που περιέχει η ουρά queue 30 | 31 | int queue_size(Queue queue); 32 | 33 | // Επιστρέφει το στοιχείο στο μπροστινό μέρος της ουράς (μη ορισμένο αποτέλεσμα αν η ουρά είναι κενή) 34 | 35 | Pointer queue_front(Queue queue); 36 | 37 | // Επιστρέφει το στοιχείο στο πίσω μέρος της ουράς (μη ορισμένο αποτέλεσμα αν η ουρά είναι κενή) 38 | 39 | Pointer queue_back(Queue queue); 40 | 41 | // Προσθέτει την τιμή value στo πίσω μέρος της ουράς queue. 42 | 43 | void queue_insert_back(Queue queue, Pointer value); 44 | 45 | // Αφαιρεί την τιμή στο μπροστά μέρος της ουράς (μη ορισμένο αποτέλεσμα αν η ουρά είναι κενή) 46 | 47 | void queue_remove_front(Queue queue); 48 | 49 | // Αλλάζει τη συνάρτηση που καλείται σε κάθε αφαίρεση/αντικατάσταση στοιχείου σε 50 | // destroy_value. Επιστρέφει την προηγούμενη τιμή της συνάρτησης. 51 | 52 | DestroyFunc queue_set_destroy_value(Queue queue, DestroyFunc destroy_value); 53 | 54 | // Ελευθερώνει όλη τη μνήμη που δεσμεύει η ουρά queue. 55 | // Οποιαδήποτε λειτουργία πάνω στη ουρά μετά το destroy είναι μη ορισμένη. 56 | 57 | void queue_destroy(Queue queue); 58 | -------------------------------------------------------------------------------- /programs/iron_bank/customer.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "customer.h" 7 | 8 | #define MAX_ARRAY_SIZE 1000 9 | #define MAX_NAME_SIZE 100 10 | 11 | 12 | // Σύγκριση πελατών με βάση το balance τους 13 | 14 | static int compare_customers(Customer* a, Customer* b) { 15 | if (a->balance < b->balance) 16 | return -1; 17 | else if (a->balance > b->balance) 18 | return 1; 19 | else 20 | return 0; 21 | } 22 | 23 | // Διαβάζει τους πελάτες από το αρχείο filename και τους επιστρέφει 24 | // σε έναν πίνακα ταξινομημένους κατά balance. Στο ret_size επιστρέφεται 25 | // ο αριθμός των πελατών που διαβάστηκαν. 26 | 27 | Customer* customer_read_from_file(String filename, int* ret_size) { 28 | FILE* file = fopen(filename, "r"); 29 | 30 | // hard-coded μέγιστος αριθμός πελατών. Στο μέλλον θα μπορούσαμε 31 | // να βάλουμε μια δομή που να μεγαλώνει. 32 | // 33 | Customer* customers = malloc(MAX_ARRAY_SIZE * sizeof(Customer)); 34 | int size = 0; 35 | 36 | // διαβάζουμε όλες τις γραμμές του αρχείου 37 | while (true) { 38 | // διαβάζουμε το balance, τερματισμός στο τέλος του αρχείου 39 | int balance; 40 | if (fscanf(file, "%d,", &balance) == EOF) 41 | break; 42 | 43 | // έχουμε νέα γραμμή, έλεγχος μεγέθους 44 | if(size == MAX_ARRAY_SIZE) 45 | break; 46 | size++; 47 | 48 | // διαβάζουμε το όνομα, αφαιρώντας το newline (\n) από το τέλος 49 | char name[MAX_NAME_SIZE]; 50 | fgets(name, MAX_NAME_SIZE, file); 51 | name[strlen(name) - 1] = '\0'; 52 | 53 | // προσθήκη στον πίνακα 54 | customers[size].balance = balance; 55 | customers[size].name = strdup(name); 56 | } 57 | fclose(file); 58 | 59 | // ταξινόμηση κατά balance 60 | qsort(customers, size, sizeof(Customer), (void*)compare_customers); 61 | 62 | *ret_size = size; 63 | return customers; 64 | } 65 | 66 | // Αναζητεί τον πελάτη με όνομα name στον πίνακα customers μεγέθους size. 67 | // Επιστρέφει τον πελάτη αν βρεθεί, αλλιώς NULL. 68 | 69 | Customer* customer_find(Customer* customers, int size, String name) { 70 | // Σειριακή αναζήτηση, γιατί οι πελάτες είναι τακινομημένοι κατά balance, όχι 71 | // κατά όνομα! Στο μέλλον θα μπορούσαμε να κάνουμε κάτι καλύτερο. 72 | // 73 | for (int i = 0; i < size; i++) 74 | if (strcmp(name, customers[i].name) == 0) 75 | return &customers[i]; 76 | 77 | return NULL; 78 | } 79 | 80 | void customer_print(Customer* customer) { 81 | printf("%d %s\n", customer->balance, customer->name); 82 | } 83 | -------------------------------------------------------------------------------- /programs/ip_projects/ipli/parser.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | 9 | // Το OPTIMIZED_VARS flag καθορίζει τον τύπο που έχουν μεταβλητές και πίνακες 10 | // όταν τελειώσει το parsing. 11 | // 12 | // - Αν το OPTIMIZED_VARS δεν είναι defined, τότε μια μεταβλητή foo είναι αποθηκευμένη 13 | // απλά σαν το string "foo", και η τιμή της γίνεται resolve κατά το execution 14 | // μέσω του Map variables. 15 | // 16 | // - Αν το OPTIMIZED_VARS είναι defined, τότε κατά το parsing δεσμεύεται μνήμη για τη 17 | // μεταβλητή, και αντί για το string "foo" αποθηκεύουμε έναν pointer προς τη θέση 18 | // μνήμης, γλιτώνοντας το επαναλλαμβανόμενο map_find κατά το runtime. Παρομοίως, ένας 19 | // πίνακας array αποθηκεύεται ως Vector, αντί για το string "array". 20 | // 21 | // Το flag μπορούμε να το ορίσουμε με το παρακάτω #define, είτε τρέχοντας 22 | // make CFLAGS=-DOPTIMIZED_VARS 23 | // 24 | // #define OPTIMIZED_VARS 25 | 26 | 27 | typedef char* String; 28 | 29 | #ifndef OPTIMIZED_VARS 30 | typedef String Var; 31 | typedef String Array; 32 | #else 33 | typedef int* Var; 34 | typedef Vector Array; 35 | #endif 36 | 37 | typedef List Program; // List of Statement 38 | 39 | typedef enum { 40 | WRITELN, // writeln 41 | ASSIGN_VAR, // = 42 | ASSIGN_EXP, // = 43 | WHILE, // while \n 44 | IF, // if \n \nelse \n 45 | 46 | WRITE, // write 47 | READ, // read 48 | RAND, // random 49 | ARG_SIZE, // argument size 50 | ARG, // argument 51 | BREAK, // break 52 | CONTINUE, // continue 53 | NEW, // new [var1] 54 | FREE, // free 55 | SIZE, // size 56 | } StatementType; 57 | 58 | // | | [] 59 | typedef struct { 60 | Var variable; // also used for cosntants 61 | Array array; // if non-NULL, variable is the index 62 | }* VarLike; 63 | 64 | typedef struct { 65 | StatementType type; 66 | Program body, else_body; 67 | VarLike var1, var2, var3; 68 | Array array; 69 | String oper; 70 | String line; 71 | int line_no; 72 | }* Statement; 73 | 74 | typedef struct { 75 | Program program; 76 | Map variables; // name => int 77 | Map arrays; // name => Vector of int 78 | Vector args; // vec of Strings 79 | bool verbose; 80 | }* Runtime; 81 | 82 | 83 | // Parses a source file (vector of strings) into a program 84 | 85 | Runtime parser_create_runtime(Vector source, Vector args, bool verbose); 86 | 87 | void parser_destroy_runtime(Runtime prog); 88 | 89 | int* create_int(int value); -------------------------------------------------------------------------------- /programs/ip_projects/mapcol/genmap.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | char *colorstr[] = {"red", "green", "blue", "yellow", "orange", 6 | "violet", "cyan", "pink", "brown", "grey"}; 7 | 8 | int main(int argc, char *argv[]) 9 | { 10 | int i, j, ncountries, uncolperc = 100, density = 30, colornum = 4; 11 | long seed; 12 | char *color, **neighb; 13 | seed = time(NULL); 14 | if (argc == 1) { 15 | printf("Wrong usage\n"); 16 | return 1; 17 | } 18 | ncountries = atoi(argv[1]); /* Number of countries */ 19 | if (argc > 2) /* Percentage of uncolored */ 20 | uncolperc = atoi(argv[2]); /* countries in the output */ 21 | if (argc > 3) 22 | density = atoi(argv[3]); /* Percentage of borders among countries */ 23 | if (argc > 4) 24 | seed = atoi(argv[4]); /* Seed for the random number generator */ 25 | if (argc > 5) 26 | colornum = atoi(argv[5]); /* Number of colors */ 27 | if (colornum > (int) sizeof(colorstr)) { 28 | printf("Too many colors\n"); 29 | return 1; 30 | } 31 | srand((unsigned int) seed); 32 | color = malloc(ncountries * sizeof(char)); /* Color matrix for the countries */ 33 | neighb = malloc(ncountries * sizeof(char *)); /* Reserve space for the 2-D triangular */ 34 | for (i = 0 ; i < ncountries - 1 ; i++) /* matrix to hold countries' borders */ 35 | neighb[i] = malloc((ncountries-i-1) * sizeof(char)); 36 | for (i = 0 ; i < ncountries ; i++) 37 | color[i] = rand() % colornum + 1; /* Select random color for each country */ 38 | for (i = 0 ; i < ncountries-1 ; i++) 39 | for (j = i+1 ; j < ncountries ; j++) { 40 | neighb[i][j-i-1] = 0; 41 | if (color[i] != color[j]) 42 | if ((100.0 * rand()) / ((double) RAND_MAX + 1.0) < (double) density) 43 | neighb[i][j-i-1] = 1; /* Decide randomly about borders */ 44 | } 45 | for (i = 0 ; i < (uncolperc * ncountries) / 100.0 ; i++) { 46 | j = rand() % ncountries; /* Uncolor some countries */ 47 | while (color[j] == 0) /* according to the given percentage */ 48 | j = (j + 1) % ncountries; 49 | color[j] = 0; 50 | } 51 | for (i = 0 ; i < ncountries ; i++) { /* Print out the map */ 52 | printf("%s C%05d", (color[i] == 0 ? "nocolor" : colorstr[color[i]-1]), i+1); 53 | for (j = 0 ; j < ncountries ; j++) { 54 | if (i < j) /* Express borders in both directions */ 55 | if (neighb[i][j-i-1] == 1) 56 | printf(" C%05d", j+1); 57 | if (i > j) 58 | if (neighb[j][i-j-1] == 1) 59 | printf(" C%05d", j+1); 60 | } 61 | printf("\n"); 62 | } 63 | return 0; 64 | } 65 | -------------------------------------------------------------------------------- /include/ADTList.h: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////// 2 | // 3 | // ADT List 4 | // 5 | // Abstract λίστα. Παρέχει σειριακή πρόσβαση στα στοιχεία, και 6 | // προσθήκη/αφαίρεση σε οποιοδήποτε σημείο της λίστας. 7 | // 8 | /////////////////////////////////////////////////////////////////// 9 | 10 | #pragma once // #include το πολύ μία φορά 11 | 12 | #include "common_types.h" 13 | 14 | // Οι σταθερές αυτές συμβολίζουν εικονικούς κόμβους _πριν_ τον πρώτο και _μετά_ τον τελευταίο 15 | #define LIST_BOF (ListNode)0 16 | #define LIST_EOF (ListNode)0 17 | 18 | 19 | // Λίστες και κόμβοι αναπαριστώνται από τους τύπους List και ListNode. Ο χρήστης δε χρειάζεται να γνωρίζει το περιεχόμενο 20 | // των τύπων αυτών, απλά χρησιμοποιεί τις συναρτήσεις list_ που δέχονται και επιστρέφουν List / ListNode. 21 | // 22 | // Οι τύποι αυτοί ορίζινται ως pointers στα "struct list" και "struct list_node" των οποίων το 23 | // περιεχόμενο είναι άγνωστο (incomplete structs), και εξαρτάται από την υλοποίηση του ADT List. 24 | // 25 | typedef struct list* List; 26 | typedef struct list_node* ListNode; 27 | 28 | 29 | 30 | // Δημιουργεί και επιστρέφει μια νέα λίστα. 31 | // Αν destroy_value != NULL, τότε καλείται destroy_value(value) κάθε φορά που αφαιρείται ένα στοιχείο. 32 | 33 | List list_create(DestroyFunc destroy_value); 34 | 35 | // Επιστρέφει τον αριθμό στοιχείων που περιέχει η λίστα. 36 | 37 | int list_size(List list); 38 | 39 | // Προσθέτει έναν νέο κόμβο __μετά__ τον node, ή στην αρχή αν node == LIST_BOF, με περιεχόμενο value. 40 | 41 | void list_insert_next(List list, ListNode node, Pointer value); 42 | 43 | // Αφαιρεί τον __επόμενο__ κόμβο από τον node, ή τον πρώτο κόμβο αν node == LIST_BOF. 44 | // Αν ο node δεν έχει επόμενο η συμπεριφορά είναι μη ορισμένη. 45 | 46 | void list_remove_next(List list, ListNode node); 47 | 48 | // Επιστρέφει την πρώτη τιμή που είναι ισοδύναμη με value 49 | // (με βάση τη συνάρτηση compare), ή NULL αν δεν υπάρχει 50 | 51 | Pointer list_find(List list, Pointer value, CompareFunc compare); 52 | 53 | // Αλλάζει τη συνάρτηση που καλείται σε κάθε αφαίρεση/αντικατάσταση στοιχείου σε 54 | // destroy_value. Επιστρέφει την προηγούμενη τιμή της συνάρτησης. 55 | 56 | DestroyFunc list_set_destroy_value(List list, DestroyFunc destroy_value); 57 | 58 | // Ελευθερώνει όλη τη μνήμη που δεσμεύει η λίστα list. 59 | // Οποιαδήποτε λειτουργία πάνω στη λίστα μετά το destroy είναι μη ορισμένη. 60 | 61 | void list_destroy(List list); 62 | 63 | 64 | // Διάσχιση της λίστας ///////////////////////////////////////////// 65 | // 66 | // Επιστρέφουν τον πρώτο και τον τελευταίο κομβο της λίστας, ή LIST_BOF / LIST_EOF αντίστοιχα αν η λίστα είναι κενή 67 | 68 | ListNode list_first(List list); 69 | ListNode list_last(List list); 70 | 71 | // Επιστρέφει τον κόμβο μετά από τον node, ή LIST_EOF αν ο node είναι ο τελευταίος 72 | 73 | ListNode list_next(List list, ListNode node); 74 | 75 | // Επιστρέφει το περιεχόμενο του κόμβου node 76 | 77 | Pointer list_node_value(List list, ListNode node); 78 | 79 | // Βρίσκει τo πρώτo στοιχείο που είναι ισοδύναμο με value (με βάση τη συνάρτηση compare). 80 | // Επιστρέφει τον κόμβο του στοιχείου, ή LIST_EOF αν δεν βρεθεί. 81 | 82 | ListNode list_find_node(List list, Pointer value, CompareFunc compare); 83 | -------------------------------------------------------------------------------- /include/ADTSet.h: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////// 2 | // 3 | // ADT Set 4 | // 5 | // Abstract διατεταγμένο σύνολο. Τα στοιχεία είναι διατεταγμένα με βάση 6 | // τη συνάρτηση compare, και καθένα εμφανίζεται το πολύ μία φορά. 7 | // Παρέχεται γρήγορη αναζήτηση με ισότητα αλλά και με ανισότητα. 8 | // 9 | //////////////////////////////////////////////////////////////////////// 10 | 11 | #pragma once // #include το πολύ μία φορά 12 | 13 | #include "common_types.h" 14 | 15 | 16 | // Ενα σύνολο αναπαριστάται από τον τύπο Set 17 | 18 | typedef struct set* Set; 19 | 20 | 21 | // Δημιουργεί και επιστρέφει ένα σύνολο, στο οποίο τα στοιχεία συγκρίνονται με βάση 22 | // τη συνάρτηση compare. 23 | // Αν destroy_value != NULL, τότε καλείται destroy_value(value) κάθε φορά που αφαιρείται ένα στοιχείο. 24 | 25 | Set set_create(CompareFunc compare, DestroyFunc destroy_value); 26 | 27 | // Επιστρέφει τον αριθμό στοιχείων που περιέχει το σύνολο set. 28 | 29 | int set_size(Set set); 30 | 31 | // Προσθέτει την τιμή value στο σύνολο, αντικαθιστώντας τυχόν προηγούμενη τιμή ισοδύναμη της value. 32 | // 33 | // ΠΡΟΣΟΧΗ: 34 | // Όσο το value είναι μέλος του set, οποιαδήποτε μεταβολή στο περιεχόμενό του (στη μνήμη που δείχνει) δεν πρέπει 35 | // να αλλάζει τη σχέση διάταξης (compare) με οποιοδήποτε άλλο στοιχείο, διαφορετικά έχει μη ορισμένη συμπεριφορά. 36 | 37 | void set_insert(Set set, Pointer value); 38 | 39 | // Αφαιρεί τη μοναδική τιμή ισοδύναμη της value από το σύνολο, αν υπάρχει. 40 | // Επιστρέφει true αν βρέθηκε η τιμή αυτή, false διαφορετικά. 41 | 42 | bool set_remove(Set set, Pointer value); 43 | 44 | // Επιστρέφει την μοναδική τιμή του set που είναι ισοδύναμη με value, ή NULL αν δεν υπάρχει 45 | 46 | Pointer set_find(Set set, Pointer value); 47 | 48 | // Αλλάζει τη συνάρτηση που καλείται σε κάθε αφαίρεση/αντικατάσταση στοιχείου σε 49 | // destroy_value. Επιστρέφει την προηγούμενη τιμή της συνάρτησης. 50 | 51 | DestroyFunc set_set_destroy_value(Set set, DestroyFunc destroy_value); 52 | 53 | // Ελευθερώνει όλη τη μνήμη που δεσμεύει το σύνολο. 54 | // Οποιαδήποτε λειτουργία πάνω στο set μετά το destroy είναι μη ορισμένη. 55 | 56 | void set_destroy(Set set); 57 | 58 | 59 | // Διάσχιση του set //////////////////////////////////////////////////////////// 60 | // 61 | // Η διάσχιση γίνεται με τη σειρά διάταξης. 62 | 63 | // Οι σταθερές αυτές συμβολίζουν εικονικούς κόμβους _πριν_ τον πρώτο και _μετά_ τον τελευταίο κόμβο του set 64 | #define SET_BOF (SetNode)0 65 | #define SET_EOF (SetNode)0 66 | 67 | typedef struct set_node* SetNode; 68 | 69 | // Επιστρέφουν τον πρώτο και τον τελευταίο κομβο του set, ή SET_BOF / SET_EOF αντίστοιχα αν το set είναι κενό 70 | 71 | SetNode set_first(Set set); 72 | SetNode set_last(Set set); 73 | 74 | // Επιστρέφουν τον επόμενο και τον προηγούμενο κομβο του node, ή SET_EOF / SET_BOF 75 | // αντίστοιχα αν ο node δεν έχει επόμενο / προηγούμενο. 76 | 77 | SetNode set_next(Set set, SetNode node); 78 | SetNode set_previous(Set set, SetNode node); 79 | 80 | // Επιστρέφει το περιεχόμενο του κόμβου node 81 | 82 | Pointer set_node_value(Set set, SetNode node); 83 | 84 | // Βρίσκει το μοναδικό στοιχείο στο set που να είναι ίσο με value. 85 | // Επιστρέφει τον κόμβο του στοιχείου, ή SET_EOF αν δεν βρεθεί. 86 | 87 | SetNode set_find_node(Set set, Pointer value); 88 | -------------------------------------------------------------------------------- /programs/visualize/examples.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | typedef struct list_node { 6 | int value; 7 | struct list_node* next; 8 | } ListNode; 9 | 10 | typedef struct { 11 | int field1; 12 | float field2; 13 | } MyStruct; 14 | 15 | 16 | 17 | void local_vars_example(void) { 18 | int my_int = 1; 19 | float my_float = 2.0; 20 | int my_array[3] = {3, 4, 5}; 21 | MyStruct my_struct = {.field1 = 1, .field2 = 2.0}; 22 | 23 | if(1) { 24 | int block_scope = 1; 25 | printf("local_vars_example: %d\n", block_scope); 26 | } 27 | 28 | printf("local_vars_example: %d %f %d %d\n", my_int, my_float, my_array[0], my_struct.field1); 29 | } 30 | 31 | void pointer_example(void) { 32 | int foo = 1; 33 | int bar = 2; 34 | 35 | int* pointer = &foo; 36 | *pointer = 3; 37 | 38 | int** double_pointer = &pointer; 39 | *double_pointer = &bar; 40 | **double_pointer = 4; 41 | } 42 | 43 | void malloc_example(void) { 44 | int* my_int = malloc(sizeof(*my_int)); 45 | *my_int = 1; 46 | 47 | float* my_float = malloc(sizeof(*my_float)); 48 | *my_float = 2.0; 49 | 50 | int* my_int_array = malloc(3 * sizeof(*my_int_array)); 51 | my_int_array[0] = 1; 52 | my_int_array[1] = 2; 53 | my_int_array[2] = 3; 54 | 55 | MyStruct* my_struct = malloc(sizeof(*my_struct)); 56 | my_struct->field1 = 1; 57 | my_struct->field2 = 2; 58 | 59 | free(my_int); 60 | free(my_float); 61 | free(my_int_array); 62 | free(my_struct); 63 | } 64 | 65 | void string_example(void) { 66 | char string1[5]; 67 | strcpy(string1, "foo"); 68 | 69 | char* string2 = malloc(5); 70 | strcpy(string2, "bar"); 71 | free(string2); 72 | } 73 | 74 | void swap(int* a, int* b) { 75 | int temp = *a; 76 | *a = *b; 77 | *b = temp; 78 | } 79 | 80 | void swap_example(void) { 81 | int foo = 1; 82 | int bar = 2; 83 | swap(&foo, &bar); 84 | } 85 | 86 | ListNode* create_node(int value) { 87 | ListNode* node = malloc(sizeof(*node)); 88 | node->value = value; 89 | node->next = NULL; 90 | return node; 91 | } 92 | 93 | void list_example(void) { 94 | ListNode* first = create_node(0); 95 | first->next = create_node(1); 96 | first->next->next = create_node(2); 97 | 98 | free(first->next->next); 99 | free(first->next); 100 | free(first); 101 | } 102 | 103 | int recursive_function(int n) { 104 | int temp = 0; 105 | if(n > 0) 106 | temp = recursive_function(n - 1); 107 | 108 | return n + temp; 109 | } 110 | 111 | void recursion_example(void) { 112 | int result = recursive_function(3); 113 | printf("recursion_example: %d\n", result); 114 | } 115 | 116 | void memory_leak_example(void) { 117 | int* my_int; 118 | 119 | my_int = malloc(sizeof(*my_int)); 120 | my_int = malloc(sizeof(*my_int)); 121 | my_int = malloc(sizeof(*my_int)); 122 | 123 | free(my_int); 124 | } 125 | 126 | 127 | 128 | int main(void) { 129 | local_vars_example(); 130 | pointer_example(); 131 | swap_example(); 132 | malloc_example(); 133 | string_example(); 134 | recursion_example(); 135 | list_example(); 136 | memory_leak_example(); 137 | 138 | printf("\nVisualization examples. To display them, start debugging (F5),\n"); 139 | printf("open Debug Visualizer and type \"memory\" in the window that opens.\n\n"); 140 | 141 | return 0; 142 | } 143 | -------------------------------------------------------------------------------- /programs/iron_bank/customers.csv: -------------------------------------------------------------------------------- 1 | 0,Ned Stark 2 | 251,Robert Baratheon 3 | -426,Jaime Lannister 4 | -391,Catelyn Stark 5 | 553,Cersei Lannister 6 | 0,Daenerys Targaryen 7 | 89,Jorah Mormont 8 | -982,Viserys Targaryen 9 | -687,Jon Snow 10 | -263,Robb Stark 11 | 483,Sansa Stark 12 | 220,Arya Stark 13 | 438,Theon Greyjoy 14 | 320,Bran Stark 15 | 912,Joffrey Baratheon 16 | -961,Sandor Clegane 17 | 546,Tyrion Lannister 18 | 758,Khal Drogo 19 | -35,Petyr Baelish 20 | -91,Davos Seaworth 21 | 458,Samwell Tarly 22 | 0,Stannis Baratheon 23 | -456,Melisandre 24 | -660,Jeor Mormont 25 | 196,Bronn 26 | 863,Varys 27 | 784,Shae 28 | -519,Margaery Tyrell 29 | 886,Tywin Lannister 30 | -204,Talisa Maegyr 31 | 951,Ygritte 32 | 0,Gendry 33 | -169,Tormund Giantsbane 34 | -877,Brienne of Tarth 35 | -4,Ramsay Bolton 36 | -947,Gilly 37 | -99,Daario Naharis 38 | -209,Missandei 39 | 167,Tommen Baratheon 40 | -899,Ellaria Sand 41 | 354,Jaqen H'ghar 42 | 62,Roose Bolton 43 | -346,The High Sparrow 44 | -690,Grey Worm 45 | 242,Maester Luwin 46 | -195,Jory Cassel 47 | -348,Rodrik Cassel 48 | 868,Benjen Stark 49 | -962,Qotho 50 | -142,Ros 51 | -18,Septa Mordane 52 | 506,Rickon Stark 53 | -542,Myrcella Baratheon 54 | 767,Hodor 55 | -677,Doreah 56 | 157,Irri 57 | -521,Ilyn Payne 58 | 370,Rast 59 | 76,Alliser Thorne 60 | 146,Barristan Selmy 61 | -776,Yoren 62 | 454,Grand Maester Pycelle 63 | -523,Renly Baratheon 64 | -162,Maester Aemon 65 | 954,Syrio Forel 66 | -411,Grenn 67 | -148,Pypar 68 | -232,Rakharo 69 | -734,Lancel Lannister 70 | -918,Janos Slynt 71 | 103,Marillion 72 | -212,Lysa Arryn 73 | 668,Robin Arryn 74 | -424,Osha 75 | 969,Greatjon Umber 76 | 83,Mirri Maz Duur 77 | 167,Kevan Lannister 78 | 714,Craster 79 | 796,Eddison Tollett 80 | -714,Hot Pie 81 | 708,Lommy Greenhands 82 | 306,Kovarro 83 | -508,Matthos Seaworth 84 | -889,Dontos Hollard 85 | 947,Alton Lannister 86 | -96,Meryn Trant 87 | 312,Daisy 88 | 50,Rorge 89 | -583,Yara Greyjoy 90 | 36,Biter 91 | -60,Podrick Payne 92 | -266,Loras Tyrell 93 | 287,Amory Lorch 94 | 617,Gregor Clegane 95 | 951,The Spice King 96 | 694,Xaro Xhoan Daxos 97 | 768,Pyat Pree 98 | 910,Dagmer Cleftjaw 99 | 694,Black Lorren 100 | -140,Qhorin Halfhand 101 | -795,Silk King 102 | -958,Mance Rayder 103 | 521,Qyburn 104 | -35,Rickard Karstark 105 | -899,Kraznys mo Nakloz 106 | 764,Olenna Tyrell 107 | -11,Orell 108 | -605,Thoros of Myr 109 | -969,Locke 110 | -547,Jojen Reed 111 | -103,Meera Reed 112 | -144,Anguy 113 | 681,Brynden Tully 114 | -297,Edmure Tully 115 | 103,Little Sam 116 | -196,Beric Dondarrion 117 | -775,Steelshanks Walton 118 | 16,Oberyn Martell 119 | 885,Styr 120 | 437,Thenn Warg 121 | 978,Othell Yarwyck 122 | -873,Selyse Florent 123 | -384,Mace Tyrell 124 | -455,Shireen Baratheon 125 | 109,The High Septon 126 | -883,Olly 127 | -344,Hizdahr zo Loraq 128 | 416,Olyvar 129 | -968,Bowen Marsh 130 | 413,Doran Martell 131 | 0,Areo Hotah 132 | 657,Trystane Martell 133 | 767,The Waif 134 | -76,Myranda 135 | -504,Walda Bolton 136 | 572,Obara Sand 137 | -962,Tyene Sand 138 | -59,Nymeria Sand 139 | 552,Septa Unella 140 | -513,Khal Moro 141 | 656,Qhono 142 | -23,Three-Eyed Raven 143 | -552,Wun Weg Wun Dar Wun 144 | -893,Leaf 145 | 0,Harald Karstark 146 | -50,Izembaro 147 | 393,Lady Crane 148 | 161,Bobono 149 | 208,Clarenzo 150 | -488,Black Walder Rivers 151 | 974,Lothar Frey 152 | -299,Lyanna Mormont 153 | 938,Archmaester Ebrose 154 | -937,Euron Greyjoy 155 | 0,Robett Glover 156 | 955,Yohn Royce 157 | 838,Maester Wolkan 158 | 43,Randyll Tarly 159 | 311,Dickon Tarly 160 | -11,Harrag 161 | 123,Night King 162 | -24,Alys Karstark 163 | -------------------------------------------------------------------------------- /include/ADTMap.h: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////// 2 | // 3 | // ADT Map 4 | // 5 | // Abstract map. Παρέχει γρήγορη αντιστοίχιση key => value. 6 | // 7 | /////////////////////////////////////////////////////////// 8 | 9 | #pragma once // #include το πολύ μία φορά 10 | 11 | #include "common_types.h" 12 | 13 | 14 | // Ενα map αναπαριστάται από τον τύπο Map 15 | 16 | typedef struct map* Map; 17 | 18 | 19 | // Δημιουργεί και επιστρέφει ένα map, στο οποίο τα στοιχεία συγκρίνονται με βάση 20 | // τη συνάρτηση compare. 21 | // Αν destroy_key ή/και destroy_value != NULL, τότε καλείται destroy_key(key) 22 | // ή/και destroy_value(value) κάθε φορά που αφαιρείται ένα στοιχείο. 23 | 24 | Map map_create(CompareFunc compare, DestroyFunc destroy_key, DestroyFunc destroy_value); 25 | 26 | // Επιστρέφει τον αριθμό στοιχείων που περιέχει το map. 27 | 28 | int map_size(Map map); 29 | 30 | // Προσθέτει το κλειδί key με τιμή value. Αν υπάρχει κλειδί ισοδύναμο με key, τα παλιά key & value αντικαθίσταται από τα νέα. 31 | // 32 | // ΠΡΟΣΟΧΗ: 33 | // Όσο το key είναι μέλος του map, οποιαδήποτε μεταβολή στο περιεχόμενό του (στη μνήμη που δείχνει) δεν πρέπει 34 | // να αλλάζει τη σχέση διάταξης (compare) με οποιοδήποτε άλλο key, διαφορετικά έχει μη ορισμένη συμπεριφορά. 35 | 36 | void map_insert(Map map, Pointer key, Pointer value); 37 | 38 | // Αφαιρεί το κλειδί που είναι ισοδύναμο με key από το map, αν υπάρχει. 39 | // Επιστρέφει true αν βρέθηκε τέτοιο κλειδί, διαφορετικά false. 40 | 41 | bool map_remove(Map map, Pointer key); 42 | 43 | // Επιστρέφει την τιμή που έχει αντιστοιχιστεί στο συγκεκριμένο key, ή NULL αν το key δεν υπάρχει στο map. 44 | // 45 | // Προσοχή: η συνάρτηση επιστρέφει NULL είτε όταν το key δεν υπάρχει, είτε όταν υπάρχει και έχει τιμή NULL. 46 | // Αν χρειάζεται να διαχωρίσουμε τις δύο περιπτώσεις μπορούμε να χρησιμοποιήσουμε την map_find_node. 47 | 48 | Pointer map_find(Map map, Pointer key); 49 | 50 | // Αλλάζει τη συνάρτηση που καλείται σε κάθε αφαίρεση/αντικατάσταση key/value. 51 | // Επιστρέφει την προηγούμενη τιμή της συνάρτησης. 52 | 53 | DestroyFunc map_set_destroy_key (Map map, DestroyFunc destroy_key ); 54 | DestroyFunc map_set_destroy_value(Map map, DestroyFunc destroy_value); 55 | 56 | // Ελευθερώνει όλη τη μνήμη που δεσμεύει το map. 57 | // Οποιαδήποτε λειτουργία πάνω στο map μετά το destroy είναι μη ορισμένη. 58 | 59 | void map_destroy(Map map); 60 | 61 | 62 | 63 | // Διάσχιση του map μέσω κόμβων //////////////////////////////////////////////////////////// 64 | // 65 | // Η σειρά διάσχισης είναι αυθαίρετη. 66 | 67 | // Η σταθερά αυτή συμβολίζει έναν εικονικό κόμβου _μετά_ τον τελευταίο κόμβο του map 68 | #define MAP_EOF (MapNode)0 69 | 70 | typedef struct map_node* MapNode; 71 | 72 | // Επιστρέφει τον πρώτο κομβο του map, ή MAP_EOF αν το map είναι κενό 73 | 74 | MapNode map_first(Map map); 75 | 76 | // Επιστρέφει τον επόμενο κόμβο του node, ή MAP_EOF αν ο node δεν έχει επόμενο 77 | 78 | MapNode map_next(Map map, MapNode node); 79 | 80 | // Επιστρέφει το κλειδί του κόμβου node 81 | 82 | Pointer map_node_key(Map map, MapNode node); 83 | 84 | // Επιστρέφει το περιεχόμενο του κόμβου node 85 | 86 | Pointer map_node_value(Map map, MapNode node); 87 | 88 | // Βρίσκει και επιστρέφεο τον κόμβο που έχει αντιστοιχιστεί στο κλειδί key, 89 | // ή MAP_EOF αν το κλειδί δεν υπάρχει στο map. 90 | 91 | MapNode map_find_node(Map map, Pointer key); 92 | 93 | 94 | //// Επιπλέον συναρτήσεις για υλοποιήσεις βασισμένες σε hashing //////////////////////////// 95 | 96 | // Τύπος συνάρτησης κατακερματισμού 97 | 98 | typedef uint (*HashFunc)(Pointer); 99 | 100 | // Υλοποιημένες συναρτήσεις κατακερματισμού για συχνούς τύπους δεδομένων 101 | 102 | uint hash_string(Pointer value); // Χρήση όταν το key είναι char* 103 | uint hash_int(Pointer value); // Χρήση όταν το key είναι int* 104 | uint hash_pointer(Pointer value); // Χρήση όταν το key είναι pointer που θεωρείται διαφορετικός από οποιονδήποτε άλλο pointer 105 | 106 | // Ορίζει τη συνάρτηση κατακερματισμού hash για το συγκεκριμένο map 107 | // Πρέπει να κληθεί μετά την map_create και πριν από οποιαδήποτε άλλη συνάρτηση. 108 | // Όπως και με την συνάρτηση compare, αλλαγές στο περιεχόμενο των keys δεν θα πρέπει να αλλάζουν την 109 | // τιμή που επιστρέφει η συνάρτηση κατακερματισμού, διαφορετικά η συμπεριφορά είναι μη ορισμένη. 110 | 111 | void map_set_hash_function(Map map, HashFunc hash_func); 112 | -------------------------------------------------------------------------------- /include/ADTVector.h: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////// 2 | // 3 | // ADT Vector 4 | // 5 | // Abstract "array" μεταβλητού μεγέθους. Παρέχει πρόσβαση σε 6 | // οποιοδήποτε στοιχείο (μέσω της θέσης του), και προσθήκη/αφαίρεση 7 | // στοιχείων στο τέλος του vector. 8 | // 9 | /////////////////////////////////////////////////////////////////// 10 | 11 | #pragma once // #include το πολύ μία φορά 12 | 13 | #include "common_types.h" 14 | 15 | 16 | // Ένα vector αναπαριστάται από τον τύπο Vector. Ο χρήστης δε χρειάζεται να γνωρίζει το περιεχόμενο 17 | // του τύπου αυτού, απλά χρησιμοποιεί τις συναρτήσεις vector_ που δέχονται και επιστρέφουν Vector. 18 | // 19 | // Ο τύπος Vector ορίζεται ως pointer στο "struct vector" του οποίου το περιεχόμενο είναι άγνωστο 20 | // (incomplete struct), και εξαρτάται από την υλοποίηση του ADT Vector. 21 | // 22 | typedef struct vector* Vector; 23 | 24 | 25 | // Δημιουργεί και επιστρέφει ένα νεό vector μεγέθους size, με στοιχεία αρχικοποιημένα σε NULL. 26 | // Αν destroy_value != NULL, τότε καλείται destroy_value(value) κάθε φορά που αφαιρείται (ή αντικαθίσταται) ένα στοιχείο. 27 | 28 | Vector vector_create(int size, DestroyFunc destroy_value); 29 | 30 | // Επιστρέφει τον αριθμό στοιχείων που περιέχει το vector vec. 31 | 32 | int vector_size(Vector vec); 33 | 34 | // Προσθέτει την τιμή value στο _τέλος_ του vector vec. Το μέγεθος του vector μεγαλώνει κατά 1. 35 | // Αν δεν υπάρχει διαθέσιμη μνήμη το vector παραμένει όπως ήταν (αυτό μπορεί να ελεγχθεί με τη vector_size) 36 | 37 | void vector_insert_last(Vector vec, Pointer value); 38 | 39 | // Αφαιρεί το τελευταίο στοιχείο του vector. Το μέγεθος του vector μικραίνει κατά 1. 40 | // Αν το vector είναι κενό η συμπεριφορά είναι μη ορισμένη. 41 | 42 | void vector_remove_last(Vector vec); 43 | 44 | // Επιστρέφει την τιμή στη θέση pos του vector vec (μη ορισμένο αποτέλεσμα αν pos < 0 ή pos >= size) 45 | 46 | Pointer vector_get_at(Vector vec, int pos); 47 | 48 | // Αλλάζει την τιμή στη θέση pos του Vector vec σε value. 49 | // ΔΕΝ μεταβάλλει το μέγεθος του vector, αν pos >= size το αποτέλεσμα δεν είναι ορισμένο. 50 | 51 | void vector_set_at(Vector vec, int pos, Pointer value); 52 | 53 | // Βρίσκει και επιστρέφει το πρώτο στοιχείο στο vector που να είναι ίσο με value 54 | // (με βάση τη συνάρτηση compare), ή NULL αν δεν βρεθεί κανένα στοιχείο. 55 | 56 | Pointer vector_find(Vector vec, Pointer value, CompareFunc compare); 57 | 58 | // Αλλάζει τη συνάρτηση που καλείται σε κάθε αφαίρεση/αντικατάσταση στοιχείου σε 59 | // destroy_value. Επιστρέφει την προηγούμενη τιμή της συνάρτησης. 60 | 61 | DestroyFunc vector_set_destroy_value(Vector vec, DestroyFunc destroy_value); 62 | 63 | // Ελευθερώνει όλη τη μνήμη που δεσμεύει το vector vec. 64 | // Οποιαδήποτε λειτουργία πάνω στο vector μετά το destroy είναι μη ορισμένη. 65 | 66 | void vector_destroy(Vector vec); 67 | 68 | 69 | // Διάσχιση του vector //////////////////////////////////////////////////////////// 70 | // 71 | // Οι παρακάτω συναρτήσεις επιτρέπουν τη διάσχιση του vector μέσω κόμβων. 72 | // Δεν είναι τόσο συχνά χρησιμοποιούμενες όσο για άλλα ADTs, γιατί μπορούμε 73 | // εύκολα να διασχίσουμε το vector και μέσω indexes. Παραμένουν πάντως χρήσιμες, 74 | // τόσο για ομοιομορφία με τους άλλους ADTs, αλλά και γιατί για κάποιες υλοποιήσεις 75 | // η διάσχιση μέσω κόμβων μπορεί να είναι πιο γρήγορη. 76 | 77 | // Οι σταθερές αυτές συμβολίζουν εικονικούς κόμβους _πριν_ τον πρώτο και _μετά_ τον τελευταίο 78 | #define VECTOR_BOF (VectorNode)0 79 | #define VECTOR_EOF (VectorNode)0 80 | 81 | typedef struct vector_node* VectorNode; 82 | 83 | // Επιστρέφουν τον πρώτο και τον τελευταίο κομβο του vector, ή VECTOR_BOF / VECTOR_EOF αντίστοιχα αν το vector είναι κενό 84 | 85 | VectorNode vector_first(Vector vec); 86 | VectorNode vector_last(Vector vec); 87 | 88 | // Επιστρέφουν τον επόμενο και τον προηγούμενο κομβο του node, ή VECTOR_EOF / VECTOR_BOF 89 | // αντίστοιχα αν ο node δεν έχει επόμενο / προηγούμενο. 90 | 91 | VectorNode vector_next(Vector vec, VectorNode node); 92 | VectorNode vector_previous(Vector vec, VectorNode node); 93 | 94 | // Επιστρέφει το περιεχόμενο του κόμβου node 95 | 96 | Pointer vector_node_value(Vector vec, VectorNode node); 97 | 98 | // Βρίσκει το πρώτο στοιχείο στο vector που να είναι ίσο με value (με βάση τη συνάρτηση compare). 99 | // Επιστρέφει τον κόμβο του στοιχείου, ή VECTOR_EOF αν δεν βρεθεί. 100 | 101 | VectorNode vector_find_node(Vector vec, Pointer value, CompareFunc compare); 102 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "Make: compile and debug", 6 | "type": "cppdbg", 7 | "request": "launch", 8 | "program": "${workspaceFolder}/.vscode/bash", 9 | "args": [ 10 | "${workspaceFolder}/.vscode/run.sh", 11 | ], 12 | "stopAtEntry": false, 13 | "cwd": "${workspaceFolder}/${config:c_project.dir}", 14 | "externalConsole": false, 15 | "linux": { 16 | "MIMode": "gdb", 17 | "internalConsoleOptions": "neverOpen", // don't show the debugger console 18 | "environment": [ 19 | {"name":"LD_PRELOAD", "value":"${workspaceFolder}/.vscode/visualize-c-memory.so"}, 20 | {"name":"CODE_DEBUG", "value":"./${config:c_project.program} ${config:c_project.args}"}, 21 | ], 22 | "setupCommands": [ 23 | { 24 | "description": "Enable pretty-printing for gdb", 25 | "text": "-enable-pretty-printing", 26 | "ignoreFailures": true 27 | }, 28 | { 29 | "text": "source ${workspaceFolder}/.vscode/visualize-c-memory.py", 30 | "ignoreFailures": true 31 | } 32 | ] 33 | }, 34 | "osx": { 35 | "MIMode": "lldb", // on macOS gdb is hard to setup 36 | "internalConsoleOptions": "openOnSessionStart", // open the debugger console, lldb sends output only there 37 | "environment": [ 38 | {"name":"CODE_DEBUG", "value":"./${config:c_project.program} ${config:c_project.args}"}, 39 | ], 40 | "setupCommands": [ 41 | { 42 | "description": "Don't stop on exec", // lldb stops execution on "exec", we need to continue since we run via bash 43 | "text": "settings set target.process.stop-on-exec false", 44 | "ignoreFailures": false 45 | } 46 | ], 47 | }, 48 | "preLaunchTask": "debug-make", 49 | }, 50 | { 51 | "name": "Single file: compile and debug", 52 | "type": "cppdbg", 53 | "request": "launch", 54 | "program": "${workspaceFolder}/.vscode/bash", 55 | "args": [ 56 | "${workspaceFolder}/.vscode/run.sh", 57 | ], 58 | "stopAtEntry": false, 59 | "cwd": "${fileDirname}", 60 | "environment": [], 61 | "externalConsole": false, 62 | "linux": { 63 | "MIMode": "gdb", 64 | "internalConsoleOptions": "neverOpen", // don't show the debugger console 65 | "environment": [ 66 | {"name":"LD_PRELOAD", "value":"${workspaceFolder}/.vscode/visualize-c-memory.so"}, 67 | {"name":"CODE_DEBUG", "value":"./${fileBasenameNoExtension} ${config:c_project.args}"}, 68 | ], 69 | "setupCommands": [ 70 | { 71 | "description": "Enable pretty-printing for gdb", 72 | "text": "-enable-pretty-printing", 73 | "ignoreFailures": true 74 | }, 75 | { 76 | "text": "source ${workspaceFolder}/.vscode/visualize-c-memory.py", 77 | "ignoreFailures": true 78 | } 79 | ] 80 | }, 81 | "osx": { 82 | "MIMode": "lldb", // on macOS gdb is hard to setup 83 | "internalConsoleOptions": "openOnSessionStart", // open the debugger console, lldb sends output only there 84 | "environment": [ 85 | {"name":"CODE_DEBUG", "value":"./${fileBasenameNoExtension} ${config:c_project.args}"}, 86 | ], 87 | "setupCommands": [ 88 | { 89 | "description": "Don't stop on exec", // lldb stops execution on "exec", we need to continue since we run via bash 90 | "text": "settings set target.process.stop-on-exec false", 91 | "ignoreFailures": false 92 | } 93 | ], 94 | }, 95 | "preLaunchTask": "debug-single-file" 96 | }, 97 | { 98 | "name": "All files in directory: compile and debug", 99 | "type": "cppdbg", 100 | "request": "launch", 101 | "program": "${workspaceFolder}/.vscode/bash", 102 | "args": [ 103 | "${workspaceFolder}/.vscode/run.sh", 104 | ], 105 | "stopAtEntry": false, 106 | "cwd": "${fileDirname}", 107 | "environment": [], 108 | "externalConsole": false, 109 | "linux": { 110 | "MIMode": "gdb", 111 | "internalConsoleOptions": "neverOpen", // don't show the debugger console 112 | "environment": [ 113 | {"name":"LD_PRELOAD", "value":"${workspaceFolder}/.vscode/visualize-c-memory.so"}, 114 | {"name":"CODE_DEBUG", "value":"./${config:c_project.program} ${config:c_project.args}"}, 115 | ], 116 | "setupCommands": [ 117 | { 118 | "description": "Enable pretty-printing for gdb", 119 | "text": "-enable-pretty-printing", 120 | "ignoreFailures": true 121 | }, 122 | { 123 | "text": "source ${workspaceFolder}/.vscode/visualize-c-memory.py", 124 | "ignoreFailures": true 125 | } 126 | ] 127 | }, 128 | "osx": { 129 | "MIMode": "lldb", // on macOS gdb is hard to setup 130 | "internalConsoleOptions": "openOnSessionStart", // open the debugger console, lldb sends output only there 131 | "environment": [ 132 | {"name":"CODE_DEBUG", "value":"./${config:c_project.program} ${config:c_project.args}"}, 133 | ], 134 | "setupCommands": [ 135 | { 136 | "description": "Don't stop on exec", // lldb stops execution on "exec", we need to continue since we run via bash 137 | "text": "settings set target.process.stop-on-exec false", 138 | "ignoreFailures": false 139 | } 140 | ], 141 | }, 142 | "preLaunchTask": "debug-all-files" 143 | }, 144 | ] 145 | } -------------------------------------------------------------------------------- /programs/ip_projects/mapcol/coloring.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "coloring.h" 7 | 8 | // Το αρχείο αυτό φτιάχτηκε στο live streaming: https://youtu.be/GbfzJBnBncw 9 | // 10 | // ΠΡΟΣΟΧΗ: είναι ελλειπής κώδικας, μπορεί να περιέχει bugs, δεν ελέγχει τη τελική λύση, 11 | // δεν απελευθερώνει μνήμη, κλπ. 12 | 13 | 14 | int color_no = 4; 15 | String colors[] = { "red", "green", "blue", "yellow" }; 16 | 17 | 18 | // Βρίσκει και επιστρέφει τη χώρα που ειναι __αχρωματιστη__ στο coloring με τα λιγότερα διαθέσιμα χρώματα 19 | Country min_avail_colors(Coloring coloring) { 20 | Country result = NULL; 21 | int min = INT_MAX; 22 | 23 | for(MapNode node = map_first(coloring); 24 | node != MAP_EOF; 25 | node = map_next(coloring, node)) { 26 | 27 | Country country = map_node_value(coloring, node); 28 | 29 | // skip τις χρωματισμένες χώρες 30 | if(country->color != NULL) 31 | continue; 32 | 33 | int no_colors = set_size(country->avail_colors); 34 | if(no_colors < min) { 35 | result = country; 36 | min = no_colors; 37 | } 38 | } 39 | 40 | return result; 41 | } 42 | 43 | bool try_color(Coloring coloring, Country country, String color) { 44 | // Χρωμάτισε με το χρώμα αυτό 45 | country->color = strdup(color); 46 | 47 | // Αποθηκεύουμε εδώ τους γείτονες (Country struct) από τους οποίους αφαιρέθηκε __πραγματικά__ το χρώμα 48 | List removed = list_create(NULL); 49 | 50 | // Αφαίρεσε το color από το avail_colors του κάθε γείτονα 51 | for(ListNode node = list_first(country->neighbors); 52 | node != LIST_EOF; 53 | node = list_next(country->neighbors, node)) { 54 | 55 | String neighbor_name = list_node_value(country->neighbors, node); 56 | Country neighbor_info = map_find(coloring, neighbor_name); 57 | 58 | // Αφαιρώ το color από τα διαθέσιμα του γείτονα. 59 | // Προσοχή: μπορεί να μην υπήρχε στα διαθέσιμα. Αποθήκευση στο remove list, τους γείτονες που πραγματικά κάναμε remove. 60 | if(set_remove(neighbor_info->avail_colors, color)) 61 | list_insert_next(removed, LIST_BOF, neighbor_info); 62 | } 63 | 64 | // Κάλεσε το τζίνι για να λύσει το μικρότερο πρόβλημα 65 | bool found = tzini(coloring); 66 | 67 | // Αν δεν υπάρχει λύση 68 | if(!found) { 69 | // Αναιρεση τησ αναθεσης 70 | country->color = NULL; 71 | 72 | // επαναφορά του color μόνο στους γείτονες που είναι στη λίστα removed 73 | for(ListNode node = list_first(removed); 74 | node != LIST_EOF; 75 | node = list_next(removed, node)) { 76 | 77 | Country neighbor_info = list_node_value(removed, node); 78 | set_insert(neighbor_info->avail_colors, color); 79 | } 80 | } 81 | 82 | list_destroy(removed); 83 | 84 | return found; 85 | } 86 | 87 | bool tzini(Coloring coloring) { 88 | // Βρες ποια χώρα θα χρωματίσουμε (αυτή με τα λιγοτερα διαθεσιμα χρωματα) 89 | Country country = min_avail_colors(coloring); 90 | 91 | // Αν όλες είναι χρωματισμένες τελειώσαμε 92 | if(country == NULL) 93 | return true; 94 | 95 | // Για κάθε διαθέσιμο χρώμα 96 | for(SetNode node = set_first(country->avail_colors); 97 | node != SET_EOF; 98 | node = set_next(country->avail_colors, node)) { 99 | 100 | // Δοκιμάσε το χρώμα, αν βρεθεί λύση τελειώσαμε 101 | String color = set_node_value(country->avail_colors, node); 102 | if(try_color(coloring, country, color)) 103 | return true; 104 | } 105 | 106 | return false; 107 | } 108 | 109 | int compare_strings(Pointer a, Pointer b) { 110 | return strcmp(a, b); 111 | } 112 | 113 | Coloring read_input(void) { 114 | int N = 50000; 115 | char line[N]; 116 | 117 | Coloring coloring = map_create(compare_strings, NULL, NULL); 118 | 119 | while(fgets(line, N, stdin) != NULL) { 120 | strtok(line, " \t\n"); 121 | String name = strdup(strtok(NULL, " \t\n")); 122 | 123 | // Δημιουργώ ένα Country και το προσθέσω στο map! 124 | Country country = malloc(sizeof(struct country)); 125 | 126 | country->color = NULL; 127 | 128 | // σύνολο από διαθέσιμα χρώματα 129 | country->avail_colors = set_create(compare_strings, NULL); 130 | for(int i = 0; i < color_no; i++) 131 | set_insert(country->avail_colors, colors[i]); 132 | 133 | // λίστα από γείτονες 134 | country->neighbors = list_create(NULL); 135 | 136 | String neighbor; 137 | while((neighbor = strtok(NULL, " \t\n")) != NULL) 138 | list_insert_next(country->neighbors, LIST_BOF, strdup(neighbor)); 139 | 140 | // insert country στο map! 141 | map_insert(coloring, name, country); 142 | } 143 | 144 | return coloring; 145 | } 146 | 147 | void print_coloring(Coloring coloring) { 148 | for(MapNode node = map_first(coloring); 149 | node != MAP_EOF; 150 | node = map_next(coloring, node)) { 151 | 152 | String name = map_node_key(coloring, node); 153 | Country country = map_node_value(coloring, node); 154 | 155 | printf("%s / %s / ", name, country->color); 156 | 157 | // γείτονες 158 | for(ListNode node = list_first(country->neighbors); 159 | node != LIST_EOF; 160 | node = list_next(country->neighbors, node)) { 161 | 162 | printf("%s ", (String)list_node_value(country->neighbors, node)); 163 | } 164 | 165 | // avail colirs 166 | printf(" / "); 167 | for(SetNode node = set_first(country->avail_colors); 168 | node != SET_EOF; 169 | node = set_next(country->avail_colors, node)) { 170 | 171 | printf("%s ", (String)set_node_value(country->avail_colors, node)); 172 | } 173 | 174 | printf("\n"); 175 | } 176 | } -------------------------------------------------------------------------------- /programs/ip_projects/ipli/parser.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | 6 | #include "parser.h" 7 | 8 | 9 | bool is_number(String s) { 10 | return s[0] >= '0' && s[0] <= '9'; 11 | } 12 | 13 | int* create_int(int value) { 14 | int* p = malloc(sizeof(int)); 15 | *p = value; 16 | return p; 17 | } 18 | 19 | // create variable or constant 20 | static Var create_variable(String name, Runtime runtime) { 21 | int* var_memory = map_find(runtime->variables, name); 22 | if(var_memory == NULL) { 23 | var_memory = create_int(atoi(name)); 24 | map_insert(runtime->variables, strdup(name), var_memory); 25 | } 26 | 27 | #ifndef OPTIMIZED_VARS 28 | return strdup(name); 29 | #else 30 | return var_memory; 31 | #endif 32 | } 33 | 34 | static Array create_array(String name, Runtime runtime) { 35 | #ifndef OPTIMIZED_VARS 36 | return strdup(name); 37 | #else 38 | Array array = map_find(runtime->arrays, name); 39 | if(array == NULL) { 40 | array = vector_create(0, NULL); 41 | map_insert(runtime->arrays, name, array); 42 | } 43 | return array; 44 | #endif 45 | } 46 | 47 | static VarLike create_varlike(String token, Runtime runtime) { 48 | VarLike varlike = malloc(sizeof(*varlike)); 49 | varlike->array = NULL; 50 | varlike->variable = NULL; 51 | char* bracket; 52 | 53 | if((bracket = strstr(token, "[")) != NULL) { 54 | // array[index] => array\0index\0 55 | *bracket = '\0'; 56 | String index = bracket + 1; 57 | index[strlen(index)-1] = '\0'; 58 | 59 | varlike->array = create_array(token, runtime); 60 | varlike->variable = create_variable(index, runtime); 61 | 62 | } else { 63 | varlike->variable = create_variable(token, runtime); 64 | varlike->array = NULL; 65 | } 66 | return varlike; 67 | } 68 | 69 | static int find_type(String tokens[], int token_n) { 70 | return 71 | strcmp(tokens[0], "writeln") == 0 ? WRITELN : 72 | token_n == 3 && strcmp(tokens[1], "=") == 0 ? ASSIGN_VAR : 73 | token_n == 5 && strcmp(tokens[1], "=") == 0 ? ASSIGN_EXP : 74 | strcmp(tokens[0], "while") == 0 ? WHILE : 75 | strcmp(tokens[0], "if") == 0 ? IF : 76 | 77 | 78 | strcmp(tokens[0], "write") == 0 ? WRITE : 79 | strcmp(tokens[0], "read") == 0 ? READ: 80 | strcmp(tokens[0], "random") == 0 ? RAND : 81 | strcmp(tokens[0], "break") == 0 ? BREAK : 82 | strcmp(tokens[0], "continue") == 0 ? CONTINUE : 83 | strcmp(tokens[0], "argument") == 0 && strcmp(tokens[1], "size") == 0 ? ARG_SIZE : 84 | strcmp(tokens[0], "argument") == 0 ? ARG : 85 | strcmp(tokens[0], "new") == 0 ? NEW : 86 | strcmp(tokens[0], "free") == 0 ? FREE : 87 | strcmp(tokens[0], "size") == 0 ? SIZE : 88 | -1; 89 | } 90 | 91 | static Statement create_statement(String tokens[], int token_n, String line, int line_no, Runtime runtime) { 92 | int type = find_type(tokens, token_n); 93 | if(type == -1) { 94 | printf("error in line %s\n", line); 95 | exit(1); 96 | } 97 | 98 | Statement stm = calloc(1, sizeof(*stm)); 99 | stm->line = line; 100 | stm->line_no = line_no; 101 | stm->type = type; 102 | 103 | if(type == WRITE || type == WRITELN || type == READ || type == RAND || 104 | ((type == BREAK || type == CONTINUE) && token_n == 2)) { 105 | 106 | stm->var1 = create_varlike(tokens[1], runtime); 107 | 108 | } else if(type == ASSIGN_VAR || type == ASSIGN_EXP) { 109 | stm->var1 = create_varlike(tokens[0], runtime); 110 | stm->var2 = create_varlike(tokens[2], runtime); 111 | 112 | if(type == ASSIGN_EXP) { 113 | stm->oper = tokens[3]; 114 | stm->var3 = create_varlike(tokens[4], runtime); 115 | } 116 | 117 | } else if(type == IF || type == WHILE) { 118 | stm->var1 = create_varlike(tokens[1], runtime); 119 | stm->oper = tokens[2]; 120 | stm->var2 = create_varlike(tokens[3], runtime); 121 | 122 | /////////////////////////// 123 | 124 | } else if(type == ARG_SIZE) { 125 | stm->var1 = create_varlike(tokens[2], runtime); 126 | 127 | } else if(type == ARG) { 128 | stm->var1 = create_varlike(tokens[1], runtime); 129 | stm->var2 = create_varlike(tokens[2], runtime); 130 | 131 | } else if(type == NEW) { 132 | stm->array = create_array(strtok(tokens[1], "[]"), runtime); 133 | stm->var1 = create_varlike(strtok(NULL, "[]"), runtime); 134 | 135 | } else if(type == FREE) { 136 | stm->array = create_array(tokens[1], runtime); 137 | 138 | } else if(type == SIZE) { 139 | stm->array = create_array(tokens[1], runtime); 140 | stm->var1 = create_varlike(tokens[2], runtime); 141 | } 142 | 143 | return stm; 144 | } 145 | 146 | static Vector get_nested_source(Vector source, int start) { 147 | Vector nested = vector_create(0, NULL); 148 | for(int i = start; i < vector_size(source); i++) { 149 | String line = vector_get_at(source, i); 150 | if(line[0] != '\t' && line[0] != '\0' && line[1] != '#') 151 | break; 152 | vector_insert_last(nested, line+1); 153 | } 154 | return nested; 155 | } 156 | 157 | static Program create_program(Vector source, Runtime runtime) { 158 | Program prog = list_create(NULL); 159 | 160 | for(int i = 0; i < vector_size(source); i++) { 161 | String orig_line = vector_get_at(source, i); 162 | 163 | // split in tokens 164 | String tokenized_line = strdup(orig_line); 165 | String tokens[50]; 166 | int token_n = 0; 167 | for(String token = strtok(tokenized_line, " \t\n"); token != NULL && token[0] != '#'; token = strtok(NULL, " \t\n")) 168 | tokens[token_n++] = token; 169 | 170 | if(token_n == 0) 171 | continue; // empty line 172 | 173 | // else branches should be inserted in the last if statement 174 | if(strcmp(tokens[0], "else") == 0) { 175 | Vector nested = get_nested_source(source, i+1); 176 | Statement if_st = list_node_value(prog, list_last(prog)); 177 | if_st->else_body = create_program(nested, runtime); 178 | i += vector_size(nested); 179 | vector_destroy(nested); 180 | continue; 181 | } 182 | 183 | Statement statement = create_statement(tokens, token_n, orig_line, i, runtime); 184 | 185 | // if/while have bodies 186 | if(statement->type == IF || statement->type == WHILE) { 187 | Vector nested = get_nested_source(source, i+1); 188 | statement->body = create_program(nested, runtime); 189 | i += vector_size(nested); 190 | vector_destroy(nested); 191 | } 192 | 193 | // insert in list 194 | list_insert_next(prog, list_last(prog), statement); 195 | } 196 | 197 | return prog; 198 | } 199 | 200 | Runtime parser_create_runtime(Vector source, Vector args, bool verbose) { 201 | Runtime runtime = malloc(sizeof(*runtime)); 202 | runtime->variables = map_create((CompareFunc)strcmp, NULL, NULL); 203 | runtime->arrays = map_create((CompareFunc)strcmp, NULL, NULL); 204 | runtime->args = args; 205 | runtime->verbose = verbose; 206 | runtime->program = create_program(source, runtime); 207 | return runtime; 208 | } 209 | 210 | void parser_destroy_program(Program prog) { 211 | 212 | } -------------------------------------------------------------------------------- /programs/ip_projects/ipli/interpreter.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | 6 | #include "ADTMap.h" 7 | #include "interpreter.h" 8 | 9 | 10 | 11 | // Επιστρέφει 12 | // n για break n 13 | // -n για continue n 14 | // 0 για οτιδήποτε άλλο 15 | // 16 | static int interpreter_run_program(Program prog, Runtime runtime); 17 | 18 | 19 | static int evaluate_variable(Var var, Runtime runtime) { 20 | #ifndef OPTIMIZED_VARS 21 | return *(int*)map_find(runtime->variables, var); 22 | #else 23 | return *var; 24 | #endif 25 | } 26 | 27 | static int evaluate_varlike(VarLike varlike, Runtime runtime) { 28 | if(varlike->array != NULL) { 29 | #ifndef OPTIMIZED_VARS 30 | Vector vec = map_find(runtime->arrays, varlike->array); 31 | if(vec == NULL) { 32 | printf("array %s not allocated\n", varlike->array); 33 | exit(-1); 34 | } 35 | #else 36 | Vector vec = varlike->array; 37 | #endif 38 | return *(int*)vector_get_at(vec, evaluate_variable(varlike->variable, runtime)); 39 | 40 | } else { 41 | return evaluate_variable(varlike->variable, runtime); 42 | } 43 | } 44 | 45 | static int evaluate_int_expression(VarLike var1, String oper, VarLike var2, Runtime runtime) { 46 | int val1 = evaluate_varlike(var1, runtime); 47 | int val2 = evaluate_varlike(var2, runtime); 48 | return 49 | oper[0] == '+' ? val1 + val2 : 50 | oper[0] == '-' ? val1 - val2 : 51 | oper[0] == '*' ? val1 * val2 : 52 | oper[0] == '/' ? val1 / val2 : 53 | oper[0] == '%' ? val1 % val2 : 54 | -1; 55 | } 56 | 57 | static bool evaluate_bool_expression(VarLike var1, String oper, VarLike var2, Runtime runtime) { 58 | int val1 = evaluate_varlike(var1, runtime); 59 | int val2 = evaluate_varlike(var2, runtime); 60 | return 61 | oper[0] == '=' ? val1 == val2 : 62 | oper[0] == '!' ? val1 != val2 : 63 | oper[0] == '>' && oper[1] == '=' ? val1 >= val2 : 64 | oper[0] == '>' && oper[1] == '=' ? val1 <= val2 : 65 | oper[0] == '>' ? val1 > val2 : 66 | oper[0] == '<' ? val1 < val2 : 67 | -1; 68 | } 69 | 70 | static void assign_to_varlike(VarLike varlike, int value, Runtime runtime) { 71 | #ifndef OPTIMIZED_VARS 72 | if(varlike->array != NULL) { 73 | Vector vec = map_find(runtime->arrays, varlike->array); 74 | int index= evaluate_variable(varlike->variable, runtime); 75 | *(int*)vector_get_at(vec, index) = value; 76 | 77 | } else { 78 | int* var_memory = map_find(runtime->variables, varlike->variable); 79 | if(var_memory == NULL) { 80 | var_memory = create_int(0); 81 | map_insert(runtime->variables, varlike->variable, var_memory); 82 | } 83 | *var_memory = value; 84 | } 85 | 86 | #else 87 | if(varlike->array != NULL) 88 | *(int*)vector_get_at(varlike->array, *(varlike->variable)) = value; 89 | else 90 | *(varlike->variable) = value; 91 | #endif 92 | } 93 | 94 | static int run_statement(Statement stm, Runtime runtime) { 95 | if(runtime->verbose) 96 | printf("%d:\t%s", stm->line_no, stm->line); 97 | 98 | switch(stm->type) { 99 | case WRITELN: 100 | printf("%d\n", evaluate_varlike(stm->var1, runtime)); 101 | return 0; 102 | 103 | case ASSIGN_VAR: 104 | assign_to_varlike(stm->var1, evaluate_varlike(stm->var2, runtime), runtime); 105 | return 0; 106 | 107 | case ASSIGN_EXP: { 108 | int value = evaluate_int_expression(stm->var2, stm->oper, stm->var3, runtime); 109 | assign_to_varlike(stm->var1, value, runtime); 110 | return 0; 111 | } 112 | 113 | case IF: { 114 | int val = evaluate_bool_expression(stm->var1, stm->oper, stm->var2, runtime); 115 | if(val) 116 | return interpreter_run_program(stm->body, runtime); 117 | else if(stm->else_body != NULL) 118 | return interpreter_run_program(stm->else_body, runtime); 119 | else 120 | return 0; 121 | } 122 | 123 | case WHILE: 124 | while(1) { 125 | int val = evaluate_bool_expression(stm->var1, stm->oper, stm->var2, runtime); 126 | if(!val) return 0; 127 | 128 | // Το τρέχων loop διακόπτεται είναι από κάποιο break, είτε και από continue >=2 129 | int result = interpreter_run_program(stm->body, runtime); 130 | if(result >= 1 || result <= -2) 131 | return result + (result > 0 ? -1 : 1); // μειώνουμε το level κατά 1 132 | } 133 | return 0; 134 | 135 | //////////////////////////// 136 | 137 | case WRITE: 138 | printf("%d ", evaluate_varlike(stm->var1, runtime)); 139 | return 0; 140 | 141 | case RAND: 142 | assign_to_varlike(stm->var1, rand(), runtime); 143 | return 0; 144 | 145 | case READ: { 146 | int value; 147 | if(scanf("%d", &value) != 1) 148 | exit(1); 149 | assign_to_varlike(stm->var1, value, runtime); 150 | return 0; 151 | } 152 | 153 | case BREAK: 154 | return stm->var1 != NULL ? evaluate_varlike(stm->var1, runtime) : 1; 155 | 156 | case CONTINUE: 157 | return -1 * (stm->var1 != NULL ? evaluate_varlike(stm->var1, runtime) : 1); 158 | 159 | 160 | case ARG_SIZE: 161 | assign_to_varlike(stm->var1, vector_size(runtime->args), runtime); 162 | return 0; 163 | 164 | case ARG: { 165 | int n = evaluate_varlike(stm->var1, runtime); 166 | assign_to_varlike(stm->var2, atoi(vector_get_at(runtime->args, n-1)), runtime); 167 | return 0; 168 | } 169 | 170 | case NEW: { 171 | int size = evaluate_varlike(stm->var1, runtime); 172 | 173 | #ifndef OPTIMIZED_VARS 174 | Vector vec = vector_create(0, NULL); 175 | for(int i = 0; i < size; i++) 176 | vector_insert_last(vec, create_int(0)); 177 | map_insert(runtime->arrays, stm->array, vec); 178 | #else 179 | while(size > vector_size(stm->array)) 180 | vector_insert_last(stm->array, calloc(1, sizeof(int))); 181 | #endif 182 | return 0; 183 | } 184 | 185 | case FREE: { 186 | #ifndef OPTIMIZED_VARS 187 | Vector vec = map_find(runtime->arrays, stm->array); 188 | map_remove(runtime->arrays, stm->array); 189 | vector_destroy(vec); 190 | #else 191 | while(vector_size(stm->array) > 0) 192 | vector_remove_last(stm->array); 193 | #endif 194 | return 0; 195 | } 196 | 197 | case SIZE: { 198 | #ifndef OPTIMIZED_VARS 199 | Vector vec = map_find(runtime->arrays, stm->array); 200 | assign_to_varlike(stm->var1, vector_size(vec), runtime); 201 | #else 202 | assign_to_varlike(stm->var1, vector_size(stm->array), runtime); 203 | #endif 204 | return 0; 205 | } 206 | } 207 | return 0; 208 | } 209 | 210 | static int interpreter_run_program(Program prog, Runtime runtime) { 211 | for(ListNode node = list_first(prog); node != LIST_EOF; node = list_next(prog, node)) { 212 | Statement statement = list_node_value(prog, node); 213 | int result = run_statement(statement, runtime); 214 | if(result != 0) 215 | return result; // break ή continue, διακόπτεται η τρέχουσα λίστα εντολών 216 | } 217 | return 0; 218 | } 219 | 220 | void interpreter_run(Runtime runtime) { 221 | interpreter_run_program(runtime->program, runtime); 222 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | ////////////// Compile /////////////////////////////////// 5 | { 6 | // we also tried "cppbuild". POS: no 1 second delay. NEG: more verbose, no color output, less reliable matched (ctrl-click often failed) 7 | "type": "shell", 8 | "label": "Single file: compile", 9 | "command": "gcc", 10 | "args": [ 11 | "-g", 12 | "-Werror", 13 | "-Wall", 14 | "${fileBasename}", 15 | "-lm", 16 | "-o", 17 | "${fileBasenameNoExtension}" 18 | ], 19 | "options": { 20 | "cwd": "${fileDirname}" 21 | }, 22 | "presentation": { 23 | "clear": true, 24 | "showReuseMessage": false 25 | }, 26 | "problemMatcher": { 27 | "base": "$gcc", 28 | // without explicitly setting a relative fileLocation the errors on the __problems pane__ are not clickable 29 | "fileLocation": [ 30 | "relative", 31 | "${fileDirname}" 32 | ] 33 | }, 34 | "group": "build" 35 | }, 36 | { 37 | "type": "shell", 38 | "label": "All files in directory: compile", 39 | "command": "gcc", 40 | "args": [ 41 | "-g", 42 | "-Werror", 43 | "-Wall", 44 | "*.c", 45 | "-lm", 46 | "-o", 47 | "${config:c_project.program}" 48 | ], 49 | "options": { 50 | "cwd": "${fileDirname}" 51 | }, 52 | "presentation": { 53 | "clear": true, 54 | "showReuseMessage": false 55 | }, 56 | "problemMatcher": { 57 | "base": "$gcc", 58 | // without explicitly setting a relative fileLocation the errors on the __problems pane__ are not clickable 59 | "fileLocation": [ 60 | "relative", 61 | "${fileDirname}" 62 | ] 63 | }, 64 | "group": "build" 65 | }, 66 | { 67 | "type": "shell", 68 | "label": "Make: compile", 69 | "command": "make", 70 | "args": [ 71 | "${config:c_project.program}" 72 | ], 73 | "options": { 74 | "cwd": "${workspaceFolder}/${config:c_project.dir}" 75 | }, 76 | "presentation": { 77 | "clear": true, 78 | "showReuseMessage": false 79 | }, 80 | "problemMatcher": { 81 | "base": "$gcc", 82 | // without explicitly setting a relative fileLocation the errors on the __problems pane__ are not clickable 83 | "fileLocation": [ 84 | "relative", 85 | "${workspaceFolder}/${config:c_project.dir}" 86 | ] 87 | }, 88 | "group": "build" 89 | }, 90 | 91 | ////////////// Compile and run /////////////////////////////////// 92 | { 93 | "type": "shell", // less verbose 94 | "label": "Single file: compile and run", 95 | "command": "${workspaceFolder}/.vscode/run.sh", 96 | "args": [], 97 | "options": { 98 | "cwd": "${fileDirname}", // important for line-matching in error messages 99 | "env": { 100 | "CODE_RUN": "./${fileBasenameNoExtension} ${config:c_project.args}", 101 | }, 102 | "shell": { 103 | "executable": "bash", 104 | } 105 | }, 106 | "presentation": { 107 | "echo": false, 108 | }, 109 | "problemMatcher": [], 110 | "dependsOn": "Single file: compile", 111 | "group": "build" 112 | }, 113 | { 114 | "type": "shell", 115 | "label": "All files in directory: compile and run", 116 | "command": "${workspaceFolder}/.vscode/run.sh", 117 | "args": [], 118 | "options": { 119 | "cwd": "${fileDirname}", // important for line-matching in error messages 120 | "env": { 121 | "CODE_RUN": "./${config:c_project.program} ${config:c_project.args}", 122 | }, 123 | "shell": { 124 | "executable": "bash", 125 | } 126 | }, 127 | "presentation": { 128 | "echo": false, 129 | }, 130 | "problemMatcher": [], 131 | "dependsOn": "All files in directory: compile", 132 | "group": "build" 133 | }, 134 | { 135 | "type": "shell", 136 | "label": "Make: compile and run", 137 | "command": "${workspaceFolder}/.vscode/run.sh", 138 | "args": [], 139 | "options": { 140 | "cwd": "${workspaceFolder}/${config:c_project.dir}", // important for line-matching in error messages 141 | "env": { 142 | "CODE_RUN": "./${config:c_project.program} ${config:c_project.args}", 143 | }, 144 | "shell": { 145 | "executable": "bash", 146 | } 147 | }, 148 | "presentation": { 149 | "echo": false, 150 | }, 151 | "problemMatcher": [], 152 | "dependsOn": "Make: compile", 153 | "group": { 154 | "kind": "build", 155 | "isDefault": true 156 | } 157 | }, 158 | 159 | 160 | ////////////// Compile and run with valgrind /////////////////////////////////// 161 | { 162 | "type": "shell", 163 | "label": "Single file: compile and run with valgrind", 164 | "command": "${workspaceFolder}/.vscode/run.sh", 165 | "args": [], 166 | "options": { 167 | "cwd": "${fileDirname}", // important for line-matching in error messages 168 | "env": { 169 | "CODE_RUN": "valgrind --error-exitcode=1 --leak-check=full --show-leak-kinds=all ./${fileBasenameNoExtension} ${config:c_project.args}", 170 | }, 171 | "shell": { 172 | "executable": "bash", 173 | } 174 | }, 175 | "presentation": { 176 | "echo": false, 177 | }, 178 | "problemMatcher": [], 179 | "dependsOn": "Single file: compile", 180 | "group": "build" 181 | }, 182 | { 183 | "type": "shell", 184 | "label": "All files in directory: compile and run with valgrind", 185 | "command": "${workspaceFolder}/.vscode/run.sh", 186 | "args": [], 187 | "options": { 188 | "cwd": "${fileDirname}", // important for line-matching in error messages 189 | "env": { 190 | "CODE_RUN": "valgrind --error-exitcode=1 --leak-check=full --show-leak-kinds=all ./${config:c_project.program} ${config:c_project.args}", 191 | }, 192 | "shell": { 193 | "executable": "bash", 194 | } 195 | }, 196 | "presentation": { 197 | "echo": false, 198 | }, 199 | "problemMatcher": [], 200 | "dependsOn": "All files in directory: compile", 201 | "group": "build" 202 | }, 203 | { 204 | "type": "shell", 205 | "label": "Make: compile and run with valgrind", 206 | "command": "${workspaceFolder}/.vscode/run.sh", 207 | "args": [], 208 | "options": { 209 | "cwd": "${workspaceFolder}/${config:c_project.dir}", // important for line-matching in error messages 210 | "env": { 211 | "CODE_RUN": "valgrind --error-exitcode=1 --leak-check=full --show-leak-kinds=all ./${config:c_project.program} ${config:c_project.args}", 212 | }, 213 | "shell": { 214 | "executable": "bash", 215 | } 216 | }, 217 | "presentation": { 218 | "echo": false, 219 | }, 220 | "problemMatcher": [], 221 | "dependsOn": "Make: compile", 222 | "group": "build" 223 | }, 224 | 225 | ////////////// Set args /////////////////////////////////// 226 | { 227 | "type": "cppbuild", // no 1 second delay 228 | "label": "Set program arguments", 229 | "command": "code", 230 | "args": [ 231 | "${workspaceFolder}/.vscode/settings.json" 232 | ], 233 | "presentation": { 234 | "echo": false, 235 | "reveal": "never" 236 | }, 237 | "problemMatcher": [], 238 | "group": "build" 239 | }, 240 | 241 | ////////////// For the debugger (see launch.json) /////////////////////////////////// 242 | { 243 | "label": "debug-single-file", 244 | "hide": true, 245 | "dependsOrder": "sequence", 246 | "dependsOn": ["Single file: compile", "Setup debugger"] 247 | }, 248 | { 249 | "label": "debug-all-files", 250 | "hide": true, 251 | "dependsOrder": "sequence", 252 | "dependsOn": ["All files in directory: compile", "Setup debugger"] 253 | }, 254 | { 255 | "label": "debug-make", 256 | "hide": true, 257 | "dependsOrder": "sequence", 258 | "dependsOn": ["Make: compile", "Setup debugger"] 259 | }, 260 | { 261 | "type": "cppbuild", // no 1 second delay 262 | "label": "Setup debugger", 263 | "hide": true, 264 | "command": "./setup-debugger.sh", 265 | "args": [], 266 | "options": { 267 | "cwd": "${workspaceFolder}/.vscode", 268 | "shell": { 269 | "executable": "bash", 270 | } 271 | }, 272 | "presentation": { 273 | "echo": false, 274 | "reveal": "never" 275 | }, 276 | "problemMatcher": [], 277 | "group": "build" 278 | } 279 | ], 280 | } -------------------------------------------------------------------------------- /.vscode/visualize-c-memory.py: -------------------------------------------------------------------------------- 1 | import gdb # pyright: reportMissingImports=false 2 | import subprocess 3 | import json 4 | import html 5 | import traceback 6 | 7 | 8 | ### Register pretty printer ###################### 9 | 10 | class MemoryPrinter: 11 | def __init__(self): 12 | pass 13 | def to_string(self): 14 | return visualize_memory() 15 | 16 | def lookup_printer(value): 17 | # Use MemoryPrinter if value is the string "memory" 18 | if value.type.strip_typedefs().code == gdb.TYPE_CODE_ARRAY and value.type.target().strip_typedefs().code == gdb.TYPE_CODE_INT and value.string() == "memory": 19 | return MemoryPrinter() 20 | else: 21 | return None 22 | 23 | gdb.pretty_printers.append(lookup_printer) 24 | 25 | 26 | ### The actual visualization ######################## 27 | 28 | # Returns a json visualization of memory that can be consumed by vscode-debug-visualizer 29 | def visualize_memory(): 30 | try: 31 | return json.dumps({ 32 | 'kind': { 'svg': True }, 33 | 'text': svg_of_memory(), 34 | }) 35 | except BaseException as e: 36 | # display errors using the text visualizer 37 | return json.dumps({ 38 | 'kind': { 'text': True }, 39 | 'text': str(e) + "\n\n\n\n\n\n\n" + traceback.format_exc() 40 | }) 41 | 42 | def svg_of_memory(): 43 | memory = { 44 | 'stack': recs_of_stack(), 45 | 'heap': rec_of_heap(), 46 | } 47 | infer_heap_types(memory) 48 | 49 | # If the heap is too large, show only the last 100 entries 50 | if(len(memory['heap']['values']) > 100): 51 | memory['heap']['name'] = 'Heap (100 most recent entries)' 52 | memory['heap']['values'] = memory['heap']['values'][-100:] 53 | memory['heap']['fields'] = memory['heap']['fields'][-100:] 54 | 55 | dot = f""" 56 | digraph G {{ 57 | layout = neato; 58 | overlap = false; 59 | node [style=none, shape=none]; 60 | 61 | {dot_of_stack(memory)} 62 | dummy[pos="1,0!",style=invis,width=0.8]; // space 63 | {dot_of_heap(memory)} 64 | {dot_of_pointers(memory)} 65 | }} 66 | """ 67 | 68 | # debugging 69 | # print(dot) 70 | # return json.dumps({ 71 | # 'kind': { 'text': True }, 72 | # 'text': dot, 73 | # }) 74 | 75 | # vscode-debug-visualizer can directly display graphviz dot format. However 76 | # its implementation has issues when the visualization is updated, after the 77 | # update it's often corrupted. Maybe this has to do with the fact that 78 | # vscode-debug-visualizer runs graphviz in wasm (via viz.js). 79 | # 80 | # To avoid the isses we run graphviz ourselves, convert to svg, and use the svg visualizer. 81 | # The downside is that graphviz needs to be installed. 82 | svg = subprocess.run( 83 | ['dot', '-T', 'svg'], 84 | input=dot.encode('utf-8'), 85 | stdout=subprocess.PIPE, stderr=subprocess.PIPE) 86 | 87 | if svg.returncode != 0: 88 | raise Exception(f"dot failed:\n {svg.stderr.decode('utf-8')}\n\ndot source:\n{dot}") 89 | 90 | return svg.stdout.decode('utf-8') 91 | 92 | def dot_of_stack(memory): 93 | rows = [['Stack']] 94 | for frame_rec in memory['stack']: 95 | rows += rows_of_rec(frame_rec, memory) 96 | 97 | return f""" 98 | stack[pos="0,0!",label=< 99 | {table_of_rows(rows)} 100 | >]; 101 | """ 102 | 103 | def dot_of_heap(memory): 104 | # pos="2,0" makes heap to be slightly on the right of stack/dummy. 105 | # overlap = false will force it further to the right, to avoid overlap. 106 | # but pos="2,0" is important to establish the relative position between the two. 107 | 108 | rows = rows_of_rec(memory['heap'], memory) 109 | return f""" 110 | heap[pos="2,0!",label=< 111 | {table_of_rows(rows)} 112 | >]; 113 | """ 114 | 115 | def table_of_rows(rows): 116 | res = f""" 117 | 118 | """ 119 | 120 | col_n = max([len(row) for row in rows]) 121 | for row in rows: 122 | # the last cell is the address, put it first 123 | row.insert(0, row.pop()) 124 | 125 | # if the row has missing columns, add a colspan to the last cell 126 | if len(row) < col_n: 127 | row[-1] = row[-1].replace('{"".join(row)}\n' 130 | 131 | res += '
' 132 | return res 133 | 134 | def dot_of_pointers(memory): 135 | # construct stack:"XXXXXXX-right":e or heap:"XXXXXX-left":w 136 | def anchor_of_rec(rec): 137 | return rec['area'] + ':"' + rec["address"] + ('-right":e' if rec['area'] == 'stack' else '-left":w') 138 | 139 | res = "" 140 | for rec in find_pointers(memory): 141 | target_rec = lookup_address(rec['value'], memory) 142 | if target_rec is not None: 143 | res += f""" 144 | {anchor_of_rec(rec)} -> {anchor_of_rec(target_rec)}; 145 | """ 146 | return res 147 | 148 | def rows_of_rec(rec, memory): 149 | if rec['kind'] in ['array', 'struct', 'frame']: 150 | res = [] 151 | for i in range(len(rec['values'])): 152 | name = rec['fields'][i] if rec['kind'] != 'array' else str(i) 153 | value_rec = rec['values'][i] 154 | rows = rows_of_rec(value_rec, memory) 155 | 156 | if len(rows) == 0: # it can happen in case of empty array 157 | continue 158 | 159 | # the name is only inserted in the first row, with a rowspan to include all of them 160 | # an empty cell is also added to all other rows, so that len(row) always gives the number of cols 161 | rows[0].insert(0, f""" 162 | {name} 163 | """) 164 | for row in rows[1:]: 165 | row.insert(0, '') 166 | 167 | res += rows 168 | 169 | if rec['kind'] == 'frame': 170 | # at least 170 width, to avoid initial heap looking tiny 171 | res.insert(0, [f'{rec["name"]}']) 172 | 173 | else: 174 | color = 'red' if rec['kind'] == 'pointer' and rec['value'] != "0" and lookup_address(rec['value'], memory) is None else 'black' 175 | res = [[ 176 | f"""{rec['value']}""", 177 | f"""{rec['address']} ({rec['size']})""", 178 | ]] 179 | 180 | return res 181 | 182 | 183 | def address_within_rec(address, rec): 184 | address_i = int(address, 16) 185 | rec_i = int(rec['address'], 16) 186 | return address_i >= rec_i and address_i < rec_i + rec['size'] 187 | 188 | # Check if address is within any of the known records, if so return that record 189 | def lookup_address(address, memory): 190 | for rec in [memory['heap']] + memory['stack']: 191 | res = lookup_address_rec(address, rec) 192 | if res != None: 193 | return res 194 | return None 195 | 196 | def lookup_address_rec(address, rec): 197 | if rec['kind'] in ['array', 'struct', 'frame']: 198 | for value in rec['values']: 199 | res = lookup_address_rec(address, value) 200 | if res != None: 201 | return res 202 | return None 203 | else: 204 | return rec if address_within_rec(address, rec) else None 205 | 206 | 207 | # Check if any of the known (non-void) pointers points to address, if so return the rec of the pointer 208 | def lookup_pointer(address, memory): 209 | for rec in find_pointers(memory): 210 | # exclud void pointers 211 | if rec['value'] == address and rec['type'].target().code != gdb.TYPE_CODE_VOID: 212 | return rec 213 | return None 214 | 215 | # recursively finds all pointers 216 | def find_pointers(memory): 217 | return find_pointers_rec(memory['heap']) + \ 218 | [pointer for frame in memory['stack'] for pointer in find_pointers_rec(frame)] 219 | 220 | def find_pointers_rec(rec): 221 | if rec['kind'] in ['frame', 'array', 'struct']: 222 | return [pointer for rec in rec['values'] for pointer in find_pointers_rec(rec)] 223 | elif rec['kind'] == 'pointer': 224 | return [rec] 225 | else: 226 | return [] 227 | 228 | def format_pointer(val): 229 | return hex(int(val)).replace('0x', "") if val is not None else "" 230 | 231 | def rec_of_heap(): 232 | # we return a 'frame' rec 233 | rec = { 234 | 'kind': 'frame', 235 | 'name': 'Heap', 236 | 'fields': [], 237 | 'values': [], 238 | } 239 | 240 | # get the linked list from watch_heap.c 241 | try: 242 | heap_node_ptr = gdb.parse_and_eval("heap_contents")['next'] 243 | except: 244 | raise Exception( 245 | "Heap information not found.\n" 246 | "You need to load visualize-c-memory.so by setting the environment variable\n" 247 | " LD_PRELOAD=/visualize-c-memory.so\n" 248 | "_or_ link your program with visualize-c-memory.c" 249 | ) 250 | 251 | while int(heap_node_ptr) != 0: 252 | # read node from the linked list 253 | heap_node = heap_node_ptr.dereference() 254 | pointer = heap_node['pointer'] 255 | size = int(heap_node['size']) 256 | source = chr(heap_node['source']) 257 | heap_node_ptr = heap_node['next'] 258 | 259 | # for the moment we have no type information, so we just create an 'untyped' record 260 | rec['fields'].append(f"{'malloc' if source == 'm' else 'realloc' if source == 'r' else 'calloc'}({size})") 261 | rec['values'].append({ 262 | 'name': " ", # space to avoid errors 263 | 'value': "?", 264 | 'size': size, 265 | 'address': format_pointer(pointer), 266 | 'area': 'heap', 267 | 'kind': 'untyped', 268 | }) 269 | 270 | # the linked list contains the heap contents in reverse order 271 | rec['fields'].reverse() 272 | rec['values'].reverse() 273 | 274 | return rec 275 | 276 | def infer_heap_types(memory): 277 | for i,rec in enumerate(memory['heap']['values']): 278 | if rec['kind'] != 'untyped': 279 | continue 280 | 281 | incoming_pointer = lookup_pointer(rec['address'], memory) 282 | if incoming_pointer is None: 283 | continue 284 | 285 | type = incoming_pointer['type'] 286 | if type.target().code == gdb.TYPE_CODE_VOID: 287 | continue # void pointer, not useful 288 | 289 | if type.target().sizeof == 0: 290 | # pointer to incomplete struct, just add the type name to the "?" value 291 | code_name = 'struct ' if type.target().code == gdb.TYPE_CODE_STRUCT else \ 292 | 'union ' if type.target().code == gdb.TYPE_CODE_UNION else '' 293 | rec['value'] = f'? ({code_name}{type.target().name})' 294 | continue 295 | 296 | # we use the type information to get a typed value, then 297 | # replace the heap rec with a new one obtained from the typed value 298 | n = int(rec['size'] / type.target().sizeof) 299 | if n > 1: 300 | # the malloced space is larger than the pointer's target type, most likely this is used as an array 301 | # we treat the pointer as a pointer to array 302 | type = type.target().array(n-1).pointer() 303 | 304 | value = gdb.Value(int(rec['address'], 16)).cast(type).dereference() 305 | memory['heap']['values'][i] = rec_of_value(value, 'heap') 306 | 307 | # the new value might itself contain pointers which can be used to 308 | # type records we already processed. So re-start frrom scratch 309 | return infer_heap_types(memory) 310 | 311 | def recs_of_stack(): 312 | res = [] 313 | frame = gdb.newest_frame() 314 | while frame is not None: 315 | res.append(rec_of_frame(frame)) 316 | frame = frame.older() 317 | 318 | res.reverse() 319 | return res 320 | 321 | def rec_of_frame(frame): 322 | # we want blocks in reverse order, but symbols within the block in the correct order! 323 | blocks = [frame.block()] 324 | while blocks[0].function is None: 325 | blocks.insert(0, blocks[0].superblock) 326 | 327 | rec = { 328 | 'kind': 'frame', 329 | 'name': frame.function().name, 330 | 'fields': [], 331 | 'values': [], 332 | } 333 | for block in blocks: 334 | for symb in block: 335 | # avoid "weird" symbols, eg labels 336 | if not (symb.is_variable or symb.is_argument or symb.is_function or symb.is_constant): 337 | continue 338 | 339 | var = symb.name 340 | rec['fields'].append(var) 341 | rec['values'].append(rec_of_value(symb.value(frame), 'stack')) 342 | 343 | return rec 344 | 345 | def rec_of_value(value, area): 346 | type = value.type.strip_typedefs() 347 | rec = { 348 | 'size': type.sizeof, 349 | 'address': format_pointer(value.address), 350 | 'type': type, 351 | 'area': area, 352 | } 353 | 354 | if type.code == gdb.TYPE_CODE_ARRAY: 355 | # stack arrays of dynamic length (eg int foo[n]) might have huge size before the 356 | # initialization code runs! In this case replace type with one of size 0 357 | if int(type.sizeof) > 1000: 358 | type = type.target().array(-1) 359 | 360 | array_size = int(type.sizeof / type.target().sizeof) 361 | 362 | rec['values'] = [rec_of_value(value[i], area) for i in range(array_size)] 363 | rec['kind'] = 'array' 364 | 365 | elif type.code == gdb.TYPE_CODE_STRUCT: 366 | rec['fields'] = [field.name for field in type.fields()] 367 | rec['values'] = [rec_of_value(value[field], area) for field in type.fields()] 368 | rec['kind'] = 'struct' 369 | 370 | else: 371 | # treat function pointers as scalar values 372 | is_pointer = type.code == gdb.TYPE_CODE_PTR 373 | func_pointer = is_pointer and type.target().code == gdb.TYPE_CODE_FUNC 374 | 375 | if is_pointer and not func_pointer: 376 | rec['value'] = format_pointer(value) 377 | rec['kind'] = 'pointer' 378 | else: 379 | try: 380 | rec['value'] = html.escape(value.format_string()) 381 | except: 382 | rec['value'] = '?' 383 | rec['kind'] = 'other' 384 | if func_pointer: 385 | rec['value'] = rec['value'].replace("0x", "").replace(" ", "
") 386 | 387 | return rec -------------------------------------------------------------------------------- /include/acutest.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Acutest -- Another C/C++ Unit Test facility 3 | * 4 | * 5 | * Copyright 2013-2020 Martin Mitas 6 | * Copyright 2019 Garrett D'Amore 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a 9 | * copy of this software and associated documentation files (the "Software"), 10 | * to deal in the Software without restriction, including without limitation 11 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 12 | * and/or sell copies of the Software, and to permit persons to whom the 13 | * Software is furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in 16 | * all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 19 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 23 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 24 | * IN THE SOFTWARE. 25 | */ 26 | 27 | #ifndef ACUTEST_H__ 28 | #define ACUTEST_H__ 29 | 30 | 31 | /************************ 32 | *** Public interface *** 33 | ************************/ 34 | 35 | /* By default, "acutest.h" provides the main program entry point (function 36 | * main()). However, if the test suite is composed of multiple source files 37 | * which include "acutest.h", then this causes a problem of multiple main() 38 | * definitions. To avoid this problem, #define macro TEST_NO_MAIN in all 39 | * compilation units but one. 40 | */ 41 | 42 | /* Macro to specify list of unit tests in the suite. 43 | * The unit test implementation MUST provide list of unit tests it implements 44 | * with this macro: 45 | * 46 | * TEST_LIST = { 47 | * { "test1_name", test1_func_ptr }, 48 | * { "test2_name", test2_func_ptr }, 49 | * ... 50 | * { 0 } 51 | * }; 52 | * 53 | * The list specifies names of each test (must be unique) and pointer to 54 | * a function implementing it. The function does not take any arguments 55 | * and has no return values, i.e. every test function has to be compatible 56 | * with this prototype: 57 | * 58 | * void test_func(void); 59 | */ 60 | #define TEST_LIST const struct test__ test_list__[] 61 | 62 | 63 | /* Macros for testing whether an unit test succeeds or fails. These macros 64 | * can be used arbitrarily in functions implementing the unit tests. 65 | * 66 | * If any condition fails throughout execution of a test, the test fails. 67 | * 68 | * TEST_CHECK takes only one argument (the condition), TEST_CHECK_ allows 69 | * also to specify an error message to print out if the condition fails. 70 | * (It expects printf-like format string and its parameters). The macros 71 | * return non-zero (condition passes) or 0 (condition fails). 72 | * 73 | * That can be useful when more conditions should be checked only if some 74 | * preceding condition passes, as illustrated in this code snippet: 75 | * 76 | * SomeStruct* ptr = allocate_some_struct(); 77 | * if(TEST_CHECK(ptr != NULL)) { 78 | * TEST_CHECK(ptr->member1 < 100); 79 | * TEST_CHECK(ptr->member2 > 200); 80 | * } 81 | */ 82 | #define TEST_CHECK_(cond,...) test_check__((cond), __FILE__, __LINE__, __VA_ARGS__) 83 | #define TEST_CHECK(cond) test_check__((cond), __FILE__, __LINE__, "%s", #cond) 84 | 85 | 86 | /* These macros are the same as TEST_CHECK_ and TEST_CHECK except that if the 87 | * condition fails, the currently executed unit test is immediately aborted. 88 | * 89 | * That is done either by calling abort() if the unit test is executed as a 90 | * child process; or via longjmp() if the unit test is executed within the 91 | * main Acutest process. 92 | * 93 | * As a side effect of such abortion, your unit tests may cause memory leaks, 94 | * unflushed file descriptors, and other fenomena caused by the abortion. 95 | * 96 | * Therefore you should not use these as a general replacement for TEST_CHECK. 97 | * Use it with some caution, especially if your test causes some other side 98 | * effects to the outside world (e.g. communicating with some server, inserting 99 | * into a database etc.). 100 | */ 101 | #define TEST_ASSERT_(cond,...) \ 102 | do { \ 103 | if (!test_check__((cond), __FILE__, __LINE__, __VA_ARGS__)) \ 104 | test_abort__(); \ 105 | } while(0) 106 | #define TEST_ASSERT(cond) \ 107 | do { \ 108 | if (!test_check__((cond), __FILE__, __LINE__, "%s", #cond)) \ 109 | test_abort__(); \ 110 | } while(0) 111 | 112 | 113 | #ifdef __cplusplus 114 | /* Macros to verify that the code (the 1st argument) throws exception of given 115 | * type (the 2nd argument). (Note these macros are only available in C++.) 116 | * 117 | * TEST_EXCEPTION_ is like TEST_EXCEPTION but accepts custom printf-like 118 | * message. 119 | * 120 | * For example: 121 | * 122 | * TEST_EXCEPTION(function_that_throw(), ExpectedExceptionType); 123 | * 124 | * If the function_that_throw() throws ExpectedExceptionType, the check passes. 125 | * If the function throws anything incompatible with ExpectedExceptionType 126 | * (or if it does not thrown an exception at all), the check fails. 127 | */ 128 | #define TEST_EXCEPTION(code, exctype) \ 129 | do { \ 130 | bool exc_ok__ = false; \ 131 | const char *msg__ = NULL; \ 132 | try { \ 133 | code; \ 134 | msg__ = "No exception thrown."; \ 135 | } catch(exctype const&) { \ 136 | exc_ok__= true; \ 137 | } catch(...) { \ 138 | msg__ = "Unexpected exception thrown."; \ 139 | } \ 140 | test_check__(exc_ok__, __FILE__, __LINE__, #code " throws " #exctype); \ 141 | if(msg__ != NULL) \ 142 | test_message__("%s", msg__); \ 143 | } while(0) 144 | #define TEST_EXCEPTION_(code, exctype, ...) \ 145 | do { \ 146 | bool exc_ok__ = false; \ 147 | const char *msg__ = NULL; \ 148 | try { \ 149 | code; \ 150 | msg__ = "No exception thrown."; \ 151 | } catch(exctype const&) { \ 152 | exc_ok__= true; \ 153 | } catch(...) { \ 154 | msg__ = "Unexpected exception thrown."; \ 155 | } \ 156 | test_check__(exc_ok__, __FILE__, __LINE__, __VA_ARGS__); \ 157 | if(msg__ != NULL) \ 158 | test_message__("%s", msg__); \ 159 | } while(0) 160 | #endif /* #ifdef __cplusplus */ 161 | 162 | 163 | /* Sometimes it is useful to split execution of more complex unit tests to some 164 | * smaller parts and associate those parts with some names. 165 | * 166 | * This is especially handy if the given unit test is implemented as a loop 167 | * over some vector of multiple testing inputs. Using these macros allow to use 168 | * sort of subtitle for each iteration of the loop (e.g. outputting the input 169 | * itself or a name associated to it), so that if any TEST_CHECK condition 170 | * fails in the loop, it can be easily seen which iteration triggers the 171 | * failure, without the need to manually output the iteration-specific data in 172 | * every single TEST_CHECK inside the loop body. 173 | * 174 | * TEST_CASE allows to specify only single string as the name of the case, 175 | * TEST_CASE_ provides all the power of printf-like string formatting. 176 | * 177 | * Note that the test cases cannot be nested. Starting a new test case ends 178 | * implicitly the previous one. To end the test case explicitly (e.g. to end 179 | * the last test case after exiting the loop), you may use TEST_CASE(NULL). 180 | */ 181 | #define TEST_CASE_(...) test_case__(__VA_ARGS__) 182 | #define TEST_CASE(name) test_case__("%s", name) 183 | 184 | 185 | /* printf-like macro for outputting an extra information about a failure. 186 | * 187 | * Intended use is to output some computed output versus the expected value, 188 | * e.g. like this: 189 | * 190 | * if(!TEST_CHECK(produced == expected)) { 191 | * TEST_MSG("Expected: %d", expected); 192 | * TEST_MSG("Produced: %d", produced); 193 | * } 194 | * 195 | * Note the message is only written down if the most recent use of any checking 196 | * macro (like e.g. TEST_CHECK or TEST_EXCEPTION) in the current test failed. 197 | * This means the above is equivalent to just this: 198 | * 199 | * TEST_CHECK(produced == expected); 200 | * TEST_MSG("Expected: %d", expected); 201 | * TEST_MSG("Produced: %d", produced); 202 | * 203 | * The macro can deal with multi-line output fairly well. It also automatically 204 | * adds a final new-line if there is none present. 205 | */ 206 | #define TEST_MSG(...) test_message__(__VA_ARGS__) 207 | 208 | 209 | /* Maximal output per TEST_MSG call. Longer messages are cut. 210 | * You may define another limit prior including "acutest.h" 211 | */ 212 | #ifndef TEST_MSG_MAXSIZE 213 | #define TEST_MSG_MAXSIZE 1024 214 | #endif 215 | 216 | 217 | /* Macro for dumping a block of memory. 218 | * 219 | * Its intended use is very similar to what TEST_MSG is for, but instead of 220 | * generating any printf-like message, this is for dumping raw block of a 221 | * memory in a hexadecimal form: 222 | * 223 | * TEST_CHECK(size_produced == size_expected && memcmp(addr_produced, addr_expected, size_produced) == 0); 224 | * TEST_DUMP("Expected:", addr_expected, size_expected); 225 | * TEST_DUMP("Produced:", addr_produced, size_produced); 226 | */ 227 | #define TEST_DUMP(title, addr, size) test_dump__(title, addr, size) 228 | 229 | /* Maximal output per TEST_DUMP call (in bytes to dump). Longer blocks are cut. 230 | * You may define another limit prior including "acutest.h" 231 | */ 232 | #ifndef TEST_DUMP_MAXSIZE 233 | #define TEST_DUMP_MAXSIZE 1024 234 | #endif 235 | 236 | 237 | /********************** 238 | *** Implementation *** 239 | **********************/ 240 | 241 | /* The unit test files should not rely on anything below. */ 242 | 243 | #include 244 | #include 245 | #include 246 | #include 247 | #include 248 | #include 249 | 250 | #if defined(unix) || defined(__unix__) || defined(__unix) || defined(__APPLE__) 251 | #define ACUTEST_UNIX__ 1 252 | #include 253 | #include 254 | #include 255 | #include 256 | #include 257 | #include 258 | #include 259 | 260 | #if defined CLOCK_PROCESS_CPUTIME_ID && defined CLOCK_MONOTONIC 261 | #define ACUTEST_HAS_POSIX_TIMER__ 1 262 | #endif 263 | #endif 264 | 265 | #if defined(__gnu_linux__) 266 | #define ACUTEST_LINUX__ 1 267 | #include 268 | #include 269 | #endif 270 | 271 | #if defined(_WIN32) || defined(__WIN32__) || defined(__WINDOWS__) 272 | #define ACUTEST_WIN__ 1 273 | #include 274 | #include 275 | #endif 276 | 277 | #ifdef __cplusplus 278 | #include 279 | #endif 280 | 281 | /* Load valgrind.h, if available. This allows to detect valgrind's presence via RUNNING_ON_VALGRIND. */ 282 | #ifdef __has_include 283 | #if __has_include() 284 | #include 285 | #endif 286 | #endif 287 | 288 | 289 | /* Note our global private identifiers end with '__' to mitigate risk of clash 290 | * with the unit tests implementation. */ 291 | 292 | 293 | #ifdef __cplusplus 294 | extern "C" { 295 | #endif 296 | 297 | 298 | struct test__ { 299 | const char* name; 300 | void (*func)(void); 301 | }; 302 | 303 | struct test_detail__ { 304 | unsigned char flags; 305 | double duration; 306 | }; 307 | 308 | enum { 309 | TEST_FLAG_RUN__ = 1 << 0, 310 | TEST_FLAG_SUCCESS__ = 1 << 1, 311 | TEST_FLAG_FAILURE__ = 1 << 2, 312 | }; 313 | 314 | extern const struct test__ test_list__[]; 315 | 316 | int test_check__(int cond, const char* file, int line, const char* fmt, ...); 317 | void test_case__(const char* fmt, ...); 318 | void test_message__(const char* fmt, ...); 319 | void test_dump__(const char* title, const void* addr, size_t size); 320 | void test_abort__(void); 321 | 322 | 323 | #ifndef TEST_NO_MAIN 324 | 325 | static char* test_argv0__ = NULL; 326 | static size_t test_list_size__ = 0; 327 | static struct test_detail__ *test_details__ = NULL; 328 | static size_t test_count__ = 0; 329 | static int test_no_exec__ = -1; 330 | static int test_no_summary__ = 0; 331 | static int test_tap__ = 0; 332 | static int test_skip_mode__ = 0; 333 | static int test_worker__ = 0; 334 | static int test_worker_index__ = 0; 335 | static int test_cond_failed__ = 0; 336 | static int test_was_aborted__ = 0; 337 | static FILE *test_xml_output__ = NULL; 338 | 339 | static int test_stat_failed_units__ = 0; 340 | static int test_stat_run_units__ = 0; 341 | 342 | static const struct test__* test_current_unit__ = NULL; 343 | static int test_current_index__ = 0; 344 | static char test_case_name__[64] = ""; 345 | static int test_current_already_logged__ = 0; 346 | static int test_case_current_already_logged__ = 0; 347 | static int test_verbose_level__ = 2; 348 | static int test_current_failures__ = 0; 349 | static int test_colorize__ = 0; 350 | static int test_timer__ = 0; 351 | 352 | static int test_abort_has_jmp_buf__ = 0; 353 | static jmp_buf test_abort_jmp_buf__; 354 | 355 | #if defined ACUTEST_WIN__ 356 | typedef LARGE_INTEGER test_timer_type__; 357 | static LARGE_INTEGER test_timer_freq__; 358 | static test_timer_type__ test_timer_start__; 359 | static test_timer_type__ test_timer_end__; 360 | 361 | static void 362 | test_timer_init__(void) 363 | { 364 | QueryPerformanceFrequency(&test_timer_freq__); 365 | } 366 | 367 | static void 368 | test_timer_get_time__(LARGE_INTEGER* ts) 369 | { 370 | QueryPerformanceCounter(ts); 371 | } 372 | 373 | static double 374 | test_timer_diff__(LARGE_INTEGER start, LARGE_INTEGER end) 375 | { 376 | double duration = (double)(end.QuadPart - start.QuadPart); 377 | duration /= (double)test_timer_freq__.QuadPart; 378 | return duration; 379 | } 380 | 381 | static void 382 | test_timer_print_diff__(void) 383 | { 384 | printf("%.6lf secs", test_timer_diff__(test_timer_start__, test_timer_end__)); 385 | } 386 | #elif defined ACUTEST_HAS_POSIX_TIMER__ 387 | static clockid_t test_timer_id__; 388 | typedef struct timespec test_timer_type__; 389 | static test_timer_type__ test_timer_start__; 390 | static test_timer_type__ test_timer_end__; 391 | 392 | static void 393 | test_timer_init__(void) 394 | { 395 | if(test_timer__ == 1) 396 | test_timer_id__ = CLOCK_MONOTONIC; 397 | else if(test_timer__ == 2) 398 | test_timer_id__ = CLOCK_PROCESS_CPUTIME_ID; 399 | } 400 | 401 | static void 402 | test_timer_get_time__(struct timespec* ts) 403 | { 404 | clock_gettime(test_timer_id__, ts); 405 | } 406 | 407 | static double 408 | test_timer_diff__(struct timespec start, struct timespec end) 409 | { 410 | double endns; 411 | double startns; 412 | 413 | endns = end.tv_sec; 414 | endns *= 1e9; 415 | endns += end.tv_nsec; 416 | 417 | startns = start.tv_sec; 418 | startns *= 1e9; 419 | startns += start.tv_nsec; 420 | 421 | return ((endns - startns)/ 1e9); 422 | } 423 | 424 | static void 425 | test_timer_print_diff__(void) 426 | { 427 | printf("%.6lf secs", 428 | test_timer_diff__(test_timer_start__, test_timer_end__)); 429 | } 430 | #else 431 | typedef int test_timer_type__; 432 | static test_timer_type__ test_timer_start__; 433 | static test_timer_type__ test_timer_end__; 434 | 435 | void 436 | test_timer_init__(void) 437 | {} 438 | 439 | static void 440 | test_timer_get_time__(int* ts) 441 | { 442 | (void) ts; 443 | } 444 | 445 | static double 446 | test_timer_diff__(int start, int end) 447 | { 448 | (void) start; 449 | (void) end; 450 | return 0.0; 451 | } 452 | 453 | static void 454 | test_timer_print_diff__(void) 455 | {} 456 | #endif 457 | 458 | #define TEST_COLOR_DEFAULT__ 0 459 | #define TEST_COLOR_GREEN__ 1 460 | #define TEST_COLOR_RED__ 2 461 | #define TEST_COLOR_DEFAULT_INTENSIVE__ 3 462 | #define TEST_COLOR_GREEN_INTENSIVE__ 4 463 | #define TEST_COLOR_RED_INTENSIVE__ 5 464 | 465 | static int 466 | test_print_in_color__(int color, const char* fmt, ...) 467 | { 468 | va_list args; 469 | char buffer[256]; 470 | int n; 471 | 472 | va_start(args, fmt); 473 | vsnprintf(buffer, sizeof(buffer), fmt, args); 474 | va_end(args); 475 | buffer[sizeof(buffer)-1] = '\0'; 476 | 477 | if(!test_colorize__) { 478 | return printf("%s", buffer); 479 | } 480 | 481 | #if defined ACUTEST_UNIX__ 482 | { 483 | const char* col_str; 484 | switch(color) { 485 | case TEST_COLOR_GREEN__: col_str = "\033[0;32m"; break; 486 | case TEST_COLOR_RED__: col_str = "\033[0;31m"; break; 487 | case TEST_COLOR_GREEN_INTENSIVE__: col_str = "\033[1;32m"; break; 488 | case TEST_COLOR_RED_INTENSIVE__: col_str = "\033[1;31m"; break; 489 | case TEST_COLOR_DEFAULT_INTENSIVE__: col_str = "\033[1m"; break; 490 | default: col_str = "\033[0m"; break; 491 | } 492 | printf("%s", col_str); 493 | n = printf("%s", buffer); 494 | printf("\033[0m"); 495 | return n; 496 | } 497 | #elif defined ACUTEST_WIN__ 498 | { 499 | HANDLE h; 500 | CONSOLE_SCREEN_BUFFER_INFO info; 501 | WORD attr; 502 | 503 | h = GetStdHandle(STD_OUTPUT_HANDLE); 504 | GetConsoleScreenBufferInfo(h, &info); 505 | 506 | switch(color) { 507 | case TEST_COLOR_GREEN__: attr = FOREGROUND_GREEN; break; 508 | case TEST_COLOR_RED__: attr = FOREGROUND_RED; break; 509 | case TEST_COLOR_GREEN_INTENSIVE__: attr = FOREGROUND_GREEN | FOREGROUND_INTENSITY; break; 510 | case TEST_COLOR_RED_INTENSIVE__: attr = FOREGROUND_RED | FOREGROUND_INTENSITY; break; 511 | case TEST_COLOR_DEFAULT_INTENSIVE__: attr = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_INTENSITY; break; 512 | default: attr = 0; break; 513 | } 514 | if(attr != 0) 515 | SetConsoleTextAttribute(h, attr); 516 | n = printf("%s", buffer); 517 | SetConsoleTextAttribute(h, info.wAttributes); 518 | return n; 519 | } 520 | #else 521 | n = printf("%s", buffer); 522 | return n; 523 | #endif 524 | } 525 | 526 | static void 527 | test_begin_test_line__(const struct test__* test) 528 | { 529 | if(!test_tap__) { 530 | if(test_verbose_level__ >= 3) { 531 | test_print_in_color__(TEST_COLOR_DEFAULT_INTENSIVE__, "Test %s:\n", test->name); 532 | test_current_already_logged__++; 533 | } else if(test_verbose_level__ >= 1) { 534 | int n; 535 | char spaces[48]; 536 | 537 | n = test_print_in_color__(TEST_COLOR_DEFAULT_INTENSIVE__, "Test %s... ", test->name); 538 | memset(spaces, ' ', sizeof(spaces)); 539 | if(n < (int) sizeof(spaces)) 540 | printf("%.*s", (int) sizeof(spaces) - n, spaces); 541 | } else { 542 | test_current_already_logged__ = 1; 543 | } 544 | } 545 | } 546 | 547 | static void 548 | test_finish_test_line__(int result) 549 | { 550 | if(test_tap__) { 551 | const char* str = (result == 0) ? "ok" : "not ok"; 552 | 553 | printf("%s %u - %s\n", str, test_current_index__ + 1, test_current_unit__->name); 554 | 555 | if(result == 0 && test_timer__) { 556 | printf("# Duration: "); 557 | test_timer_print_diff__(); 558 | printf("\n"); 559 | } 560 | } else { 561 | int color = (result == 0) ? TEST_COLOR_GREEN_INTENSIVE__ : TEST_COLOR_RED_INTENSIVE__; 562 | const char* str = (result == 0) ? "OK" : "FAILED"; 563 | printf("[ "); 564 | test_print_in_color__(color, str); 565 | printf(" ]"); 566 | 567 | if(result == 0 && test_timer__) { 568 | printf(" "); 569 | test_timer_print_diff__(); 570 | } 571 | 572 | printf("\n"); 573 | } 574 | } 575 | 576 | static void 577 | test_line_indent__(int level) 578 | { 579 | static const char spaces[] = " "; 580 | int n = level * 2; 581 | 582 | if(test_tap__ && n > 0) { 583 | n--; 584 | printf("#"); 585 | } 586 | 587 | while(n > 16) { 588 | printf("%s", spaces); 589 | n -= 16; 590 | } 591 | printf("%.*s", n, spaces); 592 | } 593 | 594 | int 595 | test_check__(int cond, const char* file, int line, const char* fmt, ...) 596 | { 597 | const char *result_str; 598 | int result_color; 599 | int verbose_level; 600 | 601 | if(cond) { 602 | result_str = "ok"; 603 | result_color = TEST_COLOR_GREEN__; 604 | verbose_level = 3; 605 | } else { 606 | if(!test_current_already_logged__ && test_current_unit__ != NULL) 607 | test_finish_test_line__(-1); 608 | 609 | result_str = "failed"; 610 | result_color = TEST_COLOR_RED__; 611 | verbose_level = 2; 612 | test_current_failures__++; 613 | test_current_already_logged__++; 614 | } 615 | 616 | if(test_verbose_level__ >= verbose_level) { 617 | va_list args; 618 | 619 | if(!test_case_current_already_logged__ && test_case_name__[0]) { 620 | test_line_indent__(1); 621 | test_print_in_color__(TEST_COLOR_DEFAULT_INTENSIVE__, "Case %s:\n", test_case_name__); 622 | test_current_already_logged__++; 623 | test_case_current_already_logged__++; 624 | } 625 | 626 | test_line_indent__(test_case_name__[0] ? 2 : 1); 627 | if(file != NULL) { 628 | #ifdef ACUTEST_WIN__ 629 | const char* lastsep1 = strrchr(file, '\\'); 630 | const char* lastsep2 = strrchr(file, '/'); 631 | if(lastsep1 == NULL) 632 | lastsep1 = file-1; 633 | if(lastsep2 == NULL) 634 | lastsep2 = file-1; 635 | file = (lastsep1 > lastsep2 ? lastsep1 : lastsep2) + 1; 636 | #else 637 | const char* lastsep = strrchr(file, '/'); 638 | if(lastsep != NULL) 639 | file = lastsep+1; 640 | #endif 641 | printf("%s:%d: Check ", file, line); 642 | } 643 | 644 | va_start(args, fmt); 645 | vprintf(fmt, args); 646 | va_end(args); 647 | 648 | printf("... "); 649 | test_print_in_color__(result_color, result_str); 650 | printf("\n"); 651 | test_current_already_logged__++; 652 | } 653 | 654 | test_cond_failed__ = (cond == 0); 655 | return !test_cond_failed__; 656 | } 657 | 658 | void 659 | test_case__(const char* fmt, ...) 660 | { 661 | va_list args; 662 | 663 | if(test_verbose_level__ < 2) 664 | return; 665 | 666 | if(test_case_name__[0]) { 667 | test_case_current_already_logged__ = 0; 668 | test_case_name__[0] = '\0'; 669 | } 670 | 671 | if(fmt == NULL) 672 | return; 673 | 674 | va_start(args, fmt); 675 | vsnprintf(test_case_name__, sizeof(test_case_name__) - 1, fmt, args); 676 | va_end(args); 677 | test_case_name__[sizeof(test_case_name__) - 1] = '\0'; 678 | 679 | if(test_verbose_level__ >= 3) { 680 | test_line_indent__(1); 681 | test_print_in_color__(TEST_COLOR_DEFAULT_INTENSIVE__, "Case %s:\n", test_case_name__); 682 | test_current_already_logged__++; 683 | test_case_current_already_logged__++; 684 | } 685 | } 686 | 687 | void 688 | test_message__(const char* fmt, ...) 689 | { 690 | char buffer[TEST_MSG_MAXSIZE]; 691 | char* line_beg; 692 | char* line_end; 693 | va_list args; 694 | 695 | if(test_verbose_level__ < 2) 696 | return; 697 | 698 | /* We allow extra message only when something is already wrong in the 699 | * current test. */ 700 | if(test_current_unit__ == NULL || !test_cond_failed__) 701 | return; 702 | 703 | va_start(args, fmt); 704 | vsnprintf(buffer, TEST_MSG_MAXSIZE, fmt, args); 705 | va_end(args); 706 | buffer[TEST_MSG_MAXSIZE-1] = '\0'; 707 | 708 | line_beg = buffer; 709 | while(1) { 710 | line_end = strchr(line_beg, '\n'); 711 | if(line_end == NULL) 712 | break; 713 | test_line_indent__(test_case_name__[0] ? 3 : 2); 714 | printf("%.*s\n", (int)(line_end - line_beg), line_beg); 715 | line_beg = line_end + 1; 716 | } 717 | if(line_beg[0] != '\0') { 718 | test_line_indent__(test_case_name__[0] ? 3 : 2); 719 | printf("%s\n", line_beg); 720 | } 721 | } 722 | 723 | void 724 | test_dump__(const char* title, const void* addr, size_t size) 725 | { 726 | static const size_t BYTES_PER_LINE = 16; 727 | size_t line_beg; 728 | size_t truncate = 0; 729 | 730 | if(test_verbose_level__ < 2) 731 | return; 732 | 733 | /* We allow extra message only when something is already wrong in the 734 | * current test. */ 735 | if(test_current_unit__ == NULL || !test_cond_failed__) 736 | return; 737 | 738 | if(size > TEST_DUMP_MAXSIZE) { 739 | truncate = size - TEST_DUMP_MAXSIZE; 740 | size = TEST_DUMP_MAXSIZE; 741 | } 742 | 743 | test_line_indent__(test_case_name__[0] ? 3 : 2); 744 | printf((title[strlen(title)-1] == ':') ? "%s\n" : "%s:\n", title); 745 | 746 | for(line_beg = 0; line_beg < size; line_beg += BYTES_PER_LINE) { 747 | size_t line_end = line_beg + BYTES_PER_LINE; 748 | size_t off; 749 | 750 | test_line_indent__(test_case_name__[0] ? 4 : 3); 751 | printf("%08lx: ", (unsigned long)line_beg); 752 | for(off = line_beg; off < line_end; off++) { 753 | if(off < size) 754 | printf(" %02x", ((unsigned char*)addr)[off]); 755 | else 756 | printf(" "); 757 | } 758 | 759 | printf(" "); 760 | for(off = line_beg; off < line_end; off++) { 761 | unsigned char byte = ((unsigned char*)addr)[off]; 762 | if(off < size) 763 | printf("%c", (iscntrl(byte) ? '.' : byte)); 764 | else 765 | break; 766 | } 767 | 768 | printf("\n"); 769 | } 770 | 771 | if(truncate > 0) { 772 | test_line_indent__(test_case_name__[0] ? 4 : 3); 773 | printf(" ... (and more %u bytes)\n", (unsigned) truncate); 774 | } 775 | } 776 | 777 | void 778 | test_abort__(void) 779 | { 780 | if(test_abort_has_jmp_buf__) 781 | longjmp(test_abort_jmp_buf__, 1); 782 | else 783 | abort(); 784 | } 785 | 786 | static void 787 | test_list_names__(void) 788 | { 789 | const struct test__* test; 790 | 791 | printf("Unit tests:\n"); 792 | for(test = &test_list__[0]; test->func != NULL; test++) 793 | printf(" %s\n", test->name); 794 | } 795 | 796 | static void 797 | test_remember__(int i) 798 | { 799 | if(test_details__[i].flags & TEST_FLAG_RUN__) 800 | return; 801 | 802 | test_details__[i].flags |= TEST_FLAG_RUN__; 803 | test_count__++; 804 | } 805 | 806 | static void 807 | test_set_success__(int i, int success) 808 | { 809 | test_details__[i].flags |= success ? TEST_FLAG_SUCCESS__ : TEST_FLAG_FAILURE__; 810 | } 811 | 812 | static void 813 | test_set_duration__(int i, double duration) 814 | { 815 | test_details__[i].duration = duration; 816 | } 817 | 818 | static int 819 | test_name_contains_word__(const char* name, const char* pattern) 820 | { 821 | static const char word_delim[] = " \t-_."; 822 | const char* substr; 823 | size_t pattern_len; 824 | int starts_on_word_boundary; 825 | int ends_on_word_boundary; 826 | 827 | pattern_len = strlen(pattern); 828 | 829 | substr = strstr(name, pattern); 830 | while(substr != NULL) { 831 | starts_on_word_boundary = (substr == name || strchr(word_delim, substr[-1]) != NULL); 832 | ends_on_word_boundary = (substr[pattern_len] == '\0' || strchr(word_delim, substr[pattern_len]) != NULL); 833 | 834 | if(starts_on_word_boundary && ends_on_word_boundary) 835 | return 1; 836 | 837 | substr = strstr(substr+1, pattern); 838 | } 839 | 840 | return 0; 841 | } 842 | 843 | static int 844 | test_lookup__(const char* pattern) 845 | { 846 | int i; 847 | int n = 0; 848 | 849 | /* Try exact match. */ 850 | for(i = 0; i < (int) test_list_size__; i++) { 851 | if(strcmp(test_list__[i].name, pattern) == 0) { 852 | test_remember__(i); 853 | n++; 854 | break; 855 | } 856 | } 857 | if(n > 0) 858 | return n; 859 | 860 | /* Try word match. */ 861 | for(i = 0; i < (int) test_list_size__; i++) { 862 | if(test_name_contains_word__(test_list__[i].name, pattern)) { 863 | test_remember__(i); 864 | n++; 865 | } 866 | } 867 | if(n > 0) 868 | return n; 869 | 870 | /* Try relaxed match. */ 871 | for(i = 0; i < (int) test_list_size__; i++) { 872 | if(strstr(test_list__[i].name, pattern) != NULL) { 873 | test_remember__(i); 874 | n++; 875 | } 876 | } 877 | 878 | return n; 879 | } 880 | 881 | 882 | /* Called if anything goes bad in Acutest, or if the unit test ends in other 883 | * way then by normal returning from its function (e.g. exception or some 884 | * abnormal child process termination). */ 885 | static void 886 | test_error__(const char* fmt, ...) 887 | { 888 | va_list args; 889 | 890 | if(test_verbose_level__ == 0) 891 | return; 892 | 893 | if(test_verbose_level__ >= 2) { 894 | test_line_indent__(1); 895 | if(test_verbose_level__ >= 3) 896 | test_print_in_color__(TEST_COLOR_RED_INTENSIVE__, "ERROR: "); 897 | va_start(args, fmt); 898 | vprintf(fmt, args); 899 | va_end(args); 900 | printf("\n"); 901 | } 902 | 903 | if(test_verbose_level__ >= 3) { 904 | printf("\n"); 905 | } 906 | } 907 | 908 | /* Call directly the given test unit function. */ 909 | static int 910 | test_do_run__(const struct test__* test, int index) 911 | { 912 | test_was_aborted__ = 0; 913 | test_current_unit__ = test; 914 | test_current_index__ = index; 915 | test_current_failures__ = 0; 916 | test_current_already_logged__ = 0; 917 | test_cond_failed__ = 0; 918 | 919 | test_begin_test_line__(test); 920 | 921 | #ifdef __cplusplus 922 | try { 923 | #endif 924 | 925 | /* This is good to do for case the test unit e.g. crashes. */ 926 | fflush(stdout); 927 | fflush(stderr); 928 | 929 | if(!test_worker__) { 930 | test_abort_has_jmp_buf__ = 1; 931 | if(setjmp(test_abort_jmp_buf__) != 0) { 932 | test_was_aborted__ = 1; 933 | goto aborted; 934 | } 935 | } 936 | 937 | test_timer_get_time__(&test_timer_start__); 938 | test->func(); 939 | aborted: 940 | test_abort_has_jmp_buf__ = 0; 941 | test_timer_get_time__(&test_timer_end__); 942 | 943 | if(test_verbose_level__ >= 3) { 944 | test_line_indent__(1); 945 | if(test_current_failures__ == 0) { 946 | test_print_in_color__(TEST_COLOR_GREEN_INTENSIVE__, "SUCCESS: "); 947 | printf("All conditions have passed.\n"); 948 | 949 | if(test_timer__) { 950 | test_line_indent__(1); 951 | printf("Duration: "); 952 | test_timer_print_diff__(); 953 | printf("\n"); 954 | } 955 | } else { 956 | test_print_in_color__(TEST_COLOR_RED_INTENSIVE__, "FAILED: "); 957 | if(!test_was_aborted__) { 958 | printf("%d condition%s %s failed.\n", 959 | test_current_failures__, 960 | (test_current_failures__ == 1) ? "" : "s", 961 | (test_current_failures__ == 1) ? "has" : "have"); 962 | } else { 963 | printf("Aborted.\n"); 964 | } 965 | } 966 | printf("\n"); 967 | } else if(test_verbose_level__ >= 1 && test_current_failures__ == 0) { 968 | test_finish_test_line__(0); 969 | } 970 | 971 | test_case__(NULL); 972 | test_current_unit__ = NULL; 973 | return (test_current_failures__ == 0) ? 0 : -1; 974 | 975 | #ifdef __cplusplus 976 | } catch(std::exception& e) { 977 | const char* what = e.what(); 978 | test_check__(0, NULL, 0, "Threw std::exception"); 979 | if(what != NULL) 980 | test_message__("std::exception::what(): %s", what); 981 | 982 | if(test_verbose_level__ >= 3) { 983 | test_line_indent__(1); 984 | test_print_in_color__(TEST_COLOR_RED_INTENSIVE__, "FAILED: "); 985 | printf("C++ exception.\n\n"); 986 | } 987 | 988 | return -1; 989 | } catch(...) { 990 | test_check__(0, NULL, 0, "Threw an exception"); 991 | 992 | if(test_verbose_level__ >= 3) { 993 | test_line_indent__(1); 994 | test_print_in_color__(TEST_COLOR_RED_INTENSIVE__, "FAILED: "); 995 | printf("C++ exception.\n\n"); 996 | } 997 | 998 | return -1; 999 | } 1000 | #endif 1001 | } 1002 | 1003 | /* Trigger the unit test. If possible (and not suppressed) it starts a child 1004 | * process who calls test_do_run__(), otherwise it calls test_do_run__() 1005 | * directly. */ 1006 | static void 1007 | test_run__(const struct test__* test, int index, int master_index) 1008 | { 1009 | int failed = 1; 1010 | test_timer_type__ start, end; 1011 | 1012 | test_current_unit__ = test; 1013 | test_current_already_logged__ = 0; 1014 | test_timer_get_time__(&start); 1015 | 1016 | if(!test_no_exec__) { 1017 | 1018 | #if defined(ACUTEST_UNIX__) 1019 | 1020 | pid_t pid; 1021 | int exit_code; 1022 | 1023 | /* Make sure the child starts with empty I/O buffers. */ 1024 | fflush(stdout); 1025 | fflush(stderr); 1026 | 1027 | pid = fork(); 1028 | if(pid == (pid_t)-1) { 1029 | test_error__("Cannot fork. %s [%d]", strerror(errno), errno); 1030 | failed = 1; 1031 | } else if(pid == 0) { 1032 | /* Child: Do the test. */ 1033 | test_worker__ = 1; 1034 | failed = (test_do_run__(test, index) != 0); 1035 | exit(failed ? 1 : 0); 1036 | } else { 1037 | /* Parent: Wait until child terminates and analyze its exit code. */ 1038 | waitpid(pid, &exit_code, 0); 1039 | if(WIFEXITED(exit_code)) { 1040 | switch(WEXITSTATUS(exit_code)) { 1041 | case 0: failed = 0; break; /* test has passed. */ 1042 | case 1: /* noop */ break; /* "normal" failure. */ 1043 | default: test_error__("Unexpected exit code [%d]", WEXITSTATUS(exit_code)); 1044 | } 1045 | } else if(WIFSIGNALED(exit_code)) { 1046 | char tmp[32]; 1047 | const char* signame; 1048 | switch(WTERMSIG(exit_code)) { 1049 | case SIGINT: signame = "SIGINT"; break; 1050 | case SIGHUP: signame = "SIGHUP"; break; 1051 | case SIGQUIT: signame = "SIGQUIT"; break; 1052 | case SIGABRT: signame = "SIGABRT"; break; 1053 | case SIGKILL: signame = "SIGKILL"; break; 1054 | case SIGSEGV: signame = "SIGSEGV"; break; 1055 | case SIGILL: signame = "SIGILL"; break; 1056 | case SIGTERM: signame = "SIGTERM"; break; 1057 | default: sprintf(tmp, "signal %d", WTERMSIG(exit_code)); signame = tmp; break; 1058 | } 1059 | test_error__("Test interrupted by %s.", signame); 1060 | } else { 1061 | test_error__("Test ended in an unexpected way [%d].", exit_code); 1062 | } 1063 | } 1064 | 1065 | #elif defined(ACUTEST_WIN__) 1066 | 1067 | char buffer[512] = {0}; 1068 | STARTUPINFOA startupInfo; 1069 | PROCESS_INFORMATION processInfo; 1070 | DWORD exitCode; 1071 | 1072 | /* Windows has no fork(). So we propagate all info into the child 1073 | * through a command line arguments. */ 1074 | _snprintf(buffer, sizeof(buffer)-1, 1075 | "%s --worker=%d %s --no-exec --no-summary %s --verbose=%d --color=%s -- \"%s\"", 1076 | test_argv0__, index, test_timer__ ? "--time" : "", 1077 | test_tap__ ? "--tap" : "", test_verbose_level__, 1078 | test_colorize__ ? "always" : "never", 1079 | test->name); 1080 | memset(&startupInfo, 0, sizeof(startupInfo)); 1081 | startupInfo.cb = sizeof(STARTUPINFO); 1082 | if(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0, NULL, NULL, &startupInfo, &processInfo)) { 1083 | WaitForSingleObject(processInfo.hProcess, INFINITE); 1084 | GetExitCodeProcess(processInfo.hProcess, &exitCode); 1085 | CloseHandle(processInfo.hThread); 1086 | CloseHandle(processInfo.hProcess); 1087 | failed = (exitCode != 0); 1088 | if(exitCode > 1) { 1089 | switch(exitCode) { 1090 | case 3: test_error__("Aborted."); break; 1091 | case 0xC0000005: test_error__("Access violation."); break; 1092 | default: test_error__("Test ended in an unexpected way [%lu].", exitCode); break; 1093 | } 1094 | } 1095 | } else { 1096 | test_error__("Cannot create unit test subprocess [%ld].", GetLastError()); 1097 | failed = 1; 1098 | } 1099 | 1100 | #else 1101 | 1102 | /* A platform where we don't know how to run child process. */ 1103 | failed = (test_do_run__(test, index) != 0); 1104 | 1105 | #endif 1106 | 1107 | } else { 1108 | /* Child processes suppressed through --no-exec. */ 1109 | failed = (test_do_run__(test, index) != 0); 1110 | } 1111 | test_timer_get_time__(&end); 1112 | 1113 | test_current_unit__ = NULL; 1114 | 1115 | test_stat_run_units__++; 1116 | if(failed) 1117 | test_stat_failed_units__++; 1118 | 1119 | test_set_success__(master_index, !failed); 1120 | test_set_duration__(master_index, test_timer_diff__(start, end)); 1121 | } 1122 | 1123 | #if defined(ACUTEST_WIN__) 1124 | /* Callback for SEH events. */ 1125 | static LONG CALLBACK 1126 | test_seh_exception_filter__(EXCEPTION_POINTERS *ptrs) 1127 | { 1128 | test_check__(0, NULL, 0, "Unhandled SEH exception"); 1129 | test_message__("Exception code: 0x%08lx", ptrs->ExceptionRecord->ExceptionCode); 1130 | test_message__("Exception address: 0x%p", ptrs->ExceptionRecord->ExceptionAddress); 1131 | 1132 | fflush(stdout); 1133 | fflush(stderr); 1134 | 1135 | return EXCEPTION_EXECUTE_HANDLER; 1136 | } 1137 | #endif 1138 | 1139 | 1140 | #define TEST_CMDLINE_OPTFLAG_OPTIONALARG__ 0x0001 1141 | #define TEST_CMDLINE_OPTFLAG_REQUIREDARG__ 0x0002 1142 | 1143 | #define TEST_CMDLINE_OPTID_NONE__ 0 1144 | #define TEST_CMDLINE_OPTID_UNKNOWN__ (-0x7fffffff + 0) 1145 | #define TEST_CMDLINE_OPTID_MISSINGARG__ (-0x7fffffff + 1) 1146 | #define TEST_CMDLINE_OPTID_BOGUSARG__ (-0x7fffffff + 2) 1147 | 1148 | typedef struct TEST_CMDLINE_OPTION__ { 1149 | char shortname; 1150 | const char* longname; 1151 | int id; 1152 | unsigned flags; 1153 | } TEST_CMDLINE_OPTION__; 1154 | 1155 | static int 1156 | test_cmdline_handle_short_opt_group__(const TEST_CMDLINE_OPTION__* options, 1157 | const char* arggroup, 1158 | int (*callback)(int /*optval*/, const char* /*arg*/)) 1159 | { 1160 | const TEST_CMDLINE_OPTION__* opt; 1161 | int i; 1162 | int ret = 0; 1163 | 1164 | for(i = 0; arggroup[i] != '\0'; i++) { 1165 | for(opt = options; opt->id != 0; opt++) { 1166 | if(arggroup[i] == opt->shortname) 1167 | break; 1168 | } 1169 | 1170 | if(opt->id != 0 && !(opt->flags & TEST_CMDLINE_OPTFLAG_REQUIREDARG__)) { 1171 | ret = callback(opt->id, NULL); 1172 | } else { 1173 | /* Unknown option. */ 1174 | char badoptname[3]; 1175 | badoptname[0] = '-'; 1176 | badoptname[1] = arggroup[i]; 1177 | badoptname[2] = '\0'; 1178 | ret = callback((opt->id != 0 ? TEST_CMDLINE_OPTID_MISSINGARG__ : TEST_CMDLINE_OPTID_UNKNOWN__), 1179 | badoptname); 1180 | } 1181 | 1182 | if(ret != 0) 1183 | break; 1184 | } 1185 | 1186 | return ret; 1187 | } 1188 | 1189 | #define TEST_CMDLINE_AUXBUF_SIZE__ 32 1190 | 1191 | static int 1192 | test_cmdline_read__(const TEST_CMDLINE_OPTION__* options, int argc, char** argv, 1193 | int (*callback)(int /*optval*/, const char* /*arg*/)) 1194 | { 1195 | 1196 | const TEST_CMDLINE_OPTION__* opt; 1197 | char auxbuf[TEST_CMDLINE_AUXBUF_SIZE__+1]; 1198 | int after_doubledash = 0; 1199 | int i = 1; 1200 | int ret = 0; 1201 | 1202 | auxbuf[TEST_CMDLINE_AUXBUF_SIZE__] = '\0'; 1203 | 1204 | while(i < argc) { 1205 | if(after_doubledash || strcmp(argv[i], "-") == 0) { 1206 | /* Non-option argument. */ 1207 | ret = callback(TEST_CMDLINE_OPTID_NONE__, argv[i]); 1208 | } else if(strcmp(argv[i], "--") == 0) { 1209 | /* End of options. All the remaining members are non-option arguments. */ 1210 | after_doubledash = 1; 1211 | } else if(argv[i][0] != '-') { 1212 | /* Non-option argument. */ 1213 | ret = callback(TEST_CMDLINE_OPTID_NONE__, argv[i]); 1214 | } else { 1215 | for(opt = options; opt->id != 0; opt++) { 1216 | if(opt->longname != NULL && strncmp(argv[i], "--", 2) == 0) { 1217 | size_t len = strlen(opt->longname); 1218 | if(strncmp(argv[i]+2, opt->longname, len) == 0) { 1219 | /* Regular long option. */ 1220 | if(argv[i][2+len] == '\0') { 1221 | /* with no argument provided. */ 1222 | if(!(opt->flags & TEST_CMDLINE_OPTFLAG_REQUIREDARG__)) 1223 | ret = callback(opt->id, NULL); 1224 | else 1225 | ret = callback(TEST_CMDLINE_OPTID_MISSINGARG__, argv[i]); 1226 | break; 1227 | } else if(argv[i][2+len] == '=') { 1228 | /* with an argument provided. */ 1229 | if(opt->flags & (TEST_CMDLINE_OPTFLAG_OPTIONALARG__ | TEST_CMDLINE_OPTFLAG_REQUIREDARG__)) { 1230 | ret = callback(opt->id, argv[i]+2+len+1); 1231 | } else { 1232 | sprintf(auxbuf, "--%s", opt->longname); 1233 | ret = callback(TEST_CMDLINE_OPTID_BOGUSARG__, auxbuf); 1234 | } 1235 | break; 1236 | } else { 1237 | continue; 1238 | } 1239 | } 1240 | } else if(opt->shortname != '\0' && argv[i][0] == '-') { 1241 | if(argv[i][1] == opt->shortname) { 1242 | /* Regular short option. */ 1243 | if(opt->flags & TEST_CMDLINE_OPTFLAG_REQUIREDARG__) { 1244 | if(argv[i][2] != '\0') 1245 | ret = callback(opt->id, argv[i]+2); 1246 | else if(i+1 < argc) 1247 | ret = callback(opt->id, argv[++i]); 1248 | else 1249 | ret = callback(TEST_CMDLINE_OPTID_MISSINGARG__, argv[i]); 1250 | break; 1251 | } else { 1252 | ret = callback(opt->id, NULL); 1253 | 1254 | /* There might be more (argument-less) short options 1255 | * grouped together. */ 1256 | if(ret == 0 && argv[i][2] != '\0') 1257 | ret = test_cmdline_handle_short_opt_group__(options, argv[i]+2, callback); 1258 | break; 1259 | } 1260 | } 1261 | } 1262 | } 1263 | 1264 | if(opt->id == 0) { /* still not handled? */ 1265 | if(argv[i][0] != '-') { 1266 | /* Non-option argument. */ 1267 | ret = callback(TEST_CMDLINE_OPTID_NONE__, argv[i]); 1268 | } else { 1269 | /* Unknown option. */ 1270 | char* badoptname = argv[i]; 1271 | 1272 | if(strncmp(badoptname, "--", 2) == 0) { 1273 | /* Strip any argument from the long option. */ 1274 | char* assignment = strchr(badoptname, '='); 1275 | if(assignment != NULL) { 1276 | size_t len = assignment - badoptname; 1277 | if(len > TEST_CMDLINE_AUXBUF_SIZE__) 1278 | len = TEST_CMDLINE_AUXBUF_SIZE__; 1279 | strncpy(auxbuf, badoptname, len); 1280 | auxbuf[len] = '\0'; 1281 | badoptname = auxbuf; 1282 | } 1283 | } 1284 | 1285 | ret = callback(TEST_CMDLINE_OPTID_UNKNOWN__, badoptname); 1286 | } 1287 | } 1288 | } 1289 | 1290 | if(ret != 0) 1291 | return ret; 1292 | i++; 1293 | } 1294 | 1295 | return ret; 1296 | } 1297 | 1298 | static void 1299 | test_help__(void) 1300 | { 1301 | printf("Usage: %s [options] [test...]\n", test_argv0__); 1302 | printf("\n"); 1303 | printf("Run the specified unit tests; or if the option '--skip' is used, run all\n"); 1304 | printf("tests in the suite but those listed. By default, if no tests are specified\n"); 1305 | printf("on the command line, all unit tests in the suite are run.\n"); 1306 | printf("\n"); 1307 | printf("Options:\n"); 1308 | printf(" -s, --skip Execute all unit tests but the listed ones\n"); 1309 | printf(" --exec[=WHEN] If supported, execute unit tests as child processes\n"); 1310 | printf(" (WHEN is one of 'auto', 'always', 'never')\n"); 1311 | printf(" -E, --no-exec Same as --exec=never\n"); 1312 | #if defined ACUTEST_WIN__ 1313 | printf(" -t, --time Measure test duration\n"); 1314 | #elif defined ACUTEST_HAS_POSIX_TIMER__ 1315 | printf(" -t, --time Measure test duration (real time)\n"); 1316 | printf(" --time=TIMER Measure test duration, using given timer\n"); 1317 | printf(" (TIMER is one of 'real', 'cpu')\n"); 1318 | #endif 1319 | printf(" --no-summary Suppress printing of test results summary\n"); 1320 | printf(" --tap Produce TAP-compliant output\n"); 1321 | printf(" (See https://testanything.org/)\n"); 1322 | printf(" -x, --xml-output=FILE Enable XUnit output to the given file\n"); 1323 | printf(" -l, --list List unit tests in the suite and exit\n"); 1324 | printf(" -v, --verbose Make output more verbose\n"); 1325 | printf(" --verbose=LEVEL Set verbose level to LEVEL:\n"); 1326 | printf(" 0 ... Be silent\n"); 1327 | printf(" 1 ... Output one line per test (and summary)\n"); 1328 | printf(" 2 ... As 1 and failed conditions (this is default)\n"); 1329 | printf(" 3 ... As 1 and all conditions (and extended summary)\n"); 1330 | printf(" -q, --quiet Same as --verbose=0\n"); 1331 | printf(" --color[=WHEN] Enable colorized output\n"); 1332 | printf(" (WHEN is one of 'auto', 'always', 'never')\n"); 1333 | printf(" --no-color Same as --color=never\n"); 1334 | printf(" -h, --help Display this help and exit\n"); 1335 | 1336 | if(test_list_size__ < 16) { 1337 | printf("\n"); 1338 | test_list_names__(); 1339 | } 1340 | } 1341 | 1342 | static const TEST_CMDLINE_OPTION__ test_cmdline_options__[] = { 1343 | { 's', "skip", 's', 0 }, 1344 | { 0, "exec", 'e', TEST_CMDLINE_OPTFLAG_OPTIONALARG__ }, 1345 | { 'E', "no-exec", 'E', 0 }, 1346 | #if defined ACUTEST_WIN__ 1347 | { 't', "time", 't', 0 }, 1348 | { 0, "timer", 't', 0 }, /* kept for compatibility */ 1349 | #elif defined ACUTEST_HAS_POSIX_TIMER__ 1350 | { 't', "time", 't', TEST_CMDLINE_OPTFLAG_OPTIONALARG__ }, 1351 | { 0, "timer", 't', TEST_CMDLINE_OPTFLAG_OPTIONALARG__ }, /* kept for compatibility */ 1352 | #endif 1353 | { 0, "no-summary", 'S', 0 }, 1354 | { 0, "tap", 'T', 0 }, 1355 | { 'l', "list", 'l', 0 }, 1356 | { 'v', "verbose", 'v', TEST_CMDLINE_OPTFLAG_OPTIONALARG__ }, 1357 | { 'q', "quiet", 'q', 0 }, 1358 | { 0, "color", 'c', TEST_CMDLINE_OPTFLAG_OPTIONALARG__ }, 1359 | { 0, "no-color", 'C', 0 }, 1360 | { 'h', "help", 'h', 0 }, 1361 | { 0, "worker", 'w', TEST_CMDLINE_OPTFLAG_REQUIREDARG__ }, /* internal */ 1362 | { 'x', "xml-output", 'x', TEST_CMDLINE_OPTFLAG_REQUIREDARG__ }, 1363 | { 0, NULL, 0, 0 } 1364 | }; 1365 | 1366 | static int 1367 | test_cmdline_callback__(int id, const char* arg) 1368 | { 1369 | switch(id) { 1370 | case 's': 1371 | test_skip_mode__ = 1; 1372 | break; 1373 | 1374 | case 'e': 1375 | if(arg == NULL || strcmp(arg, "always") == 0) { 1376 | test_no_exec__ = 0; 1377 | } else if(strcmp(arg, "never") == 0) { 1378 | test_no_exec__ = 1; 1379 | } else if(strcmp(arg, "auto") == 0) { 1380 | /*noop*/ 1381 | } else { 1382 | fprintf(stderr, "%s: Unrecognized argument '%s' for option --exec.\n", test_argv0__, arg); 1383 | fprintf(stderr, "Try '%s --help' for more information.\n", test_argv0__); 1384 | exit(2); 1385 | } 1386 | break; 1387 | 1388 | case 'E': 1389 | test_no_exec__ = 1; 1390 | break; 1391 | 1392 | case 't': 1393 | #if defined ACUTEST_WIN__ || defined ACUTEST_HAS_POSIX_TIMER__ 1394 | if(arg == NULL || strcmp(arg, "real") == 0) { 1395 | test_timer__ = 1; 1396 | #ifndef ACUTEST_WIN__ 1397 | } else if(strcmp(arg, "cpu") == 0) { 1398 | test_timer__ = 2; 1399 | #endif 1400 | } else { 1401 | fprintf(stderr, "%s: Unrecognized argument '%s' for option --time.\n", test_argv0__, arg); 1402 | fprintf(stderr, "Try '%s --help' for more information.\n", test_argv0__); 1403 | exit(2); 1404 | } 1405 | #endif 1406 | break; 1407 | 1408 | case 'S': 1409 | test_no_summary__ = 1; 1410 | break; 1411 | 1412 | case 'T': 1413 | test_tap__ = 1; 1414 | break; 1415 | 1416 | case 'l': 1417 | test_list_names__(); 1418 | exit(0); 1419 | 1420 | case 'v': 1421 | test_verbose_level__ = (arg != NULL ? atoi(arg) : test_verbose_level__+1); 1422 | break; 1423 | 1424 | case 'q': 1425 | test_verbose_level__ = 0; 1426 | break; 1427 | 1428 | case 'c': 1429 | if(arg == NULL || strcmp(arg, "always") == 0) { 1430 | test_colorize__ = 1; 1431 | } else if(strcmp(arg, "never") == 0) { 1432 | test_colorize__ = 0; 1433 | } else if(strcmp(arg, "auto") == 0) { 1434 | /*noop*/ 1435 | } else { 1436 | fprintf(stderr, "%s: Unrecognized argument '%s' for option --color.\n", test_argv0__, arg); 1437 | fprintf(stderr, "Try '%s --help' for more information.\n", test_argv0__); 1438 | exit(2); 1439 | } 1440 | break; 1441 | 1442 | case 'C': 1443 | test_colorize__ = 0; 1444 | break; 1445 | 1446 | case 'h': 1447 | test_help__(); 1448 | exit(0); 1449 | 1450 | case 'w': 1451 | test_worker__ = 1; 1452 | test_worker_index__ = atoi(arg); 1453 | break; 1454 | case 'x': 1455 | test_xml_output__ = fopen(arg, "w"); 1456 | if (!test_xml_output__) { 1457 | fprintf(stderr, "Unable to open '%s': %s\n", arg, strerror(errno)); 1458 | exit(2); 1459 | } 1460 | break; 1461 | 1462 | case 0: 1463 | if(test_lookup__(arg) == 0) { 1464 | fprintf(stderr, "%s: Unrecognized unit test '%s'\n", test_argv0__, arg); 1465 | fprintf(stderr, "Try '%s --list' for list of unit tests.\n", test_argv0__); 1466 | exit(2); 1467 | } 1468 | break; 1469 | 1470 | case TEST_CMDLINE_OPTID_UNKNOWN__: 1471 | fprintf(stderr, "Unrecognized command line option '%s'.\n", arg); 1472 | fprintf(stderr, "Try '%s --help' for more information.\n", test_argv0__); 1473 | exit(2); 1474 | 1475 | case TEST_CMDLINE_OPTID_MISSINGARG__: 1476 | fprintf(stderr, "The command line option '%s' requires an argument.\n", arg); 1477 | fprintf(stderr, "Try '%s --help' for more information.\n", test_argv0__); 1478 | exit(2); 1479 | 1480 | case TEST_CMDLINE_OPTID_BOGUSARG__: 1481 | fprintf(stderr, "The command line option '%s' does not expect an argument.\n", arg); 1482 | fprintf(stderr, "Try '%s --help' for more information.\n", test_argv0__); 1483 | exit(2); 1484 | } 1485 | 1486 | return 0; 1487 | } 1488 | 1489 | 1490 | #ifdef ACUTEST_LINUX__ 1491 | static int 1492 | test_is_tracer_present__(void) 1493 | { 1494 | char buf[256+32+1]; 1495 | int tracer_present = 0; 1496 | int fd; 1497 | ssize_t n_read; 1498 | 1499 | fd = open("/proc/self/status", O_RDONLY); 1500 | if(fd == -1) 1501 | return 0; 1502 | 1503 | n_read = read(fd, buf, sizeof(buf)-1); 1504 | while(n_read > 0) { 1505 | static const char pattern[] = "TracerPid:"; 1506 | const char* field; 1507 | 1508 | buf[n_read] = '\0'; 1509 | field = strstr(buf, pattern); 1510 | if(field != NULL && field < buf + sizeof(buf) - 32) { 1511 | pid_t tracer_pid = (pid_t) atoi(field + sizeof(pattern) - 1); 1512 | tracer_present = (tracer_pid != 0); 1513 | break; 1514 | } 1515 | 1516 | if(n_read == sizeof(buf)-1) { 1517 | memmove(buf, buf + sizeof(buf)-1 - 32, 32); 1518 | n_read = read(fd, buf+32, sizeof(buf)-1-32); 1519 | if(n_read > 0) 1520 | n_read += 32; 1521 | } 1522 | } 1523 | 1524 | close(fd); 1525 | return tracer_present; 1526 | } 1527 | #endif 1528 | 1529 | int 1530 | main(int argc, char** argv) 1531 | { 1532 | int i; 1533 | test_argv0__ = argv[0]; 1534 | 1535 | #if defined ACUTEST_UNIX__ 1536 | test_colorize__ = isatty(STDOUT_FILENO); 1537 | #elif defined ACUTEST_WIN__ 1538 | #if defined __BORLANDC__ 1539 | test_colorize__ = isatty(_fileno(stdout)); 1540 | #else 1541 | test_colorize__ = _isatty(_fileno(stdout)); 1542 | #endif 1543 | #else 1544 | test_colorize__ = 0; 1545 | #endif 1546 | 1547 | /* Count all test units */ 1548 | test_list_size__ = 0; 1549 | for(i = 0; test_list__[i].func != NULL; i++) 1550 | test_list_size__++; 1551 | 1552 | test_details__ = (struct test_detail__*)calloc(test_list_size__, sizeof(struct test_detail__)); 1553 | if(test_details__ == NULL) { 1554 | fprintf(stderr, "Out of memory.\n"); 1555 | exit(2); 1556 | } 1557 | 1558 | /* Parse options */ 1559 | test_cmdline_read__(test_cmdline_options__, argc, argv, test_cmdline_callback__); 1560 | 1561 | /* Initialize the proper timer. */ 1562 | test_timer_init__(); 1563 | 1564 | #if defined(ACUTEST_WIN__) 1565 | SetUnhandledExceptionFilter(test_seh_exception_filter__); 1566 | #endif 1567 | 1568 | /* By default, we want to run all tests. */ 1569 | if(test_count__ == 0) { 1570 | for(i = 0; test_list__[i].func != NULL; i++) 1571 | test_remember__(i); 1572 | } 1573 | 1574 | /* Guess whether we want to run unit tests as child processes. */ 1575 | if(test_no_exec__ < 0) { 1576 | test_no_exec__ = 0; 1577 | 1578 | if(test_count__ <= 1) { 1579 | test_no_exec__ = 1; 1580 | } else { 1581 | #ifdef ACUTEST_WIN__ 1582 | if(IsDebuggerPresent()) 1583 | test_no_exec__ = 1; 1584 | #endif 1585 | #ifdef ACUTEST_LINUX__ 1586 | if(test_is_tracer_present__()) 1587 | test_no_exec__ = 1; 1588 | #endif 1589 | #ifdef RUNNING_ON_VALGRIND 1590 | /* RUNNING_ON_VALGRIND is provided by valgrind.h */ 1591 | if(RUNNING_ON_VALGRIND) 1592 | test_no_exec__ = 1; 1593 | #endif 1594 | } 1595 | } 1596 | 1597 | if(test_tap__) { 1598 | /* TAP requires we know test result ("ok", "not ok") before we output 1599 | * anything about the test, and this gets problematic for larger verbose 1600 | * levels. */ 1601 | if(test_verbose_level__ > 2) 1602 | test_verbose_level__ = 2; 1603 | 1604 | /* TAP harness should provide some summary. */ 1605 | test_no_summary__ = 1; 1606 | 1607 | if(!test_worker__) 1608 | printf("1..%d\n", (int) test_count__); 1609 | } 1610 | 1611 | int index = test_worker_index__; 1612 | for(i = 0; test_list__[i].func != NULL; i++) { 1613 | int run = (test_details__[i].flags & TEST_FLAG_RUN__); 1614 | if (test_skip_mode__) /* Run all tests except those listed. */ 1615 | run = !run; 1616 | if(run) 1617 | test_run__(&test_list__[i], index++, i); 1618 | } 1619 | 1620 | /* Write a summary */ 1621 | if(!test_no_summary__ && test_verbose_level__ >= 1) { 1622 | if(test_verbose_level__ >= 3) { 1623 | test_print_in_color__(TEST_COLOR_DEFAULT_INTENSIVE__, "Summary:\n"); 1624 | 1625 | printf(" Count of all unit tests: %4d\n", (int) test_list_size__); 1626 | printf(" Count of run unit tests: %4d\n", test_stat_run_units__); 1627 | printf(" Count of failed unit tests: %4d\n", test_stat_failed_units__); 1628 | printf(" Count of skipped unit tests: %4d\n", (int) test_list_size__ - test_stat_run_units__); 1629 | } 1630 | 1631 | if(test_stat_failed_units__ == 0) { 1632 | test_print_in_color__(TEST_COLOR_GREEN_INTENSIVE__, "SUCCESS:"); 1633 | printf(" All unit tests have passed.\n"); 1634 | } else { 1635 | test_print_in_color__(TEST_COLOR_RED_INTENSIVE__, "FAILED:"); 1636 | printf(" %d of %d unit tests %s failed.\n", 1637 | test_stat_failed_units__, test_stat_run_units__, 1638 | (test_stat_failed_units__ == 1) ? "has" : "have"); 1639 | } 1640 | 1641 | if(test_verbose_level__ >= 3) 1642 | printf("\n"); 1643 | } 1644 | 1645 | if (test_xml_output__) { 1646 | #if defined ACUTEST_UNIX__ 1647 | char *suite_name = basename(argv[0]); 1648 | #elif defined ACUTEST_WIN__ 1649 | char suite_name[_MAX_FNAME]; 1650 | _splitpath(argv[0], NULL, NULL, suite_name, NULL); 1651 | #else 1652 | const char *suite_name = argv[0]; 1653 | #endif 1654 | fprintf(test_xml_output__, "\n"); 1655 | fprintf(test_xml_output__, "\n", 1656 | suite_name, (int)test_list_size__, test_stat_failed_units__, test_stat_failed_units__, 1657 | (int)test_list_size__ - test_stat_run_units__); 1658 | for(i = 0; test_list__[i].func != NULL; i++) { 1659 | struct test_detail__ *details = &test_details__[i]; 1660 | fprintf(test_xml_output__, " \n", test_list__[i].name, details->duration); 1661 | if (details->flags & TEST_FLAG_FAILURE__) 1662 | fprintf(test_xml_output__, " \n"); 1663 | if (!(details->flags & TEST_FLAG_FAILURE__) && !(details->flags & TEST_FLAG_SUCCESS__)) 1664 | fprintf(test_xml_output__, " \n"); 1665 | fprintf(test_xml_output__, " \n"); 1666 | } 1667 | fprintf(test_xml_output__, "\n"); 1668 | fclose(test_xml_output__); 1669 | } 1670 | 1671 | free((void*) test_details__); 1672 | 1673 | return (test_stat_failed_units__ == 0) ? 0 : 1; 1674 | } 1675 | 1676 | 1677 | #endif /* #ifndef TEST_NO_MAIN */ 1678 | 1679 | #ifdef __cplusplus 1680 | } /* extern "C" */ 1681 | #endif 1682 | 1683 | 1684 | #endif /* #ifndef ACUTEST_H__ */ 1685 | --------------------------------------------------------------------------------