├── uninstall.sh ├── tests ├── test.txt └── test_compile.sh ├── example.png ├── grammars ├── participant.c ├── arrow_connection.c ├── grammar.h ├── symbol.h ├── statement.h ├── arrow_connection.h └── participant.h ├── .gitmodules ├── main.h ├── fetch.h ├── terminal.h ├── diagram.txt ├── scanner.h ├── .gitignore ├── Makefile ├── mem.h ├── parser.h ├── style.c ├── fetch.c ├── renderer.h ├── install.sh ├── scanner.c ├── style.h ├── main.c ├── parser.c ├── split.h ├── README.md ├── LICENSE └── renderer.c /uninstall.sh: -------------------------------------------------------------------------------- 1 | make clean 2 | sudo rm /usr/local/bin/seqdia 3 | -------------------------------------------------------------------------------- /tests/test.txt: -------------------------------------------------------------------------------- 1 | A->B: sdjakls 2 | B->A: alskjdka 3 | A-->B: aksld 4 | -------------------------------------------------------------------------------- /example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ja-sonYun/sequence-diagram-cli/HEAD/example.png -------------------------------------------------------------------------------- /grammars/participant.c: -------------------------------------------------------------------------------- 1 | #include "participant.h" 2 | 3 | Participants participants = { 4 | .members_num = 0 5 | }; 6 | -------------------------------------------------------------------------------- /grammars/arrow_connection.c: -------------------------------------------------------------------------------- 1 | #include "arrow_connection.h" 2 | 3 | ArrowConnections arrow_connections = { 4 | .cons_num = 0 5 | }; 6 | -------------------------------------------------------------------------------- /tests/test_compile.sh: -------------------------------------------------------------------------------- 1 | gcc -g -o test ../grammars/arrow_connection.c ../grammars/participant.c ../renderer.c ../parser.c ../scanner.c ../main.c 2 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "seqdia-python-binding"] 2 | path = seqdia-python-binding 3 | url = https://github.com/Ja-sonYun/seqdia-python-binding.git 4 | -------------------------------------------------------------------------------- /main.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #ifdef CHECK_UPDATE 7 | # include "fetch.h" 8 | #endif 9 | 10 | int SHOW_LOG; 11 | char *prefix; 12 | char *suffix; 13 | bool printRaw; 14 | 15 | #include "renderer.h" 16 | -------------------------------------------------------------------------------- /fetch.h: -------------------------------------------------------------------------------- 1 | #ifdef CHECK_UPDATE 2 | #ifndef FETCH_H 3 | #define FETCH_H 4 | #define VERSION "v1.3.4" 5 | 6 | #include 7 | #include "mem.h" 8 | #include 9 | #include 10 | #include 11 | 12 | void check_version(); 13 | 14 | #endif 15 | #endif 16 | -------------------------------------------------------------------------------- /grammars/grammar.h: -------------------------------------------------------------------------------- 1 | #ifndef GRAMMAR_H 2 | #define GRAMMAR_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | // grammars 10 | #include "participant.h" 11 | #include "arrow_connection.h" 12 | 13 | 14 | #include "symbol.h" 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /terminal.h: -------------------------------------------------------------------------------- 1 | #ifndef TERMINAL_H 2 | #define TERMINAL_H 3 | 4 | #define RESET "\033[0m" 5 | #define KNRM "\x1B[0m" 6 | #define KRED "\x1B[31m" 7 | #define KGRN "\x1B[32m" 8 | #define KYEL "\x1B[33m" 9 | #define KBLU "\x1B[34m" 10 | #define KMAG "\x1B[35m" 11 | #define KCYN "\x1B[36m" 12 | #define KWHT "\x1B[37m" 13 | 14 | #define printf_c(string) printf(string RESET) 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /diagram.txt: -------------------------------------------------------------------------------- 1 | participant "entry 多国語支援point 2 | -> main.c" as main 3 | participant "parser.c" as parser 4 | participant "アイウエオ.c" as scanner 5 | participant "안뇽하ㅅㄴ" as renderer 6 | participant " * grammars 7 | - participantsdkjaskldjaklsd 8 | - arrow" as grammars 9 | 10 | main->parser안녕 : normal arr 11 | main<--parser : callsdkアイウエオ parsersdkjalskjd 12 | grammars->grammars:trslkjd 13 | a->a: sdk 14 | -------------------------------------------------------------------------------- /scanner.h: -------------------------------------------------------------------------------- 1 | #ifndef SCANNER_H 2 | #define SCANNER_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "mem.h" 11 | #include "split.h" 12 | #include "grammars/grammar.h" 13 | 14 | bool is_sym(char ch); 15 | int rdw_ignspc(Word *word, char *ch, bool *wrapped); 16 | int rd_sym(Word *l_sym, char *line); 17 | int rdw(Word *curw, char *ch); 18 | #endif 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Object files 5 | *.o 6 | *.ko 7 | *.obj 8 | *.elf 9 | 10 | # Linker output 11 | *.ilk 12 | *.map 13 | *.exp 14 | 15 | # Precompiled Headers 16 | *.gch 17 | *.pch 18 | 19 | # Libraries 20 | *.lib 21 | *.a 22 | *.la 23 | *.lo 24 | 25 | # Shared objects (inc. Windows DLLs) 26 | *.dll 27 | *.so 28 | *.so.* 29 | *.dylib 30 | 31 | # Executables 32 | *.exe 33 | *.out 34 | *.app 35 | *.i*86 36 | *.x86_64 37 | *.hex 38 | 39 | # Debug files 40 | *.dSYM/ 41 | *.su 42 | *.idb 43 | *.pdb 44 | 45 | # Kernel Module Compile Results 46 | *.mod* 47 | *.cmd 48 | .tmp_versions/ 49 | modules.order 50 | Module.symvers 51 | Mkfile.old 52 | dkms.conf 53 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | OBJ_DIR = obj 2 | 3 | # TODO use other func instead of 'strdup' 4 | override CFLAG += -c -Wall -O2 # std=c99 5 | OBJECTS = main.o arrow_connection.o participant.o renderer.o parser.o scanner.o style.o fetch.o 6 | TARGET = seqdia 7 | 8 | B_FLAG=-DCLI -DOPTS_SUP 9 | CURLF = -I/usr/local/opt/curl/include -L/usr/local/opt/curl/lib -lcurl 10 | 11 | ARROW_C_C = ./grammars/arrow_connection.c 12 | PARTICIPANT_C = ./grammars/participant.c 13 | RENDERER_C = ./renderer.c 14 | PARSER_C = ./parser.c 15 | SCANNER_C = ./scanner.c 16 | MAIN_C = ./main.c 17 | STYLE_C = ./style.c 18 | FETCH_C = ./fetch.c 19 | 20 | .PHONY: clean 21 | 22 | $(TARGET): $(OBJECTS) 23 | gcc $(OBJECTS) -o $(TARGET) $(B_FLAG) 24 | 25 | main.o: $(MAIN_C) 26 | gcc $(CFLAG) $(MAIN_C) 27 | 28 | renderer.o: $(RENDERER_C) 29 | gcc $(CFLAG) $(RENDERER_C) 30 | 31 | parser.o: $(PARSER_C) 32 | gcc $(CFLAG) $(PARSER_C) 33 | 34 | arrow_connection.o: $(ARROW_C_C) 35 | gcc $(CFLAG) $(ARROW_C_C) 36 | 37 | participant.o: $(PARTICIPANT_C) 38 | gcc $(CFLAG) $(PARTICIPANT_C) 39 | 40 | scanner.o: $(SCANNER_C) 41 | gcc $(CFLAG) $(SCANNER_C) 42 | 43 | style.o: $(STYLE_C) 44 | gcc $(CFLAG) $(STYLE_C) 45 | 46 | fetch.o: $(FETCH_C) 47 | gcc $(CFLAG) $(FETCH_C) 48 | 49 | clean: 50 | rm -f seqdia && rm -f *.o 51 | 52 | all: $(TARGET) 53 | -------------------------------------------------------------------------------- /mem.h: -------------------------------------------------------------------------------- 1 | #ifndef MEM_H 2 | #define MEM_H 3 | #include 4 | #include 5 | // bit control 6 | #define UNFLAG_R(flag, len) flag >> len << len // unflag bits by length from front bit 7 | 8 | // memory 9 | #define CHECK_MEM_ERR(v) \ 10 | if (!v) \ 11 | { \ 12 | fprintf(stderr, "Insufficient memory"); \ 13 | exit(EXIT_FAILURE); \ 14 | } 15 | 16 | #define BYTE_TO_BINARY_PATTERN "%c%c%c%c%c%c%c%c" 17 | #define BYTE_TO_BINARY(byte) \ 18 | (byte & 0x80 ? '1' : '0'), \ 19 | (byte & 0x40 ? '1' : '0'), \ 20 | (byte & 0x20 ? '1' : '0'), \ 21 | (byte & 0x10 ? '1' : '0'), \ 22 | (byte & 0x08 ? '1' : '0'), \ 23 | (byte & 0x04 ? '1' : '0'), \ 24 | (byte & 0x02 ? '1' : '0'), \ 25 | (byte & 0x01 ? '1' : '0') 26 | // #define print_uint32_t(head, bin) printf(head BYTE_TO_BINARY_PATTERN" "BYTE_TO_BINARY_PATTERN" "BYTE_TO_BINARY_PATTERN" "BYTE_TO_BINARY_PATTERN"\n", \ 27 | // BYTE_TO_BINARY(bin>>24), BYTE_TO_BINARY(bin>>16), BYTE_TO_BINARY(bin>>8), BYTE_TO_BINARY(bin)); 28 | #define print_uint32_t(head, bin) NULL 29 | 30 | 31 | // malloc 32 | static inline void* malloc_s(size_t t) 33 | { 34 | void *n = malloc(t); 35 | CHECK_MEM_ERR(n); 36 | return n; 37 | } 38 | 39 | // calloc 40 | static inline void* calloc_s(size_t num, size_t size) 41 | { 42 | void *n = calloc(num, size); 43 | CHECK_MEM_ERR(n); 44 | return n; 45 | } 46 | 47 | // realloc 48 | static inline void* realloc_s(void *ptr, size_t size) 49 | { 50 | void *n = realloc(ptr, size); 51 | CHECK_MEM_ERR(n); 52 | return n; 53 | } 54 | #endif 55 | -------------------------------------------------------------------------------- /parser.h: -------------------------------------------------------------------------------- 1 | #include "scanner.h" 2 | #include "mem.h" 3 | #include 4 | #include 5 | #include 6 | 7 | #include "terminal.h" 8 | 9 | #ifdef CLI 10 | extern int SHOW_LOG; 11 | #endif 12 | 13 | #ifdef PYTHON_BINDING 14 | extern char** PY_list_result; 15 | extern int result_list_size; 16 | extern int failed_at; 17 | extern int chunk_size; 18 | extern int buffer_size; 19 | #endif 20 | 21 | Words* split(char *line); 22 | bool parse(Words *words, int line_num); 23 | 24 | inline static void parse_line(char *line, int line_num) 25 | { 26 | // printf("==========================\n - parsing \"\"\" %s \"\"\"\n", line); 27 | Words* newline = split(line); 28 | if (newline != NULL) 29 | { 30 | bool result = parse(newline, line_num); 31 | 32 | #ifdef CLI 33 | if (SHOW_LOG >= 1 && result) 34 | { 35 | printf_c(KBLU " - line parsed.\n"); 36 | } 37 | #endif 38 | } 39 | else 40 | { 41 | #ifdef PYTHON_BINDING 42 | failed_at = line_num; 43 | #else 44 | printf(KRED"**** PARSE FAILED at line %d ****\n"RESET, line_num); 45 | #endif 46 | } 47 | } 48 | 49 | #ifdef PYTHON_BINDING 50 | inline static void clear_temps() 51 | { 52 | // ---------------------------- 53 | // TODO: clear dangling pointer 54 | // ---------------------------- 55 | arrow_connections.cons_num = 0; 56 | participants.members_num = 0; 57 | PY_list_result = NULL; 58 | // for (int i = 0; i < result_list_size; i++) 59 | // free(PY_list_result[i]); 60 | // free(PY_list_result); 61 | result_list_size = 0; 62 | failed_at = -1; 63 | chunk_size = 0; 64 | buffer_size = 0; 65 | } 66 | #endif 67 | -------------------------------------------------------------------------------- /style.c: -------------------------------------------------------------------------------- 1 | #include "style.h" 2 | 3 | char *style[STYLE_S]; 4 | 5 | void set_style(int stylet) 6 | { 7 | if (stylet) 8 | { 9 | PARTICIPANT_VERTICAL_LINE = D_PARTICIPANT_VERTICAL_LINE; 10 | PARTICIPANT_HORIZONTAL_LINE = D_PARTICIPANT_HORIZONTAL_LINE; 11 | PARTICIPANT_TOP_LEFT = D_PARTICIPANT_TOP_LEFT; 12 | PARTICIPANT_TOP_RIGHT = D_PARTICIPANT_TOP_RIGHT; 13 | PARTICIPANT_BOTTOM_LEFT = D_PARTICIPANT_BOTTOM_LEFT; 14 | PARTICIPANT_BOTTOM_RIGHT = D_PARTICIPANT_BOTTOM_RIGHT; 15 | PARTICIPANT_BOTTOM_CONNECTION = D_PARTICIPANT_BOTTOM_CONNECTION; 16 | VERTICAL_LINE = D_VERTICAL_LINE; 17 | ARROW_NORMAL_VERTICAL_LINE = D_ARROW_NORMAL_VERTICAL_LINE; 18 | ARROW_RET_VERTICAL_LINE = D_ARROW_RET_VERTICAL_LINE; 19 | ARROW_RET_LINE_L = D_ARROW_RET_LINE_L; 20 | ARROW_RET_LINE_R = D_ARROW_RET_LINE_R; 21 | ARROW_NORMAL_R = D_ARROW_NORMAL_R; 22 | ARROW_NORMAL_L = D_ARROW_NORMAL_L; 23 | ARROW_RET_L = D_ARROW_RET_L; 24 | ARROW_RET_R = D_ARROW_RET_R; 25 | ARROW_ORIGIN_R = D_ARROW_ORIGIN_R; 26 | ARROW_ORIGIN_L = D_ARROW_ORIGIN_L; 27 | ARROW_LINE_R = D_ARROW_LINE_R; 28 | ARROW_LINE_L = D_ARROW_LINE_L; 29 | } 30 | else 31 | { 32 | PARTICIPANT_VERTICAL_LINE = U_PARTICIPANT_VERTICAL_LINE; 33 | PARTICIPANT_HORIZONTAL_LINE = U_PARTICIPANT_HORIZONTAL_LINE; 34 | PARTICIPANT_TOP_LEFT = U_PARTICIPANT_TOP_LEFT; 35 | PARTICIPANT_TOP_RIGHT = U_PARTICIPANT_TOP_RIGHT; 36 | PARTICIPANT_BOTTOM_LEFT = U_PARTICIPANT_BOTTOM_LEFT; 37 | PARTICIPANT_BOTTOM_RIGHT = U_PARTICIPANT_BOTTOM_RIGHT; 38 | PARTICIPANT_BOTTOM_CONNECTION = U_PARTICIPANT_BOTTOM_CONNECTION; 39 | VERTICAL_LINE = U_VERTICAL_LINE; 40 | ARROW_NORMAL_VERTICAL_LINE = U_ARROW_NORMAL_VERTICAL_LINE; 41 | ARROW_RET_VERTICAL_LINE = U_ARROW_RET_VERTICAL_LINE; 42 | ARROW_RET_LINE_L = U_ARROW_RET_LINE_L; 43 | ARROW_RET_LINE_R = U_ARROW_RET_LINE_R; 44 | ARROW_NORMAL_R = U_ARROW_NORMAL_R; 45 | ARROW_NORMAL_L = U_ARROW_NORMAL_L; 46 | ARROW_RET_L = U_ARROW_RET_L; 47 | ARROW_RET_R = U_ARROW_RET_R; 48 | ARROW_ORIGIN_R = U_ARROW_ORIGIN_R; 49 | ARROW_ORIGIN_L = U_ARROW_ORIGIN_L; 50 | ARROW_LINE_R = U_ARROW_LINE_R; 51 | ARROW_LINE_L = U_ARROW_LINE_L; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /fetch.c: -------------------------------------------------------------------------------- 1 | #ifdef CHECK_UPDATE 2 | #include "fetch.h" 3 | 4 | struct string { 5 | char *ptr; 6 | size_t len; 7 | }; 8 | 9 | void init_string(struct string *s) 10 | { 11 | s->len = 0; 12 | s->ptr = malloc_s(s->len+1); 13 | s->ptr[0] = '\0'; 14 | } 15 | 16 | size_t writefunc(void *ptr, size_t size, size_t nmemb, struct string *s) 17 | { 18 | size_t new_len = s->len + size*nmemb; 19 | s->ptr = realloc_s(s->ptr, new_len+1); 20 | memcpy(s->ptr+s->len, ptr, size*nmemb); 21 | s->ptr[new_len] = '\0'; 22 | s->len = new_len; 23 | 24 | return size*nmemb; 25 | } 26 | 27 | void check_version() 28 | { 29 | CURL *curl; 30 | CURLcode res; 31 | 32 | curl = curl_easy_init(); 33 | struct string s; 34 | 35 | int version_size = 6; 36 | 37 | if(curl) { 38 | init_string(&s); 39 | 40 | struct curl_slist *chunk = NULL; 41 | chunk = curl_slist_append(chunk, "Accept: application/vnd.github.v3+json"); 42 | chunk = curl_slist_append(chunk, "User-Agent: curl/7.64.1"); 43 | curl_easy_setopt(curl, CURLOPT_HTTPHEADER, chunk); 44 | curl_easy_setopt(curl, CURLOPT_URL, "https://api.github.com/repos/Ja-sonYun/sequence-diagram-cli/tags"); 45 | curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); 46 | curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); 47 | 48 | res = curl_easy_perform(curl); 49 | 50 | char *version = strstr(s.ptr, "\"name\": \"v"); 51 | if (version != NULL) 52 | { 53 | char *end = strstr(version, "\","); 54 | if (end != NULL) 55 | { 56 | int size = end - version - 9; 57 | version += 9; 58 | version[size] = '\0'; 59 | if (size <= version_size && size > 0 && version[0] == 'v') 60 | { 61 | if (strcmp(version, VERSION)) 62 | { 63 | printf("** New version %s is released! **\nYou can update with below command,\n+-----------------------------------------------------------------------------------------+\n| wget -qO - https://github.com/Ja-sonYun/sequence-diagram-cli/raw/main/install.sh | bash |\n+-----------------------------------------------------------------------------------------+\n", version); 64 | printf("** You can also download or see release note from below link.\n - https://github.com/Ja-sonYun/sequence-diagram-cli\n"); 65 | } 66 | } 67 | 68 | } 69 | 70 | } 71 | 72 | } 73 | 74 | free(s.ptr); 75 | 76 | curl_easy_cleanup(curl); 77 | 78 | } 79 | #endif 80 | -------------------------------------------------------------------------------- /grammars/symbol.h: -------------------------------------------------------------------------------- 1 | #ifndef SYMBOL_H 2 | #define SYMBOL_H 3 | #include 4 | #include "../split.h" 5 | 6 | // Symbols 7 | #define SYM(BIN) BIN ^ (0b01 << 30) 8 | #define SYMBOL_MASK 0b10000000000000000000000000000000 9 | #define IS_SYM(BIN) (BIN | SYMBOL_MASK) 10 | static const struct _Symbols { 11 | char *name; 12 | uint32_t flag; // use last bit for direction. 13 | } _symbols[] = { 14 | // Arrow styles 15 | #define ARROW_MASK 0b01000000000000000000000000000000 16 | #define IS_ARROW(BIN) (BIN | ARROW_MASK) 17 | #define ARROW(BIN) BIN ^ ARROW_MASK 18 | #define LEFT_DIR_MASK 0b00100000000000000000000000000000 19 | #define LEFT_DIR(ARROW_F) ARROW_F ^ LEFT_DIR_MASK 20 | #define IS_LEFT_DIR(ARROW_F) ARROW_F & LEFT_DIR_MASK 21 | #define UNSET_DIR(ARROW_F) ARROW_F & 0b11011111111111111111111111111111 22 | #define R_ARROW_F SYM(ARROW(0x00000001)) 23 | #define R_ARROW "->" 24 | { R_ARROW, R_ARROW_F }, 25 | #define L_ARROW_F LEFT_DIR(R_ARROW_F) 26 | #define L_ARROW "<-" 27 | { L_ARROW, L_ARROW_F }, 28 | #define R_EMP_AR_F SYM(ARROW(0x00000002)) 29 | #define R_EMP_AR "->>" 30 | { R_EMP_AR, R_EMP_AR_F }, 31 | #define L_EMP_AR_F LEFT_DIR(R_EMP_AR_F) 32 | #define L_EMP_AR "<<-" 33 | { L_EMP_AR, L_EMP_AR_F }, 34 | #define R_RET_AR_F SYM(ARROW(0x00000003)) 35 | #define R_RET_AR "-->" 36 | { R_RET_AR, R_RET_AR_F }, 37 | #define L_RET_AR_F LEFT_DIR(R_RET_AR_F) 38 | #define L_RET_AR "<--" 39 | { L_RET_AR, L_RET_AR_F }, 40 | 41 | //=========================================== 42 | #define STYLE_MASK 0b00010000000000000000000000000000 43 | #define IS_STYLE(BIN) (BIN | STYLE_MASK) 44 | #define STYLE(BIN) BIN ^ STYLE_MASK 45 | 46 | #define DELAY_F SYM(STYLE(0x00000001)) 47 | #define DELAY "..." 48 | { DELAY, DELAY_F }, 49 | #define SPACE_F SYM(STYLE(0x00000002)) 50 | #define SPACE "|||" 51 | { SPACE, SPACE_F }, 52 | #define SEPERATER_F SYM(STYLE(0x00000003)) 53 | #define SEPERATER "==" 54 | { SEPERATER, SEPERATER_F }, 55 | }; 56 | static const int _symbols_s = 6; 57 | 58 | static inline char* find_symbol(uint32_t F) 59 | { 60 | for (int _i = 0; _i < _symbols_s; _i++) 61 | { 62 | if (_symbols[_i].flag == F) 63 | { 64 | return _symbols[_i].name; 65 | } 66 | } 67 | return NULL; 68 | } 69 | 70 | static inline void check_symbol(Word *word) 71 | { 72 | for (int _i = 0; _i < _symbols_s; _i++) 73 | { 74 | if (!strcmp(_symbols[_i].name, word->string)) 75 | { 76 | word->d_type = _symbols[_i].flag; 77 | } 78 | } 79 | } 80 | #endif 81 | -------------------------------------------------------------------------------- /renderer.h: -------------------------------------------------------------------------------- 1 | #ifndef COMPILER_H 2 | #define COMPILER_H 3 | 4 | #include "parser.h" 5 | #include "style.h" 6 | #include "string.h" 7 | #include 8 | 9 | extern char *prefix; 10 | extern char *suffix; 11 | #ifdef OPTS_SUP 12 | extern bool printRaw; 13 | #endif 14 | 15 | #ifdef PYTHON_BINDING 16 | #define LINE_BUF_CHUNK 50 17 | #define LINE_BUF_PADDING 5 18 | char **PY_list_result; 19 | char *line_buffer; 20 | int result_list_size = 0; 21 | int buffer_size = 0; 22 | int chunk_size = 0; 23 | int failed_at = -1; 24 | #endif 25 | 26 | #define GET_LINE_M(from, to) \ 27 | (to - from) / 2 + from 28 | 29 | typedef struct _coordinates 30 | { 31 | int x; 32 | int y; 33 | } Pos; 34 | 35 | #define POS_INIT { .x = 0, .y = 0 } 36 | 37 | struct arrow_def_coor 38 | { 39 | int top_padding; 40 | Pos size; 41 | Pos from; 42 | Pos to; 43 | }; 44 | 45 | struct group 46 | { 47 | Pos from; 48 | Pos to; 49 | char **cases; 50 | int cases_num; 51 | }; 52 | 53 | struct participant_render 54 | { 55 | int left_padding; 56 | int right_padding; 57 | int middle; 58 | Pos from; 59 | Pos word_size; 60 | Pos to; 61 | }; 62 | 63 | struct lifeline 64 | { 65 | Pos from; 66 | Pos to; 67 | }; 68 | 69 | struct seperater 70 | { 71 | int x; 72 | }; 73 | 74 | typedef struct _area 75 | { 76 | char *title; 77 | struct _header 78 | { 79 | #ifdef UTF_SUPPORT 80 | char ***buffer; 81 | #else 82 | char **buffer; 83 | #endif 84 | Pos pos; 85 | struct participant_render *participants; 86 | } header; 87 | struct _body 88 | { 89 | #ifdef UTF_SUPPORT 90 | char ***buffer; 91 | #else 92 | char **buffer; 93 | #endif 94 | Pos pos; 95 | int *e_s; 96 | int l_p; 97 | struct lifeline *lifelines; 98 | struct group *groups; 99 | struct arrow_def_coor *arrow_defs; 100 | } body; 101 | Pos pos; 102 | } Area; 103 | 104 | void render(); 105 | void render_test(); 106 | 107 | // debug 108 | 109 | static inline void _debug_header_(Area *area) 110 | { 111 | for (int i = 0; i < participants.members_num; i++) 112 | { 113 | printf("participant '%s'\n", participants.members[i]->name->string); 114 | printf("from -> x: %d, y: %d\n", area->header.participants[i].from.x, area->header.participants[i].from.y); 115 | printf("to -> x: %d, y: %d\n", area->header.participants[i].to.x, area->header.participants[i].to.y); 116 | printf("size -> x: %d, y: %d\n", area->header.participants[i].word_size.x, area->header.participants[i].word_size.y); 117 | printf("============================\n"); 118 | } 119 | printf("xpos -> x: %d, y: %d\n", area->header.pos.x, area->header.pos.y); 120 | } 121 | 122 | #endif 123 | -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | if [ -d "/usr/local/bin" ] 4 | then 5 | echo "\x1B[32m** Downloading file from github...\033[0m" 6 | else 7 | echo "\x1B[31mERROR: couldn't find path!\033[0m" 8 | exit 1 9 | fi 10 | 11 | D=`uname -a | awk '{print $1}'` 12 | if [ "$D" = "Darwin" ]; then 13 | wget https://github.com/Ja-sonYun/sequence-diagram-cli/releases/download/v1.3.4/Darwin_seqdia -P ~/.seqdia 14 | mv ~/.seqdia/Darwin_seqdia ~/.seqdia/seqdia 15 | CHECKSUMD="d9f4cfc86a732a838b9686df30349155" 16 | elif [ "$D" = "Linux" ]; then 17 | echo "Clone repository..." 18 | git clone https://github.com/Ja-sonYun/sequence-diagram-cli.git ~/.seqdia 19 | echo "installing dependencies..." 20 | sudo apt-get install libcurl4-gnutls-dev -y 21 | echo "running make" 22 | make -C ~/.seqdia 23 | echo "move binary to /usr/local/bin" 24 | sudo mv ~/.seqdia/seqdia /usr/local/bin 25 | echo "remove temp folder" 26 | rm -rf ~/.seqdia 27 | echo "Installed!" 28 | exit 1 29 | else 30 | echo "This architecture does not supported!" 31 | echo "Download source file from my github, and please compile yourself." 32 | exit 1 33 | fi 34 | 35 | CHECKSUM=`md5 ~/.seqdia/seqdia | awk '{ print $4 }'` 36 | 37 | if [ "$CHECKSUM" != "" ]; then 38 | echo "Compare Checksum $CHECKSUMD" 39 | if [ "$CHECKSUM" = "$CHECKSUMD" ]; then 40 | echo "PASSED" 41 | else 42 | echo "FAILED. Please download manually." 43 | echo "\x1B[32m** Removing temp folder ~/.seqdia\033[0m" 44 | rm -rf ~/.seqdia 45 | exit 1 46 | fi 47 | 48 | 49 | if [ -f "/usr/local/bin/seqdia" ]; then 50 | CHECKSUMO=($(md5sum /usr/local/bin/seqdia)) 51 | if [ "$CHECKSUMD" = "$CHECKSUMO" ]; then 52 | echo "Nothing changed." 53 | echo "\x1B[32m** Removing temp folder ~/.seqdia\033[0m" 54 | rm -rf ~/.seqdia 55 | exit 1 56 | else 57 | echo "Updating..." 58 | fi 59 | fi 60 | fi 61 | 62 | echo "\x1B[32m** Moving binary file to '/usr/local/bin'.\033[0m" 63 | sudo mv ~/.seqdia/seqdia /usr/local/bin/seqdia 64 | 65 | echo "\x1B[32m** chmod 755 /usr/local/bin/seqdia...\033[0m" 66 | sudo chmod 755 /usr/local/bin/seqdia 67 | 68 | echo "\x1B[32m** Removing temp folder ~/.seqdia\033[0m" 69 | rm -rf ~/.seqdia 70 | 71 | if [ -f "/usr/local/bin/seqdia" ] 72 | then 73 | : 74 | else 75 | echo "\x1B[33mWARNING: something is wrong! Please move binary file to your PATH manually." 76 | echo "\x1B[33mWARNING: you can also compile this package from github source." 77 | echo "\x1B[33mWARNING: https://github.com/Ja-sonYun/sequence-diagram-cli\033[0m" 78 | exit 1 79 | fi 80 | 81 | CHECKSUM=`md5 /usr/local/bin/seqdia | awk '{ print $4 }'` 82 | if [ -x "/usr/local/bin/seqdia" ] || [ "$CHECKSUM" = "$CHECKSUMD" ]; 83 | then 84 | echo "\x1B[32m** Installed!\033[0m" 85 | else 86 | echo "\x1B[33mPermission Error! Please run \`sudo chmod 755 /usr/local/bin/seqdia\`" 87 | exit 1 88 | fi 89 | 90 | -------------------------------------------------------------------------------- /scanner.c: -------------------------------------------------------------------------------- 1 | #include "scanner.h" 2 | 3 | /* #define DEBUG */ 4 | static inline void debug(char *str) 5 | { 6 | #ifdef DEBUG 7 | printf(" - %s\n", str); 8 | #endif 9 | } 10 | 11 | static char _SYMS[] = { '|', '\\', '-', '>', '<', 'x', ':', '.' }; 12 | static int _SYMS_S = sizeof(_SYMS) / sizeof(char); 13 | static char _NOT_ALLOWED[] = { ' ', '\0', TEXT_WRAP_SYM }; 14 | static int _NOT_ALLOWED_S = sizeof(_NOT_ALLOWED) / sizeof(char); 15 | 16 | // is symbol 17 | bool is_sym(char ch) 18 | { 19 | int i = 0; 20 | while (i < _SYMS_S) 21 | { 22 | if (ch == _SYMS[i]) 23 | return true; 24 | i++; 25 | } 26 | return false; 27 | } 28 | 29 | bool _is_word(char ch) 30 | { 31 | bool is_ALLOWED = true; 32 | bool is_SYM = true; 33 | for (int i = 0; i < _NOT_ALLOWED_S; i++) 34 | { 35 | if (ch == _NOT_ALLOWED[i]) 36 | is_ALLOWED = false; 37 | } 38 | for (int i = 0; i < _SYMS_S; i++) 39 | { 40 | if (ch == _SYMS[i]) 41 | is_SYM = false; 42 | } 43 | return is_ALLOWED && is_SYM; 44 | } 45 | 46 | bool _is_closed(char ch) 47 | { 48 | return ch != TEXT_WRAP_SYM; 49 | } 50 | 51 | void _read_w(Word *word, char *ch, bool (*stopper)(char), Type type) 52 | { 53 | debug("_read_w entered"); 54 | bool is_wrapped = false; 55 | int i = 0; 56 | while (stopper(ch[i])) 57 | { 58 | if (ch[i] == EOA) 59 | { 60 | debug("return empty"); 61 | is_wrapped = true; 62 | break; 63 | } 64 | i++; 65 | } 66 | 67 | if (!word->init) 68 | { 69 | char temp[i]; 70 | strncpy(temp, ch, i); 71 | if (!word->length) 72 | word->length = i - word->l_spc; 73 | word->string = (char*)malloc_s(sizeof(char) * word->length); 74 | strncpy(word->string, temp, word->length); 75 | word->string[word->length] = '\0'; 76 | word->type = is_wrapped ? Uninitialized : type; 77 | word->init = !is_wrapped; 78 | } 79 | } 80 | 81 | 82 | // read word ignore space 83 | int rdw_ignspc(Word *word, char *ch, bool *wrapped) 84 | { 85 | debug("called rdw_ignspc()"); 86 | _read_w(word, ch, _is_closed, Plain); 87 | debug(word->string); 88 | if (word->type != Uninitialized) 89 | { 90 | if (word->length != strlen(word->string)) 91 | word->string[word->length] = '\0'; 92 | *wrapped = false; 93 | } 94 | word->d_type = WRAPPED_WORD; 95 | debug("return rdw_ignspc()"); 96 | return word->length; 97 | } 98 | 99 | int rd_sym(Word *l_sym, char *ch) 100 | { 101 | debug("called rd_sym()"); 102 | _read_w(l_sym, ch, is_sym, Symbol); 103 | check_symbol(l_sym); 104 | debug(l_sym->string); 105 | debug("return rd_sym()"); 106 | return l_sym->length; 107 | } 108 | 109 | // read word 110 | // - return last ch pointer as int 111 | int rdw(Word *curw, char *ch) 112 | { 113 | debug("called rdw()"); 114 | _read_w(curw, ch, _is_word, Plain); 115 | check_statement(curw); 116 | debug(curw->string); 117 | debug("return rdw()"); 118 | return curw->length; 119 | } 120 | -------------------------------------------------------------------------------- /grammars/statement.h: -------------------------------------------------------------------------------- 1 | #ifndef STATEMENT_H 2 | #define STATEMENT_H 3 | 4 | #include "../split.h" 5 | #include "../mem.h" 6 | // 'participant' | string | 'as' | word 7 | #define PARTICIPANT_AS_BIN 0b01100110 8 | // 'participant' | word 9 | #define PARTICIPANT_BIN 0b0110 10 | 11 | // word | symbol | word | symbol | string 12 | #define ARROW_CONNECTION_BIN 0b0111011101 13 | 14 | 15 | #define ST(BIN) (BIN ^ (0b01 << 28)) 16 | #define ST_MASK 0b00100000000000000000000000000000 17 | // ^ ^ ^ ^ 18 | #define IS_ST(BIN) (BIN | ST_MASK) 19 | static const int _statements_s = 3; 20 | static const struct _Statements { 21 | char name[20]; 22 | uint32_t o; 23 | } _statements[] = { 24 | #define WRAPPED_WORD ST(0x00000001) 25 | // *** participant *** 26 | #define PARTICIPANT_F ST(0x00000003) 27 | #define PARTICIPANT "participant" 28 | { PARTICIPANT, PARTICIPANT_F }, 29 | // *** as *** 30 | #define AS_F ST(0x00000004) 31 | #define AS "as" 32 | { AS, AS_F }, 33 | // *** note *** 34 | #define NOTE_F ST(0x00000005) 35 | #define NOTE "note" 36 | { NOTE, NOTE_F }, 37 | #define OVER_F ST(0x00000006) 38 | #define OVER "over" 39 | { OVER, OVER_F }, 40 | #define LEFT_F ST(0x00000007) 41 | #define LEFT "left" 42 | { LEFT, LEFT_F }, 43 | #define RIGHT_F ST(0x00000008) 44 | #define RIGHT "right" 45 | { RIGHT, RIGHT_F }, 46 | 47 | // *** arrow_connection *** 48 | #define ARROW_CONNCETION_F ST(0x00000006) 49 | // #define ARROW_CONNCETION "sd" 50 | // { ARROW_CONNCETION, ARROW_CONNCETION_F } 51 | 52 | }; 53 | 54 | static inline void check_statement(Word *word) 55 | { 56 | for (int _i = 0; _i < _statements_s; _i++) 57 | { 58 | if (!strcmp(word->string, _statements[_i].name)) 59 | { 60 | word->type = Statement; 61 | word->d_type = _statements[_i].o; 62 | } 63 | } 64 | } 65 | 66 | #define AS_BIN 0b011001 67 | 68 | static inline void preprocessing(Words *words) 69 | { 70 | return ; 71 | uint32_t as_b = AS_BIN; 72 | // #define DEBUG_ 73 | #ifdef DEBUG_ 74 | print_uint32_t("words->ewp:", words->ewp); 75 | #endif 76 | for (int i = 1; i < words->lp-1; i++) 77 | { 78 | #ifdef DEBUG_ 79 | print_uint32_t("as_b :", as_b); 80 | print_uint32_t("fods :", words->ewp & as_b); 81 | #endif 82 | if (as_b == (words->ewp & as_b) && words->c[i]->d_type == AS_F && words->c[i-1]->d_type == WRAPPED_WORD && words->c[i+1]->d_type != WRAPPED_WORD) 83 | { 84 | words->c[i+1]->has_alias = true; 85 | words->c[i+1]->alias = words->c[i-1]->string; 86 | uint32_t mask = 0b001111 << i*2; 87 | words->ewp &= ~mask; 88 | // clean up 89 | printf(" - %s\n", words->c[i]->string); 90 | // free((char*)words->c[i]->string); // free 'as' 91 | // free(&words->c[i]); // free word:as 92 | // printf(" ere\n"); 93 | // free(&words->c[i-1]); // free word:' ' 94 | // realign 95 | // for (int j = i+1; j < words->lp; j++) 96 | // { 97 | // printf(" - move %s to %s\n", words->c[j].string, words->c[j-2].string); 98 | // words->c[j] = words->c[j-2]; 99 | // } 100 | // words->lp -= 2; i -= 2; 101 | // words->c = (Word *)realloc_s(words->c, sizeof(Words) * words->lp); 102 | #ifdef DEBUG_ 103 | print_uint32_t("masked :", words->ewp); 104 | #endif 105 | } 106 | as_b <<= 2; 107 | } 108 | } 109 | #endif 110 | -------------------------------------------------------------------------------- /style.h: -------------------------------------------------------------------------------- 1 | #ifndef STYLE_H 2 | #define STYLE_H 3 | 4 | #define PARTICIPANT_TOP_GAP 0 5 | #define PARTICIPANT_BOTTOM_GAP 0 6 | #define PARTICIPANT_VERTICAL_GAP (PARTICIPANT_BOTTOM_GAP+PARTICIPANT_TOP_GAP+2) 7 | // +-------+ <-+ 8 | // | somth | | 9 | // +-------+ <-+- : default = 2 10 | #define PARTICIPANT_LEFT_GAP 1 11 | #define PARTICIPANT_RIGHT_GAP 1 12 | #define PARTICIPANT_HORIZONTAL_GAP (PARTICIPANT_LEFT_GAP+PARTICIPANT_RIGHT_GAP+2) 13 | // +-------+ 14 | // | somth | 15 | // +-------+ 16 | // ^-------^---: default = 2 17 | //-------------------------------------------------- 18 | // ***STYLES*** 19 | #define DEFAULT 0 20 | #define UTF_DEFAULT 1 21 | //-------------------------------------------------- 22 | #define STYLE_S 21 23 | #define UTF_SUPPORT 24 | # define D_PARTICIPANT_VERTICAL_LINE "|" 25 | # define D_PARTICIPANT_HORIZONTAL_LINE "-" 26 | # define D_PARTICIPANT_TOP_LEFT "," 27 | # define D_PARTICIPANT_TOP_RIGHT "." 28 | # define D_PARTICIPANT_BOTTOM_LEFT "`" 29 | # define D_PARTICIPANT_BOTTOM_RIGHT "'" 30 | # define D_PARTICIPANT_BOTTOM_CONNECTION "-" 31 | # define D_VERTICAL_LINE "|" 32 | # define D_ARROW_NORMAL_VERTICAL_LINE "|" 33 | # define D_ARROW_RET_VERTICAL_LINE ":" 34 | # define D_ARROW_RET_LINE_L "." 35 | # define D_ARROW_RET_LINE_R "." 36 | # define D_ARROW_NORMAL_R ">" 37 | # define D_ARROW_NORMAL_L "<" 38 | # define D_ARROW_RET_L "<" 39 | # define D_ARROW_RET_R ">" 40 | # define D_ARROW_ORIGIN_R "+" 41 | # define D_ARROW_ORIGIN_L "+" 42 | # define D_ARROW_LINE_R "-" 43 | # define D_ARROW_LINE_L "-" 44 | //-------------------------------------------------- 45 | # define U_PARTICIPANT_VERTICAL_LINE "│" 46 | # define U_PARTICIPANT_HORIZONTAL_LINE "─" 47 | # define U_PARTICIPANT_TOP_LEFT "╭" 48 | # define U_PARTICIPANT_TOP_RIGHT "╮" 49 | # define U_PARTICIPANT_BOTTOM_LEFT "╰" 50 | # define U_PARTICIPANT_BOTTOM_RIGHT "╯" 51 | # define U_PARTICIPANT_BOTTOM_CONNECTION "┬" 52 | # define U_VERTICAL_LINE "│" 53 | # define U_ARROW_NORMAL_VERTICAL_LINE "│" 54 | # define U_ARROW_RET_VERTICAL_LINE "┊" 55 | # define U_ARROW_RET_LINE_L "╴" 56 | # define U_ARROW_RET_LINE_R "╶" 57 | # define U_ARROW_NORMAL_R "▶" 58 | # define U_ARROW_NORMAL_L "◀" 59 | # define U_ARROW_RET_L "≺" 60 | # define U_ARROW_RET_R "≻" 61 | # define U_ARROW_ORIGIN_R "├" 62 | # define U_ARROW_ORIGIN_L "┤" 63 | # define U_ARROW_LINE_R "╌" 64 | # define U_ARROW_LINE_L "╌" 65 | //-------------------------------------------------- 66 | #define PARTICIPANT_VERTICAL_LINE style[ 0 ] 67 | #define PARTICIPANT_HORIZONTAL_LINE style[ 1 ] 68 | #define PARTICIPANT_TOP_LEFT style[ 2 ] 69 | #define PARTICIPANT_TOP_RIGHT style[ 3 ] 70 | #define PARTICIPANT_BOTTOM_LEFT style[ 4 ] 71 | #define PARTICIPANT_BOTTOM_RIGHT style[ 5 ] 72 | #define PARTICIPANT_BOTTOM_CONNECTION style[ 6 ] 73 | #define VERTICAL_LINE style[ 7 ] 74 | #define ARROW_NORMAL_VERTICAL_LINE style[ 8 ] 75 | #define ARROW_RET_VERTICAL_LINE style[ 9 ] 76 | #define ARROW_RET_LINE_L style[ 10 ] 77 | #define ARROW_RET_LINE_R style[ 11 ] 78 | #define ARROW_NORMAL_R style[ 12 ] 79 | #define ARROW_NORMAL_L style[ 13 ] 80 | #define ARROW_RET_L style[ 14 ] 81 | #define ARROW_RET_R style[ 15 ] 82 | #define ARROW_ORIGIN_R style[ 16 ] 83 | #define ARROW_ORIGIN_L style[ 17 ] 84 | #define ARROW_LINE_R style[ 18 ] 85 | #define ARROW_LINE_L style[ 19 ] 86 | 87 | #ifdef UTF_SUPPORT 88 | static inline char* C(char* ch) 89 | { 90 | return ch; 91 | } 92 | #else 93 | static inline char C(char* ch) 94 | { 95 | return *ch; 96 | } 97 | #endif 98 | 99 | char* style[STYLE_S]; 100 | void set_style(int style); 101 | #endif 102 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | #include "main.h" 2 | #define STDIN_OPTION_POSITION 1 3 | #define STANDARD_OPTION_POSITION 2 4 | void read_lines_and_render(FILE *fp) 5 | { 6 | int CUR_LINE_NUM = 0; 7 | char *line = NULL; 8 | size_t len = 0; 9 | ssize_t read; 10 | 11 | while ((read = getline(&line, &len, fp)) != -1) 12 | { 13 | CUR_LINE_NUM++; 14 | if (strcmp(line, "\n") && line[0] != ';') 15 | { 16 | #ifdef CLI 17 | if (SHOW_LOG > 1) 18 | { 19 | int line_s = strlen(line) + 3; 20 | char sep[line_s]; 21 | memset(sep, '-', line_s); 22 | sep[line_s] = '\0'; 23 | printf(KGRN"%s\n", sep); 24 | printf(KBLU" +"KCYN" %s\n"RESET, line); 25 | } 26 | #endif 27 | line[strlen(line)-1] = '\0'; 28 | parse_line(line, CUR_LINE_NUM); 29 | } 30 | } 31 | 32 | render(); 33 | fclose(fp); 34 | if (line) 35 | free(line); 36 | } 37 | 38 | 39 | void readfile(char *filename) 40 | { 41 | FILE *fp = fopen(filename, "r"); 42 | if (fp == NULL) 43 | { 44 | printf("file not found \n"); 45 | exit(0); 46 | } 47 | read_lines_and_render(fp); 48 | } 49 | 50 | void read_stdin() 51 | { 52 | FILE *fp = fdopen(STDIN_FILENO, "r"); 53 | if (fp == NULL) 54 | { 55 | printf("stdin not found \n"); 56 | exit(0); 57 | } 58 | read_lines_and_render(fp); 59 | } 60 | 61 | 62 | int main(int argc, char *argv[]) 63 | { 64 | SHOW_LOG = 0; 65 | 66 | prefix = NULL; 67 | suffix = NULL; 68 | printRaw = false; 69 | 70 | bool is_text = false; 71 | bool is_stdin_disabled = isatty(0); 72 | 73 | #ifdef CHECK_UPDATE 74 | if (argc == 1) 75 | { 76 | check_version(); 77 | printf("sequence-diagram-cli %s\ngithub@Ja-sonYun, email: jason@abex.dev\ngithub: https://github.com/Ja-sonYun/sequence-diagram-cli\nwebsite: https://abex.dev\n", VERSION); 78 | 79 | return 0; 80 | } 81 | 82 | printf("*** sequence-diagram-cli %s, github@Ja-sonYun ***\n", VERSION); 83 | #endif 84 | 85 | int option_starting_pos = is_stdin_disabled ? STANDARD_OPTION_POSITION : STDIN_OPTION_POSITION; 86 | for (int i = option_starting_pos; i < argc; i++) 87 | { 88 | if (!strncmp(argv[i], "log=", 4)) 89 | { 90 | if (strlen(argv[i]) == 5 && (argv[i][4] == 1 || argv[i][4 == 0])) 91 | { 92 | SHOW_LOG = argv[i][4] - 48; 93 | } 94 | else 95 | { 96 | printf(KRED"wrong log option!\n"RESET); 97 | } 98 | } 99 | else if (!strcmp(argv[i], "al")) 100 | { 101 | is_text = true; 102 | } 103 | else if (!strcmp(argv[i], "raw")) 104 | { 105 | printRaw = true; 106 | } 107 | else if (!strncmp(argv[i], "prefix=", 7)) 108 | { 109 | if (strlen(argv[i]) > 7) 110 | { 111 | prefix = &argv[i][7]; 112 | } 113 | else 114 | { 115 | printf(KRED"wrong prefix option!\n"RESET); 116 | } 117 | } 118 | else if (!strncmp(argv[i], "suffix=", 7)) 119 | { 120 | if (strlen(argv[i]) > 7) 121 | { 122 | suffix = &argv[i][7]; 123 | } 124 | else 125 | { 126 | printf(KRED"wrong suffix option!\n"RESET); 127 | } 128 | } 129 | else 130 | { 131 | printf(KRED"wrong option!\n"RESET); 132 | } 133 | } 134 | 135 | set_style(is_text); 136 | 137 | bool file_exists = access(argv[1], F_OK) == 0; 138 | if (!file_exists && is_stdin_disabled) 139 | { 140 | printf("file not found \n"); 141 | } 142 | else if (file_exists && !is_stdin_disabled) 143 | { 144 | printf("Undefined behaviour: stdin and file specified\n"); 145 | } 146 | else if (is_stdin_disabled) 147 | { 148 | readfile(argv[1]); 149 | } 150 | else 151 | { 152 | read_stdin(); 153 | } 154 | 155 | 156 | #ifdef CHECK_UPDATE 157 | check_version(); 158 | #endif 159 | 160 | return 0; 161 | } 162 | 163 | -------------------------------------------------------------------------------- /grammars/arrow_connection.h: -------------------------------------------------------------------------------- 1 | #ifndef AROROWCONNECTION_H 2 | #define AROROWCONNECTION_H 3 | 4 | #include 5 | #include "../mem.h" 6 | #include "../split.h" 7 | #include "participant.h" 8 | #include "statement.h" 9 | #include "symbol.h" 10 | 11 | #include "../terminal.h" 12 | 13 | #ifdef CLI 14 | extern int SHOW_LOG; 15 | #endif 16 | 17 | // word -> word : string 18 | #define IS_ARROW_LINE(words) \ 19 | (words->ewp == (UNFLAG_R(words->ewp, 10) | ARROW_CONNECTION_BIN) \ 20 | && IS_ARROW(words->c[1]->d_type) \ 21 | && *words->c[3]->string == ':') \ 22 | 23 | typedef struct { 24 | Participant *from; 25 | Participant *to; 26 | char *content; 27 | int number; 28 | Color color; 29 | uint32_t type; 30 | } ArrowConnection; 31 | 32 | typedef struct { 33 | ArrowConnection **cons; 34 | int cons_num; 35 | } ArrowConnections; 36 | 37 | extern ArrowConnections arrow_connections; 38 | 39 | static inline void define_new_arrow(Words *words) 40 | { 41 | ArrowConnection *new_arrow = (ArrowConnection *)calloc_s(1, sizeof(ArrowConnection)); 42 | // strcpy(new_arrow->type, words->c[1]->string); 43 | new_arrow->type = words->c[1]->d_type; 44 | 45 | Participant *exist_first = find_participant(words->c[0]->string); 46 | if (!exist_first) 47 | { 48 | exist_first = (Participant *)calloc_s(1, sizeof(Participant)); 49 | exist_first->has_alias = false; 50 | exist_first->priority = participants.members_num; 51 | exist_first->name = words->c[0]; 52 | join_participants(exist_first); 53 | } 54 | 55 | Participant *exist_second = find_participant(words->c[2]->string); 56 | if (!exist_second) 57 | { 58 | exist_second = (Participant *)calloc_s(1, sizeof(Participant)); 59 | exist_first->has_alias = false; 60 | exist_second->priority = participants.members_num; 61 | exist_second->name = words->c[2]; 62 | join_participants(exist_second); 63 | } 64 | if (IS_LEFT_DIR(words->c[1]->d_type)) 65 | { 66 | new_arrow->to = exist_first; 67 | new_arrow->from = exist_second; 68 | } 69 | else 70 | { 71 | new_arrow->from = exist_first; 72 | new_arrow->to = exist_second; 73 | } 74 | new_arrow->content = words->c[4]->string; 75 | 76 | arrow_connections.cons_num++; 77 | arrow_connections.cons = (ArrowConnection**)realloc_s(arrow_connections.cons, sizeof(ArrowConnection) * arrow_connections.cons_num); 78 | arrow_connections.cons[arrow_connections.cons_num - 1] = new_arrow; 79 | 80 | #ifdef CLI 81 | if(SHOW_LOG >= 1) 82 | { 83 | printf_c(KCYN " * "); 84 | printf("define new arrow: '%s' %d '%s' : %s\n", new_arrow->from->name->string, new_arrow->type, new_arrow->to->name->string, new_arrow->content); 85 | 86 | } 87 | #endif 88 | } 89 | 90 | static inline bool check_is_arrow(Words *words) 91 | { 92 | if (IS_ARROW_LINE(words)) 93 | { 94 | static const int REAL_WORD_OFFSET = 4; // A(0) ->(1) B(2) :(3) something(4) 95 | 96 | if (!(words->c[REAL_WORD_OFFSET]->d_type == WRAPPED_WORD)) // check ^something is wrapped 97 | { // if wrapped, merge all words after ':' 98 | int wc_to_merge = words->lp - REAL_WORD_OFFSET; 99 | int i; 100 | Word *mergew[wc_to_merge]; 101 | for (i = REAL_WORD_OFFSET; i < words->lp; i++) 102 | { 103 | mergew[i-REAL_WORD_OFFSET] = words->c[i]; 104 | } 105 | Word *re = merge_words(wc_to_merge, mergew, true); 106 | words->c[REAL_WORD_OFFSET] = re; 107 | // resize words 108 | for (i = 0; i < words->lp - REAL_WORD_OFFSET; i++) 109 | { 110 | if (mergew[i]->string != NULL) 111 | free(mergew[i]->string); 112 | if (mergew[i] != NULL) 113 | free(mergew[i]); 114 | } 115 | words->c = (Word **)realloc_s(words->c, sizeof(Word) * REAL_WORD_OFFSET + 1); 116 | words->lp = REAL_WORD_OFFSET; 117 | } 118 | else if (words->lp > REAL_WORD_OFFSET+1 && words->c[REAL_WORD_OFFSET]->d_type == WRAPPED_WORD) 119 | { 120 | printf_c(KYEL "WARNING"); 121 | printf(": words after : '%s' will be ignored.\n", words->c[4]->string); 122 | } 123 | define_new_arrow(words); 124 | return true; 125 | } 126 | return false; 127 | } 128 | 129 | #endif 130 | -------------------------------------------------------------------------------- /parser.c: -------------------------------------------------------------------------------- 1 | #include "parser.h" 2 | 3 | static int max_call_lim = 0; 4 | static inline void watch_compile_error(int size) 5 | { 6 | if (max_call_lim > size) 7 | { 8 | printf("\ncompile error!\n"); 9 | exit(0); 10 | }; 11 | max_call_lim++; 12 | } 13 | 14 | // EP 15 | Words* split(char *line) 16 | { 17 | if (line[0] == '"') 18 | { 19 | printf(" * you may have empty line that end with ` \" `\n"); 20 | return NULL; // raise error 21 | } 22 | max_call_lim = 0; 23 | static bool nl = false; 24 | static bool wrapped = false; 25 | int w_size; 26 | /* bool may_empty = false; */ 27 | if (nl) nl = false; 28 | 29 | Words* line_temp = new_words(); 30 | 31 | Word* word = new_word_p("", 0); 32 | 33 | for (int len = 0; len < strlen(line); len++) 34 | { 35 | watch_compile_error(strlen(line)); 36 | if (wrapped) goto STILL_WRAPPED; 37 | switch (line[len]) 38 | { 39 | case '\\': 40 | nl = true; 41 | /* return; */ 42 | 43 | case ' ': 44 | case '\t': 45 | break; 46 | 47 | case TEXT_WRAP_SYM: 48 | wrapped = true; 49 | len++; // go inside of symbols 50 | STILL_WRAPPED: 51 | w_size = rdw_ignspc(word, &line[len], &wrapped); 52 | if (w_size) 53 | { 54 | COPY_TO_WORDS(line_temp, word); 55 | word = new_word_p("", 0); 56 | } 57 | len += w_size - 1; 58 | if (!wrapped) 59 | len++; // escape wrapped string 60 | break; 61 | 62 | default: 63 | if (is_sym(line[len])) 64 | { 65 | w_size = rd_sym(word, &line[len]); 66 | if (w_size) 67 | { 68 | COPY_TO_WORDS(line_temp, word); 69 | word = new_word_p("", 0); 70 | } 71 | len += w_size - 1; 72 | } 73 | else 74 | { 75 | w_size = rdw(word, &line[len]); 76 | int temp_p = w_size + len; 77 | word->l_spc = 0; 78 | while (line[temp_p] == ' ') 79 | { 80 | temp_p++; 81 | word->l_spc++; 82 | } 83 | if (w_size) 84 | { 85 | if (strcmp(word->string, " ")) 86 | { 87 | COPY_TO_WORDS(line_temp, word); 88 | } 89 | word = new_word_p("", 0); 90 | } 91 | len += w_size - 1; 92 | } 93 | } 94 | } 95 | if (wrapped) 96 | line_temp->is_eol = true; 97 | return line_temp; 98 | } 99 | 100 | bool parse(Words *words, int line_num) 101 | { 102 | static Words* prev_words; 103 | static bool still_wrapped = false; 104 | #ifdef DEBUG 105 | printf("is wrapped : %d\n", words->is_eol); 106 | #endif 107 | if (still_wrapped) 108 | { 109 | // merge prev_words[-1] cur_words[0] 110 | #ifdef DEBUG 111 | printf("merge -> '%s' : '%s' \n", prev_words->c[prev_words->lp-1]->string, words->c[words->lp-1]->string); 112 | #endif 113 | words = connect_words(prev_words, words); 114 | #ifdef DEBUG 115 | printf("merged. '%s'\n", words->c[words->lp - 1]->string); 116 | #endif 117 | still_wrapped = false; 118 | prev_words = NULL; 119 | } 120 | if (words->is_eol) 121 | { 122 | prev_words = words; 123 | still_wrapped = true; 124 | return false; 125 | } 126 | for (int i = 0; i < words->lp; i++) 127 | { 128 | // search and set each words roll 129 | Type temp_T = words->c[i]->type; 130 | if (temp_T == Uninitialized) 131 | temp_T = Unknown; 132 | words->ewp ^= temp_T << (i * 2); 133 | #ifdef DEBUG 134 | printf(" - word : %s, type : %d, d_type : %d, size : %d, r_size : %lu, spc : %d\n", words->c[i]->string, words->c[i]->type, words->c[i]->d_type, words->c[i]->length, strlen(words->c[i]->string), words->c[i]->l_spc); 135 | print_uint32_t("ewp : ", words->ewp); 136 | #endif 137 | } 138 | #ifdef DEBUG 139 | print_uint32_t("ewp : ", words->ewp); 140 | #endif 141 | preprocessing(words); // '"A" as a' to single word 142 | 143 | if (check_is_participant(words) || 144 | check_is_arrow(words)) 145 | return true; 146 | 147 | #ifdef PYTHON_BINDING 148 | failed_at = line_num; 149 | #else 150 | printf(KRED"**** PARSE FAILED at line %d ****\n"RESET, line_num); 151 | #endif 152 | return false; 153 | } 154 | -------------------------------------------------------------------------------- /grammars/participant.h: -------------------------------------------------------------------------------- 1 | #ifndef PARTICIPANT_H 2 | #define PARTICIPANT_H 3 | 4 | #include 5 | #include "../mem.h" 6 | #include "../split.h" 7 | #include "statement.h" 8 | 9 | #include "../terminal.h" 10 | 11 | #define IS_PARTICIPANT_LINE(words) \ 12 | (words->ewp == (UNFLAG_R(words->ewp, 4) | PARTICIPANT_BIN) \ 13 | && words->c[0]->d_type == PARTICIPANT_F) 14 | 15 | // same participants, with header icon 16 | #define PARTICIPANT_TYPE_ACTOR 1 17 | #define PARTICIPANT_TYPE_BOUNDARY 2 18 | #define PARTICIPANT_TYPE_CONTROL 3 19 | #define PARTICIPANT_TYPE_ENTIRY 4 20 | #define PARTICIPANT_TYPE_DATABASE 5 21 | #define PARTICIPANT_TYPE_COLLECTIONS 6 22 | #define PARTICIPANT_START_SYM 'participant' 23 | 24 | #ifdef CLI 25 | extern int SHOW_LOG; 26 | #endif 27 | 28 | typedef struct 29 | { 30 | unsigned int icon : 1; 31 | bool has_alias; 32 | Word *name; 33 | Word *alias_name; 34 | int priority; 35 | uint8_t alignment; 36 | Color color; 37 | } Participant; 38 | 39 | typedef struct 40 | { 41 | int members_num; 42 | Participant **members; 43 | } Participants; 44 | 45 | extern Participants participants; 46 | 47 | static inline void join_participants(Participant* new_member) 48 | { 49 | participants.members_num++; 50 | participants.members = (Participant**)realloc_s(participants.members, sizeof(Participant) * participants.members_num); 51 | participants.members[participants.members_num - 1] = new_member; 52 | #ifdef CLI 53 | if (SHOW_LOG >= 1) 54 | { 55 | if (new_member->has_alias) 56 | { 57 | printf_c(KCYN " * "); 58 | printf("register new participant with alias: '%s' as %s , priority: '%d'\n", new_member->name->string, new_member->alias_name->string, participants.members_num); 59 | } 60 | else 61 | { 62 | printf_c(KCYN " * "); 63 | printf("register new participant: '%s' , priority: '%d'\n", new_member->name->string, participants.members_num); 64 | } 65 | } 66 | #endif 67 | } 68 | 69 | static inline Participant *find_participant(char *str) 70 | { 71 | for (int _i = 0; _i < participants.members_num; _i++) 72 | { 73 | if ((participants.members[_i]->has_alias && !strcmp(participants.members[_i]->alias_name->string, str)) 74 | || !strcmp(participants.members[_i]->name->string, str)) 75 | { 76 | return participants.members[_i]; 77 | } 78 | } 79 | return NULL; 80 | } 81 | 82 | //( words->lp == 2 || ( words->lp == 4 && words->c[1].d_type & WRAPPED_WORD 83 | // && words->c[2].d_type & AS_F ) )) 84 | static inline bool check_is_participant(Words *words) 85 | { 86 | if (IS_PARTICIPANT_LINE(words)) // checking participant exist 87 | { 88 | bool has_alias; 89 | 90 | if (words->lp == 2 && !(words->c[1]->d_type == WRAPPED_WORD)) 91 | has_alias = false; 92 | else if (words->lp == 4 && words->c[1]->d_type == WRAPPED_WORD && words->c[2]->d_type == AS_F) // checking "A" as a 93 | has_alias = true; 94 | else 95 | { 96 | // error message 97 | if (words->lp > 3 && !(words->c[1]->d_type == WRAPPED_WORD) ) 98 | { 99 | printf_c(KRED "ERROR"); 100 | printf(": replace '%s' to ' \"%s\" '. \n", words->c[1]->string, words->c[1]->string); 101 | dump_words(words, 1); 102 | } 103 | else if (words->lp == 2 && (words->c[1]->d_type == WRAPPED_WORD)) 104 | { 105 | printf_c(KRED "ERROR"); 106 | printf(": Replace to 'participant '%s' as $(ALIAS)'!\n", words->c[1]->string); 107 | dump_words(words, 1); 108 | } 109 | else 110 | { 111 | printf_c(KRED "ERROR"); 112 | printf(": Something wrong!\n"); 113 | } 114 | 115 | return false; 116 | } 117 | 118 | if ((!has_alias && find_participant(words->c[1]->string)) 119 | || (has_alias && find_participant(words->c[3]->string))) 120 | { 121 | printf_c(KRED "ERROR"); 122 | printf(": '%s' already registered!\n", has_alias ? words->c[3]->string : words->c[1]->string); 123 | return false; 124 | } 125 | 126 | // when executed without any error 127 | Participant *new_participant = (Participant *)calloc_s(1, sizeof(Participant)); 128 | new_participant->has_alias = has_alias; 129 | new_participant->alias_name = has_alias ? words->c[3] : NULL; 130 | new_participant->priority = participants.members_num; 131 | new_participant->name = words->c[1]; 132 | join_participants(new_participant); 133 | return true; 134 | } 135 | return false; 136 | } 137 | 138 | #endif 139 | -------------------------------------------------------------------------------- /split.h: -------------------------------------------------------------------------------- 1 | #ifndef SPLIT_H 2 | #define SPLIT_H 3 | #include 4 | #include 5 | #include 6 | #include "mem.h" 7 | #include "terminal.h" 8 | 9 | #define GLOBAL_WS 3 10 | 11 | #define SET_WORD_TYPE_AND_RET(word, thistype) \ 12 | int string_size = strlen(word->string); \ 13 | if (!word->init && string_size) \ 14 | { \ 15 | word->length = string_size; \ 16 | word->init = true; \ 17 | } \ 18 | if (string_size) \ 19 | word->type = thistype; \ 20 | return string_size 21 | 22 | #define TEXT_WRAP_SYM '\"' 23 | // #define TEXT_WRAP_SYM '\'' 24 | #define EOA '\0' 25 | #define UNINITIALIZED -1 26 | 27 | #define TEXT_STRING 0 28 | //TODO: color syntax 29 | 30 | typedef struct 31 | { 32 | char rgb[3][2]; 33 | } Color; 34 | 35 | typedef enum { 36 | Uninitialized = -1, 37 | Unknown = 0b00, 38 | Plain = 0b01, 39 | Statement = 0b10, 40 | Symbol = 0b11, 41 | } Type; 42 | 43 | typedef struct 44 | { 45 | bool init; 46 | int length; 47 | char *string; 48 | bool has_alias; 49 | char *alias; 50 | int l_spc; 51 | int f_spc; 52 | Type type; 53 | uint32_t d_type; 54 | } Word; 55 | 56 | typedef union 57 | { 58 | char *c_ptr; 59 | bool init; 60 | } c_i; 61 | 62 | static inline Word* new_word_p(char *str, int size) 63 | { 64 | bool init = true; 65 | Type type = Uninitialized; 66 | if (!str[0] && !size) init = false; 67 | if (!str[0] && size) 68 | { 69 | init = false; 70 | str = (char *)calloc_s(size, sizeof(char)); 71 | } 72 | if (str[0] && !size) size = strlen(str); 73 | Word *n_word = (Word *)calloc_s(1, sizeof(Word)); 74 | n_word->init = init; 75 | n_word->type = type; 76 | n_word->length = size; 77 | n_word->l_spc = 0; 78 | n_word->has_alias = false; 79 | n_word->string = strdup(str); 80 | n_word->d_type = 0; 81 | return n_word; 82 | } 83 | 84 | static inline void inc_word(Word *target, int len) 85 | { 86 | int t = target->length; 87 | target->length += len; 88 | target->string = (char*)realloc_s(target->string, sizeof(char) * target->length); 89 | memset(target->string+t, 0, len); 90 | } 91 | 92 | static inline Word* merge_words(int count, Word *words[], bool withspc) 93 | { 94 | Word *merged_words = new_word_p((char*)"", 0); 95 | char *bufw; 96 | for (int i = 0; i < count; i++) 97 | { 98 | bufw = (char*)calloc_s(words[i]->length, sizeof(char)); 99 | strcpy(bufw, words[i]->string); 100 | bufw[words[i]->length] = '\0'; 101 | #ifdef _DEBUG 102 | printf("w:%s, s:%d, r:%lu, lp:%d\n", bufw, words[i]->length, strlen(bufw), words[i]->l_spc); 103 | #endif 104 | inc_word(merged_words, words[i]->length + words[i]->l_spc); 105 | strncat(merged_words->string, words[i]->string, words[i]->length); 106 | free(bufw); 107 | if (withspc && words[i]->l_spc) 108 | { 109 | bufw = (char*)malloc_s(words[i]->l_spc); 110 | memset(bufw, ' ', words[i]->l_spc); 111 | bufw[words[i]->l_spc] = '\0'; 112 | strcat(merged_words->string, bufw); 113 | free(bufw); 114 | } 115 | } 116 | if (strlen(merged_words->string) != merged_words->length) 117 | merged_words->string[merged_words->length] = '\0'; 118 | return merged_words; 119 | } 120 | 121 | typedef struct 122 | { 123 | int lp; 124 | Word **c; 125 | uint32_t ewp; // each words position 126 | bool is_eol; 127 | // 0x '00' '00' '00' '00', each '00' means a word, 128 | // 01: plain, 10: statement, 11: symbol, 00: something else(this could be an empty) 129 | // When it's something else, check inside of the word (d_type). 130 | } Words; 131 | 132 | #include "grammars/statement.h" 133 | 134 | static inline Words* connect_words(Words* prev, Words* cur) 135 | { 136 | inc_word(prev->c[prev->lp - 1], 1); 137 | prev->c[prev->lp - 1]->string[prev->c[prev->lp - 1]->length - 1] = '\n'; 138 | Word* words_to_merge[] = { prev->c[prev->lp - 1], cur->c[0] }; 139 | prev->c[prev->lp - 1] = merge_words(2, words_to_merge, false); 140 | prev->c[prev->lp - 1]->type = Plain; 141 | prev->c[prev->lp - 1]->d_type = WRAPPED_WORD; 142 | prev->is_eol = cur->is_eol; 143 | int lp_b = prev->lp; 144 | #ifdef DEBUG 145 | printf("prev : %d, cur : %d, lp_b : %d, merged word: %s\n", prev->lp, cur->lp, lp_b, prev->c[prev->lp-1]->string); 146 | #endif 147 | prev->lp += cur->lp - 1; 148 | if (prev->lp != lp_b) 149 | prev->c = (Word **)realloc_s(prev->c, sizeof(Word) * (prev->lp + 1)); 150 | for (int _i = 1; _i < cur->lp; _i++) 151 | { 152 | #ifdef DEBUG 153 | printf("_i : %d, new_cur : %d\n", _i, lp_b + _i - 1); 154 | #endif 155 | prev->c[lp_b + _i - 1] = cur->c[_i]; 156 | } 157 | free(cur->c[0]->string); 158 | free(cur->c[0]); 159 | free(cur); 160 | return prev; 161 | } 162 | 163 | static inline Words* new_words() 164 | { 165 | Words* n_words = (Words*)calloc_s(1, sizeof(Words)); 166 | n_words->lp = 0; 167 | n_words->ewp = 0; 168 | n_words->is_eol = 0; 169 | return n_words; 170 | } 171 | 172 | #define COPY_TO_WORDS(words, word) \ 173 | words->lp++; \ 174 | words->c = (Word **)realloc_s(words->c, sizeof(Word) * (words->lp)); \ 175 | words->c[words->lp - 1] = word; \ 176 | 177 | static inline void drop_word_from_words(Words *words, int from, int to) 178 | { 179 | for (int _i = from; _i < words->lp && _i < to; _i++) 180 | { 181 | free(words->c[_i]->string); 182 | free(&words->c[_i]); 183 | } 184 | words->lp -= to - from; 185 | } 186 | 187 | static inline void dump_words(Words* words, int i) 188 | { 189 | int _i, _j; 190 | for (_i = 0; _i < words->lp; _i++) 191 | { 192 | for (_j = 0; _j < words->c[_i]->f_spc; _j++) 193 | putchar(' '); 194 | if (i == _i) 195 | printf(KYEL "%s" RESET, words->c[_i]->string); 196 | else 197 | printf("%s", words->c[_i]->string); 198 | for (_j = 0; _j < words->c[_i]->l_spc; _j++) 199 | putchar(' '); 200 | } 201 | putchar('\n'); 202 | printf(KRED); 203 | for (_i = 0; _i < words->lp; _i++) 204 | { 205 | for (_j = 0; _j < words->c[_i]->f_spc; _j++) 206 | putchar(' '); 207 | for (_j = 0; _j < words->c[_i]->length; _j++) 208 | { 209 | if (i == _i && !_j) 210 | putchar('^'); 211 | else if (i == _i) 212 | putchar('~'); 213 | else 214 | putchar(' '); 215 | } 216 | for (_j = 0; _j < words->c[_i]->l_spc; _j++) 217 | putchar(' '); 218 | } 219 | printf(RESET); 220 | putchar('\n'); 221 | } 222 | 223 | #endif 224 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # sequence-diagram-cli 2 | Draw seqence diagram from terminal. 3 | 4 | 5 | Korean and Japanese(which takes two spaces by character in terminal) will looks like above image only in terminal. 6 | 7 | **You can also customize styles by editing style.h.** 8 | 9 | ## Use from python 10 | ``` 11 | git clone --recurse-submodules https://github.com/Ja-sonYun/sequence-diagram-cli.git 12 | cd sequence-diagram-cli/seqdia-python-binding 13 | python setup.py build 14 | python setup.py install 15 | python -c "import seqdia" 16 | ``` 17 | [more details](https://github.com/Ja-sonYun/seqdia-python-binding) 18 | 19 | ## Installation 20 | `wget -qO - https://github.com/Ja-sonYun/sequence-diagram-cli/raw/main/install.sh | bash` 21 | ``` 22 | Usage 23 | 24 | $ seqdia 'YOUR_SEQUENCE_DIAGRAM_FILE' 25 | $ seqdia 'YOUR_SEQUENCE_DIAGRAM_FILE' prefix='// ' suffix='|' al raw 26 | 27 | v1.1 28 | // with ' al ' option will draw sequence diagram in pure character not utf-8. 29 | 30 | // with ' raw ' the surrounding lines will not be printed 31 | 32 | //examples 33 | $ seqdia tests/test.txt 34 | $ echo "Alice -> Bob:Hello" | seqdia 35 | ``` 36 | 37 | ## Example ouput 38 | ```scala 39 | ./tests/test.txt 40 | ------------------ 41 | ; participants 42 | participant User 43 | participant " * TODO 44 | - clean living room " as todo 45 | participant Dev 46 | 47 | ; connections 48 | User->User: default arrow 49 | User-->Dev: styled arrow 50 | todo->todo : "self connecting 51 | with 52 | new line" 53 | User->Dev : yes? sd 54 | User<--Dev : no 55 | todo<--Dev: reverse arrow 56 | S->todo: "EEEE 57 | E 58 | EEEE 59 | E 60 | EEEE" 61 | ------------------- 62 | 63 | $ ~/sequence-diagram-cli > seqdia tests/test.txt prefix="<-- " suffix=" -->" 64 | =========================================================== 65 | <-- ╭────────────────────────╮ --> 66 | <-- ╭──────╮│ * TODO │ ╭─────╮╭───╮ --> 67 | <-- │ User ││ - clean living room │ │ Dev ││ S │ --> 68 | <-- ╰───┬──╯╰────────────┬───────────╯ ╰──┬──╯╰─┬─╯ --> 69 | <-- │ │ │ │ --> 70 | <-- │ │ │ │ --> 71 | <-- │ default arrow │ │ │ --> 72 | <-- ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮ │ │ │ --> 73 | <-- │ │ │ │ │ --> 74 | <-- │◀╌╌╌╌╌╌╌╌╌╌╌╌╌╯ │ │ │ --> 75 | <-- │ │ │ │ --> 76 | <-- │ styled arrow │ │ --> 77 | <-- ├╶╶╶╶╶╶╶╶╶╶╶╶╶╶╶╶╶╶╶╶╶╶╶╶╶╶╶╶╶╶╶╶╶╶≻│ │ --> 78 | <-- │ │ │ │ --> 79 | <-- │ │ self connecting │ │ --> 80 | <-- │ │ with │ │ --> 81 | <-- │ │ new line │ │ --> 82 | <-- │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮ │ │ --> 83 | <-- │ │ │ │ │ --> 84 | <-- │ │◀╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯ │ │ --> 85 | <-- │ │ │ │ --> 86 | <-- │ yes? sd │ │ --> 87 | <-- ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌▶│ │ --> 88 | <-- │ │ │ │ --> 89 | <-- │ no │ │ --> 90 | <-- │≺╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴┤ │ --> 91 | <-- │ │ │ │ --> 92 | <-- │ │ reverse arrow │ │ --> 93 | <-- │ │≺╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴┤ │ --> 94 | <-- │ │ │ │ --> 95 | <-- │ │ EEEE │ │ --> 96 | <-- │ │ E │ │ --> 97 | <-- │ │ EEEE │ │ --> 98 | <-- │ │ E │ │ --> 99 | <-- │ │ EEEE │ │ --> 100 | <-- │ │◀╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ --> 101 | <-- │ │ │ │ --> 102 | <-- │ │ │ │ --> 103 | <-- │ │ │ │ --> 104 | <-- │ │ │ │ --> 105 | <-- │ │ │ │ --> 106 | <-- │ │ │ │ --> 107 | =========================================================== 108 | 109 | **** with al option **** 110 | 111 | $ ~/sequence-diagram-cli > seqdia tests/test.txt al prefix='<--' 112 | ===================================================== 113 | // +------------------------+ 114 | //+------+| * TODO | +-----++---+ 115 | //| User || - clean living room | | Dev || S | 116 | //+---+--++------------+-----------+ +--+--++-+-+ 117 | // | | | | 118 | // | | | | 119 | // | default arrow | | | 120 | // +--------------+ | | | 121 | // | | | | | 122 | // |<-------------+ | | | 123 | // | | | | 124 | // | styled arrow | | 125 | // +..................................>| | 126 | // | | | | 127 | // | | self connecting | | 128 | // | | with | | 129 | // | | new line | | 130 | // | +----------------+ | | 131 | // | | | | | 132 | // | |<---------------+ | | 133 | // | | | | 134 | // | yes? sd | | 135 | // +---------------------------------->| | 136 | // | | | | 137 | // | no | | 138 | // |<..................................+ | 139 | // | | | | 140 | // | | reverse arrow | | 141 | // | |<.................+ | 142 | // | | | | 143 | // | | EEEE | | 144 | // | | E | | 145 | // | | EEEE | | 146 | // | | E | | 147 | // | | EEEE | | 148 | // | |<-----------------------+ 149 | // | | | | 150 | // | | | | 151 | // | | | | 152 | // | | | | 153 | // | | | | 154 | // | | | | 155 | ===================================================== 156 | 157 | ``` 158 | ## Syntax 159 | ### participant 160 | You don't need to define participant, but for alignment, you should. 161 | ```scala 162 | participant User // this will be front 163 | ; or 164 | participant "with a2a!@#!$" as A // you can use this like 'A -> User: ***' 165 | ; or 166 | participant "new 167 | line" as nl // this will be last 168 | ``` 169 | 170 | ### arrow 171 | ```scala 172 | User->New: message // participant 'New' will defined automatically at here 173 | ; or 174 | User-->New: message 175 | ; or 176 | User<-A: message 177 | ; or 178 | User<--A: message 179 | ; or 180 | nl -> nl: " realllly // with multiple lines 181 | looooooong 182 | messageee" 183 | ; or 184 | ee <- ee: " 185 | you can't use like this" // when using new line, you should add 1 character to first line at least. I'll fix this soon. 186 | ``` 187 | 188 | ### TODO 189 | - note 190 | - more arrow design 191 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /renderer.c: -------------------------------------------------------------------------------- 1 | #include "renderer.h" 2 | 3 | void push_to_buf(char *format, char *ref_val) 4 | { 5 | #ifdef PYTHON_BINDING 6 | // expect that ref_val is str, therefore we will not use format at here 7 | 8 | int ref_val_len = strlen(ref_val); 9 | if (buffer_size + ref_val_len > LINE_BUF_CHUNK * chunk_size - LINE_BUF_PADDING) 10 | { 11 | // increase chunk 12 | chunk_size++; 13 | size_t new_size = LINE_BUF_CHUNK * chunk_size * sizeof(char); 14 | size_t old_size = LINE_BUF_CHUNK * (chunk_size - 1) * sizeof(char); 15 | line_buffer = (char*)realloc_s(line_buffer, new_size); 16 | size_t diff = new_size - old_size; 17 | void* pStart = ((char*)line_buffer) + old_size; 18 | 19 | memset(pStart, 0, diff); 20 | } 21 | 22 | buffer_size += ref_val_len; 23 | strcat(line_buffer, ref_val); 24 | 25 | #else 26 | printf(format, ref_val); 27 | #endif 28 | } 29 | 30 | void move_next_buf() 31 | { 32 | #ifdef PYTHON_BINDING 33 | result_list_size++; 34 | PY_list_result = (char**)realloc_s(PY_list_result, sizeof(char*) * result_list_size); 35 | PY_list_result[result_list_size-1] = line_buffer; 36 | char *new_buffer; 37 | line_buffer = new_buffer; 38 | buffer_size = 0; 39 | chunk_size = 0; 40 | #else 41 | putchar('\n'); 42 | #endif 43 | } 44 | 45 | 46 | Area init() 47 | { 48 | struct participant_render* participants_r = (struct participant_render*)malloc(sizeof(struct participant_render) * participants.members_num); 49 | for (int i = 0; i < participants.members_num; i++) 50 | { 51 | participants_r[i].right_padding = 0; 52 | participants_r[i].left_padding = 0; 53 | } 54 | return (Area) { 55 | .header = { 56 | .pos = POS_INIT, 57 | .participants = participants_r, 58 | }, 59 | .body = { 60 | .pos = POS_INIT, 61 | .l_p = 0, 62 | }, 63 | .pos = POS_INIT, 64 | }; 65 | } 66 | 67 | Pos get_word_size(char *word, int length) 68 | { 69 | Pos w_s = { .x = 0, .y = 1 }; 70 | int wxs = 0; 71 | int g_c = 0; 72 | int g_c_y = 0; 73 | for (int i = 0; i < length; i++) // 3, 4, 3 74 | { 75 | if (word[i] == '\n') 76 | { 77 | if (w_s.x < wxs) // 3>0 | 78 | w_s.x = wxs; 79 | if (g_c_y < g_c) 80 | g_c_y = g_c; 81 | g_c = 0; 82 | wxs = 0; 83 | w_s.y++; 84 | } 85 | else 86 | { 87 | #if GLOBAL_WS == 3 88 | if (word[i] >> 7 && word[i+1] >> 7 && word[i+2]) 89 | { 90 | g_c++; 91 | i += 2; 92 | } 93 | #elif GLOBAL_WS == 2 94 | if (word[i] >> 7 && word[i+1] >> 7) 95 | { 96 | g_c++; 97 | i += 1; 98 | } 99 | #endif 100 | else 101 | { 102 | wxs++; 103 | } 104 | } 105 | } 106 | if (w_s.x == 0 || w_s.x < wxs) 107 | w_s.x = wxs; 108 | if (g_c_y == 0 || g_c_y < g_c) 109 | g_c_y = g_c; 110 | w_s.x = w_s.x + g_c_y * 2; 111 | return w_s; 112 | } 113 | 114 | void cal_coordinates_of_participants(Area *area) 115 | { 116 | int i, last_y = 0; 117 | for (i = 0; i < participants.members_num; i++) 118 | { 119 | area->header.participants[i].word_size = get_word_size(participants.members[i]->name->string, participants.members[i]->name->length); 120 | if (last_y < area->header.participants[i].word_size.y) 121 | last_y = area->header.participants[i].word_size.y; 122 | 123 | if (i == 0) 124 | area->header.participants[i].from.x = area->header.pos.x + area->header.participants[i].left_padding; 125 | else 126 | area->header.participants[i].from.x = area->header.pos.x + area->header.participants[i].left_padding + area->header.participants[i-1].right_padding; 127 | area->header.participants[i].from.y = 0; 128 | 129 | area->header.participants[i].to.x = area->header.participants[i].word_size.x + PARTICIPANT_HORIZONTAL_GAP + area->header.participants[i].from.x; 130 | area->header.participants[i].to.y = area->header.participants[i].word_size.y + PARTICIPANT_VERTICAL_GAP + area->header.participants[i].from.y; 131 | 132 | area->header.pos = area->header.participants[i].to; 133 | area->header.participants[i].middle = GET_LINE_M(area->header.participants[i].from.x, area->header.participants[i].to.x); 134 | } 135 | if (last_y > 1) 136 | { 137 | last_y = last_y + PARTICIPANT_VERTICAL_GAP; 138 | for (i = 0; i < participants.members_num; i++) 139 | { 140 | area->header.participants[i].to.y = last_y; 141 | area->header.participants[i].from.y = area->header.participants[i].to.y - area->header.participants[i].word_size.y - PARTICIPANT_VERTICAL_GAP; 142 | } 143 | area->header.pos.y = last_y; 144 | } 145 | } 146 | 147 | void recal_coordinates_of_participants(Area *area) 148 | { // padding, for example 149 | int i, y, x; 150 | area->header.participants[0].from.x = 0; 151 | int pos_x_b = area->header.pos.x; 152 | area->header.pos.x = 0; 153 | for (i = 0; i < participants.members_num; i++) 154 | { 155 | if (i == 0) 156 | area->header.participants[i].from.x = area->header.pos.x; 157 | else 158 | area->header.participants[i].from.x = area->header.pos.x + area->header.participants[i-1].right_padding; 159 | area->header.participants[i].to.x = area->header.participants[i].word_size.x + PARTICIPANT_HORIZONTAL_GAP + area->header.participants[i].from.x; 160 | area->header.pos.x = area->header.participants[i].to.x; 161 | } 162 | area->header.pos.x = area->header.pos.x + area->header.participants[participants.members_num-1].right_padding + 2; 163 | area->body.pos.x = area->header.pos.x; 164 | 165 | bool do_realloc = area->header.pos.x != pos_x_b; 166 | for (y = 0; y < area->body.pos.y; y++) 167 | { 168 | if (do_realloc) 169 | { 170 | #ifdef UTF_SUPPORT 171 | area->body.buffer[y] = (char**)realloc_s(area->body.buffer[y], area->body.pos.x * sizeof(char*)); 172 | #else 173 | area->body.buffer[y] = (char*)realloc_s(area->body.buffer[y], area->body.pos.x * sizeof(char)); 174 | #endif 175 | } 176 | for (x = 0; x < area->body.pos.x; x++) 177 | { 178 | #ifdef UTF_SUPPORT 179 | area->body.buffer[y][x] = " "; 180 | #else 181 | memset(area->body.buffer[y], ' ', area->body.pos.x); 182 | area->body.buffer[y][area->body.pos.x] = '\0'; 183 | #endif 184 | } 185 | for (i = 0; i < participants.members_num; i++) 186 | area->body.buffer[y][GET_LINE_M(area->header.participants[i].from.x, area->header.participants[i].to.x)] = C(VERTICAL_LINE); 187 | } 188 | 189 | } 190 | 191 | void add_participants_to_buffer(Area* area) 192 | { 193 | // TODO: free when this called again 194 | int y, x, m, t; 195 | #ifdef UTF_SUPPORT 196 | area->header.buffer = (char***)malloc_s(area->header.pos.y * sizeof(char**)); 197 | #else 198 | area->header.buffer = (char**)malloc_s(area->header.pos.y * sizeof(char*)); 199 | #endif 200 | for (y = 0; y < area->header.pos.y; y++) 201 | { 202 | #ifdef UTF_SUPPORT 203 | area->header.buffer[y] = (char**)malloc_s(area->header.pos.x * sizeof(char*)); 204 | for (x = 0; x < area->header.pos.x; x++) 205 | area->header.buffer[y][x] = " "; 206 | #else 207 | area->header.buffer[y] = (char*)malloc_s(area->header.pos.x * sizeof(char)); 208 | memset(area->header.buffer[y], ' ', area->header.pos.x); 209 | area->header.buffer[y][area->header.pos.x] = '\0'; 210 | #endif 211 | } 212 | // push border to buffer 213 | for (m = 0; m < participants.members_num; m++) 214 | { 215 | for (y = area->header.participants[m].from.y; y < area->header.participants[m].to.y; y++) 216 | { 217 | for (x = area->header.participants[m].from.x; x < area->header.participants[m].to.x; x++) 218 | { 219 | if (y == area->header.participants[m].from.y) // TOP 220 | { 221 | if (x == area->header.participants[m].from.x) 222 | area->header.buffer[y][x] = C(PARTICIPANT_TOP_LEFT); // TOP - LEFT 223 | else if (x == area->header.participants[m].to.x - 1) 224 | area->header.buffer[y][x] = C(PARTICIPANT_TOP_RIGHT); // TOP - RIGHT 225 | else 226 | area->header.buffer[y][x] = C(PARTICIPANT_HORIZONTAL_LINE); // TOP - BORDER 227 | } 228 | else if (y == area->header.participants[m].to.y - 1) // BOTTOM 229 | { 230 | area->header.participants[m].middle = GET_LINE_M(area->header.participants[m].from.x, area->header.participants[m].to.x); 231 | if (area->header.participants[m].middle == x) 232 | area->header.buffer[y][x] = C(PARTICIPANT_BOTTOM_CONNECTION); // BOTTOM - LEFT 233 | else if (x == area->header.participants[m].from.x) 234 | area->header.buffer[y][x] = C(PARTICIPANT_BOTTOM_LEFT); // BOTTOM - LEFT 235 | else if (x == area->header.participants[m].to.x - 1) 236 | area->header.buffer[y][x] = C(PARTICIPANT_BOTTOM_RIGHT); // BOTTOM - RIGHT 237 | else 238 | area->header.buffer[y][x] = C(PARTICIPANT_HORIZONTAL_LINE); // BOTTOM - BORDER 239 | } 240 | else if (x == area->header.participants[m].from.x || 241 | x == area->header.participants[m].to.x - 1) 242 | area->header.buffer[y][x] = C(PARTICIPANT_VERTICAL_LINE); // VERTICAL - LINE 243 | } 244 | } 245 | // TODO : alignment 246 | y = area->header.participants[m].from.y + 1 + PARTICIPANT_TOP_GAP; 247 | x = area->header.participants[m].from.x + 1 + PARTICIPANT_LEFT_GAP; 248 | char *c; 249 | char *tc; 250 | int g_c = 0; 251 | for (t = 0; t < participants.members[m]->name->length; t++) 252 | { 253 | if (participants.members[m]->name->string[t] == '\n') 254 | { 255 | tc = area->header.buffer[y][x + g_c + PARTICIPANT_LEFT_GAP]; 256 | if (g_c > 0 && tc[0] != ' ') 257 | { 258 | char *n = (char*)malloc_s(g_c * sizeof(char)); 259 | memset(&n[0], '\b', g_c); 260 | memcpy(&n[g_c], tc, strlen(tc)+1); 261 | area->header.buffer[y][x + g_c + PARTICIPANT_LEFT_GAP] = n; 262 | g_c = 0; 263 | } 264 | y++; 265 | x = area->header.participants[m].from.x + 1 + PARTICIPANT_LEFT_GAP; 266 | } 267 | else 268 | { 269 | #ifdef UTF_SUPPORT 270 | if (participants.members[m]->name->string[t] >> 7) 271 | { 272 | c = (char*)malloc_s(sizeof(char)*4); 273 | c[3] = '\0'; 274 | memcpy(&c[0], &participants.members[m]->name->string[t], 3); 275 | t += 2; 276 | g_c++; 277 | } 278 | else 279 | { 280 | c = (char*)malloc_s(sizeof(char)*2); 281 | c[1] = '\0'; 282 | c[0] = participants.members[m]->name->string[t]; 283 | } 284 | area->header.buffer[y][x] = c; 285 | #else 286 | area->header.buffer[y][x] = participants.members[m]->name->string[t]; 287 | #endif 288 | x++; 289 | } 290 | } 291 | tc = area->header.buffer[y][x + g_c + PARTICIPANT_LEFT_GAP]; 292 | if (g_c > 0 && tc[0] != ' ') 293 | { 294 | char *n = (char*)malloc_s(g_c * sizeof(char)); 295 | memset(&n[0], '\b', g_c); 296 | memcpy(&n[g_c], tc, strlen(tc)+1); 297 | area->header.buffer[y][x + g_c + PARTICIPANT_LEFT_GAP] = n; 298 | } 299 | } 300 | } 301 | 302 | void print_header(Area area) 303 | { 304 | int x, y; 305 | #ifdef UTF_SUPPORT 306 | for (y = 0; y < area.header.pos.y; y++) 307 | { 308 | #ifdef OPTS_SUP 309 | if (prefix != NULL) 310 | push_to_buf("%s", prefix); 311 | #endif 312 | for (x = 0; x < area.header.pos.x; x++) 313 | { 314 | push_to_buf("%s", area.header.buffer[y][x]); 315 | } 316 | #ifdef OPTS_SUP 317 | if (suffix != NULL) 318 | push_to_buf("%s", suffix); 319 | #endif 320 | #ifdef PYTHON_BINDING 321 | move_next_buf(); 322 | #else 323 | putchar('\n'); 324 | #endif 325 | } 326 | #else 327 | for (y = 0; y < area.header.pos.y; y++) 328 | #ifdef OPTS_SUP 329 | if (prefix != NULL) 330 | push_to_buf("%s", prefix); 331 | #endif 332 | push_to_buf("%s", area.header.buffer[y]); 333 | #ifdef OPTS_SUP 334 | if (suffix != NULL) 335 | push_to_buf("%s", suffix); 336 | #endif 337 | #ifdef PYTHON_BINDING 338 | move_next_buf(); 339 | #else 340 | putchar('\n'); 341 | #endif 342 | 343 | #endif 344 | } 345 | 346 | void print_body(Area area) 347 | { 348 | int x, y; 349 | #ifdef UTF_SUPPORT 350 | for (y = 0; y < area.body.pos.y; y++) 351 | { 352 | #ifdef OPTS_SUP 353 | if (prefix != NULL) 354 | push_to_buf("%s", prefix); 355 | #endif 356 | for (x = 0; x < area.header.pos.x; x++) 357 | { 358 | push_to_buf("%s", area.body.buffer[y][x]); 359 | } 360 | #ifdef OPTS_SUP 361 | if (suffix != NULL) 362 | push_to_buf("%s", suffix); 363 | #endif 364 | #ifdef PYTHON_BINDING 365 | move_next_buf(); 366 | #else 367 | putchar('\n'); 368 | #endif 369 | } 370 | #else 371 | for (y = 0; y < area.body.pos.y; y++) 372 | { 373 | #ifdef OPTS_SUP 374 | if (prefix != NULL) 375 | push_to_buf("%s", prefix); 376 | #endif 377 | push_to_buf("%s", area.body.buffer[y]); 378 | #ifdef OPTS_SUP 379 | if (suffix != NULL) 380 | push_to_buf("%s", suffix); 381 | #endif 382 | #ifdef PYTHON_BINDING 383 | move_next_buf(); 384 | #else 385 | putchar('\n'); 386 | #endif 387 | } 388 | #endif 389 | } 390 | 391 | void add_participants_line(Area *area, int size) 392 | { 393 | int x, y, m; 394 | area->body.pos.y += size; 395 | area->body.pos.x = (area->body.pos.x == 0) ? area->header.pos.x : area->body.pos.x; 396 | #ifdef UTF_SUPPORT 397 | area->body.buffer = (char***)realloc_s(area->body.buffer, area->body.pos.y * sizeof(char**)); 398 | #else 399 | area->body.buffer = (char**)realloc_s(area->body.buffer, area->body.pos.y * sizeof(char*)); 400 | #endif 401 | for (y = area->body.pos.y - size; y < area->body.pos.y; y++) 402 | { 403 | #ifdef UTF_SUPPORT 404 | area->body.buffer[y] = (char**)malloc_s(area->body.pos.x * sizeof(char*)); 405 | for (x = 0; x < area->body.pos.x; x++) 406 | area->body.buffer[y][x] = " "; 407 | #else 408 | area->body.buffer[y] = (char*)malloc_s(area->body.pos.x * sizeof(char)); 409 | memset(area->body.buffer[y], ' ', area->body.pos.x); 410 | area->body.buffer[y][area->body.pos.x] = '\0'; 411 | #endif 412 | for (m = 0; m < participants.members_num; m++) 413 | { 414 | area->body.buffer[y][GET_LINE_M(area->header.participants[m].from.x, area->header.participants[m].to.x)] = C(VERTICAL_LINE); 415 | } 416 | } 417 | } 418 | 419 | void cal_arrow_coordinates(Area *area) 420 | { 421 | int a, x, y; 422 | for (a = 0; a < arrow_connections.cons_num; a++) 423 | { 424 | add_participants_line(area, 1); 425 | int from = GET_LINE_M(area->header.participants[arrow_connections.cons[a]->from->priority].from.x, area->header.participants[arrow_connections.cons[a]->from->priority].to.x); 426 | int to = GET_LINE_M(area->header.participants[arrow_connections.cons[a]->to->priority].from.x, area->header.participants[arrow_connections.cons[a]->to->priority].to.x); 427 | area->body.arrow_defs[a].from.x = from; 428 | area->body.arrow_defs[a].to.x = to; 429 | int y_size = area->body.arrow_defs[a].size.y + 2; 430 | area->body.l_p += y_size; 431 | area->body.arrow_defs[a].from.y = area->body.l_p; 432 | area->body.arrow_defs[a].to.y = area->body.l_p; 433 | 434 | // ============================================= 435 | // If there's nothing above current arrow, decrease y position. <- doesn't work with self assign TODO: fix here 436 | /* if (a != 0) */ 437 | /* { */ 438 | /* bool has_space = false; */ 439 | /* int temp = 0; */ 440 | /* for (y = 0; y < area->body.arrow_defs[a].size.y; y++) */ 441 | /* { */ 442 | /* #ifdef UTF_SUPPORT */ 443 | /* if (!strcmp(area->body.buffer[area->body.l_p - y_size - y][GET_LINE_M(from, to)], " ")) */ 444 | /* { */ 445 | /* temp++; */ 446 | /* has_space = true; */ 447 | /* } */ 448 | /* else break; */ 449 | /* #endif */ 450 | /* } */ 451 | /* if (has_space) */ 452 | /* { */ 453 | /* area->body.l_p -= temp - 1; */ 454 | /* area->body.arrow_defs[a].from.y -= temp - 1; */ 455 | /* area->body.arrow_defs[a].to.y -= temp - 1; */ 456 | /* y_size -= temp - 1; */ 457 | /* } */ 458 | /* } */ 459 | // ============================================= 460 | 461 | add_participants_line(area, y_size); 462 | bool left_dir = false; 463 | bool self_message = false; 464 | if (from == to) 465 | { 466 | self_message = true; 467 | } 468 | else if (from > to) // self message 469 | { 470 | left_dir = true; 471 | int temp = to; 472 | to = from; 473 | from = temp; 474 | } 475 | if (self_message) 476 | { 477 | add_participants_line(area, 2); 478 | for (y = 0; y < 3; y++) // y = 3 479 | { 480 | for (x = from; x < from + area->body.arrow_defs[a].size.x + 3; x++) 481 | { 482 | if (x == from && y == 0) 483 | area->body.buffer[area->body.l_p+y][x] = ARROW_ORIGIN_R; 484 | else if (x == from + area->body.arrow_defs[a].size.x + 2 && y == 0) 485 | area->body.buffer[area->body.l_p+y][x] = PARTICIPANT_TOP_RIGHT; 486 | else if (x == from + area->body.arrow_defs[a].size.x + 2 && y == 2) 487 | area->body.buffer[area->body.l_p+y][x] = PARTICIPANT_BOTTOM_RIGHT; 488 | else if (x == from + area->body.arrow_defs[a].size.x + 2 && y == 1) 489 | { 490 | if (arrow_connections.cons[a]->type == (L_RET_AR_F) || 491 | arrow_connections.cons[a]->type == (R_RET_AR_F)) 492 | area->body.buffer[area->body.l_p+y][x] = ARROW_RET_VERTICAL_LINE; 493 | else 494 | area->body.buffer[area->body.l_p+y][x] = ARROW_NORMAL_VERTICAL_LINE; 495 | } 496 | else if (y == 2 && x == from + 1) 497 | { 498 | if (arrow_connections.cons[a]->type == (L_RET_AR_F) || 499 | arrow_connections.cons[a]->type == (R_RET_AR_F)) 500 | area->body.buffer[area->body.l_p+y][x] = ARROW_RET_L; 501 | else 502 | area->body.buffer[area->body.l_p+y][x] = ARROW_NORMAL_L; 503 | } 504 | else if (x != from && y == 0) 505 | { 506 | if (arrow_connections.cons[a]->type == (L_RET_AR_F) || 507 | arrow_connections.cons[a]->type == (R_RET_AR_F)) 508 | area->body.buffer[area->body.l_p+y][x] = ARROW_RET_LINE_R; 509 | else 510 | area->body.buffer[area->body.l_p+y][x] = ARROW_LINE_R; 511 | } 512 | else if (x != from && y == 2) 513 | { 514 | if (arrow_connections.cons[a]->type == (L_RET_AR_F) || 515 | arrow_connections.cons[a]->type == (R_RET_AR_F)) 516 | area->body.buffer[area->body.l_p+y][x] = ARROW_RET_LINE_L; 517 | else 518 | area->body.buffer[area->body.l_p+y][x] = ARROW_LINE_L; 519 | } 520 | } 521 | } 522 | 523 | area->body.l_p += 2; 524 | } 525 | else 526 | { 527 | for (x = from; x < to + 1; x++) 528 | { 529 | #ifdef UTF_SUPPORT 530 | if (left_dir) 531 | { 532 | if (x == to) 533 | area->body.buffer[area->body.l_p][x] = ARROW_ORIGIN_L; 534 | else if (x == from + 1) 535 | { 536 | if (arrow_connections.cons[a]->type == (L_RET_AR_F) 537 | || arrow_connections.cons[a]->type == (R_RET_AR_F)) 538 | area->body.buffer[area->body.l_p][x] = ARROW_RET_L; 539 | else 540 | area->body.buffer[area->body.l_p][x] = ARROW_NORMAL_L; 541 | } 542 | else if (x != from) 543 | { 544 | if (arrow_connections.cons[a]->type == (L_RET_AR_F) 545 | || arrow_connections.cons[a]->type == (R_RET_AR_F)) 546 | area->body.buffer[area->body.l_p][x] = ARROW_RET_LINE_L; 547 | else 548 | area->body.buffer[area->body.l_p][x] = ARROW_LINE_L; 549 | } 550 | } 551 | else 552 | { 553 | if (x == from) 554 | area->body.buffer[area->body.l_p][x] = ARROW_ORIGIN_R; 555 | else if (x == to) 556 | { 557 | if (arrow_connections.cons[a]->type == (L_RET_AR_F) 558 | || arrow_connections.cons[a]->type == (R_RET_AR_F)) 559 | area->body.buffer[area->body.l_p][x-1] = ARROW_RET_R; 560 | else 561 | area->body.buffer[area->body.l_p][x-1] = ARROW_NORMAL_R; 562 | } 563 | else 564 | { 565 | if (arrow_connections.cons[a]->type == (L_RET_AR_F) 566 | || arrow_connections.cons[a]->type == (R_RET_AR_F)) 567 | area->body.buffer[area->body.l_p][x] = ARROW_RET_LINE_R; 568 | else 569 | area->body.buffer[area->body.l_p][x] = ARROW_LINE_R; 570 | } 571 | } 572 | #else 573 | area->body.buffer[area->body.l_p][x] = '-'; 574 | #endif 575 | } 576 | 577 | } 578 | } 579 | } 580 | 581 | void cal_arrow_message_size(Area *area) 582 | { 583 | int a; 584 | 585 | area->body.arrow_defs = (struct arrow_def_coor*)malloc_s(sizeof(struct arrow_def_coor) * arrow_connections.cons_num); 586 | #ifdef DEBUG 587 | printf("arrow num: %d\n", arrow_connections.cons_num); 588 | #endif 589 | for (a = 0; a < arrow_connections.cons_num; a++) 590 | { 591 | area->body.arrow_defs[a].size = get_word_size(arrow_connections.cons[a]->content, (int)strlen(arrow_connections.cons[a]->content)); 592 | 593 | area->body.arrow_defs[a].top_padding = area->body.arrow_defs[a].size.y + 1; 594 | 595 | int width = area->header.participants[arrow_connections.cons[a]->to->priority].middle - area->header.participants[arrow_connections.cons[a]->from->priority].middle; 596 | int *from; 597 | if (width < 0) 598 | from = &area->header.participants[arrow_connections.cons[a]->to->priority].right_padding; 599 | else 600 | from = &area->header.participants[arrow_connections.cons[a]->from->priority].right_padding; 601 | width = abs(width); 602 | bool self_arrow = false; 603 | if (width == 0) 604 | { 605 | self_arrow = true; 606 | width = area->body.arrow_defs[a].size.x; 607 | } 608 | 609 | if (width < area->body.arrow_defs[a].size.x + 3) 610 | { 611 | //TODO: working on here 612 | int temp = 0; 613 | #ifdef DEBUG 614 | printf("cc:%d\n", area->body.arrow_defs[a].size.x); 615 | #endif 616 | if (self_arrow) 617 | { 618 | int klp = (area->header.participants[arrow_connections.cons[a]->to->priority].middle - area->header.participants[arrow_connections.cons[a]->to->priority].from.x); 619 | if (arrow_connections.cons[a]->from->priority == participants.members_num - 1) // last arrow of self 620 | temp = width - klp + 2; 621 | else 622 | { 623 | int kj = 0; 624 | if (arrow_connections.cons[a]->to->priority != arrow_connections.cons_num - 1) 625 | kj = (area->header.participants[arrow_connections.cons[a]->to->priority+1].middle - area->header.participants[arrow_connections.cons[a]->to->priority+1].from.x); 626 | temp = width+2 - klp; 627 | temp -= kj - 2; 628 | } 629 | } 630 | else 631 | temp = area->body.arrow_defs[a].size.x - width + 3; 632 | if (temp > *from) 633 | { 634 | *from = temp; 635 | } 636 | #ifdef DEBUG 637 | printf("ss:%d\n", area->body.arrow_defs[a].size.x); 638 | #endif 639 | } 640 | #ifdef DEBUG 641 | printf("arrow message size : %d, %d, %d\n", area->body.arrow_defs[a].size.x, area->body.arrow_defs[a].size.y, width); 642 | #endif 643 | } 644 | } 645 | 646 | void add_arrow_messages(Area *area) 647 | { 648 | int a, x = 0, y; 649 | bool print_at_middle = false; 650 | char *c; 651 | int dir; 652 | for (a = 0; a < arrow_connections.cons_num; a++) 653 | { 654 | int g_c = 0; 655 | print_at_middle = false; 656 | if (abs(area->body.arrow_defs[a].from.x - area->body.arrow_defs[a].to.x) - 4 > area->body.arrow_defs[a].size.x) 657 | { 658 | print_at_middle = true; 659 | x = (abs(area->body.arrow_defs[a].from.x - area->body.arrow_defs[a].to.x) / 2) - (area->body.arrow_defs[a].size.x / 2) - 2; 660 | } 661 | y = area->body.arrow_defs[a].size.y; 662 | for (int w = 0; w < strlen(arrow_connections.cons[a]->content); w++) 663 | { 664 | if (arrow_connections.cons[a]->content[w] == '\n') 665 | { 666 | c = area->body.buffer[area->body.arrow_defs[a].from.y - y][dir + 1 + x + g_c]; 667 | if (g_c > 0 && c[0] == ' ') 668 | { 669 | char *n = (char*)malloc_s(g_c * sizeof(char)); 670 | memset(&n[0], '\b', g_c); 671 | memcpy(&n[g_c], c, strlen(c)+1); 672 | area->body.buffer[area->body.arrow_defs[a].from.y - y][dir + 1 + x + g_c + 1] = n; 673 | g_c = 0; 674 | } 675 | y--; 676 | if (print_at_middle) 677 | x = (abs(area->body.arrow_defs[a].from.x - area->body.arrow_defs[a].to.x) / 2) - (area->body.arrow_defs[a].size.x / 2) - 2; 678 | else 679 | x = 0; 680 | } 681 | else 682 | { 683 | dir = area->body.arrow_defs[a].from.x; 684 | if (dir > area->body.arrow_defs[a].to.x) 685 | dir = area->body.arrow_defs[a].to.x; 686 | x++; 687 | #ifdef UTF_SUPPORT 688 | if (arrow_connections.cons[a]->content[w] >> 7) 689 | { 690 | c = (char*)malloc_s(sizeof(char)*4); 691 | c[3] = '\0'; 692 | memcpy(&c[0], &arrow_connections.cons[a]->content[w], 3); 693 | w += 2; 694 | g_c++; 695 | } 696 | else 697 | { 698 | c = (char*)malloc_s(sizeof(char)*2); 699 | c[1] = '\0'; 700 | c[0] = arrow_connections.cons[a]->content[w]; 701 | } 702 | area->body.buffer[area->body.arrow_defs[a].from.y - y][dir + 1 + x] = c; 703 | #endif 704 | } 705 | } 706 | c = area->body.buffer[area->body.arrow_defs[a].from.y - y][dir + 1 + x + g_c]; 707 | if (g_c > 0 && c[0] == ' ') 708 | { 709 | char *n = (char*)malloc_s(g_c * sizeof(char)); 710 | memset(&n[0], '\b', g_c); 711 | memcpy(&n[g_c], c, strlen(c)+1); 712 | area->body.buffer[area->body.arrow_defs[a].from.y - y][dir + 1 + x + g_c + 1] = n; 713 | } 714 | y = 0; 715 | x = 0; 716 | } 717 | } 718 | 719 | void render() 720 | { 721 | 722 | // initialize 723 | Area new_area = init(); 724 | 725 | cal_coordinates_of_participants(&new_area); 726 | 727 | cal_arrow_message_size(&new_area); 728 | 729 | recal_coordinates_of_participants(&new_area); 730 | add_participants_to_buffer(&new_area); 731 | 732 | cal_arrow_coordinates(&new_area); 733 | add_arrow_messages(&new_area); 734 | 735 | int l = new_area.header.pos.x; 736 | #ifdef OPTS_SUP 737 | if (prefix != NULL) 738 | l += strlen(prefix); 739 | if (suffix != NULL) 740 | l += strlen(suffix); 741 | #endif 742 | 743 | char a[l]; 744 | #ifdef OPTS_SUP 745 | if (!printRaw) { 746 | memset(a, '=', l); 747 | a[l] = '\0'; 748 | push_to_buf(KGRN"%s\n"RESET, a); 749 | } 750 | #endif 751 | 752 | print_header(new_area); 753 | print_body(new_area); 754 | 755 | #ifdef OPTS_SUP 756 | if (!printRaw) { 757 | push_to_buf(KGRN"%s\n"RESET, a); 758 | } 759 | #endif 760 | } 761 | --------------------------------------------------------------------------------