Note
The spec has been updated with additional instructions on the link file format and line parsing. Please review the Link File Format and Line Parsing section below.
Overview¶
For this homework, you will be doing the reverse of the previous homework: implementing the interface. You should already be generally familiar with the interface from writing tests for it in the previous homework.
In this assignment you will use dynamic memory allocation and structs to create data structures in C. You have been given some skeleton code in the following files:
Article.cWiki.c
You should implement all of the functions listed in the header files.
You can refer back to the HW4 spec for more context.
There is also a helper function provided to you called StrDup(), which you may find useful.
The provided Makefile will build a test suite against the tests you wrote in the previous HW,
assuming that it’s still in the same location.
Link File Format and Line Parsing¶
One of the functions you need to implement for Article is NewArticleFromLinkFile. This function initializes an article from a link file. The file contains zero or more lines of text. Links within each line use the following format:
[[<link_name>]]
[[<link_name>|<display_name>]]
link_name is the name of the article being linked to. If the |<display_name> portion is omitted, the display name is the same as the link name.
A line may contain multiple links. Text outside of [[...]] link markup should be ignored.
A link:
- begins with [[;
- ends at the first ]] following the opening [[;
- if it contains a pipe (|), the text before the first pipe is the link_name, and the text after the pipe is the display_name;
- if it does not contain a pipe, the link_name should also be used as the display_name.
For example, given the following lines:
[[Apple]] and [[Orange|orange fruit]]
the parser should add the following two links:
Link: Apple
Display: Apple
Link: Orange
Display: orange fruit
Note that malformed or incomplete link does not need to be treated as an error. For example, if a line contains [[ without a matching ]], the parser may stop processing that line and end as that.
File Parsing Skeleton Code¶
In this example implementation of NewArticleFromLinkFile(), we use two helper functions that have not been provided for you, so you’ll have to implement them. One is BasenameNoExt() which turns Article_Title.links into Article_Title. The other is ParseLineLinks(), which will perform the kind of parsing described in the above paragraph. We didn’t go into much detail about file parsing in the C standard library in CSE 374, so we are providing some code that demonstrates how to use fopen(), fgets(), and fclose().
It is entirely possible to implement this in a different way, and you don’t have to create separate helper functions like the ones we demonstrate here, but this is a good way to modularize your code and make it easier to read.
Article* NewArticleFromLinkFile(const char* filename) {
FILE* in;
char* title;
Article* article;
char line[4096];
if (filename == NULL) {
return NULL;
}
in = fopen(filename, "r");
if (in == NULL) {
return NULL;
}
title = BasenameNoExt(filename);
if (title == NULL) {
fclose(in);
return NULL;
}
article = NewArticle(title);
free(title);
if (article == NULL) {
fclose(in);
return NULL;
}
while (fgets(line, sizeof(line), in) != NULL) {
if (!ParseLineLinks(article, line)) {
fclose(in);
FreeArticle(article);
return NULL;
}
}
fclose(in);
return article;
}
Slugify Skeleton Code¶
To make things easier, we are also providing a helper function to “slugify” an article title, similar to what you did in Bash in a previous HW. Again, this is just one implementation, and your code doesn’t have to look exactly like this, but there are some places where you might need to perform this action, so this kind of helper function would be useful. The keyword static in a function name just means that it
is a helper function, so it should be treated as “private”, but it’s not necessary.
static char* Slugify(const char* text) {
size_t n;
char* slug;
size_t out_i;
bool prev_dash;
if (text == NULL) {
return NULL;
}
n = strlen(text);
slug = malloc(n + 1);
if (slug == NULL) {
return NULL;
}
out_i = 0;
prev_dash = false;
for (size_t i = 0; i < n; i++) {
unsigned char c = (unsigned char)text[i];
if (isalnum(c) != 0) {
slug[out_i++] = (char)tolower(c);
prev_dash = false;
} else if (isspace(c) != 0 || c == '_' || c == '-') {
if (!prev_dash && out_i > 0) {
slug[out_i++] = '-';
prev_dash = true;
}
}
}
while (out_i > 0 && slug[out_i - 1] == '-') {
out_i--;
}
slug[out_i] = '\0';
return slug;
}