/* Document editing engine * * The document editing engine takes an input document and performs an editing * operation to produce an output document. This engine could be used in a * text editor or diff tool. * * Only insert and delete operations are defined in order to keep this exercise * short. * * The insert operation works as follows: * * void doc_insert(Document *doc, * const Location *pos, * char *str); * * The delete operation works as follows: * * void doc_delete(Document *doc, * const Location *start, * const Location *end); * * You will need to implement init and finish functions as well: * * void doc_init(Document *doc); * * int doc_finish(Document *doc, * char **str); * * For example: * * struct Document doc; * char *str; * * doc_init(&doc); * * doc_insert(&doc, * &(Location){ .lineno = 1, .charno = 1 }, * "Hello\nworld\n"); * * doc_delete(&doc, * &(Location){ .lineno = 1, .charno = 4 }, * &(Location){ .lineno = 2, .charno = 4 }); * * if (doc_finish(&doc, &str) < 0) { * fprintf(stderr, "error"); * } else { * printf("%s", str); * } * * The output is: * * Held * * Your task is to implement the operations. Note that nor doc_insert() nor * doc_delete() are able to return an error because of their return type is * void. Therefore, if an error occurs, they note that in passed document and * the error is returned in doc_finish() then. * * You may assume that caller plays it nicely and will not call doc_insert() or * doc_delete() over uninitialized Document structure. Nor they will call any * other doc_*() API after doc_finish(). */ #include #include #include /* TODO You may include C Standard Library headers and define additional * structs. */ /** * Location in a document * * For example: * * hello * world * ^ * * This location is { .lineno = 2, .charno = 4 }. Note that both line and * character numbers start at 1. */ typedef struct { unsigned int lineno; /* Line number */ unsigned int charno; /* Character number */ } Location; /** * Document structure * * You may change this structure as you please. */ typedef struct{ /* TODO */ } Document; /** * doc_init: * @doc: document to initialize * * Initialize document structure. */ void doc_init(Document *doc) { /* TODO * Your code here. Feel free to write helper functions, too. * * Do not change this function's prototype, the arguments and return type * are fixed. */ } /** * doc_insert: * @doc: document to insert to * @pos: start * @str: string to * * Insert whole string @str into document @doc starting from position @start. * * For example: * * Hello world * This is a test * * inserting "ABC" at {1, 1} will result in: * * ABCHello world * This is a test * * However, inserting "X\nY\nZ\n" at {1, 11} will result in: * * Hello worlX * Y * Z * d * This is a test * * As can be seen if @str contains new line characters, those are copied too and * expands line count for @doc. * * Inserting at non-existent position results in error, except for the position * {1, 1} when the document is empty. */ void doc_insert(Document *doc, const Location *pos, const char *str) { /* TODO * Your code here. Feel free to write helper functions, too. * * Do not change this function's prototype, the arguments and return type * are fixed. */ } /** * doc_delete: * @doc: input document * @start: start of range (inclusive) * @end: end of range (inclusive) * @errmsg: filled in if there was an error (must be freed with free(3)) * * Delete characters from @start to @end (inclusive). * * For example: * * Hello world * This is a test * * To delete the 'H' use start={1, 1}, end={1, 1}. * * To delete " world" use start={1, 6}, end={1, 11}. * * To empty the second line use start={2, 1}, end={2, 14}. This does not * delete the newline character at the end of the second line. * * To delete the second line use start={2, 1}, end={2, 15}. This includes the * newline character at the end of the line. * * To join the lines to "Hello worldThis is a test" use start={1, 12}, * end={1, 12}. */ void doc_delete(Document *doc, const Location *start, const Location *end) { /* TODO * Your code here. Feel free to write helper functions, too. * * Do not change this function's prototype, the arguments and return type * are fixed. */ } /** * doc_finish: * @doc: input document * @str: string where to store content * * Get document content and store it in @str. * * After all editing operations have been done, this function will dump document * content and store it in @str. It is caller's responsibility to free @str when * no longer needed. * * If there has been an error in any previous doc_*() call, this should return * -1. * * Returns: 0 on success, * -1 on error */ int doc_finish(Document *doc, char **str) { /* TODO * Your code here. Feel free to write helper functions, too. * * Do not change this function's prototype, the arguments and return type * are fixed. */ return 0; } /* === DO NOT MODIFY ANYTHING BELOW THIS LINE === */ /* except for adding a test case */ enum { OP_INSERT, OP_DELETE }; typedef struct { int op; const char *str; const Location start; const Location end; } test_op; static void doc_test(void *doc, test_op *ops[]) { size_t i; if (!ops) return; for (i = 0; ops[i]; i++) { test_op *o = ops[i]; if (o->op == OP_INSERT) doc_insert(doc, &o->start, o->str); else if (o->op == OP_DELETE) doc_delete(doc, &o->start, &o->end); else { fprintf(stderr, "Unknown operation %d", o->op); abort(); } } } /* Test harness */ int main(int argc, char **argv) { int ret = EXIT_SUCCESS; size_t testcnt = 1; #define DO_TEST(exp_str, ...) \ do { \ Document doc; \ int rv; \ char *str = NULL; \ test_op *ops[] = {__VA_ARGS__, NULL}; \ \ doc_init(&doc); \ doc_test(&doc, ops); \ rv = doc_finish(&doc, &str); \ if (rv < 0) { \ fprintf(stderr, "Test %zu failed: %d\n", testcnt, rv); \ ret = EXIT_FAILURE; \ } else { \ if (!str || strcmp(exp_str, str) != 0) { \ fprintf(stderr, \ "Test %zu failed. Expected string \"%s\", returned \"%s\"\n", \ testcnt, exp_str, str); \ ret = EXIT_FAILURE; \ } else { \ printf("Test %zu succeeded\n", testcnt); \ } \ } \ free(str); \ testcnt++; \ } while (0) #define INSERT(input, line, chrno) \ &(test_op) {.op = OP_INSERT, .str = input, .start = {.lineno = line, .charno = chrno}} #define DELETE(sline, scharno, eline, echarno) \ &(test_op) {.op = OP_DELETE, .start = {.lineno = sline, .charno = scharno}, \ .end = {.lineno = eline, .charno = echarno}} DO_TEST("Hello world!", INSERT("Hello ", 1, 1), INSERT("world!", 1, 7)); DO_TEST("Hello\nworld!", INSERT("Hello\n", 1, 1), INSERT("world!", 2, 1)); DO_TEST("world!Hello ", INSERT("Hello ", 1, 1), INSERT("world!", 1, 1)); DO_TEST("Hello world!", INSERT("Hel", 1, 1), INSERT("world!", 1, 4), INSERT("lo ", 1, 4)); DO_TEST("Held\n", INSERT("Hello\nworld\n", 1, 1), DELETE(1, 4, 2, 4)); DO_TEST("ello world\n", INSERT("Hello world\n", 1, 1), DELETE(1, 1, 1, 1)); DO_TEST("Hello\n", INSERT("Hello world\n", 1, 1), DELETE(1, 6, 1, 11)); DO_TEST("Hello world\n\n", INSERT("Hello world\nThis is a test\n", 1, 1), DELETE(2, 1, 2, 14)); DO_TEST("Hello worldThis is a test\n", INSERT("Hello world\nThis is a test\n", 1, 1), DELETE(1, 12, 1, 12)); DO_TEST("Hend\n", INSERT("Hello\nmiddle\nend\n", 1, 1), DELETE(1, 3, 3, 1)); return ret; }