diff --git a/README.md b/README.md index 2db4085..13740c4 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ over explain every line of code. ## Features - [x] Rendering wikilinks -- [ ] Ability to keep track of incoming links +- [x] Ability to keep track of incoming links - [ ] Named wikilinks (i.e. `[[link title|actual page]]`) ## installation diff --git a/calathea.c b/calathea.c index 6f7ad7c..be98934 100644 --- a/calathea.c +++ b/calathea.c @@ -11,6 +11,7 @@ struct Page { char title[80]; char *permalink; struct Page *next; + struct PageList *incoming; char *content; }; @@ -93,14 +94,52 @@ struct Page * map_get(struct Page **map, int mapSize, char title[]) { return page; } +/*** Dynamic Array Implementation ***/ +struct PageList { + struct Page **pages; + int length; + int capacity; +}; + +struct PageList * page_list(int capacity) { + struct PageList *pageList = malloc(sizeof(struct PageList)); + pageList->pages = malloc(sizeof(struct Page *) * capacity); + pageList->capacity = capacity; + pageList->length = 0; + + return pageList; +} + +void page_list_insert(struct PageList *list, struct Page *page) { + // If the page is already present in the list, we don't bother adding it again + for (int i = 0; i < list->length; i++) { + if (page == list->pages[i]) { + return; + } + } + + // Increase the length of the array if necessary + if (list->length >= list->capacity) { + list->pages = realloc(list->pages, (list->capacity + 1)*sizeof(struct Page *)); + } + + list->pages[list->length] = page; + list->length++; +} + +void page_list_free(struct PageList *list) { + free(list->pages); + free(list); +} + /*** Templating ***/ char * substitute_string(char dest[], char sub[], char *start, char *end) { int startIndex = start - dest; int newLength = strlen(dest) - (end - start) + strlen(sub) + 1; - char * compiled = malloc(newLength * sizeof(char)); - compiled[0] = 0; + char *compiled = malloc(newLength * sizeof(char)); + memset(compiled, 0, newLength * sizeof(char)); strncpy(compiled, dest, startIndex); strcat(compiled, sub); strcat(compiled, end); @@ -111,6 +150,7 @@ char * substitute_string(char dest[], char sub[], char *start, char *end) { int main(int argc, char *argv[]) { char pagesLocation[256] = "./pages"; int mapSize = 1000; + int initialInboundCapacity = 2; char templateFileName[256] = "./template.html"; char outputDirectoryName[256] = "./build"; @@ -137,6 +177,11 @@ int main(int argc, char *argv[]) { i++; strcpy(outputDirectoryName, argv[i]); } + } else if (strcmp(argv[i], "--incoming-cap") == 0) { + if (i + 1 < argc) { + i++; + initialInboundCapacity = atoi(argv[i]); + } } else { printf("Unknown argument: %s\n", argv[i]); } @@ -194,6 +239,8 @@ int main(int argc, char *argv[]) { } } + currentPage->incoming = page_list(initialInboundCapacity); + // Build the page's permalink currentPage->permalink = malloc(strlen(fileBasename) + 6); @@ -274,7 +321,7 @@ int main(int argc, char *argv[]) { free(createOutputDir); - /*** Page Processing ***/ + /*** Link Processing ***/ currentPage = firstPage; while (currentPage != NULL) { // Scan the file for links @@ -298,6 +345,7 @@ int main(int argc, char *argv[]) { title[linkLength - 2] = 0; struct Page *linkedPage = map_get(pageMap, mapSize, title); + char *compiledLink; if (linkedPage == NULL) { @@ -307,6 +355,7 @@ int main(int argc, char *argv[]) { strcat(compiledLink, title); strcat(compiledLink, "\0"); } else { + page_list_insert(linkedPage->incoming, currentPage); compiledLink = malloc(strlen(title) + strlen(linkedPage->permalink) + 5); strcpy(compiledLink, "["); strcat(compiledLink, title); @@ -331,6 +380,12 @@ int main(int argc, char *argv[]) { nextLinkStart = strstr(currentPage->content, "[["); } + currentPage = currentPage->next; + } + + /*** Rendering ***/ + currentPage = firstPage; + while (currentPage != NULL) { // Compile the markdown currentPage->content = cmark_markdown_to_html( currentPage->content, @@ -338,19 +393,58 @@ int main(int argc, char *argv[]) { (1 << 17) // Disables safe mode, allowing raw HTML ); + // Insert the content into the template + char *renderedPage = NULL; char *templateTagStart = strstr(templateContent, "{{content}}"); - char *renderedPageContent = substitute_string( - templateContent, - currentPage->content, - templateTagStart, - templateTagStart + 11 + char *renderedWithContent = substitute_string( + templateContent, + currentPage->content, + templateTagStart, + templateTagStart + 11 ); + renderedPage = renderedWithContent; + + char *incomingTagStart = strstr(renderedPage, "{{incoming}}"); + if (incomingTagStart != NULL) { + // Build the incoming links list + int incomingListSize = 37; // \n + for (int i = 0; i < currentPage->incoming->length; i++) { + // `
  • [title]
  • \n` + struct Page *page = currentPage->incoming->pages[i]; + incomingListSize += 27 + strlen(page->title) + strlen(page->permalink); + } + + char *incomingLinksList = malloc((incomingListSize + 1) * sizeof(char)); + memset(incomingLinksList, 0, (incomingListSize + 1) * sizeof(char)); + + strcpy(incomingLinksList, "\n"); + + char *renderedWithInbound = substitute_string( + renderedPage, + incomingLinksList, + incomingTagStart, + incomingTagStart + 12 + ); + + renderedPage = renderedWithInbound; + free(renderedWithContent); + } + // Output the page to the file char *outputFileName = malloc( - (strlen(outputDirectoryName) - + strlen(currentPage->permalink) - + 2)); + (strlen(outputDirectoryName) + + strlen(currentPage->permalink) + + 2)); strcpy(outputFileName, outputDirectoryName); strcat(outputFileName, "/\0"); strcat(outputFileName, currentPage->permalink); @@ -358,23 +452,25 @@ int main(int argc, char *argv[]) { FILE *outputFile = fopen(outputFileName, "w"); if (outputFile != NULL) { - fputs(renderedPageContent, outputFile); + fputs(renderedPage, outputFile); fclose(outputFile); } else { printf("Warning: failed to create %s\n", outputFileName); } free(outputFileName); - free(renderedPageContent); + free(renderedPage); currentPage = currentPage->next; } + /*** Deallocation and whatnot ***/ currentPage = firstPage; while (currentPage != NULL) { free(currentPage->content); free(currentPage->permalink); + page_list_free(currentPage->incoming); struct Page *next = currentPage->next; free(currentPage); currentPage = next;