Implement incoming links
This commit is contained in:
parent
c7e413c6e5
commit
046e31f91e
|
@ -12,7 +12,7 @@ over explain every line of code.
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
- [x] Rendering wikilinks
|
- [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]]`)
|
- [ ] Named wikilinks (i.e. `[[link title|actual page]]`)
|
||||||
|
|
||||||
## installation
|
## installation
|
||||||
|
|
106
calathea.c
106
calathea.c
|
@ -11,6 +11,7 @@ struct Page {
|
||||||
char title[80];
|
char title[80];
|
||||||
char *permalink;
|
char *permalink;
|
||||||
struct Page *next;
|
struct Page *next;
|
||||||
|
struct PageList *incoming;
|
||||||
char *content;
|
char *content;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -93,6 +94,44 @@ struct Page * map_get(struct Page **map, int mapSize, char title[]) {
|
||||||
return page;
|
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 ***/
|
/*** Templating ***/
|
||||||
char * substitute_string(char dest[], char sub[], char *start, char *end) {
|
char * substitute_string(char dest[], char sub[], char *start, char *end) {
|
||||||
int startIndex = start - dest;
|
int startIndex = start - dest;
|
||||||
|
@ -100,7 +139,7 @@ char * substitute_string(char dest[], char sub[], char *start, char *end) {
|
||||||
int newLength = strlen(dest) - (end - start) + strlen(sub) + 1;
|
int newLength = strlen(dest) - (end - start) + strlen(sub) + 1;
|
||||||
|
|
||||||
char *compiled = malloc(newLength * sizeof(char));
|
char *compiled = malloc(newLength * sizeof(char));
|
||||||
compiled[0] = 0;
|
memset(compiled, 0, newLength * sizeof(char));
|
||||||
strncpy(compiled, dest, startIndex);
|
strncpy(compiled, dest, startIndex);
|
||||||
strcat(compiled, sub);
|
strcat(compiled, sub);
|
||||||
strcat(compiled, end);
|
strcat(compiled, end);
|
||||||
|
@ -111,6 +150,7 @@ char * substitute_string(char dest[], char sub[], char *start, char *end) {
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
char pagesLocation[256] = "./pages";
|
char pagesLocation[256] = "./pages";
|
||||||
int mapSize = 1000;
|
int mapSize = 1000;
|
||||||
|
int initialInboundCapacity = 2;
|
||||||
char templateFileName[256] = "./template.html";
|
char templateFileName[256] = "./template.html";
|
||||||
char outputDirectoryName[256] = "./build";
|
char outputDirectoryName[256] = "./build";
|
||||||
|
|
||||||
|
@ -137,6 +177,11 @@ int main(int argc, char *argv[]) {
|
||||||
i++;
|
i++;
|
||||||
strcpy(outputDirectoryName, argv[i]);
|
strcpy(outputDirectoryName, argv[i]);
|
||||||
}
|
}
|
||||||
|
} else if (strcmp(argv[i], "--incoming-cap") == 0) {
|
||||||
|
if (i + 1 < argc) {
|
||||||
|
i++;
|
||||||
|
initialInboundCapacity = atoi(argv[i]);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
printf("Unknown argument: %s\n", argv[i]);
|
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
|
// Build the page's permalink
|
||||||
currentPage->permalink = malloc(strlen(fileBasename) + 6);
|
currentPage->permalink = malloc(strlen(fileBasename) + 6);
|
||||||
|
|
||||||
|
@ -274,7 +321,7 @@ int main(int argc, char *argv[]) {
|
||||||
|
|
||||||
free(createOutputDir);
|
free(createOutputDir);
|
||||||
|
|
||||||
/*** Page Processing ***/
|
/*** Link Processing ***/
|
||||||
currentPage = firstPage;
|
currentPage = firstPage;
|
||||||
while (currentPage != NULL) {
|
while (currentPage != NULL) {
|
||||||
// Scan the file for links
|
// Scan the file for links
|
||||||
|
@ -298,6 +345,7 @@ int main(int argc, char *argv[]) {
|
||||||
title[linkLength - 2] = 0;
|
title[linkLength - 2] = 0;
|
||||||
|
|
||||||
struct Page *linkedPage = map_get(pageMap, mapSize, title);
|
struct Page *linkedPage = map_get(pageMap, mapSize, title);
|
||||||
|
|
||||||
char *compiledLink;
|
char *compiledLink;
|
||||||
|
|
||||||
if (linkedPage == NULL) {
|
if (linkedPage == NULL) {
|
||||||
|
@ -307,6 +355,7 @@ int main(int argc, char *argv[]) {
|
||||||
strcat(compiledLink, title);
|
strcat(compiledLink, title);
|
||||||
strcat(compiledLink, "</a>\0");
|
strcat(compiledLink, "</a>\0");
|
||||||
} else {
|
} else {
|
||||||
|
page_list_insert(linkedPage->incoming, currentPage);
|
||||||
compiledLink = malloc(strlen(title) + strlen(linkedPage->permalink) + 5);
|
compiledLink = malloc(strlen(title) + strlen(linkedPage->permalink) + 5);
|
||||||
strcpy(compiledLink, "[");
|
strcpy(compiledLink, "[");
|
||||||
strcat(compiledLink, title);
|
strcat(compiledLink, title);
|
||||||
|
@ -331,6 +380,12 @@ int main(int argc, char *argv[]) {
|
||||||
nextLinkStart = strstr(currentPage->content, "[[");
|
nextLinkStart = strstr(currentPage->content, "[[");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
currentPage = currentPage->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*** Rendering ***/
|
||||||
|
currentPage = firstPage;
|
||||||
|
while (currentPage != NULL) {
|
||||||
// Compile the markdown
|
// Compile the markdown
|
||||||
currentPage->content = cmark_markdown_to_html(
|
currentPage->content = cmark_markdown_to_html(
|
||||||
currentPage->content,
|
currentPage->content,
|
||||||
|
@ -338,14 +393,53 @@ int main(int argc, char *argv[]) {
|
||||||
(1 << 17) // Disables safe mode, allowing raw HTML
|
(1 << 17) // Disables safe mode, allowing raw HTML
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Insert the content into the template
|
||||||
|
char *renderedPage = NULL;
|
||||||
char *templateTagStart = strstr(templateContent, "{{content}}");
|
char *templateTagStart = strstr(templateContent, "{{content}}");
|
||||||
char *renderedPageContent = substitute_string(
|
char *renderedWithContent = substitute_string(
|
||||||
templateContent,
|
templateContent,
|
||||||
currentPage->content,
|
currentPage->content,
|
||||||
templateTagStart,
|
templateTagStart,
|
||||||
templateTagStart + 11
|
templateTagStart + 11
|
||||||
);
|
);
|
||||||
|
|
||||||
|
renderedPage = renderedWithContent;
|
||||||
|
|
||||||
|
char *incomingTagStart = strstr(renderedPage, "{{incoming}}");
|
||||||
|
if (incomingTagStart != NULL) {
|
||||||
|
// Build the incoming links list
|
||||||
|
int incomingListSize = 37; // <ul class="calathea-incoming">\n</ul>\n
|
||||||
|
for (int i = 0; i < currentPage->incoming->length; i++) {
|
||||||
|
// ` <li><a href=\"[permalink]\">[title]</a></li>\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, "<ul class=\"calathea-incoming\">\n");
|
||||||
|
for (int i = 0; i < currentPage->incoming->length; i++) {
|
||||||
|
struct Page *page = currentPage->incoming->pages[i];
|
||||||
|
strcat(incomingLinksList, " <li><a href=\"");
|
||||||
|
strcat(incomingLinksList, page->permalink);
|
||||||
|
strcat(incomingLinksList, "\">");
|
||||||
|
strcat(incomingLinksList, page->title);
|
||||||
|
strcat(incomingLinksList, "</a></li>\n");
|
||||||
|
}
|
||||||
|
strcat(incomingLinksList, "</ul>\n");
|
||||||
|
|
||||||
|
char *renderedWithInbound = substitute_string(
|
||||||
|
renderedPage,
|
||||||
|
incomingLinksList,
|
||||||
|
incomingTagStart,
|
||||||
|
incomingTagStart + 12
|
||||||
|
);
|
||||||
|
|
||||||
|
renderedPage = renderedWithInbound;
|
||||||
|
free(renderedWithContent);
|
||||||
|
}
|
||||||
|
|
||||||
// Output the page to the file
|
// Output the page to the file
|
||||||
char *outputFileName = malloc(
|
char *outputFileName = malloc(
|
||||||
(strlen(outputDirectoryName)
|
(strlen(outputDirectoryName)
|
||||||
|
@ -358,23 +452,25 @@ int main(int argc, char *argv[]) {
|
||||||
FILE *outputFile = fopen(outputFileName, "w");
|
FILE *outputFile = fopen(outputFileName, "w");
|
||||||
|
|
||||||
if (outputFile != NULL) {
|
if (outputFile != NULL) {
|
||||||
fputs(renderedPageContent, outputFile);
|
fputs(renderedPage, outputFile);
|
||||||
fclose(outputFile);
|
fclose(outputFile);
|
||||||
} else {
|
} else {
|
||||||
printf("Warning: failed to create %s\n", outputFileName);
|
printf("Warning: failed to create %s\n", outputFileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
free(outputFileName);
|
free(outputFileName);
|
||||||
free(renderedPageContent);
|
free(renderedPage);
|
||||||
|
|
||||||
currentPage = currentPage->next;
|
currentPage = currentPage->next;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*** Deallocation and whatnot ***/
|
/*** Deallocation and whatnot ***/
|
||||||
currentPage = firstPage;
|
currentPage = firstPage;
|
||||||
while (currentPage != NULL) {
|
while (currentPage != NULL) {
|
||||||
free(currentPage->content);
|
free(currentPage->content);
|
||||||
free(currentPage->permalink);
|
free(currentPage->permalink);
|
||||||
|
page_list_free(currentPage->incoming);
|
||||||
struct Page *next = currentPage->next;
|
struct Page *next = currentPage->next;
|
||||||
free(currentPage);
|
free(currentPage);
|
||||||
currentPage = next;
|
currentPage = next;
|
||||||
|
|
Loading…
Reference in New Issue